如何为溶解效果编写 sceneKit 着色器修改器
Posted
技术标签:
【中文标题】如何为溶解效果编写 sceneKit 着色器修改器【英文标题】:How to write a sceneKit shader modifier for a dissolve in effect 【发布时间】:2019-06-30 21:47:41 【问题描述】:我想为 Scenekit 游戏构建溶解效果。我一直在研究着色器修改器,因为它们似乎是最轻的并且在复制这种效果方面没有任何运气:
是否可以使用着色器修改器来创建这种效果? 你将如何实施一个?
【问题讨论】:
【参考方案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 参数作为材质参数公开,因为您可能希望对其进行动画处理。还有其他内部常数,例如边缘宽度和噪声比例,可以微调以获得所需的内容效果。
不同的噪波纹理会产生不同的溶解效果,因此您也可以尝试一下。我刚刚使用了这个多倍频程值噪声图像:
将图像加载为UIImage
或NSImage
,并将其设置为暴露为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)以上是关于如何为溶解效果编写 sceneKit 着色器修改器的主要内容,如果未能解决你的问题,请参考以下文章