2

On my SceneKit node, I want to apply a frosted glass effect like this:

https://www.shadertoy.com/view/WdSGz1

This shader takes a texture as input and outputs a "blurry" version of the texture. What I want is to create a SCNProgram() and apply it to the material of one my node so that it takes this frosted glass texture.

I tried reproducing this with a metal shader but I am not very familiar with how it works. This is what I wrote:

#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

struct NodeBuffer {
    float4x4 modelTransform;
    float4x4 modelViewProjectionTransform;
    float4x4 modelViewTransform;
    float4x4 normalTransform;
    float2x3 boundingBox;
};

struct MyNodeBuffer {
    float4x4 modelTransform;
    float4x4 inverseModelTransform;
    float4x4 modelViewTransform;
    float4x4 inverseModelViewTransform;
    float4x4 normalTransform;
    float4x4 modelViewProjectionTransform;
    float4x4 inverseModelViewProjectionTransform;
};

struct VertexInput {
    float3 position  [[attribute(SCNVertexSemanticPosition)]];
    float2 uv [[attribute(SCNVertexSemanticTexcoord0)]];
};

struct VertexOut {
    float4 position [[position]];
    float2 uv;
};

vertex VertexOut vertexFunction(VertexInput in [[ stage_in ]], constant NodeBuffer& scn_node [[buffer(1)]]) {
    VertexOut out;
    out.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    out.uv = in.uv;
    return out;
};

float stepfun(float x) {
    return (sign(x) + 1.0) / 2.0;
}

float square(float2 pos) {
    return (stepfun(pos.x + 1.0) * stepfun(1.0 - pos.x)) *
        (stepfun(pos.y + 1.0) * stepfun(1.0 - pos.y));
};

constexpr sampler textureSampler(coord::normalized, filter::linear, address::repeat);

float2 dist(float2 pos, texture2d<float,access::sample> texture [[ texture(0) ]])
{
    float2 offset = pos;
    return pos + square((offset - 0.5) * 3.0) * texture.sample(textureSampler, (offset - 0.5) * 5.0).xy * 0.05;
}

fragment float4 fragmentFunction(VertexOut out [[ stage_in ]],
                            texture2d<float, access::sample> texture [[ texture(0) ]],
                            device float3 *resolution [[ buffer(0) ]])
{
    float2 uv = out.uv / resolution[0].xy;
    float4 tex = texture.sample(textureSampler, dist(uv, texture));
    tex.a = 0.5;
    return tex;
}

However, I need to give a texture as input to my fragmentFunction and I don't know what this is supposed to be. I would like it to be the texture my node would have without the shader.

Bael
  • 23
  • 4
  • What ever you do, in Scenekit using a SCNProgram you will not see any Node Object behind your glass. It will just disappear. The only exeption would be a Skybox – ZAY Jan 12 '23 at 16:51
  • here is a guidance to implement a SCNProgram with Scenekit: https://stackoverflow.com/questions/31276054/is-refraction-in-scenekit-possible – ZAY Jan 12 '23 at 18:16
  • Hi Zay, thank you for your answer. What do you mean it will just disappear ? – Bael Jan 13 '23 at 07:12
  • any SCNNode Object behind the semi transparent Node (the Node that is rendered by a SCNProgram) will not be visible in renderer or just partially on the edges – ZAY Jan 13 '23 at 07:17
  • I don't get what a skybox is in your other answer. What are the 6 cube maps images that you use as skybox1, 2 ... 6 ? And in my case what should I use ? Thank you again for your quick answers – Bael Jan 13 '23 at 07:59
  • as you can see the background (the uffizi galleria) in my other article metioned, this is a composition of 6 images that build a 360 degrees background around the scene. it is set as the background of the scene. It is not a SCNNode. but assuming you have a SCNPlane with your SCNProgram attached to it, and behind that node (from the point of view) you have another SCNNode like a SCNBox, or a SCNSphere or any other geometry model, it will just disappear from the rendered image. I can hardly explain it in other words. I will try, if I can make a Test using your shader. – ZAY Jan 13 '23 at 08:07
  • I prepared a small project for you (https://drive.google.com/file/d/1dAi_L6Xhgz68cAk8PWc950OASDSTXHBe/view?usp=share_link) It uses my refraction shader and you will probably see what I mean, if you run it. I also tried to run your frost shader, but with no luck. Let me know what you think. – ZAY Jan 13 '23 at 08:44
  • Thank you very much ZAY, it's very helpful. Apart from shaders, is there another way to produce refraction on a material ? To see other nodes through the texture ? – Bael Jan 13 '23 at 10:04
  • Please have a look at my answer – ZAY Jan 13 '23 at 10:53

1 Answers1

1

Well, I have probably found something similar.

Try to use a Normal Map, that is kind alike to some frost effect.

func foregroundObjectFrostNormal() {
    
    let plane = SCNPlane(width: 3.0, height: 3.0) // SCNSphere(radius: 1.5)
    
    plane.firstMaterial?.diffuse.contents   = UIColor.lightGray
    plane.firstMaterial?.normal.contents    = UIImage.init(named: "art.scnassets/frost_normal/frost_long.png")
    plane.firstMaterial?.roughness.contents = 0.1
    plane.firstMaterial?.metalness.contents = 0.0
    plane.firstMaterial?.transparency = 0.25
    plane.firstMaterial?.lightingModel = .physicallyBased
    
    let node = SCNNode(geometry: plane)
    node.position = SCNVector3(0.0, 0.0, +3.0)
    scene.rootNode.addChildNode(node)
    
}

simulated frost

Here is a project, you can see the effect and play around with: https://drive.google.com/file/d/1mCZ2OrxkCENO6TMeQe5HctyAdpgz5gv8/view?usp=share_link

ZAY
  • 3,882
  • 2
  • 13
  • 21
  • That's great ! It's close to what I want, how did you find the frost_long image ? I would like to have something closer to this: https://gustoemergencyglassreplacement.com.au/wp-content/uploads/2022/01/frosted-glass.jpg – Bael Jan 13 '23 at 11:56
  • I did a quick research with google. You will find a better normal map with another granulation, that meets your needs. Try to search for a seamless normal map, which you can endless scale on a Plane. (the long map I added you only need with a SCNSphere). Use resolutions of 1024x1024 or lower, and always as a power of two, like 256x256 / 512x512 etc... – ZAY Jan 13 '23 at 12:08