在SceneKit的着色器修改器中访问世界空间中的z坐标

3

寻找

我遇到了一个问题,无法获取渲染像素在世界空间中的z坐标。在SceneKit中,我正在寻找一个3D平面,其渲染颜色直接与渲染点的z坐标相关。

情况

我正在使用SpriteKit,并使用SK3DNode将SceneKit场景嵌入到我的SpriteKit场景中。对于SceneKit场景,我正在使用从Blender导出的.dae Collada文件,其中包含平面网格和灯光。我正在应用着色器修改器来修改几何形状和照明模型。

  self.waterGeometry.shaderModifiers = @{
    SCNShaderModifierEntryPointGeometry : self.geomModifier,
    SCNShaderModifierEntryPointSurface : self.cellShadingModifier
  };

几何修饰器代码(self.geomModifier):
// Waves Modifier
float Amplitude = 0.02;
float Frequency = 15.0;
vec2 nrm = _geometry.position.xz;
float len = length(nrm)+0.0001; // for robustness
nrm /= len;
float a = len + Amplitude*sin(Frequency * _geometry.position.z + u_time * 1.6);
_geometry.position.xz = nrm * a;

几何修饰符将正弦变换应用于_surface属性,以模拟波浪。在下面的图像中,草图精灵是SpriteKit精灵,它们具有更高的zPosition并且不会干扰SK3DNode。请注意几何修改器的微妙波浪(z位移)的结果。
下一步,我希望基于点在世界空间中的z坐标计算输出颜色。这可能是_surface.diffuse或_output.color,这并不重要(这将意味着着色器修改器的不同插入点,但不是问题)。
我在surface修饰符(self.cellShadingModifier)中尝试了以下代码。
vec4 geometry = u_inverseViewTransform * vec4(_surface.position, 1.0);
if (geometry.y < 0.0) {
_surface.diffuse.rgb *= vec3(0.4);
}

_surface.position 是在视图空间中的,我希望通过使用 u_inverseViewTransform 将其转换为世界空间。根据苹果文档

几何字段(例如位置和法线)是在视图空间中表示的。您可以使用 SceneKit 的 uniform(例如 u_inverseViewTransform)在不同的坐标空间中操作,[...]

The 3D plane with the geometry modifier and the surface modifier

如你所见,它在闪烁,似乎并不基于我刚刚修改的 geometry.position。我已经在模拟器和设备上进行了测试(iPad Air)。我认为我犯了一个简单的错误,因为我可能混淆了 _surface_geometry 属性。

有人能告诉我在网格的当前着色点中获取 z 坐标(世界空间)的位置吗,这样我就可以在我的渲染方法中使用它?

注意

我还尝试在表面着色器修改器中访问 _geometry,但我收到错误信息 Use of undeclared identifier '_geometry',这很奇怪,因为苹果文档说:

您可以在后面的入口点中使用早期入口点定义的结构。例如,与 SCNShaderModifierEntryPointFragment 入口点相关联的代码片段可以从 SCNShaderModifierEntryPointSurface 入口点定义的 _surface 结构中读取。

注意 2

我可以让 LightingModel 着色器基于生成的正弦波进行计算(并避免搜索 z 坐标),但在将来,我可能会添加其他波,并且使用 z 坐标更易于维护,更加优雅。


1
文档说的有一半是对的。你可以在同一个着色器阶段中使用早期入口点的信息。但是几何体入口点在顶点阶段运行,表面入口点在片段阶段运行。(除非你关闭每像素照明) - rickster
1个回答

4

我还学习了如何使用着色器修饰器。针对这个问题,我有一个适合我自己的解决方案,同时使用模型变换的逆变换和视图变换的逆变换。

下面的代码将在场景中心用红色偏色绘制模型的右侧。你可以检查其他位置元素(我想是y)来获得所需的结果。

vec4 orig = _surface.diffuse;
vec4 transformed_position = u_inverseModelTransform * u_inverseViewTransform * vec4(_surface.position, 1.0);
if (transformed_position.z < 0.0) {
    _surface.diffuse = mix(vec4(1.0,0.0,0.0,1.0), orig, 0.5);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接