【问题标题】:How to write a sceneKit shader modifier for a dissolve in effect如何为溶解效果编写 sceneKit 着色器修改器
【发布时间】:2019-06-30 21:47:41
【问题描述】:

我想为 Scenekit 游戏构建溶解效果。我一直在研究着色器修改器,因为它们似乎是最轻的并且在复制这种效果方面没有任何运气:

是否可以使用着色器修改器来创建这种效果? 你将如何实施一个?

【问题讨论】:

    标签: ios swift shader scenekit metal


    【解决方案1】:

    您可以使用片段着色器修改器获得非常接近预期效果。基本做法如下:

    • 噪声纹理样本
    • 如果噪声样本低于某个阈值(我称之为“揭示”),则将其丢弃,使其完全透明
    • 否则,如果片段靠近边缘,请将其颜色替换为您喜欢的边缘颜色(或渐变)
    • 应用绽放使边缘发光

    这是执行此操作的着色器修改器代码:

    #pragma arguments
    
    float revealage;
    texture2d<float, access::sample> noiseTexture;
    
    #pragma transparent
    #pragma body
    
    const float edgeWidth = 0.02;
    const float edgeBrightness = 2;
    const float3 innerColor = float3(0.4, 0.8, 1);
    const float3 outerColor = float3(0, 0.5, 1);
    const float noiseScale = 3;
    
    constexpr sampler noiseSampler(filter::linear, address::repeat);
    float2 noiseCoords = noiseScale * _surface.ambientTexcoord;
    float noiseValue = noiseTexture.sample(noiseSampler, noiseCoords).r;
    
    if (noiseValue > revealage) {
        discard_fragment();
    }
    
    float edgeDist = revealage - noiseValue;
    if (edgeDist < edgeWidth) {
        float t = edgeDist / edgeWidth;
        float3 edgeColor = edgeBrightness * mix(outerColor, innerColor, t);
        _output.color.rgb = edgeColor;
    }
    

    请注意,revealage 参数作为材质参数公开,因为您可能希望对其进行动画处理。还有其他内部常数,例如边缘宽度和噪声比例,可以微调以获得所需的内容效果。

    不同的噪波纹理会产生不同的溶解效果,因此您也可以尝试一下。我刚刚使用了这个多倍频程值噪声图像:

    将图像加载为UIImageNSImage,并将其设置为暴露为noiseTexture 的材质属性:

    material.setValue(SCNMaterialProperty(contents: noiseImage), forKey: "noiseTexture")
    

    您需要添加光晕作为后期处理,以获得发光的电子线效果。在 SceneKit 中,这就像启用 HDR 管道并设置一些参数一样简单:

    let camera = SCNCamera()
    camera.wantsHDR = true
    camera.bloomThreshold = 0.8
    camera.bloomIntensity = 2
    camera.bloomBlurRadius = 16.0
    camera.wantsExposureAdaptation = false
    

    所有数字参数都可能需要根据您的内容进行调整。

    为了保持整洁,我更喜欢将着色器修改器保存在它们自己的文本文件中(我将我的命名为“dissolve.fragment.txt”)。以下是如何加载一些修改器代码并将其附加到材质上。

    let modifierURL = Bundle.main.url(forResource: "dissolve.fragment", withExtension: "txt")!
    let modifierString = try! String(contentsOf: modifierURL)
    material.shaderModifiers = [
        SCNShaderModifierEntryPoint.fragment : modifierString
    ]
    

    最后,为了动画效果,您可以使用 CABasicAnimation 包裹 SCNAnimation

    let revealAnimation = CABasicAnimation(keyPath: "revealage")
    revealAnimation.timingFunction = CAMediaTimingFunction(name: .linear)
    revealAnimation.duration = 2.5
    revealAnimation.fromValue = 0.0
    revealAnimation.toValue = 1.0
    let scnRevealAnimation = SCNAnimation(caAnimation: revealAnimation)
    material.addAnimation(scnRevealAnimation, forKey: "Reveal")
    

    【讨论】:

    • 沃伦这太棒了。对正在发生的事情的高级概述以及详细的实现确实非常有帮助。当您将其分解并使用带有 SCNAnimation 的 CABasicAnimation 时,这很有意义。
    • 感谢您的出色回答。我认为上面的动画代码缺少一行。确保添加revealAnimation.autoreverses = true。它将自动反转动画,您的动画将像上面一样工作。
    • 是的,为了创建上图,我使用了具有无限重复计数/重复持续时间的自动反转动画。两者都不是演示效果所必需的,所以我在代码中省略了它们。
    • 首先......这太棒了!说真的……不仅效果非常好,而且答案也非常出色!投票赞成!问题虽然......你如何检测边缘?我是否理解“你接近隐藏和揭示之间的门槛,所以这是一个边缘?”
    • 当我尝试这段代码时,即使着色器代码有这个revealage参数,它也找不到revealage keyPath。可能是什么问题呢?我这样设置平面材质。 let material = SCNMaterial() material.setValue(SCNMaterialProperty(contents: noiseImage), forKey: "noiseTexture") plane.materials = [material] 得到这个错误:[SceneKit] Error: _C3DModelPathResolverRegistryResolvePathWithClassName unknown path (revealage)
    猜你喜欢
    • 2018-03-03
    • 2017-01-17
    • 2018-03-29
    • 2021-09-16
    • 2015-02-17
    • 1970-01-01
    • 2015-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多