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

Unity URP中HLSL的_Time重定义错误排查求助

问题:Unity URP Shader中_Time重定义错误及平滑着色失效

在Unity URP环境中编写Shader实现程序化地形的Lambert漫反射光照,原本用片元着色器偏导数(ddx、ddy)实现硬着色正常,修改顶点着色器法线计算逻辑后Shader失效,控制台提示_Time重定义错误,报错位置在Projects/TerrainGeneration/Library/PackageCache/com.unity.render-pipelines.universal@bf6d194af387/ShaderLibrary/UnityInput.hlsl(42),但未手动定义_Time变量。项目仅包含3个自定义文件:PerlinNoiseInclude.hlsl、Lambert_Include.hlsl及地形Shader文件,重新导入资源、新建URP项目后问题依旧。

相关代码

地形Shader文件

Shader "Unlit/PerlinTest"
{
    Properties
    {
        _Frequency ("Frequency", Range(0,5)) = 1.0
        _Amplitude ("Amplitude", Range(0,50)) = 1.0
        _Lacunarity ("Lacunarity", Range(0,4)) = 1.0
        _Persistence ("Persistence", Range(0,1)) = 1.0
        _Octaves ("Octaves", Integer) = 1
        _Displacement ("Displacement", Range(0,10)) = 0.0
        _DiffuseStr ("Diffuse Strength", Range(0,1)) = 1.0
        _DiffuseColor ("Diffuse Color", Color) = (1,1,1,1)

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "LightMode" = "UniversalForward" }
        Pass
        {
            CGPROGRAM
            #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"

            #include "PerlinNoiseInclude.hlsl"
            #include "Lambert_Include.hlsl"

            #define EPSILON 0.00001

            float _Displacement;
            float _Amplitude;
            float _Frequency;
            float _Persistence;
            float _Lacunarity;
            int _Octaves;
            float _DiffuseStr;
            fixed4 _DiffuseColor;

            struct MeshData
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct Interpolator
            {
                float4 vertex       : SV_POSITION;
                float3 worldNormal  : TEXCOORD0;
                float3 worldPos     : TEXCOORD1;
            };


            Interpolator vert (MeshData v)
            {
                Interpolator o;

                float noise = fBM(v.uv.x, v.uv.y, _Frequency, _Octaves, _Persistence, _Lacunarity);
                float noiseX = fBM(v.uv.x + EPSILON, v.uv.y, _Frequency, _Octaves, _Persistence, _Lacunarity);
                float noiseY = fBM(v.uv.x, v.uv.y + EPSILON, _Frequency, _Octaves, _Persistence, _Lacunarity);

                float normalizedNoise = (noise + 1) / 2;
                float normalizedNoise_X = (noiseX + 1) / 2;
                float normalizedNoise_Y = (noiseY + 1) / 2;

                float3 displacement = v.normal * normalizedNoise * _Amplitude * _Displacement;
                float3 displacement_X = v.normal * normalizedNoise_X * _Amplitude * _Displacement;
                float3 displacement_Y = v.normal * normalizedNoise_Y * _Amplitude * _Displacement;

                float4 displacedVertex = v.vertex + float4(displacement, 0.0);
                float4 displacedPos_X = v.vertex + float4(displacement_X, 0.0);
                float4 displacedPos_Y = v.vertex + float4(displacement_Y, 0.0);

                float3 tangentX = normalize(displacedPos_X - displacedVertex);
                float3 tangentY = normalize(displacedPos_Y - displacedVertex);

                float3 newNormal = normalize(cross(tangentX, tangentY));

                o.vertex = TransformObjectToHClip(displacedVertex.xyz);

                o.worldPos = TransformObjectToWorld(displacedVertex.xyz);
                o.worldNormal = normalize(TransformObjectToWorldNormal(newNormal * sign(dot(newNormal, v.normal))));

                return o;
            }

            fixed4 frag (Interpolator i) : SV_Target
            {
                Light mainLight = GetMainLight();
                
                float diffuseIntensity = DiffuseLightIntensity(_DiffuseStr, i.worldNormal, mainLight.direction);
                fixed3 diffuseColor = LambertColorOut(diffuseIntensity, _DiffuseColor, mainLight.color);
                
                return fixed4(diffuseColor, 1.0);
            }
            ENDCG
        }
    }
}

PerlinNoiseInclude.hlsl

#ifndef PERLIN_INCLUDE
#define PERLIN_INCLUDE

#define PI 3.14159265358979323846

float2 RandomGradient(int ix, int iy)
{
    const uint w = 32u;
    const uint s = w / 2u;
    uint a = (uint) ix;
    uint b = (uint) iy;
    a *= 3284157443u;
    b ^= (a << s) | (a >> (w - s));
    b *= 1911520717u;
    a ^= (b << s) | (b >> (w - s));
    a *= 2048419325u;
    float random = a * (PI / 2147483648.0f);
    float2 v;
    v.x = sin(random);
    v.y = cos(random);
    return v;
}

float dotGridGradient(int ix, int iy, float x, float y)
{
    float2 gradient = RandomGradient(ix, iy);
    float dx = x - (float) ix;
    float dy = y - (float) iy;
    return (dx * gradient.x + dy * gradient.y);
}

