这是URP学习笔记的第二篇,主要针对URP内置的Shader进行学习与分析
URP学习笔记
3. URP Shader 相关
3.1 Lit.shader
我们可以在 URP 的 Shaders 文件夹下找到多个预先写好的shader
从官方文档里可以知道,Lit 使用的是 PBR,而 SimpleLit 使用的是 Blinn-Phong 模型
我们先看 Lit.shader 文件
3.1.1 Properties
学习一个 shader,需要看 Properties 、Tags、Pass 几个部分,我们先从 Properties 入手进行分析
Properties
{
// Specular vs Metallic workflow
[HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
_GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
_SmoothnessTextureChannel("Smoothness texture channel", Float) = 0
_Metallic("Metallic", Range(0.0, 1.0)) = 0.0
_MetallicGlossMap("Metallic", 2D) = "white" {}
_SpecColor("Specular", Color) = (0.2, 0.2, 0.2)
_SpecGlossMap("Specular", 2D) = "white" {}
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[ToggleOff] _EnvironmentReflections("Environment Reflections", Float) = 1.0
_BumpScale("Scale", Float) = 1.0
_BumpMap("Normal Map", 2D) = "bump" {}
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
_EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
// Blending state
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _AlphaClip("__clip", Float) = 0.0
[HideInInspector] _SrcBlend("__src", Float) = 1.0
[HideInInspector] _DstBlend("__dst", Float) = 0.0
[HideInInspector] _ZWrite("__zw", Float) = 1.0
[HideInInspector] _Cull("__cull", Float) = 2.0
_ReceiveShadows("Receive Shadows", Float) = 1.0
// Editmode props
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
// ObsoleteProperties
[HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
[HideInInspector] _Color("Base Color", Color) = (1, 1, 1, 1)
[HideInInspector] _GlossMapScale("Smoothness", Float) = 0.0
[HideInInspector] _Glossiness("Smoothness", Float) = 0.0
[HideInInspector] _GlossyReflections("EnvironmentReflections", Float) = 0.0
}
上面是基础贴图、颜色、平滑度、金属度、镜面反射的颜色、法线贴图和凹凸程度、遮挡剔除相关以及自发光这些基本的参数
下面是透明测试、透明混合、深度测试等等这些属性的设置
3.1.2 Tags
然后是 SubShader 的标签
Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True"}
渲染类型是不透明物体,管线是 URP,忽视投影器
3.1.3 Pass
Lit 里面有 5 个通道,分别是 ForwardLit、ShadowCaster、DepthOnly、Meta、Universal2D
3.1.3.1 LitForwardPass.hlsl
这个是 ForwardLit 通道里使用的文件,主要做与前向渲染有关的内容
先来看 Vertex Shader
Varyings LitPassVertex(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);
// normalWS and tangentWS already normalize.
// this is required to avoid skewing the direction during interpolation
// also required for per-vertex lighting and SH evaluation
VertexNormalInputs normalInput = GetVertexNormalInputs(input.normalOS, input.tangentOS);
float3 viewDirWS = GetCameraPositionWS() - vertexInput.positionWS;
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
// already normalized from normal transform to WS.
output.normalWS = normalInput.normalWS;
output.viewDirWS = viewDirWS;
#ifdef _NORMALMAP
real sign = input.tangentOS.w * GetOddNegativeScale();
output.tangentWS = half4(normalInput.tangentWS.xyz, sign);
#endif
OUTPUT_LIGHTMAP_UV(input.lightmapUV, unity_LightmapST, output.lightmapUV);
OUTPUT_SH(output.normalWS.xyz, output.vertexSH);
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
output.positionWS = vertexInput.positionWS;
#endif
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
output.shadowCoord = GetShadowCoord(vertexInput);
#endif
output.positionCS = vertexInput.positionCS;
return output;
}
可以看到进行了一大堆参数的空间变换,比如常用的不同空间下的位置(VertexPositionInputs),TBN相关(VertexNormalInputs),viewDir、light、fog、uv等等
然后看 Fragment Shader
half4 LitPassFragment(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
SurfaceData surfaceData;
InitializeStandardLitSurfaceData(input.uv, surfaceData);
InputData inputData;
InitializeInputData(input, surfaceData.normalTS, inputData);
half4 color = UniversalFragmentPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
color.a = OutputAlpha(color.a, _Surface);
return color;
}
片元着色器里对传入的数据进行了 PBR 相关的计算,也就是 UniversalFragmentPBR() 这个函数,这个函数在后面学习 Lighting.hlsl 的时候会展开来讲,这里就先跳过了
3.1.3.2 ShadowCasterPass.hlsl
这个是 ShadowCaster 通道使用的文件,主要是对阴影的计算
先来看 Vertex Shader
Varyings ShadowPassVertex(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
output.positionCS = GetShadowPositionHClip(input);
return output;
}
顶点着色器非常的短,基本上就是获取一些 Shadow Map 需要的参数
再看 Fragment Shader
half4 ShadowPassFragment(Varyings input) : SV_TARGET
{
Alpha(SampleAlbedoAlpha(input.uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap)).a, _BaseColor, _Cutoff);
return 0;
}
就是根据 uv 把 shadow map 画上去,没什么好说的
3.1.3.3 其他
- DepthOnly:就是画一张深度图
- Meta:这个 Pass 只有 bake 才会使用到,笔者没学过bake相关,这里先占个坑
- Universal2D:2D渲染相关
3.2 SimpleLit.shader
大体和 Lit.shader 没有区别,只是将少部分内容进行了简化
3.2.1 Properties 和 Tags
我们可以看到 Properties 少了很多东西,比如金属度、遮挡剔除等等
Properties
{
[MainTexture] _BaseMap("Base Map (RGB) Smoothness / Alpha (A)", 2D) = "white" {}
[MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
_Cutoff("Alpha Clipping", Range(0.0, 1.0)) = 0.5
_SpecColor("Specular Color", Color) = (0.5, 0.5, 0.5, 0.5)
_SpecGlossMap("Specular Map", 2D) = "white" {}
[Enum(Specular Alpha,0,Albedo Alpha,1)] _SmoothnessSource("Smoothness Source", Float) = 0.0
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
[HideInInspector] _BumpScale("Scale", Float) = 1.0
[NoScaleOffset] _BumpMap("Normal Map", 2D) = "bump" {}
_EmissionColor("Emission Color", Color) = (0,0,0)
[NoScaleOffset]_EmissionMap("Emission Map", 2D) = "white" {}
// Blending state
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _AlphaClip("__clip", Float) = 0.0
[HideInInspector] _SrcBlend("__src", Float) = 1.0
[HideInInspector] _DstBlend("__dst", Float) = 0.0
[HideInInspector] _ZWrite("__zw", Float) = 1.0
[HideInInspector] _Cull("__cull", Float) = 2.0
[ToogleOff] _ReceiveShadows("Receive Shadows", Float) = 1.0
// Editmode props
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
[HideInInspector] _Smoothness("SMoothness", Float) = 0.5
// ObsoleteProperties
[HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
[HideInInspector] _Color("Base Color", Color) = (1, 1, 1, 1)
[HideInInspector] _Shininess("Smoothness", Float) = 0.0
[HideInInspector] _GlossinessSource("GlossinessSource", Float) = 0.0
[HideInInspector] _SpecSource("SpecularHighlights", Float) = 0.0
}
而 Tags 则是没有变化
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True"}
3.2.2 Pass
Pass基本没有变化,主要还是 ForwardLit 的不同
下面是 ForwardLit 通道的片元着色器
half4 LitPassFragmentSimple(Varyings input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
float2 uv = input.uv;
half4 diffuseAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
half3 diffuse = diffuseAlpha.rgb * _BaseColor.rgb;
half alpha = diffuseAlpha.a * _BaseColor.a;
AlphaDiscard(alpha, _Cutoff);
#ifdef _ALPHAPREMULTIPLY_ON
diffuse *= alpha;
#endif
half3 normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap));
half3 emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
half4 specular = SampleSpecularSmoothness(uv, alpha, _SpecColor, TEXTURE2D_ARGS(_SpecGlossMap, sampler_SpecGlossMap));
half smoothness = specular.a;
InputData inputData;
InitializeInputData(input, normalTS, inputData);
half4 color = UniversalFragmentBlinnPhong(inputData, diffuse, specular, smoothness, emission, alpha);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
color.a = OutputAlpha(color.a, _Surface);
return color;
};
可以看到,着色模型改成了 UniversalFragmentBlinnPhong(),也就是 Blinn-Phong 模型
3.3 BakedLit 和 Unlit
由于笔者没学过 bake 相关,所以 BakedLit 这部分先带过
先来看 Unlit.shader
Shader "Universal Render Pipeline/Unlit"
{
Properties
{
[MainTexture] _BaseMap("Texture", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1)
_Cutoff("AlphaCutout", Range(0.0, 1.0)) = 0.5
// BlendMode
[HideInInspector] _Surface("__surface", Float) = 0.0
[HideInInspector] _Blend("__blend", Float) = 0.0
[HideInInspector] _AlphaClip("__clip", Float) = 0.0
[HideInInspector] _SrcBlend("Src", Float) = 1.0
[HideInInspector] _DstBlend("Dst", Float) = 0.0
[HideInInspector] _ZWrite("ZWrite", Float) = 1.0
[HideInInspector] _Cull("__cull", Float) = 2.0
// Editmode props
[HideInInspector] _QueueOffset("Queue offset", Float) = 0.0
// ObsoleteProperties
[HideInInspector] _MainTex("BaseMap", 2D) = "white" {}
[HideInInspector] _Color("Base Color", Color) = (0.5, 0.5, 0.5, 1)
[HideInInspector] _SampleGI("SampleGI", float) = 0.0 // needed from bakedlit
}
SubShader
{
Tags { "RenderType" = "Opaque" "IgnoreProjector" = "True" "RenderPipeline" = "UniversalPipeline" }
LOD 100
Blend [_SrcBlend][_DstBlend]
ZWrite [_ZWrite]
Cull [_Cull]
Pass
{
Name "Unlit"
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex vert
#pragma fragment frag
#pragma shader_feature _ALPHATEST_ON
#pragma shader_feature _ALPHAPREMULTIPLY_ON
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fog
#pragma multi_compile_instancing
#include "UnlitInput.hlsl"
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
};
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;
}
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);
alpha = OutputAlpha(alpha, _Surface);
return half4(color, alpha);
}
ENDHLSL
}
Pass
{
Tags{"LightMode" = "DepthOnly"}
ZWrite On
ColorMask 0
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma target 2.0
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature _ALPHATEST_ON
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
// This pass it not used during regular rendering, only for lightmap baking.
Pass
{
Name "Meta"
Tags{"LightMode" = "Meta"}
Cull Off
HLSLPROGRAM
// Required to compile gles 2.0 with standard srp library
#pragma prefer_hlslcc gles
#pragma exclude_renderers d3d11_9x
#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMetaUnlit
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/UnlitMetaPass.hlsl"
ENDHLSL
}
}
FallBack "Hidden/Universal Render Pipeline/FallbackError"
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.UnlitShader"
}
因为没了光照的相关计算,所以 Properties 直接缩水了,仅有一个基础贴图,颜色,以及透明测试、深度测试相关的属性了,顶点着色器也仅用计算位置和uv,片元着色器只需要计算 Texture 的颜色和 baseColor
4. Lighting.hlsl
我们单独开一个章节去分析这个 hlsl 文件,因为它里面涉及到的内容非常重要,去学习和分析它有助于我们了解整个光照系统在 shader 下是如何工作的
urp 在 shader 中与光照相关的计算都放到了这个文件内,包括上面所说的 UniversalFragmentPBR() 和 UniversalFragmentBlinnPhong(),当然,这个文件不仅包含这些着色模型的计算,还有 GI(Global Illumination) 的相关计算,在上文没有细说的 BakedLit.shader 中就使用了文件里面的 GI 计算
这部分先开个头,详细的分析将在下周进行