新聞中心
上篇中我們分析了Unlit shader的Properties在ShaderGUI中的處理,接下來看Sub Shader。
unlit shader以及其他URP shader包含兩個SubShader,分別是針對ShaderModel4.5和2.0。由于unlit shader本身很簡單,這兩個SubShader幾乎一樣,唯一的差別是ShaderModel 4.5的SubShader會定義#pragma multi_compile _ DOTS_INSTANCING_ON
,這個可參考dots-instancing-shaders,本文中先忽略它??傊@兒的要點是URP會針對比較強大的設備使用一個SubShader,而比較弱的設備使用另一個。但這并不是說URP只是把設備分成兩類這么簡單,這兒只說Shader。由于硬件總是要發(fā)展,我們關注的重點是ShaderModel4.5,因此就只看這第一個SubShader了。
我們看到unlit shader的SubShader中包含了3個Pass,分別是Unlit
,DepthOnly
和Meta
。我們知道URP是單Pass渲染,這只是說URP是在一個Pass中完成物體的大部分計算,包含各種光照的計算等等。而Sub Shader中仍然是會包含多個pass,這些Pass是在渲染管線中特定的時候被執(zhí)行。例如這兒的DepthOnly
pass,是在渲染所有不透明物體之前預先生成一張depth texture的pass;而Meta
pass則只在烘焙光照貼圖時使用。而決定這些pass被使用的并不是他們的名字,而是上面說過的LightMode
tag。但是Unlit
pass并沒有指定這個tag,所以它使用的就是默認的SRPDefaultUnlit
。
首先分析unlit pass
- keyword定義:
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fog
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
shader feature支持alpha test和 alpha premultiply,muti compile則包含fog, instacing和 dots instancing。這些都是一些宏定義開關,shader使用了不同的開關,會編譯成不同的變體,變體才是最終使用的shader。對于shader feature是可以定義在材質上的,上面處理Properties的時候看到會根據不同情況設置不同的關鍵字,就是設置使用這兒的關鍵字,所以要首先包含這些關鍵字。而multi compile是指無論如何都要包含這些關鍵字(和材質是否開啟沒關系)。
- uniform定義:
uniform定義被包含在文件UnlitInput.hlsl
中。這個hlsl文件定義了unlit shader使用的uniform,根據SRP的規(guī)則,使用的是CBuffer。
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
half4 _BaseColor;
half _Cutoff;
half _Surface;
CBUFFER_END
CBUFFER_START
和CBUFFER_END
是定義在SRP Core的Shader Library中的宏,并且根據不同的API有不同的定義,在GLES2中由于不支持constant uniform buffer,因此這兩個宏被定義為空。
- Attributes 和 Varyings
這兩個分別是vertex shader的輸入,以及fragment shader的輸入,這是URP的命名習慣,和內置流水線不一樣。
struct Attributes
{float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{float2 uv : TEXCOORD0;
float fogCoord : TEXCOORD1;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
先忽略gpu instancing相關的宏。vs的輸入只有位置和uv坐標,而fs的輸入還多了一個fogCoord。
- Vertex shader
Varyings vert(Attributes input)
{Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
output.vertex = vertexInput.positionCS;
output.uv = TRANSFORM_TEX(input.uv, _BaseMap);
output.fogCoord = ComputeFogFactor(vertexInput.positionCS.z);
return output;
}
同樣先忽略instacing。VS就是簡單的從物體局部坐標系變換到clip space,將結果寫入Varying的vertex;變換uv坐標;以及計算霧參數。
這兒要注意的是GetVertexPositionInputs
方法。這個方法位于Packages\com.unity.render-pipelines.universal\ShaderLibrary\ShaderVariablesFunctions.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;
}
可以看到,這個方法里面計算了世界坐標,view坐標和clip space坐標,以及NDC坐標。幾乎所有的URP shader都會調用這個方法。雖然說unlit shader里面只需要clip space坐標,但是為了統(tǒng)一就直接調用了。由于shader的函數都是內聯(lián)的,不使用的變量和方法應該會被編譯器優(yōu)化掉(錯了請告訴我),所以也不用太擔心性能浪費。
關于uv坐標的變換,使用了TRANSFORM_TEX
這個宏,這個宏的定義位于SRP Core的Shader Library中:#define TRANSFORM_TEX(tex, name) ((tex.xy) * name##_ST.xy + name##_ST.zw)
就是使用_BaseMap_ST去縮放偏移了一下uv坐標。
最后,ComputeFogFactor
也位于ShaderVariablesFunctions.hlsl
中:
real ComputeFogFactor(float z)
{float clipZ_01 = UNITY_Z_0_FAR_FROM_CLIPSPACE(z);
#if defined(FOG_LINEAR)
// factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
float fogFactor = saturate(clipZ_01 * unity_FogParams.z + unity_FogParams.w);
return real(fogFactor);
#elif defined(FOG_EXP) || defined(FOG_EXP2)
// factor = exp(-(density*z)^2)
// -density * z computed at vertex
return real(unity_FogParams.x * clipZ_01);
#else
return 0.0h;
#endif
}
首先,real是一個宏定義,位于SRP Core的Common.hlsl
中,對于支持half的平臺,real就是half,否則就是float。
而UNITY_Z_0_FAR_FROM_CLIPSPACE
是定義在URP的core.hlsl
中的一個宏,會根據是否是reverse Z做不同的計算,關于z相關的話題是很重要也比較復雜的,將會單獨寫一篇,這兒就不說了。
- Fragment Shader
half4 frag(Varyings input) : SV_Target
{UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
half2 uv = input.uv;
half4 texColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, uv);
half3 color = texColor.rgb * _BaseColor.rgb;
half alpha = texColor.a * _BaseColor.a;
AlphaDiscard(alpha, _Cutoff);
#ifdef _ALPHAPREMULTIPLY_ON
color *= alpha;
#endif
color = MixFog(color, input.fogCoord);
return half4(color, alpha);
}
SAMPLE_TEXTURE2D
是一個定義在SRP Core shader library中的一個跨平臺的宏,例如:
dx11是#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
GLES2是#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) tex2D(textureName, coord2)
AlphaDiscard
也還是位于ShaderVariablesFunctions.hlsl
中:
void AlphaDiscard(real alpha, real cutoff, real offset = 0.0h)
{
#ifdef _ALPHATEST_ON
clip(alpha - cutoff + offset);
#endif
}
很簡單,根據是否定義_ALPHATEST_ON
關鍵字,使用cutoff計算clip。
這兒還會判斷關鍵字_ALPHAPREMULTIPLY_ON
,如果定義,則會將alpha值預先乘到color上,這就是傳統(tǒng)的Alpha預乘技術。
Depth only pass用于生成場景的深度貼圖。這個pass的代碼在Packages\com.unity.render-pipelines.universal\Shaders\DepthOnlyPass.hlsl
中。
本篇中我們主要分析了Unlit pass的內容,可以看到很多宏定義,函數都來自于URP和SRP Core的ShaderLibrary中。熟悉這些宏和函數方便我們寫自己的自定義Shader。另外我們要注意CBuffer這個結構,后面會說一下SRP Batcher相關的內容。
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
文章標題:深入URP之Shader篇3:UnlitShader分析[下]-創(chuàng)新互聯(lián)
本文地址:http://www.dlmjj.cn/article/djcpse.html