float interpolate(float a0, float a1, float w)
{
    return (a1 - a0) * w * w * w * (w * (w * 6.0f - 15.0f) + 10.0f) + a0;
}

float perlin(float x, float y, float frequency)
{
    x *= frequency;
    y *= frequency;

    int x0 = (int) x;
    int y0 = (int) y;
    int x1 = x0 + 1;
    int y1 = y0 + 1;

    float sx = x - (float) x0;
    float sy = y - (float) y0;

    float n0 = dotGridGradient(x0, y0, x, y);
    float n1 = dotGridGradient(x1, y0, x, y);
    float ix0 = interpolate(n0, n1, sx);

    n0 = dotGridGradient(x0, y1, x, y);
    n1 = dotGridGradient(x1, y1, x, y);
    float ix1 = interpolate(n0, n1, sx);

    float value = interpolate(ix0, ix1, sy);

    return value;
}

float fBM(float x, float y, float freq, int octaves, float persistence, float lacunarity)
{
    float total = 0.0;
    float frequency = freq;
    float amplitude = 1.0;
    float maxAmplitude = 0.0;

    for (int i = 0; i < octaves; i++)
    {
        total += perlin(x, y, frequency) * amplitude;

        maxAmplitude += amplitude;

        frequency *= lacunarity;
        amplitude *= persistence;
    }

    return total / maxAmplitude;
}

#endif

Lambert_Include.hlsl

#ifndef LAMBERT_INCLUDE
#define LAMBERT_INCLUDE

float DiffuseLightIntensity(float kD, float3 n, float3 l)
{
    return kD * saturate(dot(n, l));
}
float3 LambertColorOut(float iD, float4 cS, float3 lC)
{
    return (iD * cS.rgb * lC);
}

#endif

解决方案

1. 修复语法错误(核心)

你提供的PerlinNoiseInclude.hlsl代码中,位移运算符<<和>>被错误地写成了HTML转义字符&lt;&lt;和&gt;&gt;,HLSL编译器无法识别这些字符,会引发语法错误,而Unity的Shader编译器有时会给出误导性的_Time重定义提示。

将代码中所有的&lt;&lt;替换为<<,&gt;&gt;替换为>>,修复后重新编译Shader,_Time重定义错误会自动消失。

2. 优化顶点着色器的法线计算逻辑

当前通过偏移UV计算位移差来生成法线的方式,在顶点着色器中计算后插值,效果可能不够理想。更可靠的方式是通过噪声函数的梯度直接计算法线:

  • 修改fBM函数,同时返回噪声值和梯度(或者单独实现梯度计算函数)
  • 基于噪声梯度计算法线:2D噪声的法线可以通过float3(-dNoise/dx, -dNoise/dy, 1.0)生成,再转换到世界空间

示例修改思路:

// 新增计算Perlin噪声梯度的函数
float2 perlinGradient(float x, float y, float frequency)
{
    x *= frequency;
    y *= frequency;

    int x0 = (int)x;
    int y0 = (int)y;
    int x1 = x0 + 1;
    int y1 = y0 + 1;

    float sx = x - (float)x0;
    float sy = y - (float)y0;

    // 计算四个网格点的梯度点积
    float n0 = dotGridGradient(x0, y0, x, y);
    float n1 = dotGridGradient(x1, y0, x, y);
    float n2 = dotGridGradient(x0, y1, x, y);
    float n3 = dotGridGradient(x1, y1, x, y);

    // 计算插值的导数
    float dx0 = (n1 - n0) * interpolate(0, 1, sx);
    float dx1 = (n3 - n2) * interpolate(0, 1, sx);
    float dy0 = (n2 - n0) * interpolate(0, 1, sy);
    float dy1 = (n3 - n1) * interpolate(0, 1, sy);

    return float2(dx0 + dx1, dy0 + dy1);
}

// 修改fBM同时返回值和梯度
float2 fBMWithGradient(float x, float y, float freq, int octaves, float persistence, float lacunarity)
{
    float totalValue = 0.0;
    float2 totalGradient = float2(0,0);
    float frequency = freq;
    float amplitude = 1.0;
    float maxAmplitude = 0.0;

    for (int i = 0; i < octaves; i++)
    {
        totalValue += perlin(x, y, frequency) * amplitude;
        totalGradient += perlinGradient(x, y, frequency) * amplitude;

        maxAmplitude += amplitude;
        frequency *= lacunarity;
        amplitude *= persistence;
    }

    return float2(totalValue / maxAmplitude, length(totalGradient) / maxAmplitude);
}

然后在顶点着色器中使用梯度计算法线:

float2 noiseData = fBMWithGradient(v.uv.x, v.uv.y, _Frequency, _Octaves, _Persistence, _Lacunarity);
float noise = noiseData.x;
float2 gradient = noiseData.y;

// 生成法线(假设地形沿Y轴向上,根据你的模型空间调整)
float3 modelNormal = normalize(float3(-gradient.x, 1.0, -gradient.y));

3. 验证Shader编译

修复语法错误后,检查Shader是否能正常编译,若仍有问题,可尝试:

  • 清除Unity的Library文件夹后重新打开项目
  • 确保URP包版本与项目兼容
  • 检查所有include文件的路径是否正确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 12:14:52