如何实现不受缩放影响、纹理平铺尺寸恒定且随对象旋转的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
相关产品推荐
相关产品推荐

