简体   繁体   中英

How to make a frosted glass effect on SceneKit

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.

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)
    
}

模拟霜

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM