GameTech/URP 渲染实战
源码 / Ch15/NPRCommon.hlsl
Ch15/NPRCommon.hlsl
// 第 15 章各个风格化 Shader 共用的顶点结构、主光获取,以及 ShadowCaster / DepthOnly 的通用实现。
// 用法:Shader 先声明自己的 CBUFFER(UnityPerMaterial),再 #include 本文件。
#ifndef COOKBOOK_CH15_NPR_COMMON_INCLUDED
#define COOKBOOK_CH15_NPR_COMMON_INCLUDED

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

struct NprAttributes
{
    float4 positionOS : POSITION;
    float3 normalOS   : NORMAL;
    float2 uv         : TEXCOORD0;
    float4 color      : COLOR;
};

struct NprVaryings
{
    float4 positionCS : SV_POSITION;
    float2 uv         : TEXCOORD0;
    float3 positionWS : TEXCOORD1;
    float3 normalWS   : TEXCOORD2;
    float4 color      : TEXCOORD3;
    float  fogFactor  : TEXCOORD4;
};

NprVaryings NprVert(NprAttributes v)
{
    NprVaryings o;
    VertexPositionInputs pos = GetVertexPositionInputs(v.positionOS.xyz);
    o.positionCS = pos.positionCS;
    o.positionWS = pos.positionWS;
    o.normalWS   = TransformObjectToWorldNormal(v.normalOS);
    o.uv         = v.uv;
    o.color      = v.color;
    o.fogFactor  = ComputeFogFactor(pos.positionCS.z);
    return o;
}

// 风格化着色只需要“原料”:未截断的 N·L(-1..1)、阴影衰减、光的颜色和方向。
// 怎么把它们变成颜色,交给各个 Shader 自己决定。
struct NprLight
{
    float3 direction;
    float3 color;
    float  ndotl;      // 未 saturate,Gooch/Hatching 需要背光面的信息
    float  shadow;     // 0 = 完全在阴影里,1 = 受光
};

NprLight GetNprLight(float3 positionWS, float3 normalWS)
{
    float4 shadowCoord = TransformWorldToShadowCoord(positionWS);  // 逐像素算,级联阴影也正确
    Light l = GetMainLight(shadowCoord);
    NprLight o;
    o.direction = l.direction;
    o.color     = l.color;
    o.ndotl     = dot(normalWS, l.direction);
    o.shadow    = l.shadowAttenuation;
    return o;
}

// ---------------- ShadowCaster / DepthOnly ----------------
float3 _LightDirection;   // 由 URP 的 ShadowUtils 在渲染阴影时设置
float3 _LightPosition;

struct NprShadowAttributes
{
    float4 positionOS : POSITION;
    float3 normalOS   : NORMAL;
};

float4 NprShadowVert(NprShadowAttributes v) : SV_POSITION
{
    float3 positionWS = TransformObjectToWorld(v.positionOS.xyz);
    float3 normalWS   = TransformObjectToWorldNormal(v.normalOS);
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
    float3 lightDir = normalize(_LightPosition - positionWS);
#else
    float3 lightDir = _LightDirection;
#endif
    float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDir));
#if UNITY_REVERSED_Z
    positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
    positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
    return positionCS;
}

float4 NprDepthVert(NprShadowAttributes v) : SV_POSITION
{
    return TransformObjectToHClip(v.positionOS.xyz);
}

half4 NprNullFrag() : SV_Target { return 0; }

#endif