URP標(biāo)準(zhǔn)光照模型
該節(jié)對(duì)之前學(xué)習(xí)的紋理、陰影、透明效果、多光源處理進(jìn)行了一個(gè)匯總,并對(duì)代碼進(jìn)行了一定的優(yōu)化。
效果圖

思路
在Shader中設(shè)置了三個(gè)Bool變量和對(duì)應(yīng)的關(guān)鍵字,用于分別控制透明度裁剪功能、法線紋理功能和多光源計(jì)算功能的開(kāi)啟和關(guān)閉;這一來(lái)達(dá)到一個(gè)Shader多次利用。


使用到的語(yǔ)法
-
通過(guò)傳入對(duì)象空間下坐標(biāo),返回一個(gè)結(jié)構(gòu)體,該結(jié)構(gòu)體持有不同空間下轉(zhuǎn)換后的坐標(biāo)
VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS.xyz);//"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" VertexPositionInputs GetVertexPositionInputs(float3 positionOS) { VertexPositionInputs input; input.positionWS = TransformObjectToWorld(positionOS); input.positionVS = TransformWorldToView(input.positionWS); input.positionCS = TransformWorldToHClip(input.positionWS); float4 ndc = input.positionCS * 0.5f; input.positionNDC.xy = float2(ndc.x, ndc.y * _ProjectionParams.x) + ndc.w; input.positionNDC.zw = input.positionCS.zw; return input; } -
通過(guò)傳入對(duì)象空間下的法線與切線,返回世界空間下切線相關(guān)的向量
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, v.tangentOS);//"Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" VertexNormalInputs GetVertexNormalInputs(float3 normalOS) { VertexNormalInputs tbn; tbn.tangentWS = real3(1.0, 0.0, 0.0); tbn.bitangentWS = real3(0.0, 1.0, 0.0); tbn.normalWS = TransformObjectToWorldNormal(normalOS); return tbn; } VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS) { VertexNormalInputs tbn; // mikkts space compliant. only normalize when extracting normal at frag. real sign = tangentOS.w * GetOddNegativeScale(); tbn.normalWS = TransformObjectToWorldNormal(normalOS); tbn.tangentWS = TransformObjectToWorldDir(tangentOS.xyz); tbn.bitangentWS = cross(tbn.normalWS, tbn.tangentWS) * sign; return tbn; }
完整代碼
Shader "URP/MultiLightShadowNormalMapAlphaTest"
{
Properties
{
// 基礎(chǔ)紋理
[MainColor] _BaseColor ("Color", Color) = (0.5, 0.5, 0.5, 1)
[MainTexture] _BaseMap ("Albedo", 2D) = "white" { }
// 透明度裁剪
[Toggle(_ALPHATEST_ON)] _EnableAlphaTest ("Alpha Cutoff", Float) = 0.0
_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
// 法線貼圖
[Toggle(_NORMALMAP)] _EnableBumpMap ("Bump Map", Float) = 0.0
_BumpScale ("Normal Scale", Float) = 1.0
_BumpMap ("Normal Map", 2D) = "bump" { }
// 漫反射疊加顏色
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
// 高光反射疊加顏色
_Specular ("Specular", Color) = (1, 1, 1, 1)
// 高光系數(shù)
_Gloss ("Gloss", Range(8.0, 256)) = 20
// 是否計(jì)算多光源
[Toggle(_AdditionalLights)] _AddLights ("AddLights", Float) = 1
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
float4 _BaseMap_ST;
half _Cutoff;
float4 _BumpMap_ST;
float _BumpScale;
float4 _Diffuse;
float4 _Specular;
float _Gloss;
CBUFFER_END
ENDHLSL
Pass
{
Tags { "LightMode" = "UniversalForward" }
Cull Off
HLSLPROGRAM
// 設(shè)置關(guān)鍵字
#pragma shader_feature _NORMALMAP
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _AdditionalLights
// 接收陰影所需關(guān)鍵字
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _SHADOWS_SOFT
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
TEXTURE2D(_BumpMap);
SAMPLER(sampler_BumpMap);
struct Attributes
{
float4 positionOS: POSITION;//位置
float3 normalOS: NORMAL;//法線
float4 tangentOS: TANGENT;//切線
float2 texcoord: TEXCOORD0;//紋理坐標(biāo)
};
struct Varyings
{
float4 positionCS: SV_POSITION;
float2 uv: TEXCOORD0;
float3 positionWS: TEXCOORD1;
#ifdef _NORMALMAP//如果使用了法線貼圖
float4 normalWS: TEXCOORD3; // xyz: normal, w: viewDir.x
float4 tangentWS: TEXCOORD4; // xyz: tangent, w: viewDir.y
float4 bitangentWS: TEXCOORD5; // xyz: bitangent, w: viewDir.z
#else
float3 normalWS: TEXCOORD3;
float3 viewDirWS: TEXCOORD4;
#endif
};
Varyings vert(Attributes v)
{
Varyings o;
// 獲取不同空間下坐標(biāo)信息
VertexPositionInputs positionInputs = GetVertexPositionInputs(v.positionOS.xyz);
o.positionCS = positionInputs.positionCS;
o.positionWS = positionInputs.positionWS;
o.uv = TRANSFORM_TEX(v.texcoord, _BaseMap);
// 獲取世界空間下法線相關(guān)向量
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, v.tangentOS);
// 視角方向
half3 viewDirWS = GetCameraPositionWS() - positionInputs.positionWS;
#ifdef _NORMALMAP// 如果使用了法線貼圖
o.normalWS = half4(normalInput.normalWS, viewDirWS.x);
o.tangentWS = half4(normalInput.tangentWS, viewDirWS.y);
o.bitangentWS = half4(normalInput.bitangentWS, viewDirWS.z);
#else
o.normalWS = NormalizeNormalPerVertex(normalInput.normalWS);
o.viewDirWS = viewDirWS;
#endif
return o;
}
/// albedo:反射系數(shù)
/// lightColor:光源顏色
/// lightDirectionWS:世界空間下光線方向
/// lightAttenuation:光照衰減
/// normalWS:世界空間下法線
/// viewDirectionWS:世界空間下視角方向
half3 LightingBased(half3 albedo, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS)
{
// 蘭伯特漫反射計(jì)算
half NdotL = saturate(dot(normalWS, lightDirectionWS));
half3 radiance = lightColor * (lightAttenuation * NdotL) * _Diffuse.rgb;
// BlinnPhong高光反射
half3 halfDir = normalize(lightDirectionWS + viewDirectionWS);
half3 specular = lightColor * pow(saturate(dot(normalWS, halfDir)), _Gloss) * _Specular.rgb;
return(radiance + specular) * albedo;
}
// 計(jì)算漫反射與高光
half3 LightingBased(half3 albedo, Light light, half3 normalWS, half3 viewDirectionWS)
{
// 注意light.distanceAttenuation * light.shadowAttenuation,這里已經(jīng)將距離衰減與陰影衰減進(jìn)行了計(jì)算
return LightingBased(albedo, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS);
}
half4 frag(Varyings i): SV_Target
{
half3 viewDirWS ;
half3 normalWS;
#ifdef _NORMALMAP// 是否使用法線紋理
viewDirWS = half3(i.normalWS.w, i.tangentWS.w, i.bitangentWS.w);
//可以使用該方法替代下面的法線紋理采樣,但是需要引用函數(shù)庫(kù)ShaderLibrary/SurfaceInput.hlsl
//half3 normalTS = SampleNormal(i.uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
//or
half3 normalTS = UnpackNormalScale(SAMPLE_TEXTURE2D(_BumpMap, sampler_BumpMap, i.uv), _BumpScale);
normalWS = TransformTangentToWorld(normalTS, half3x3(i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz));
#else
viewDirWS = i.viewDirWS;
normalWS = i.normalWS;
#endif
normalWS = NormalizeNormalPerPixel(normalWS);
viewDirWS = SafeNormalize(viewDirWS);
//紋理采樣
//可以使用該方法替代下面的紋理采集操作,但是需要引用函數(shù)庫(kù)ShaderLibrary/SurfaceInput.hlsl
//half4 albedoAlpha = SampleAlbedoAlpha(i.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
// or
half4 albedoAlpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv);
// 透明度裁剪
//可以使用該方法替代下面的裁剪操作,但是需要引用函數(shù)庫(kù)ShaderLibrary/SurfaceInput.hlsl
//half alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
//or
#if defined(_ALPHATEST_ON)
clip(albedoAlpha.a - _Cutoff);
#endif
// 漫反射系數(shù)
//half3 albedo = albedoAlpha.rgb * _BaseColor.rgb;
// or
half3 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, i.uv).rgb * _BaseColor.rgb;
// 獲取陰影坐標(biāo)
float4 shadowCoord = TransformWorldToShadowCoord(i.positionWS.xyz);
// 計(jì)算主光源與陰影
Light mainLight = GetMainLight(shadowCoord);
half3 diffuse = LightingBased(albedo, mainLight, normalWS, viewDirWS);
// 計(jì)算其他光源與陰影
// 需要將光源組件的ShadowType參數(shù)打開(kāi),并且將管線中的CastShadows勾選,副光源才會(huì)產(chǎn)生陰影
#ifdef _AdditionalLights
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++ lightIndex)
{
Light light = GetAdditionalLight(lightIndex, i.positionWS);
diffuse += LightingBased(albedo, light, normalWS, viewDirWS);
}
#endif
// 計(jì)算環(huán)境光
half3 ambient = SampleSH(normalWS) * albedo;
return half4(ambient + diffuse, 1.0);
}
ENDHLSL
}
// 計(jì)算陰影的Pass
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
Cull Off
ZWrite On
ZTest LEqual
HLSLPROGRAM
// 設(shè)置關(guān)鍵字
#pragma shader_feature _ALPHATEST_ON
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
float3 _LightDirection;
struct Attributes
{
float4 positionOS: POSITION;
float3 normalOS: NORMAL;
float2 texcoord: TEXCOORD0;
};
struct Varyings
{
float2 uv: TEXCOORD0;
float4 positionCS: SV_POSITION;
};
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
// 獲取裁剪空間下的陰影坐標(biāo)
float4 GetShadowPositionHClips(Attributes input)
{
float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
// 獲取陰影專用裁剪空間下的坐標(biāo)
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _LightDirection));
// 判斷是否是在DirectX平臺(tái)翻轉(zhuǎn)過(guò)坐標(biāo)
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, positionCS.w * UNITY_NEAR_CLIP_VALUE);
#endif
return positionCS;
}
Varyings vert(Attributes input)
{
Varyings output;
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
output.positionCS = GetShadowPositionHClips(input);
return output;
}
half4 frag(Varyings input): SV_TARGET
{
//可以使用該方法替代下面的裁剪操作,但是需要引用函數(shù)庫(kù)ShaderLibrary/SurfaceInput.hlsl
//Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
//or
half4 albedoAlpha = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
#if defined(_ALPHATEST_ON)
clip(albedoAlpha.a - _Cutoff);
#endif
return 0;
}
ENDHLSL
}
}
FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}