You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 09:09:07