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

如何实现不受缩放影响、纹理平铺尺寸恒定且随对象旋转的URP Shader?

适配纹理随模型旋转、不受缩放影响的恒定平铺Shader实现方案

核心实现逻辑

世界空间UV的问题根源是直接使用了全局坐标系的位置值,没有和模型的旋转绑定,也没有剔除缩放带来的坐标尺度变化。我们只需要构造一个仅保留模型旋转朝向、剔除缩放分量的局部坐标系,用这个坐标系下的坐标作为UV采样纹理,就能实现你要的「可拉伸砖墙」效果:

  • 模型旋转时,纹理会跟随模型朝向同步旋转
  • 模型缩放时,纹理的实际显示尺寸保持恒定,不会跟着拉伸或缩小
  • 不会出现世界空间UV强制沿全局主轴投影的错位问题

核心代码片段(以Unity URP管线为例)

// 顶点着色器输入
struct Attributes
{
    float4 positionOS   : POSITION;
    float3 normalOS     : NORMAL;
};

// 顶点到片元的传递结构
struct Varyings
{
    float4 positionHCS  : SV_POSITION;
    float3 positionWS   : TEXCOORD0;
    float3 normalWS     : TEXCOORD1;
    float3x3 noScaleModelMatrix : TEXCOORD2;
};

Varyings vert (Attributes input)
{
    Varyings output;
    // 计算裁剪空间坐标
    output.positionHCS = TransformObjectToHClip(input.positionOS.xyz);
    // 拿到世界空间位置、法线
    output.positionWS = TransformObjectToWorld(input.positionOS.xyz);
    output.normalWS = TransformObjectToWorldNormal(input.normalOS);
    
    // 构造无缩放的模型矩阵:对模型矩阵的三个轴向量做归一化,剔除缩放分量,仅保留旋转
    float3 right = normalize(UNITY_MATRIX_M._m00_m01_m02);
    float3 up = normalize(UNITY_MATRIX_M._m10_m11_m12);
    float3 forward = normalize(UNITY_MATRIX_M._m20_m21_m22);
    output.noScaleModelMatrix = float3x3(right, up, forward);
    
    return output;
}

half4 frag (Varyings input) : SV_Target
{
    // 把世界空间位置转回仅带旋转、无缩放的局部空间
    float3 noScalePositionOS = mul(input.noScaleModelMatrix, input.positionWS);
    // 平面模型直接取对应轴向作为平铺UV,_Tiling为自定义的平铺系数参数
    float2 uv = noScalePositionOS.xz * _Tiling;
    
    // 采样纹理输出
    half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv);
    return col;
}

可选优化:适配任意曲面的三平面映射

如果应用场景不是平面模型,而是立方体、不规则曲面等,可以在片元着色器中加入三平面映射逻辑,用法线分量作为权重混合三个轴向的采样结果,避免单轴投影带来的纹理拉伸问题:

// 片元着色器中替换UV采样部分
float3 normalWS = normalize(input.normalWS);
float3 blendWeights = abs(normalWS);
blendWeights /= (blendWeights.x + blendWeights.y + blendWeights.z);

// 三个轴向分别采样
half4 xSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, noScalePositionOS.yz * _Tiling);
half4 ySample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, noScalePositionOS.xz * _Tiling);
half4 zSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, noScalePositionOS.xy * _Tiling);

half4 col = xSample * blendWeights.x + ySample * blendWeights.y + zSample * blendWeights.z;

内容的提问来源于stack exchange,提问作者Dave

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 01:45:04