URP版本16.0.6
前一篇 URP Shader 源码阅读 (一) Simple Lit
打开 Lit.shader 文件;
1. 自定义材质球显示:
此处涉及到编辑器开发IMGUI,可参阅 Unity编辑器开发 Immediate Mode GUI (IMGUI)
MaterialEditor 使用自定义 ShaderGUI 来绘制 Shader 属性;
Lit.shader 使用的自定义 ShaderGUI 为 LitShader:
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UnityEditor.Rendering.Universal.ShaderGUI
{
internal class LitShader : BaseShaderGUI
{
static readonly string[] workflowModeNames = Enum.GetNames(typeof(LitGUI.WorkflowMode));
private LitGUI.LitProperties litProperties;
private LitDetailGUI.LitProperties litDetailProperties;
public override void FillAdditionalFoldouts(MaterialHeaderScopeList materialScopesList)
{
materialScopesList.RegisterHeaderScope(LitDetailGUI.Styles.detailInputs, Expandable.Details, _ => LitDetailGUI.DoDetailArea(litDetailProperties, materialEditor));
}
// collect properties from the material properties
public override void FindProperties(MaterialProperty[] properties)
{
base.FindProperties(properties);
litProperties = new LitGUI.LitProperties(properties);
litDetailProperties = new LitDetailGUI.LitProperties(properties);
}
//材质球首次加载和发生更改时调用
// material changed check
public override void ValidateMaterial(Material material)
{
SetMaterialKeywords(material, LitGUI.SetMaterialKeywords, LitDetailGUI.SetMaterialKeywords);
}
// material main surface options
public override void DrawSurfaceOptions(Material material)
{
// Use default labelWidth
EditorGUIUtility.labelWidth = 0f;
if (litProperties.workflowMode != null)
DoPopup(LitGUI.Styles.workflowModeText, litProperties.workflowMode, workflowModeNames);
base.DrawSurfaceOptions(material);
}
// material main surface inputs
public override void DrawSurfaceInputs(Material material)
{
base.DrawSurfaceInputs(material);
LitGUI.Inputs(litProperties, materialEditor, material);
DrawEmissionProperties(material, true);
DrawTileOffset(materialEditor, baseMapProp);
}
// material main advanced options
public override void DrawAdvancedOptions(Material material)
{
if (litProperties.reflections != null && litProperties.highlights != null)
{
materialEditor.ShaderProperty(litProperties.highlights, LitGUI.Styles.highlightsText);
materialEditor.ShaderProperty(litProperties.reflections, LitGUI.Styles.reflectionsText);
}
base.DrawAdvancedOptions(material);
}
public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
{
if (material == null)
throw new ArgumentNullException("material");
// _Emission property is lost after assigning Standard shader to the material
// thus transfer it before assigning the new shader
if (material.HasProperty("_Emission"))
{
material.SetColor("_EmissionColor", material.GetColor("_Emission"));
}
base.AssignNewShaderToMaterial(material, oldShader, newShader);
if (oldShader == null || !oldShader.name.Contains("Legacy Shaders/"))
{
SetupMaterialBlendMode(material);
return;
}
SurfaceType surfaceType = SurfaceType.Opaque;
BlendMode blendMode = BlendMode.Alpha;
if (oldShader.name.Contains("/Transparent/Cutout/"))
{
surfaceType = SurfaceType.Opaque;
material.SetFloat("_AlphaClip", 1);
}
else if (oldShader.name.Contains("/Transparent/"))
{
// NOTE: legacy shaders did not provide physically based transparency
// therefore Fade mode
surfaceType = SurfaceType.Transparent;
blendMode = BlendMode.Alpha;
}
material.SetFloat("_Blend", (float)blendMode);
material.SetFloat("_Surface", (float)surfaceType);
if (surfaceType == SurfaceType.Opaque)
{
material.DisableKeyword("_SURFACE_TYPE_TRANSPARENT");
}
else
{
material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
}
if (oldShader.name.Equals("Standard (Specular setup)"))
{
material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Specular);
Texture texture = material.GetTexture("_SpecGlossMap");
if (texture != null)
material.SetTexture("_MetallicSpecGlossMap", texture);
}
else
{
material.SetFloat("_WorkflowMode", (float)LitGUI.WorkflowMode.Metallic);
Texture texture = material.GetTexture("_MetallicGlossMap");
if (texture != null)
material.SetTexture("_MetallicSpecGlossMap", texture);
}
}
}
}
基类 BaseShaderGUI,绘制 GUI 入口:
public override void OnGUI(MaterialEditor materialEditorIn, MaterialProperty[] properties)
{
if (materialEditorIn == null)
throw new ArgumentNullException("materialEditorIn");
materialEditor = materialEditorIn;
Material material = materialEditor.target as Material;
FindProperties(properties); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly
// Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing
// material to a universal shader.
if (m_FirstTimeApply)
{
OnOpenGUI(material, materialEditorIn);
m_FirstTimeApply = false;
}
ShaderPropertiesGUI(material);
}
/// <summary>
/// Filter for the surface options, surface inputs, details and advanced foldouts.
/// </summary>
protected virtual uint materialFilter => uint.MaxValue;
/// <summary>
/// Draws the GUI for the material.
/// </summary>
/// <param name="material">The material to use.</param>
/// <param name="materialEditor">The material editor to use.</param>
public virtual void OnOpenGUI(Material material, MaterialEditor materialEditor)
{
var filter = (Expandable)materialFilter;
// Generate the foldouts
if (filter.HasFlag(Expandable.SurfaceOptions))
m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceOptions, (uint)Expandable.SurfaceOptions, DrawSurfaceOptions);
if (filter.HasFlag(Expandable.SurfaceInputs))
m_MaterialScopeList.RegisterHeaderScope(Styles.SurfaceInputs, (uint)Expandable.SurfaceInputs, DrawSurfaceInputs);
if (filter.HasFlag(Expandable.Details))
FillAdditionalFoldouts(m_MaterialScopeList);
if (filter.HasFlag(Expandable.Advanced))
m_MaterialScopeList.RegisterHeaderScope(Styles.AdvancedLabel, (uint)Expandable.Advanced, DrawAdvancedOptions);
}
/// <summary>
/// Draws the shader properties GUI.
/// </summary>
/// <param name="material">The material to use.</param>
public void ShaderPropertiesGUI(Material material)
{
m_MaterialScopeList.DrawHeaders(materialEditor, material);
}
LitShader:材质球发生变化时,设置 shader keyword:
//材质球首次加载和发生更改时调用
// material changed check
public override void ValidateMaterial(Material material)
{
SetMaterialKeywords(material, LitGUI.SetMaterialKeywords, LitDetailGUI.SetMaterialKeywords);
}
// this is the function used by Lit.shader, Unlit.shader GUIs
/// <summary>
/// Sets up the keywords for the material and shader.
/// </summary>
/// <param name="material">The material to use.</param>
/// <param name="shadingModelFunc">Function to set shading models.</param>
/// <param name="shaderFunc">Function to set some extra shader parameters.</param>
public static void SetMaterialKeywords(Material material, Action<Material> shadingModelFunc = null, Action<Material> shaderFunc = null)
{
UpdateMaterialSurfaceOptions(material, automaticRenderQueue: true);
// Setup double sided GI based on Cull state
if (material.HasProperty(Property.CullMode))
material.doubleSidedGI = (RenderFace)material.GetFloat(Property.CullMode) != RenderFace.Front;
// Temporary fix for lightmapping. TODO: to be replaced with attribute tag.
if (material.HasProperty("_MainTex") && material.HasProperty("_BaseMap"))
{
material.SetTexture("_MainTex", material.GetTexture("_BaseMap"));
material.SetTextureScale("_MainTex", material.GetTextureScale("_BaseMap"));
material.SetTextureOffset("_MainTex", material.GetTextureOffset("_BaseMap"));
}
if (material.HasProperty("_Color") && material.HasProperty("_BaseColor"))
material.SetColor("_Color", material.GetColor("_BaseColor"));
// Emission
if (material.HasProperty(Property.EmissionColor))
MaterialEditor.FixupEmissiveFlag(material);
bool shouldEmissionBeEnabled =
(material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
// Not sure what this is used for, I don't see this property declared by any Unity shader in our repo...
// I'm guessing it is some kind of legacy material upgrade support thing? Or maybe just dead code now...
if (material.HasProperty("_EmissionEnabled") && !shouldEmissionBeEnabled)
shouldEmissionBeEnabled = material.GetFloat("_EmissionEnabled") >= 0.5f;
CoreUtils.SetKeyword(material, ShaderKeywordStrings._EMISSION, shouldEmissionBeEnabled);
// Normal Map
if (material.HasProperty("_BumpMap"))
CoreUtils.SetKeyword(material, ShaderKeywordStrings._NORMALMAP, material.GetTexture("_BumpMap"));
BaseShaderGUI.UpdateMotionVectorKeywordsAndPass(material);
// Shader specific keyword functions
shadingModelFunc?.Invoke(material);
shaderFunc?.Invoke(material);
}
2. Lit.shader 源码:
Shader "Universal Render Pipeline/Lit"
{
Properties
{
// Specular vs Metallic workflow
//当前的工作流
_WorkflowMode("WorkflowMode", Float) = 1.0
//主贴图和颜色
[MainTexture] _BaseMap("Albedo", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1,1,1,1)
//Alpha测试阈值
_Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
//平滑度和来源
_Smoothness("Smoothness", Range(0.0, 1.0)) = 0.5
_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" {}
//高度贴图
_Parallax("Scale", Range(0.005, 0.08)) = 0.005
_ParallaxMap("Height Map", 2D) = "black" {}
//遮挡贴图
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_OcclusionMap("Occlusion", 2D) = "white" {}
//自发光颜色和贴图
[HDR] _EmissionColor("Color", Color) = (0,0,0)
_EmissionMap("Emission", 2D) = "white" {}
//细节纹理
_DetailMask("Detail Mask", 2D) = "white" {}
_DetailAlbedoMapScale("Scale", Range(0.0, 2.0)) = 1.0
_DetailAlbedoMap("Detail Albedo x2", 2D) = "linearGrey" {}
_DetailNormalMapScale("Scale", Range(0.0, 2.0)) = 1.0
[Normal] _DetailNormalMap("Normal Map", 2D) = "bump" {}
//透明图层,没使用到
// SRP batching compatibility for Clear Coat (Not used in Lit)
[HideInInspector] _ClearCoatMask("_ClearCoatMask", Float) = 0.0
[HideInInspector] _ClearCoatSmoothness("_ClearCoatSmoothness", Float) = 0.0
// Blending state
_Surface("__surface", Float) = 0.0 //记录是否是透明
_Blend("__blend", Float) = 0.0 //记录混合模式
_Cull("__cull", Float) = 2.0 //正反面剔除设置
[ToggleUI] _AlphaClip("__clip", Float) = 0.0 //AlphaTest是否开启
[HideInInspector] _SrcBlend("__src", Float) = 1.0
[HideInInspector] _DstBlend("__dst", Float) = 0.0
[HideInInspector] _SrcBlendAlpha("__srcA", Float) = 1.0
[HideInInspector] _DstBlendAlpha("__dstA", Float) = 0.0
[HideInInspector] _ZWrite("__zw", Float) = 1.0 //是否写入深度
[HideInInspector] _BlendModePreserveSpecular("_BlendModePreserveSpecular", Float) = 1.0
[HideInInspector] _AlphaToMask("__alphaToMask", Float) = 0.0
[HideInInspector] _AddPrecomputedVelocity("_AddPrecomputedVelocity", Float) = 0.0
//接收阴影
[ToggleUI] _ReceiveShadows("Receive Shadows", Float) = 1.0
// Editmode props,编辑器使用,用于确定最终renderQueue
_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
[HideInInspector][NoScaleOffset]unity_Lightmaps("unity_Lightmaps", 2DArray) = "" {}
[HideInInspector][NoScaleOffset]unity_LightmapsInd("unity_LightmapsInd", 2DArray) = "" {}
[HideInInspector][NoScaleOffset]unity_ShadowMasks("unity_ShadowMasks", 2DArray) = "" {}
}
SubShader
{
// Universal Pipeline tag is required. If Universal render pipeline is not set in the graphics settings
// this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this
// material work with both Universal Render Pipeline and Builtin Unity Pipeline
Tags
{
"RenderType" = "Opaque" //自定义ShaderGUI中会覆盖设置正确的值
"RenderPipeline" = "UniversalPipeline" //标识该SubShader用于Universal Pipeline
"UniversalMaterialType" = "Lit" //延迟渲染使用
"IgnoreProjector" = "True" //忽略投影
}
LOD 300
// ------------------------------------------------------------------
// Forward pass. Shades all light in a single pass. GI + emission + Fog
// 前向渲染pass,计算所有的光照
Pass
{
// Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Universal Render Pipeline
Name "ForwardLit"
Tags
{
"LightMode" = "UniversalForward"
}
// -------------------------------------
// Render State Commands
Blend[_SrcBlend][_DstBlend], [_SrcBlendAlpha][_DstBlendAlpha]
ZWrite[_ZWrite]
Cull[_Cull]
AlphaToMask[_AlphaToMask]
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _RECEIVE_SHADOWS_OFF
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _SURFACE_TYPE_TRANSPARENT
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ _ALPHAPREMULTIPLY_ON _ALPHAMODULATE_ON
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local_fragment _OCCLUSIONMAP
#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature_local_fragment _SPECULAR_SETUP
// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#pragma multi_compile _ _LIGHT_LAYERS
#pragma multi_compile _ _FORWARD_PLUS
#include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl"
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
#pragma multi_compile_fog
#pragma multi_compile_fragment _ DEBUG_DISPLAY
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl"
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
ENDHLSL
}
// 阴影投射pass
Pass
{
Name "ShadowCaster"
Tags
{
"LightMode" = "ShadowCaster"
}
// -------------------------------------
// Render State Commands
ZWrite On
ZTest LEqual
ColorMask 0
Cull[_Cull]
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Universal Pipeline keywords
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
// This is used during shadow map generation to differentiate between directional and punctual light shadows, as they use different formulas to apply Normal Bias
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
ENDHLSL
}
// 延迟渲染 渲染GBuffer
Pass
{
// Lightmode matches the ShaderPassName set in UniversalRenderPipeline.cs. SRPDefaultUnlit and passes with
// no LightMode tag are also rendered by Universal Render Pipeline
Name "GBuffer"
Tags
{
"LightMode" = "UniversalGBuffer"
}
// -------------------------------------
// Render State Commands
ZWrite[_ZWrite]
ZTest LEqual
Cull[_Cull]
HLSLPROGRAM
#pragma target 4.5
// Deferred Rendering Path does not support the OpenGL-based graphics API:
// Desktop OpenGL, OpenGL ES 3.0, WebGL 2.0.
// 指定的api不编译该shader
#pragma exclude_renderers gles3 glcore
// -------------------------------------
// Shader Stages
#pragma vertex LitGBufferPassVertex
#pragma fragment LitGBufferPassFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local_fragment _ALPHATEST_ON
//#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local_fragment _OCCLUSIONMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF
#pragma shader_feature_local_fragment _SPECULAR_SETUP
#pragma shader_feature_local _RECEIVE_SHADOWS_OFF
// -------------------------------------
// Universal Pipeline keywords
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
//#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
//#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
#pragma multi_compile_fragment _ _RENDER_PASS_ENABLED
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile _ DYNAMICLIGHTMAP_ON
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl"
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#pragma instancing_options renderinglayer
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitGBufferPass.hlsl"
ENDHLSL
}
// 渲染深度纹理
Pass
{
Name "DepthOnly"
Tags
{
"LightMode" = "DepthOnly"
}
// -------------------------------------
// Render State Commands
ZWrite On
ColorMask R
Cull[_Cull]
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex DepthOnlyVertex
#pragma fragment DepthOnlyFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
ENDHLSL
}
// This pass is used when drawing to a _CameraNormalsTexture texture
// 渲染深度法线纹理 _CameraNormalsTexture
Pass
{
Name "DepthNormals"
Tags
{
"LightMode" = "DepthNormals"
}
// -------------------------------------
// Render State Commands
ZWrite On
Cull[_Cull]
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex DepthNormalsVertex
#pragma fragment DepthNormalsFragment
// -------------------------------------
// Material Keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _PARALLAXMAP
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local _ALPHATEST_ON
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
// -------------------------------------
// Unity defined keywords
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
// -------------------------------------
// Universal Pipeline keywords
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl"
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitDepthNormalsPass.hlsl"
ENDHLSL
}
// This pass it not used during regular rendering, only for lightmap baking.
// 烘培光照贴图使用
Pass
{
Name "Meta"
Tags
{
"LightMode" = "Meta"
}
// -------------------------------------
// Render State Commands
Cull Off
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex UniversalVertexMeta
#pragma fragment UniversalFragmentMetaLit
// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _SPECULAR_SETUP
#pragma shader_feature_local_fragment _EMISSION
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature_local _ _DETAIL_MULX2 _DETAIL_SCALED
#pragma shader_feature_local_fragment _SPECGLOSSMAP
#pragma shader_feature EDITOR_VISUALIZATION
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitMetaPass.hlsl"
ENDHLSL
}
//用于2D渲染
Pass
{
Name "Universal2D"
Tags
{
"LightMode" = "Universal2D"
}
// -------------------------------------
// Render State Commands
Blend[_SrcBlend][_DstBlend]
ZWrite[_ZWrite]
Cull[_Cull]
HLSLPROGRAM
#pragma target 2.0
// -------------------------------------
// Shader Stages
#pragma vertex vert
#pragma fragment frag
// -------------------------------------
// Material Keywords
#pragma shader_feature_local_fragment _ALPHATEST_ON
#pragma shader_feature_local_fragment _ALPHAPREMULTIPLY_ON
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl"
// -------------------------------------
// Includes
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Universal2D.hlsl"
ENDHLSL
}
// 运动矢量
Pass
{
Name "MotionVectors"
Tags { "LightMode" = "MotionVectors" }
ColorMask RG
HLSLPROGRAM
#pragma shader_feature_local _ALPHATEST_ON
#pragma multi_compile_fragment _ LOD_FADE_CROSSFADE
#pragma shader_feature_local_vertex _ADD_PRECOMPUTED_VELOCITY
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl"
ENDHLSL
}
}
FallBack "Hidden/Universal Render Pipeline/FallbackError"
CustomEditor "UnityEditor.Rendering.Universal.ShaderGUI.LitShader"
}
3. ForwardLit pass:
顶点着色器 LitPassVertex:
// Used in Standard (Physically Based) shader
Varyings LitPassVertex(Attributes input)
{
Varyings output = (Varyings)0; // 0 初始化结构体
UNITY_SETUP_INSTANCE_ID(input); // 设置全局变量 static uint unity_InstanceID;
UNITY_TRANSFER_INSTANCE_ID(input, output); // 将 instanceID 传递到 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);
// 所有额外光源顶点光照计算,使用兰伯特漫反射光照模型
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
// 计算雾效系数
half fogFactor = 0;
#if !defined(_FOG_FRAGMENT)
fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
#endif
// uv缩放偏移
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);
// already normalized from normal transform to WS.
output.normalWS = normalInput.normalWS;
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR) || defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
real sign = input.tangentOS.w * GetOddNegativeScale();
half4 tangentWS = half4(normalInput.tangentWS.xyz, sign);
#endif
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR)
output.tangentWS = tangentWS;
#endif
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
// 计算切线空间下viewDir
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);
half3 viewDirTS = GetViewDirectionTangentSpace(tangentWS, output.normalWS, viewDirWS);
output.viewDirTS = viewDirTS;
#endif
// 光照贴图uv缩放偏移
OUTPUT_LIGHTMAP_UV(input.staticLightmapUV, unity_LightmapST, output.staticLightmapUV);
#ifdef DYNAMICLIGHTMAP_ON
output.dynamicLightmapUV = input.dynamicLightmapUV.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
// 顶点球谐光照
OUTPUT_SH4(vertexInput.positionWS, output.normalWS.xyz, GetWorldSpaceNormalizeViewDir(vertexInput.positionWS), output.vertexSH);
#ifdef _ADDITIONAL_LIGHTS_VERTEX
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
#else
output.fogFactor = fogFactor;
#endif
#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;
}
输入输出:
struct Attributes
{
float4 positionOS : POSITION; // 对象空间位置
float3 normalOS : NORMAL; // 对象空间法线
float4 tangentOS : TANGENT; // 对象空间切线
float2 texcoord : TEXCOORD0; // 纹理坐标
float2 staticLightmapUV : TEXCOORD1; // 静态光照贴图uv坐标
float2 dynamicLightmapUV : TEXCOORD2; // 动态光照贴图uv坐标
UNITY_VERTEX_INPUT_INSTANCE_ID // uint instanceID : SV_InstanceID
};
struct Varyings
{
float2 uv : TEXCOORD0;
#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
float3 positionWS : TEXCOORD1; // 世界空间位置
#endif
float3 normalWS : TEXCOORD2; // 世界空间法线
#if defined(REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR)
half4 tangentWS : TEXCOORD3; // 世界空间切线,xyz: tangent, w: sign
#endif
#ifdef _ADDITIONAL_LIGHTS_VERTEX //额外光照采用顶点计算
half4 fogFactorAndVertexLight : TEXCOORD5; // x: fogFactor, yzw: vertex light
#else
half fogFactor : TEXCOORD5; // 雾效系数
#endif
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
float4 shadowCoord : TEXCOORD6; // 阴影坐标
#endif
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
half3 viewDirTS : TEXCOORD7; // 切线空间观察方向
#endif
DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 8); //float2 staticLightmapUV 或者 half3 vertexSH
#ifdef DYNAMICLIGHTMAP_ON
float2 dynamicLightmapUV : TEXCOORD9; // Dynamic lightmap UVs
#endif
float4 positionCS : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID // uint instanceID : SV_InstanceID
UNITY_VERTEX_OUTPUT_STEREO
};
获取顶点在各坐标系下的位置:
VertexPositionInputs GetVertexPositionInputs(float3 positionOS)
{
VertexPositionInputs input;
input.positionWS = TransformObjectToWorld(positionOS);
input.positionVS = TransformWorldToView(input.positionWS);
input.positionCS = TransformWorldToHClip(input.positionWS);
//为防止除以w导致非线性插值,留待在片元着色器中执行,此处ndc应该是用于计算屏幕纹理坐标
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;
}
获取切线空间各坐标轴在世界空间下的表示:
VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS)
{
VertexNormalInputs tbn;
// mikkts space compliant. only normalize when extracting normal at frag.
//tangentOS.w表示切线空间的手性,OddNegativeScale表示xyz中的奇数次缩放
real sign = real(tangentOS.w) * GetOddNegativeScale();
tbn.normalWS = TransformObjectToWorldNormal(normalOS);
tbn.tangentWS = real3(TransformObjectToWorldDir(tangentOS.xyz));
tbn.bitangentWS = real3(cross(tbn.normalWS, float3(tbn.tangentWS))) * sign;
return tbn;
}
所有额外光源顶点光照计算,使用兰伯特漫反射光照模型:
half3 VertexLighting(float3 positionWS, half3 normalWS)
{
half3 vertexLightColor = half3(0.0, 0.0, 0.0);
#ifdef _ADDITIONAL_LIGHTS_VERTEX
uint lightsCount = GetAdditionalLightsCount();
uint meshRenderingLayers = GetMeshRenderingLayer();
//渲染时,urp将所有光源信息传递给shader
LIGHT_LOOP_BEGIN(lightsCount)
Light light = GetAdditionalLight(lightIndex, positionWS);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
half3 lightColor = light.color * light.distanceAttenuation;
//不考虑阴影,兰伯特漫反射
vertexLightColor += LightingLambert(lightColor, light.direction, normalWS);
}
LIGHT_LOOP_END
#endif
return vertexLightColor;
}
获取 AdditionalLight:
// Fills a light struct given a loop i index. This will convert the i
// index to a perObjectLightIndex
Light GetAdditionalLight(uint i, float3 positionWS)
{
#if USE_FORWARD_PLUS
int lightIndex = i;
#else
int lightIndex = GetPerObjectLightIndex(i);
#endif
return GetAdditionalPerObjectLight(lightIndex, positionWS);
}
// Fills a light struct given a perObjectLightIndex
Light GetAdditionalPerObjectLight(int perObjectLightIndex, float3 positionWS)
{
//读取光源信息
// Abstraction over Light input constants
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
float4 lightPositionWS = _AdditionalLightsBuffer[perObjectLightIndex].position;
half3 color = _AdditionalLightsBuffer[perObjectLightIndex].color.rgb;
half4 distanceAndSpotAttenuation = _AdditionalLightsBuffer[perObjectLightIndex].attenuation;
half4 spotDirection = _AdditionalLightsBuffer[perObjectLightIndex].spotDirection;
uint lightLayerMask = _AdditionalLightsBuffer[perObjectLightIndex].layerMask;
#else
float4 lightPositionWS = _AdditionalLightsPosition[perObjectLightIndex];
half3 color = _AdditionalLightsColor[perObjectLightIndex].rgb;
half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[perObjectLightIndex];
half4 spotDirection = _AdditionalLightsSpotDir[perObjectLightIndex];
uint lightLayerMask = asuint(_AdditionalLightsLayerMasks[perObjectLightIndex]);
#endif
// Directional lights store direction in lightPosition.xyz and have .w set to 0.0.
// This way the following code will work for both directional and punctual lights.
float3 lightVector = lightPositionWS.xyz - positionWS * lightPositionWS.w;
float distanceSqr = max(dot(lightVector, lightVector), HALF_MIN);
half3 lightDirection = half3(lightVector * rsqrt(distanceSqr)); //rsqrt 平方根的倒数
// full-float precision required on some platforms
//计算距离衰减
float attenuation = DistanceAttenuation(distanceSqr, distanceAndSpotAttenuation.xy) * AngleAttenuation(spotDirection.xyz, lightDirection, distanceAndSpotAttenuation.zw);
Light light;
light.direction = lightDirection;
light.distanceAttenuation = attenuation;
//阴影缩减后续计算
light.shadowAttenuation = 1.0; // This value can later be overridden in GetAdditionalLight(uint i, float3 positionWS, half4 shadowMask)
light.color = color;
light.layerMask = lightLayerMask;
return light;
}
计算雾效系数:
real ComputeFogFactor(float zPositionCS)
{
//将zPositionCS的范围修正到[0, Far]
float clipZ_0Far = UNITY_Z_0_FAR_FROM_CLIPSPACE(zPositionCS);
return ComputeFogFactorZ0ToFar(clipZ_0Far);
}
real ComputeFogFactorZ0ToFar(float z)
{
#if defined(FOG_LINEAR)
// factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
float fogFactor = saturate(z * 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 * z);
#else
return real(0.0);
#endif
}
计算切线空间下viewDir:
// Return view direction in tangent space, make sure tangentWS.w is already multiplied by GetOddNegativeScale()
half3 GetViewDirectionTangentSpace(half4 tangentWS, half3 normalWS, half3 viewDirWS)
{
// must use interpolated tangent, bitangent and normal before they are normalized in the pixel shader.
half3 unnormalizedNormalWS = normalWS;
const half renormFactor = 1.0 / length(unnormalizedNormalWS);
// use bitangent on the fly like in hdrp
// IMPORTANT! If we ever support Flip on double sided materials ensure bitangent and tangent are NOT flipped.
half crossSign = (tangentWS.w > 0.0 ? 1.0 : -1.0); // we do not need to multiple GetOddNegativeScale() here, as it is done in vertex shader
half3 bitang = crossSign * cross(normalWS.xyz, tangentWS.xyz);
half3 WorldSpaceNormal = renormFactor * normalWS.xyz; // we want a unit length Normal Vector node in shader graph
// to preserve mikktspace compliance we use same scale renormFactor as was used on the normal.
// This is explained in section 2.2 in "surface gradient based bump mapping framework"
half3 WorldSpaceTangent = renormFactor * tangentWS.xyz;
half3 WorldSpaceBiTangent = renormFactor * bitang;
// 若将切线空间各坐标轴在世界空间下的表示按列排列,则构成切线空间到世界空间的变换矩阵
// 由于需要世界空间到切线空间的变换矩阵,则按行排列
half3x3 tangentSpaceTransform = half3x3(WorldSpaceTangent, WorldSpaceBiTangent, WorldSpaceNormal);
half3 viewDirTS = mul(tangentSpaceTransform, viewDirWS);
return viewDirTS;
}
计算阴影坐标:
float4 GetShadowCoord(VertexPositionInputs vertexInput)
{
#if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(_SURFACE_TYPE_TRANSPARENT)
//屏幕空间阴影纹理,和先前计算input.positionNDC一样
return ComputeScreenPos(vertexInput.positionCS);
#else
return TransformWorldToShadowCoord(vertexInput.positionWS);
#endif
}
//将世界坐标转换为阴影坐标
float4 TransformWorldToShadowCoord(float3 positionWS)
{
#ifdef _MAIN_LIGHT_SHADOWS_CASCADE
half cascadeIndex = ComputeCascadeIndex(positionWS);
#else
half cascadeIndex = half(0.0);
#endif
float4 shadowCoord = mul(_MainLightWorldToShadow[cascadeIndex], float4(positionWS, 1.0));
return float4(shadowCoord.xyz, 0);
}
片元着色器 LitPassFragment:
// Used in Standard (Physically Based) shader
void LitPassFragment(
Varyings input
, out half4 outColor : SV_Target0
#ifdef _WRITE_RENDERING_LAYERS
, out float4 outRenderingLayers : SV_Target1
#endif
)
{
UNITY_SETUP_INSTANCE_ID(input); // 设置全局变量 static uint unity_InstanceID;
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); //设置 int unity_StereoEyeIndex;
#if defined(_PARALLAXMAP)
#if defined(REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR)
half3 viewDirTS = input.viewDirTS;
#else
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
half3 viewDirTS = GetViewDirectionTangentSpace(input.tangentWS, input.normalWS, viewDirWS);
#endif
// 使用 _ParallaxMap 偏移 uv 坐标
ApplyPerPixelDisplacement(viewDirTS, input.uv);
#endif
// 获取光照表面数据
SurfaceData surfaceData;
InitializeStandardLitSurfaceData(input.uv, surfaceData);
#ifdef LOD_FADE_CROSSFADE
LODFadeCrossFade(input.positionCS);
#endif
// 获取光照计算相关数据
InputData inputData;
InitializeInputData(input, surfaceData.normalTS, inputData);
SETUP_DEBUG_TEXTURE_DATA(inputData, input.uv, _BaseMap);
#ifdef _DBUFFER
// 应用Decal贴花,修改 SurfaceData,以及 InputData 法线
ApplyDecalToSurfaceData(input.positionCS, surfaceData, inputData);
#endif
// 基于物理的光照计算
half4 color = UniversalFragmentPBR(inputData, surfaceData);
// 混合雾效
color.rgb = MixFog(color.rgb, inputData.fogCoord);
color.a = OutputAlpha(color.a, IsSurfaceTypeTransparent(_Surface));
outColor = color;
#ifdef _WRITE_RENDERING_LAYERS
uint renderingLayers = GetMeshRenderingLayer();
outRenderingLayers = float4(EncodeMeshRenderingLayer(renderingLayers), 0, 0, 0);
#endif
}
获取光照表面数据:
inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
{
// 采样 albedo
half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
// 计算最终 alpha 以及进行 AlphaTest
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
// 计算最终 albedo
outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;
// BlendMode.Multiply
outSurfaceData.albedo = AlphaModulate(outSurfaceData.albedo, outSurfaceData.alpha);
// 获取金属度/高光颜色 以及光滑度
half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
#if _SPECULAR_SETUP
outSurfaceData.metallic = half(1.0);
outSurfaceData.specular = specGloss.rgb;
#else
outSurfaceData.metallic = specGloss.r;
outSurfaceData.specular = half3(0.0, 0.0, 0.0);
#endif
outSurfaceData.smoothness = specGloss.a;
// 切线空间的法线
outSurfaceData.normalTS = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap), _BumpScale);
// 采样遮挡贴图,1表示不遮挡
outSurfaceData.occlusion = SampleOcclusion(uv);
// 采样自发光颜色
outSurfaceData.emission = SampleEmission(uv, _EmissionColor.rgb, TEXTURE2D_ARGS(_EmissionMap, sampler_EmissionMap));
// 透明漆
#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
half2 clearCoat = SampleClearCoat(uv);
outSurfaceData.clearCoatMask = clearCoat.r;
outSurfaceData.clearCoatSmoothness = clearCoat.g;
#else
outSurfaceData.clearCoatMask = half(0.0);
outSurfaceData.clearCoatSmoothness = half(0.0);
#endif
#if defined(_DETAIL)
half detailMask = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, uv).a;
float2 detailUv = uv * _DetailAlbedoMap_ST.xy + _DetailAlbedoMap_ST.zw;
// albedo 添加细节 albedo
outSurfaceData.albedo = ApplyDetailAlbedo(detailUv, outSurfaceData.albedo, detailMask);
// 混合细节 normal
outSurfaceData.normalTS = ApplyDetailNormal(detailUv, outSurfaceData.normalTS, detailMask);
#endif
}
获取光照计算相关数据:
void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
{
inputData = (InputData)0;
// 世界位置
#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
inputData.positionWS = input.positionWS;
#endif
// 世界坐标下观察方向
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
inputData.viewDirectionWS = viewDirWS;
// 最终世界法线
#if defined(_NORMALMAP) || defined(_DETAIL)
float sgn = input.tangentWS.w; // should be either +1 or -1
float3 bitangent = sgn * cross(input.normalWS.xyz, input.tangentWS.xyz);
// 实际应该将切线空间各坐标轴在世界空间下的表示按列摆放,构成切线空间到世界空间的变换矩阵
// 但为了方便存储,按行摆放,TransformTangentToWorld 函数计算时使用左乘
half3x3 tangentToWorld = half3x3(input.tangentWS.xyz, bitangent.xyz, input.normalWS.xyz);
#if defined(_NORMALMAP)
inputData.tangentToWorld = tangentToWorld;
#endif
inputData.normalWS = TransformTangentToWorld(normalTS, tangentToWorld);
#else
inputData.normalWS = input.normalWS;
#endif
inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS);
// 阴影坐标
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
#else
inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
// 雾效系数
#ifdef _ADDITIONAL_LIGHTS_VERTEX
inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactorAndVertexLight.x);
inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
#else
inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactor);
#endif
// 全局光照
#if defined(DYNAMICLIGHTMAP_ON)
// 启用动态光照贴图
// 静态光照贴图是否启用都走这个流程,宏里面有区分
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.dynamicLightmapUV, input.vertexSH, inputData.normalWS);
#elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2))
// 只使用光照探针
inputData.bakedGI = SAMPLE_GI(input.vertexSH,
GetAbsolutePositionWS(inputData.positionWS),
inputData.normalWS,
inputData.viewDirectionWS,
input.positionCS.xy);
#else
// 静态光照贴图是否启用都走这个流程
inputData.bakedGI = SAMPLE_GI(input.staticLightmapUV, input.vertexSH, inputData.normalWS);
#endif
// 顶点对应的屏幕uv坐标
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
// 采样shadowMask
inputData.shadowMask = SAMPLE_SHADOWMASK(input.staticLightmapUV);
#if defined(DEBUG_DISPLAY)
#if defined(DYNAMICLIGHTMAP_ON)
inputData.dynamicLightmapUV = input.dynamicLightmapUV;
#endif
#if defined(LIGHTMAP_ON)
inputData.staticLightmapUV = input.staticLightmapUV;
#else
inputData.vertexSH = input.vertexSH;
#endif
#endif
}
// this function is intended to work on Normals (handles non-uniform scale)
// tangentToWorld is the matrix representing the transformation of a normal from tangent to world space
real3 TransformTangentToWorld(float3 normalTS, real3x3 tangentToWorld, bool doNormalize = false)
{
//tangentToWorld矩阵是实际矩阵的转置,故计算时要左乘
// Note matrix is in row major convention with left multiplication as it is build on the fly
real3 result = mul(normalTS, tangentToWorld);
if (doNormalize)
return SafeNormalize(result);
return result;
}
// We either sample GI from baked lightmap or from probes.
// If lightmap: sampleData.xy = lightmapUV
// If probe: sampleData.xyz = L2 SH terms
#if defined(LIGHTMAP_ON) && defined(DYNAMICLIGHTMAP_ON) //烘培的光照贴图 和 实时的光照贴图
#define SAMPLE_GI(staticLmName, dynamicLmName, shName, normalWSName) SampleLightmap(staticLmName, dynamicLmName, normalWSName)
#elif defined(DYNAMICLIGHTMAP_ON) //实时的光照贴图
#define SAMPLE_GI(staticLmName, dynamicLmName, shName, normalWSName) SampleLightmap(0, dynamicLmName, normalWSName)
#elif defined(LIGHTMAP_ON) //烘培的光照贴图
#define SAMPLE_GI(staticLmName, shName, normalWSName) SampleLightmap(staticLmName, 0, normalWSName)
#elif defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2) //光照探针
#define SAMPLE_GI(shName, absolutePositionWS, normalWS, viewDir, positionSS) SampleProbeVolumePixel(shName, absolutePositionWS, normalWS, viewDir, positionSS)
#else //逐像素球谐光照
#define SAMPLE_GI(staticLmName, shName, normalWSName) SampleSHPixel(shName, normalWSName)
#endif
float2 GetNormalizedScreenSpaceUV(float2 positionCS)
{
//在片元着色器中,positionCS表示屏幕像素坐标,归一化则除以屏幕宽高
float2 normalizedScreenSpaceUV = positionCS.xy * rcp(GetScaledScreenParams().xy);
TransformNormalizedScreenUV(normalizedScreenSpaceUV); //处理UNITY_UV_STARTS_AT_TOP
return normalizedScreenSpaceUV;
}
应用Decal贴花:
void ApplyDecalToSurfaceData(float4 positionCS, inout SurfaceData surfaceData, inout InputData inputData)
{
#ifdef _SPECULAR_SETUP
half metallic = 0;
ApplyDecal(positionCS,
surfaceData.albedo,
surfaceData.specular,
inputData.normalWS,
metallic,
surfaceData.occlusion,
surfaceData.smoothness);
#else
half3 specular = 0;
ApplyDecal(positionCS,
surfaceData.albedo,
specular,
inputData.normalWS,
surfaceData.metallic,
surfaceData.occlusion,
surfaceData.smoothness);
#endif
}
void ApplyDecal(float4 positionCS,
inout half3 baseColor,
inout half3 specularColor,
inout half3 normalWS,
inout half metallic,
inout half occlusion,
inout half smoothness)
{
//使用Load读取在指定像素处的纹理值
FETCH_DBUFFER(DBuffer, _DBufferTexture, int2(positionCS.xy));
//读取贴花颜色,法线等其他信息
DecalSurfaceData decalSurfaceData;
DECODE_FROM_DBUFFER(DBuffer, decalSurfaceData);
// using alpha compositing https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch23.html, mean weight of 1 is neutral
// Note: We only test weight (i.e decalSurfaceData.xxx.w is < 1.0) if it can save something
baseColor.xyz = baseColor.xyz * decalSurfaceData.baseColor.w + decalSurfaceData.baseColor.xyz;
#if defined(_DBUFFER_MRT2) || defined(_DBUFFER_MRT3)
// Always test the normal as we can have decompression artifact
if (decalSurfaceData.normalWS.w < 1.0)
{
normalWS.xyz = normalize(normalWS.xyz * decalSurfaceData.normalWS.w + decalSurfaceData.normalWS.xyz);
}
#endif
#if defined(_DBUFFER_MRT3)
#ifdef _SPECULAR_SETUP
if (decalSurfaceData.MAOSAlpha.x < 1.0)
{
half3 decalSpecularColor = ComputeFresnel0((decalSurfaceData.baseColor.w < 1.0) ? decalSurfaceData.baseColor.xyz : half3(1.0, 1.0, 1.0), decalSurfaceData.metallic, DEFAULT_SPECULAR_VALUE);
specularColor = specularColor * decalSurfaceData.MAOSAlpha + decalSpecularColor * (1.0f - decalSurfaceData.MAOSAlpha);
}
#else
metallic = metallic * decalSurfaceData.MAOSAlpha + decalSurfaceData.metallic;
#endif
occlusion = occlusion * decalSurfaceData.MAOSAlpha + decalSurfaceData.occlusion;
smoothness = smoothness * decalSurfaceData.MAOSAlpha + decalSurfaceData.smoothness;
#endif
}
基于物理的光照计算:
half4 UniversalFragmentPBR(InputData inputData, SurfaceData surfaceData)
{
#if defined(_SPECULARHIGHLIGHTS_OFF)
bool specularHighlightsOff = true;
#else
bool specularHighlightsOff = false;
#endif
// 初始化 BRDF 数据
BRDFData brdfData;
// NOTE: can modify "surfaceData"...
InitializeBRDFData(surfaceData, brdfData);
#if defined(DEBUG_DISPLAY)
half4 debugColor;
if (CanDebugOverrideOutputColor(inputData, surfaceData, brdfData, debugColor))
{
return debugColor;
}
#endif
// Clear-coat calculation...
BRDFData brdfDataClearCoat = CreateClearCoatBRDFData(surfaceData, brdfData);
// 阴影遮挡
half4 shadowMask = CalculateShadowMask(inputData);
// 环境遮挡
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
uint meshRenderingLayers = GetMeshRenderingLayer();
// 获取主光源
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
// NOTE: We don't apply AO to the GI here because it's done in the lighting calculation below...
// bakedGI 减去主光源
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI);
// 创建一个结构体,保存各种光照结果
LightingData lightingData = CreateLightingData(inputData, surfaceData);
// 计算全局光照颜色,间接光照结果
lightingData.giColor = GlobalIllumination(brdfData, brdfDataClearCoat, surfaceData.clearCoatMask,
inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS,
inputData.normalWS, inputData.viewDirectionWS, inputData.normalizedScreenSpaceUV);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
{
// 主光源光照颜色,基于物理的光照计算
lightingData.mainLightColor = LightingPhysicallyBased(brdfData, brdfDataClearCoat,
mainLight,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
// 额外逐像素光照
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
{
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
}
#endif //USE_FORWARD_PLUS
// 使用 USE_FORWARD_PLUS 时,pixelLightCount 为 0
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
LIGHT_LOOP_END
#endif //_ADDITIONAL_LIGHTS
// 顶点光照,只有漫反射
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
lightingData.vertexLightingColor += inputData.vertexLighting * brdfData.diffuse;
#endif
// 将所有光照结果累加
#if REAL_IS_HALF
// Clamp any half.inf+ to HALF_MAX
return min(CalculateFinalColor(lightingData, surfaceData.alpha), HALF_MAX);
#else
return CalculateFinalColor(lightingData, surfaceData.alpha);
#endif
}
初始化 BRDF 数据:
inline void InitializeBRDFData(inout SurfaceData surfaceData, out BRDFData brdfData)
{
InitializeBRDFData(surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.alpha, brdfData);
}
// Initialize BRDFData for material, managing both specular and metallic setup using shader keyword _SPECULAR_SETUP.
inline void InitializeBRDFData(half3 albedo, half metallic, half3 specular, half smoothness, inout half alpha, out BRDFData outBRDFData)
{
#ifdef _SPECULAR_SETUP // 高光工作流
// 由高光颜色获取高光反射率
half reflectivity = ReflectivitySpecular(specular);
half oneMinusReflectivity = half(1.0) - reflectivity;
// 为了能量守恒,一部分光线进行高光反射,一部分进行漫反射
half3 brdfDiffuse = albedo * oneMinusReflectivity;
half3 brdfSpecular = specular;
#else // 金属工作流
// 由金属度获取 (1 - 高光反射率)
half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
half reflectivity = half(1.0) - oneMinusReflectivity;
half3 brdfDiffuse = albedo * oneMinusReflectivity;
half3 brdfSpecular = lerp(kDielectricSpec.rgb, albedo, metallic);
#endif
InitializeBRDFDataDirect(albedo, brdfDiffuse, brdfSpecular, reflectivity, oneMinusReflectivity, smoothness, alpha, outBRDFData);
}
half ReflectivitySpecular(half3 specular)
{
return Max3(specular.r, specular.g, specular.b);
}
half OneMinusReflectivityMetallic(half metallic)
{
// metallic为0时,高光反射率为dielectricSpec,metallic为1时,高光反射率为1
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in kDielectricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = kDielectricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
inline void InitializeBRDFDataDirect(half3 albedo, half3 diffuse, half3 specular, half reflectivity, half oneMinusReflectivity, half smoothness, inout half alpha, out BRDFData outBRDFData)
{
outBRDFData = (BRDFData)0;
outBRDFData.albedo = albedo;
outBRDFData.diffuse = diffuse;
outBRDFData.specular = specular;
outBRDFData.reflectivity = reflectivity;
outBRDFData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(smoothness); // 视觉粗糙度
outBRDFData.roughness = max(PerceptualRoughnessToRoughness(outBRDFData.perceptualRoughness), HALF_MIN_SQRT);
outBRDFData.roughness2 = max(outBRDFData.roughness * outBRDFData.roughness, HALF_MIN);
outBRDFData.grazingTerm = saturate(smoothness + reflectivity);
outBRDFData.normalizationTerm = outBRDFData.roughness * half(4.0) + half(2.0);
outBRDFData.roughness2MinusOne = outBRDFData.roughness2 - half(1.0);
// Input is expected to be non-alpha-premultiplied while ROP is set to pre-multiplied blend.
// We use input color for specular, but (pre-)multiply the diffuse with alpha to complete the standard alpha blend equation.
// In shader: Cs' = Cs * As, in ROP: Cs' + Cd(1-As);
// i.e. we only alpha blend the diffuse part to background (transmittance).
#if defined(_ALPHAPREMULTIPLY_ON) // alpha 预乘
// TODO: would be clearer to multiply this once to accumulated diffuse lighting at end instead of the surface property.
outBRDFData.diffuse *= alpha;
#endif
}
计算全局光照颜色,间接光照结果:
half3 GlobalIllumination(BRDFData brdfData, BRDFData brdfDataClearCoat, float clearCoatMask,
half3 bakedGI, half occlusion, float3 positionWS,
half3 normalWS, half3 viewDirectionWS, float2 normalizedScreenSpaceUV)
{
// 视野反射方向
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
// 计算菲涅尔系数
half NoV = saturate(dot(normalWS, viewDirectionWS));
half fresnelTerm = Pow4(1.0 - NoV);
// 间接漫反射
half3 indirectDiffuse = bakedGI;
// 间接高光
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, positionWS, brdfData.perceptualRoughness, 1.0h, normalizedScreenSpaceUV);
// 获取间接光照结果
half3 color = EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
if (IsOnlyAOLightingFeatureEnabled())
{
color = half3(1,1,1); // "Base white" for AO debug lighting mode
}
#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
half3 coatIndirectSpecular = GlossyEnvironmentReflection(reflectVector, positionWS, brdfDataClearCoat.perceptualRoughness, 1.0h, normalizedScreenSpaceUV);
// TODO: "grazing term" causes problems on full roughness
half3 coatColor = EnvironmentBRDFClearCoat(brdfDataClearCoat, clearCoatMask, coatIndirectSpecular, fresnelTerm);
// Blend with base layer using khronos glTF recommended way using NoV
// Smooth surface & "ambiguous" lighting
// NOTE: fresnelTerm (above) is pow4 instead of pow5, but should be ok as blend weight.
half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * fresnelTerm;
return (color * (1.0 - coatFresnel * clearCoatMask) + coatColor) * occlusion;
#else
return color * occlusion;
#endif
}
基于物理的光照计算:
half3 LightingPhysicallyBased(BRDFData brdfData, BRDFData brdfDataClearCoat, Light light, half3 normalWS, half3 viewDirectionWS, half clearCoatMask, bool specularHighlightsOff)
{
return LightingPhysicallyBased(brdfData, brdfDataClearCoat, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS, clearCoatMask, specularHighlightsOff);
}
half3 LightingPhysicallyBased(BRDFData brdfData, BRDFData brdfDataClearCoat,
half3 lightColor, half3 lightDirectionWS, float lightAttenuation,
half3 normalWS, half3 viewDirectionWS,
half clearCoatMask, bool specularHighlightsOff)
{
half NdotL = saturate(dot(normalWS, lightDirectionWS));
half3 radiance = lightColor * (lightAttenuation * NdotL);
// 漫反射
half3 brdf = brdfData.diffuse; // 标准BRDF为 1/ Pi,历遗留问题默认乘以Pi
#ifndef _SPECULARHIGHLIGHTS_OFF
[branch] if (!specularHighlightsOff)
{
// brdf 高光
brdf += brdfData.specular * DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS);
#if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
// Clear coat evaluates the specular a second timw and has some common terms with the base specular.
// We rely on the compiler to merge these and compute them only once.
half brdfCoat = kDielectricSpec.r * DirectBRDFSpecular(brdfDataClearCoat, normalWS, lightDirectionWS, viewDirectionWS);
// Mix clear coat and base layer using khronos glTF recommended formula
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_clearcoat/README.md
// Use NoV for direct too instead of LoH as an optimization (NoV is light invariant).
half NoV = saturate(dot(normalWS, viewDirectionWS));
// Use slightly simpler fresnelTerm (Pow4 vs Pow5) as a small optimization.
// It is matching fresnel used in the GI/Env, so should produce a consistent clear coat blend (env vs. direct)
half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * Pow4(1.0 - NoV);
brdf = brdf * (1.0 - clearCoatMask * coatFresnel) + brdfCoat * clearCoatMask;
#endif // _CLEARCOAT
}
#endif // _SPECULARHIGHLIGHTS_OFF
return brdf * radiance;
}
brdf 高光:
half DirectBRDFSpecular(BRDFData brdfData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS)
{
float3 lightDirectionWSFloat3 = float3(lightDirectionWS);
float3 halfDir = SafeNormalize(lightDirectionWSFloat3 + float3(viewDirectionWS));
float NoH = saturate(dot(float3(normalWS), halfDir));
half LoH = half(saturate(dot(lightDirectionWSFloat3, halfDir)));
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// BRDFspec = (D * V * F) / 4.0
// D = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2
// V * F = 1.0 / ( LoH^2 * (roughness + 0.5) )
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
// Final BRDFspec = roughness^2 / ( NoH^2 * (roughness^2 - 1) + 1 )^2 * (LoH^2 * (roughness + 0.5) * 4.0)
// We further optimize a few light invariant terms
// brdfData.normalizationTerm = (roughness + 0.5) * 4.0 rewritten as roughness * 4.0 + 2.0 to a fit a MAD.
float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
half LoH2 = LoH * LoH;
half specularTerm = brdfData.roughness2 / ((d * d) * max(0.1h, LoH2) * brdfData.normalizationTerm);
// On platforms where half actually means something, the denominator has a risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if REAL_IS_HALF
specularTerm = specularTerm - HALF_MIN;
// Update: Conservative bump from 100.0 to 1000.0 to better match the full float specular look.
// Roughly 65504.0 / 32*2 == 1023.5,
// or HALF_MAX / ((mobile) MAX_VISIBLE_LIGHTS * 2),
// to reserve half of the per light range for specular and half for diffuse + indirect + emissive.
specularTerm = clamp(specularTerm, 0.0, 1000.0); // Prevent FP16 overflow on mobiles
#endif
return specularTerm;
}