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

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"
}

使用说明

  1. 创建新Shader文件,粘贴上述代码
  2. 新建Material并选择该Shader,赋值基础颜色和法线贴图
  3. 调整贴图缩放参数:值为1时,贴图每1米重复一次;值越大,重复密度越高
  4. 将Material赋予垂直墙面网格即可

适配调整

如果墙面是沿X轴垂直的(比如左右墙面),只需把片元着色器里的UV计算改成float2 uv = input.positionWS.yz * _TextureScale即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 19:20:22