Metal/SceneKit自定义片段着色器下纹理无法重复且contentsTransform无效问题
问题原因
SceneKit 仅对内置材质槽(如diffuse、normal等)自动应用SCNMaterialProperty的contentsTransform变换,自定义着色器传入的纹理不会自动处理坐标变换,需要手动实现。
1. 让contentsTransform生效的修改方案
需要手动将纹理变换矩阵作为Uniform传入着色器,在采样前对纹理坐标做变换:
着色器修改后代码
fragment float4 bFragment( VertexOut vertexOut [[stage_in]], texture2d<float, access::sample> textureInput [[texture(0)]], constant float4x4 &textureTransform [[buffer(0)]]) { constexpr sampler sampler2d(coord::normalized, address::repeat, filter::linear); // 对纹理坐标应用contentsTransform变换 float4 transformedUV = textureTransform * float4(vertexOut.texCoord.x, vertexOut.texCoord.y, 0.0, 1.0); float4 outputColor = textureInput.sample(sampler2d, transformedUV.xy); return float4(outputColor.rgb , 1.0); }
Swift端新增代码
原有纹理配置不变,新增一行传递变换矩阵的逻辑:
// 原有配置 let imageProperty = SCNMaterialProperty(contents: texture) imageProperty.wrapS = .repeat imageProperty.wrapT = .repeat imageProperty.contentsTransform = SCNMatrix4MakeScale(screenRatio * numberOfRepetitionsOnX, numberOfRepetitionsOnX , 1) node.geometry!.firstMaterial!.setValue(imageProperty, forKey: "textureInput") // 新增:将变换矩阵转为Metal可用格式传入着色器 node.geometry!.firstMaterial?.setValue(SCNMatrix4ToMat4(imageProperty.contentsTransform), forKey: "textureTransform")
2. 更简单的实现方案(无需使用contentsTransform)
直接将重复参数传入着色器,手动缩放UV坐标即可,采样器的repeat模式会自动处理超出[0,1]区间的坐标:
着色器修改后代码
fragment float4 bFragment( VertexOut vertexOut [[stage_in]], texture2d<float, access::sample> textureInput [[texture(0)]], constant float &screenRatio [[buffer(0)]], constant float &numberOfRepetitions [[buffer(1)]]) { constexpr sampler sampler2d(coord::normalized, address::repeat, filter::linear); // 直接对UV做缩放 float2 uv = float2(vertexOut.texCoord.x * screenRatio * numberOfRepetitions, vertexOut.texCoord.y * numberOfRepetitions); float4 outputColor = textureInput.sample(sampler2d, uv); return float4(outputColor.rgb , 1.0); }
Swift端新增代码
// 原有纹理传入逻辑不变,新增参数传递 node.geometry!.firstMaterial?.setValue(screenRatio, forKey: "screenRatio") node.geometry!.firstMaterial?.setValue(numberOfRepetitionsOnX, forKey: "numberOfRepetitions")
注意事项
- 采样器必须保留
coord::normalized配置,否则标准化坐标的重复模式不会生效 - 你当前着色器硬编码了sampler的repeat模式,因此Swift端设置的
wrapS/wrapT不会生效,如需动态修改包裹模式,可以将sampler也作为参数从Swift端传入,对应配置就会自动生效
内容的提问来源于stack exchange,提问作者czane
相关产品推荐
相关产品推荐

