我认为解决这个问题的方法是函数常量.这是我如何在顶点着色器中处理此问题的示例.
constant bool HasColor0 [[ function_constant(FunctionConstantHasColor0) ]];
constant bool HasNormal [[ function_constant(FunctionConstantHasNormal) ]];
constant bool HasTangent [[ function_constant(FunctionConstantHasTangent) ]];
constant bool HasTexCoord0 [[ function_constant(FunctionConstantHasTexCoord0) ]];
constant bool AlphaMask [[ function_constant(FunctionConstantAlphaMask) ]];
// ...
struct VertexIn
{
float3 position [[ attribute(AttributeBindingPosition) ]];
float3 normal [[ attribute(AttributeBindingNormal), function_constant(HasNormal) ]];
float4 tangent [[ attribute(AttributeBindingTangent), function_constant(HasTangent) ]];
float4 color [[ attribute(AttributeBindingColor0), function_constant(HasColor0) ]];
float2 texCoord [[ attribute(AttributeBindingTexcoord0), function_constant(HasTexCoord0) ]];
};
struct VertexOut
{
float4 positionCS [[ position ]];
float4 tangentVS = float4();
float3 positionVS = float3();
float3 normalVS = float3();
float2 texCoord = float2();
half4 color = half4();
};
static VertexOut ForwardVertexImpl(Vertex in, constant CameraUniform& camera, constant MeshUniform& meshUniform)
{
VertexOut out;
float4x4 viewModel = camera.view * meshUniform.model;
float4 positionVS = viewModel * float4(in.position.xyz, 1.0);
out.positionCS = camera.projection * positionVS;
out.positionVS = positionVS.xyz;
float4x4 normalMatrix;
if(HasNormal || HasTangent)
{
normalMatrix = transpose(meshUniform.inverseModel * camera.inverseView);
}
if(HasNormal)
{
out.normalVS = (normalMatrix * float4(in.normal, 0.0)).xyz;
}
if(HasTexCoord0)
{
out.texCoord = in.texCoord;
}
if(HasColor0)
{
out.color = half4(in.color);
}
else
{
out.color = half4(1.0);
}
if(HasTangent)
{
// Normal matrix or viewmodel matrix?
out.tangentVS.xyz = (normalMatrix * float4(in.tangent.xyz, 0.0)).xyz;
out.tangentVS.w = in.tangent.w;
}
return out;
}
vertex VertexOut ForwardVertex(
VertexIn in [[ stage_in ]],
constant CameraUniform& camera [[ buffer(BufferBindingCamera) ]],
constant MeshUniform& meshUniform [[ buffer(BufferBindingMesh) ]])
{
Vertex v
{
.color = in.color,
.tangent = in.tangent,
.position = in.position,
.normal = in.normal,
.texCoord = in.texCoord,
};
return ForwardVertexImpl(v, camera, meshUniform);
}
在宿主应用程序中,我根据几何体实际具有的语义填写了MTLFunctionConstantValues
个对象:
func addVertexDescriptorFunctionConstants(toConstantValues values: MTLFunctionConstantValues) {
var unusedSemantics = Set<AttributeSemantic>(AttributeSemantic.allCases)
for attribute in attributes.compactMap({ $0 }) {
unusedSemantics.remove(attribute.semantic)
if let constant = attribute.semantic.functionConstant {
values.setConstantValue(true, index: constant)
}
}
for unusedSemantic in unusedSemantics {
if let constant = unusedSemantic.functionConstant {
values.setConstantValue(false, index: constant)
}
}
}
它的一个好处是,编译器应该将这些函数常量if
s转换为没有分支的代码,所以在运行时这不应该是一个问题,这使您可以脱机编译着色器,而不必使用联机编译和定义.