原文链接
urp管线的自学hlsl之路 第十篇 主光源阴影投射和接收 | 一骑gens
出处:bilibili
正文
- 主光源阴影透射和接收
阴影这块官方改了一些东西,和 Built-in 的实现方式不一样,翻了官方文档和网上的一些资料,研究出来了怎么实现阴影的接受和投射,这里分开讲一下,这篇只涉及 主光源 的阴影内容,额外多光源 的阴影下一篇再讲(还没研究出来)。
TransformWorldToShadowCoord
顶点着色器和之前的一样无变化,而片元着色器里,这里使用TransformWorldToShadowCoord(i.positionWS)
函数,该函数定义在 Shadow.hlsl 里
定义
float4 TransformWorldToShadowCoord(float3 positionWS)
{
#ifdef _MAIN_LIGHT_SHADOWS_CASCADE
half cascadeIndex = ComputeCascadeIndex(positionWS);
#else
half cascadeIndex = 0;
#endif
return mul(_MainLightWorldToShadow[cascadeIndex], float4(positionWs, 1.0));
}
作用
把模型的 世界空间 顶点坐标输入,得到 阴影坐标,用于在 ShadowMap 下进行比较。
注意
该函数里是要定义关键字 _MAIN_LIGHT_SHADOWS_CASCADE
,这样才能得到正确的阴影坐标。
MainLightRealtimeShadow
第二个函数还是 GetMainLight(float4 shadowcoord)
,该函数是之前用的 GetMainLight
的重载形式,它会调用另外一个函数 half MainLightRealtimeShadow(float4 shadowCoord)
,该函数在 Shadow.hlsl ,它的定义如下。
定义
half MainLightRealtimeShadow(float4 shadowCoord)
{
#if !defined(MAIN_LIGHT_CALCULATE_SHADOWS)
return 1.0h;
#endif
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
half4 shadowParams = GetMainLightShadowParams();
return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture),
shadowCoord, shadowSamplingData, shadowParams, false);
}
作用
该函数是专门用来计算阴影衰减的
注意
使用它时,需声明关键字MAIN_LIGHT_CALCULATE_SHADOWS
,但是这里直接定义该关键字是不会生效的,猜测还需要额外的关键字,找到了 _MAIN_LIGHT_SHADOWS
,定义它时则定义了 calculate shadow,猜测它还定义了其他的内容,这里我们直接使用 _MAIN_LIGHT_SHADOWS
关键字即可,如有大佬明白其中原理希望评论指出。
_MAIN_LIGHT_SHADOWS
关键字
#if !defined(_RECEIVE_SHADOWS_OFF)
#if defined(_MAIN_LIGHT_SHADOWS)
#define MAIN_LIGHT_CALCULATE_SHADOWS
#if !defined(_MAIN_LIGHT_SHADOWS_CASCADE)
#define REOUIRES_VERTEX_SHADOW_COORD
#endif
主光源阴影投射和接收 Shader 代码说明
以下为上面两个函数需要的关键字定义,还额外多了个 _SHADOWS_SOFT
,该关键字是用来设置 软阴影 的(但是也会造成性能的降低),请酌情使用。
- 关键字编译多个变体 shader
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT // 柔化阴影,得到软阴影
计算带 阴影衰减 的主光源。
- 计算主光源
Light mylight = GetMainLight(TransformworldToShadowCoord(i.positionWS));
剩下的就常规的计算 高光 和 半兰伯特。
- 片元的剩余部分
float3 Ws_L=normalize(mylight.direction);
float3 WS_N=normalize(i.normalws);
float3 Ws_V=normalize(_WorldSpaceCameraPos - i.positionWS);
float3 WS_H=normalize(WS_V + WS_L);
tex*=(dot(WS_L, WS_N) * 0.5 + 0.5) * mylight.shadowAttenuation * real4(mylight.color, 1);
float4 Specular = pow(max(dot(WS_N, WS_H), 0), _Gloss) * _SpecularColor * mylight.shadowAttenuation;
return tex + Specular;
这样,接受阴影就完成了,如果想实现阴影的投射,直接使用 URP 自带的 ShadowCaster 的 Pass 把模型的信息写到 ShadowMap 上去让其他模型接受本模型的阴影。
最后,附上 shader 源码:
Shader "URP/MainLightShadow"
{
Properties
{
_MainTex("MainTex",2D)="white"{}
_BaseColor("BaseColor",Color)=(1,1,1,1)
_Gloss("gloss",Range(10,300))=20
_SpecularColor("SpecularColor",Color )=(1,1,1,1)
}
SubShader
{
Tags{
"RenderPipeline"="UniversalRenderPipeline"
"RenderType"="Opaque"
}
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
half4 _BaseColor;
half _Gloss;
real4 _SpecularColor;
CBUFFER_END
TEXTURE2D( _MainTex);
SAMPLER(sampler_MainTex);
struct a2v
{
float4 positionOS:POSITION;
float4 normalOS:NORMAL;
float2 texcoord:TEXCOORD;
};
struct v2f
{
float4 positionCS:SV_POSITION;
float2 texcoord:TEXCOORD;
float3 positionWS:TEXCOORD1;
float3 normalWS:NORMAL;
};
ENDHLSL
pass
{
Tags{
"LightMode"="UniversalForward"
}
HLSLPROGRAM
#pragma vertex VERT
#pragma fragment FRAG
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _SHADOWS_SOFT//柔化阴影,得到软阴影
v2f VERT(a2v i)
{
v2f o;
o.positionCS=TransformObjectToHClip(i.positionOS.xyz);
o.texcoord=TRANSFORM_TEX(i.texcoord,_MainTex);
o.positionWS=TransformObjectToWorld(i.positionOS.xyz);
o.normalWS=TransformObjectToWorldNormal(i.normalOS);
return o;
}
half4 FRAG(v2f i):SV_TARGET
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoord)*_BaseColor;
Light mylight = GetMainLight(TransformWorldToShadowCoord(i.positionWS));
float3 WS_L = normalize(mylight.direction);
float3 WS_N = normalize(i.normalWS);
float3 WS_V = normalize(_WorldSpaceCameraPos-i.positionWS);
float3 WS_H = normalize(WS_V + WS_L);
tex *= (dot(WS_L, WS_N) * 0.5 + 0.5) * mylight.shadowAttenuation * real4(mylight.color, 1);
float4 Specular = pow(max(dot(WS_N, WS_H), 0), _Gloss) * _SpecularColor * mylight.shadowAttenuation;
return tex+Specular ;
}
ENDHLSL
}
UsePass "Universal Render Pipeline/Lit/ShadowCaster"
}
}