在 Xcode SceneKit 编辑器中,可以可视化 SCNGeoemtry 的表面法线
编辑器似乎使用了 SCNGeometrySource.Semantic 来渲染图像: https://developer.apple.com/documentation/scenekit/scngeometrysource/semantic
如何将几何体的表面法线渲染到图像?
我们来尝试一下吧。您可以通过使用
SCNProgram()
进行渲染来实现几何体的法线或类似法线的着色。这取代了 Apple 提供的整个渲染(如 constant
、phong
、blinn
或 physicallyBased
)
开始使用 Xcode 附带的默认 SceneKit 模板,即包含旋转宇宙飞船的模板。
首先,创建一个 Metal-File,并将其命名为
shaders.metal
。
将以下代码复制到此文件中。
#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>
// Nodebuffer
struct MyNodeBuffer {
// float4x4 modelTransform;
// float4x4 inverseModelTransform;
float4x4 modelViewTransform; // required
// float4x4 inverseModelViewTransform;
float4x4 normalTransform; // required
// float4x4 modelViewProjectionTransform;
// float4x4 inverseModelViewProjectionTransform;
};
// as is from StackOverflow
typedef struct {
float3 position [[ attribute(SCNVertexSemanticPosition) ]];
float3 normal [[ attribute(SCNVertexSemanticNormal) ]];
float4 color [[ attribute(SCNVertexSemanticColor) ]];
float2 texCoords [[ attribute(SCNVertexSemanticTexcoord0) ]];
} MyVertexInput;
// MARK: - Structs filled by Vertex Shader
struct SimpleVertexNormal
{
float4 position [[ position ]];
float3 normal;
};
// MARK: - Normal Shader
vertex SimpleVertexNormal myVertexNormal(MyVertexInput in [[stage_in]],
constant SCNSceneBuffer& scn_frame [[buffer(0)]],
constant MyNodeBuffer& scn_node [[buffer(1)]])
{
float4 modelSpacePosition(in.position, 1.0f);
float4 eyeSpacePosition(scn_node.modelViewTransform * modelSpacePosition);
SimpleVertexNormal out;
out.position = scn_frame.projectionTransform * eyeSpacePosition;
out.normal = in.normal;
return out;
}
fragment float4 myFragmentNormal(SimpleVertexNormal in [[stage_in]])
{
// Normal Color
float3 normal = normalize(in.normal);
float3 normalColor = float3(abs(normal));
// Final Color
float4 color = float4(normalColor, 1.0);
return color;
}
在 GameViewController 中插入以下函数,该函数会将这个着色器附加到给定的节点(您也可以全局定义 SCNProgram 并重新使用它)。
func applyNormalShader(node: SCNNode) {
let sceneProgramNormal = SCNProgram() // Metal Shader Program
sceneProgramNormal.fragmentFunctionName = "myFragmentNormal"
sceneProgramNormal.vertexFunctionName = "myVertexNormal"
node.geometry?.firstMaterial?.program = sceneProgramNormal
}
最后一步是将节点归档到该着色器。我将在这里使用默认的太空飞船。 (确保归档包含要使用该自定义着色器程序着色的几何体的确切节点。)
修改代码如下: (这里:在
viewDidLoad
)
// retrieve the ship node
let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
// animate the 3d object
ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
// apply shader
applyNormalShader(node: ship.childNodes.first!)
注意:这可能与 Xcode 预览中的不完全相同,但它应该让您了解如何继续/扩展这些内容。希望这会对您有所帮助。