Unity URP:如何实现垂直墙面法线贴图恒定缩放(Shader方案)
URP垂直墙面法线贴图统一缩放解决方案
核心思路
针对垂直墙面,直接用世界空间坐标替代传统UV来采样法线贴图。因为世界坐标是基于场景单位的,无论墙面多边形尺寸多大,贴图都会按设定的世界单位比例重复,无需提前做复杂UV映射。
简化版URP Shader实现
不需要修改复杂的内置Simple Lit Shader,直接用以下轻量化自定义Shader,它保留了URP的基础光照逻辑,同时实现了贴图统一缩放:
Shader "Custom/URP_WallNormalMap" { Properties { _BaseColor ("基础颜色", Color) = (1,1,1,1) _NormalMap ("法线贴图", 2D) = "bump" {} _NormalScale ("法线强度", Float) = 1.0 _TextureScale ("贴图缩放(每米重复次数)", Float) = 1.0 } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" } LOD 100 Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; }; struct Varyings { float4 positionHCS : SV_POSITION; float3 positionWS : TEXCOORD0; float3 normalWS : TEXCOORD1; float4 tangentWS : TEXCOORD2; float3 bitangentWS : TEXCOORD3; }; CBUFFER_START(UnityPerMaterial) float4 _BaseColor; sampler2D _NormalMap; float _NormalScale; float _TextureScale; CBUFFER_END Varyings vert(Attributes input) { Varyings output; // 模型空间转世界空间坐标 output.positionWS = TransformObjectToWorld(input.positionOS.xyz); output.positionHCS = TransformWorldToHClip(output.positionWS); // 计算世界空间下的法线、切线、副切线 output.normalWS = TransformObjectToWorldNormal(input.normalOS); float3 tangentWS = TransformObjectToWorldDir(input.tangentOS.xyz); float sign = input.tangentOS.w * GetOddNegativeScale(); output.bitangentWS = cross(output.normalWS, tangentWS) * sign; output.tangentWS = float4(tangentWS, sign); return output; } half4 frag(Varyings input) : SV_Target { // 用世界空间XZ轴生成UV,按设定比例缩放 float2 uv = input.positionWS.xz * _TextureScale; // 采样法线贴图并转换到世界空间 half3 normalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, uv), _NormalScale); half3x3 TBN = half3x3(input.tangentWS.xyz, input.bitangentWS, input.normalWS); half3 normalWS = normalize(mul(normalTS, TBN)); // 计算主光照漫反射 Light mainLight = GetMainLight(); half NdotL = saturate(dot(normalWS, mainLight.direction)); half3 diffuse = _BaseColor.rgb * mainLight.color * NdotL; // 添加环境光 half3 ambient = SampleSH(normalWS) * _BaseColor.rgb; return half4(diffuse + ambient, _BaseColor.a); } ENDHLSL } } FallBack "Hidden/Universal Render Pipeline/FallbackError" }
使用说明
- 创建新Shader文件,粘贴上述代码
- 新建Material并选择该Shader,赋值基础颜色和法线贴图
- 调整
贴图缩放参数:值为1时,贴图每1米重复一次;值越大,重复密度越高 - 将Material赋予垂直墙面网格即可
适配调整
如果墙面是沿X轴垂直的(比如左右墙面),只需把片元着色器里的UV计算改成float2 uv = input.positionWS.yz * _TextureScale即可。
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

