[MainTexture]
主纹理,可以使用Material.mainTexture访问
[MainColor]
主颜色,可以使用Material.color访问
[ToggleOff]
使数值类型的属性在材质面板上显示为开关样式,并在编译指令中可声明关键字(大写属性名称+OFF
)
[Toggle]
使数值类型的属性在材质面板上显示为开关样式,并在编译指令中可声明关键字(大写属性名称+ON
)
只有标注了如下标签的SubShader,才能在URP中运行
"RenderPipeline" = "UniversalPipeline"
SubShader一共含有5个标签:
Tags{"LightMode" = "UniversalForward"}
不为 gles gles3 glcore功能级别的渲染平台编译Shader,使用4.5版本
#pragma exclude_renderers gles gles3 glcore
#pragma target 4.5
关键字分为三类:
// -------------------------------------
// 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 // 混合模式为Premultiply
#pragma shader_feature_local_fragment _EMISSION // 开启自发光
#pragma shader_feature_local_fragment _METALLICSPECGLOSSMAP // 金属(metallic)工作流使用金属贴图、或高光工作流使用高光贴图
#pragma shader_feature_local_fragment _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A // 当光滑度数据选择了Specular Alpha
#pragma shader_feature_local_fragment _OCCLUSIONMAP // 当添加了AO贴图
#pragma shader_feature_local_fragment _SPECULARHIGHLIGHTS_OFF // 当关闭了镜面高光反射
#pragma shader_feature_local_fragment _ENVIRONMENTREFLECTIONS_OFF // 当关闭了环境反射
#pragma shader_feature_local_fragment _SPECULAR_SETUP // 当选择了高光工作流
作为整体流水线的设置
在RenderPipelineAsset资源下设置即可
// -------------------------------------
// 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_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
// SSAO
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
// 其他的一些奇奇怪怪的参数
#pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3
#pragma multi_compile_fragment _ _LIGHT_LAYERS
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#pragma multi_compile _ _CLUSTERED_RENDERING
这一部分的关键字与场景光照有关,在Lighting设置面板中
// -------------------------------------
// Unity defined keywords
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING // 光照贴图阴影混合
#pragma multi_compile _ SHADOWS_SHADOWMASK //
#pragma multi_compile _ DIRLIGHTMAP_COMBINED //是否使用Directional模式光照烘培
#pragma multi_compile _ LIGHTMAP_ON //是否启用光照贴图
#pragma multi_compile _ DYNAMICLIGHTMAP_ON //是否启用动态光照贴图
#pragma multi_compile_fog //是否启用雾效
#pragma multi_compile_fragment _ DEBUG_DISPLAY //
//--------------------------------------
// GPU Instancing
#pragma multi_compile_instancing // 使Shader支持GPU实例
#pragma instancing_options renderinglayer
#pragma multi_compile _ DOTS_INSTANCING_ON
#pragma vertex LitPassVertex
#pragma fragment LitPassFragment
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitForwardPass.hlsl"
包含头文件:
#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/SurfaceInput.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ParallaxMapping.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DBuffer.hlsl"
常量缓冲区
CBUFFER_START(UnityPerMaterial)
... 可在Shader中使用
CBUFFER_END
纹理
TEXTURE2D(_ParallaxMap); SAMPLER(sampler_ParallaxMap);
SAMPLER可以有其他使用方法
SAMPLER(filter_wrap)
SAMPLER(filter_wrapU_wrapV)
filter
:point、linear、triLinearwrap
:clamp、repeat、mirror、mirrorOnce对金属度或高光度进行采样得到 half4 specGloss
返回
half4 SampleMetallicSpecGloss(float2 uv, half albedoAlpha)
返回AO贴图的采样结果
half SampleOcclusion(float2 uv)
inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
表面数据初始化函数需要用到SurfaceData
SurfaceData.hlsl
中struct SurfaceData
{
half3 albedo; //颜色(反照率)
half3 specular; //高光反射
half metallic; //金属度
half smoothness; //光滑度
half3 normalTS; //法线 TS (瓷砖式显示Tiling,偏移Offset)
half3 emission; //自发光
half occlusion; //遮蔽值
half alpha; //透明度
half clearCoatMask; //透明层Mask
half clearCoatSmoothness; //--------
};
SurfaceInput.hlsl
中包含库:
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/SurfaceData.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
纹理:
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); float4 _BaseMap_TexelSize; float4 _BaseMap_MipInfo;
TEXTURE2D(_BumpMap); SAMPLER(sampler_BumpMap);
TEXTURE2D(_EmissionMap); SAMPLER(sampler_EmissionMap);
函数:
透明度函数: 获取透明属性,并根据_ALPHATEST_ON
宏,决定是否开启剔除,并剔除。
// 参数: Albedo纹理(BaseMap)的Alpha通道,基础色(BaseColor),裁剪值
half Alpha(half albedoAlpha, half4 color, half cutoff)
Albedo纹理采样函数:
// 纹理坐标 纹理变量 采样器
half4 SampleAlbedoAlpha(float2 uv, TEXTURE2D_PARAM(albedoAlphaMap, sampler_albedoAlphaMap))
{
return half4(SAMPLE_TEXTURE2D(albedoAlphaMap, sampler_albedoAlphaMap, uv));
}
法线贴图采样函数
half3 SampleNormal(float2 uv, TEXTURE2D_PARAM(bumpMap, sampler_bumpMap), half scale = half(1.0))
自发光贴图采样函数
half3 SampleEmission(float2 uv, half3 emissionColor, TEXTURE2D_PARAM(emissionMap, sampler_emissionMap))
现在我们回到表面数据初始化函数
初始化SurfaceData结构体中的变量,并将其输出。
inline void InitializeStandardLitSurfaceData(float2 uv, out SurfaceData outSurfaceData)
{
// 得到纹理alpha值
half4 albedoAlpha = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_BaseMap, sampler_BaseMap));
// 混合系数alpha值,并剔除
outSurfaceData.alpha = Alpha(albedoAlpha.a, _BaseColor, _Cutoff);
// 采样高光或金属值
half4 specGloss = SampleMetallicSpecGloss(uv, albedoAlpha.a);
// 保存颜色值
outSurfaceData.albedo = albedoAlpha.rgb * _BaseColor.rgb;
// 保存高光值、或金属值
#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);
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;
outSurfaceData.albedo = ApplyDetailAlbedo(detailUv, outSurfaceData.albedo, detailMask);
outSurfaceData.normalTS = ApplyDetailNormal(detailUv, outSurfaceData.normalTS, detailMask);
#endif
}
struct Varyings
{
// 直接参数
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD2;
// 光照贴图的纹理坐标:float2 lmName : TEXCOORD##index
// 或球谐函数的纹理坐标?:half3 shName : TEXCOORD##index
// 需要传入(光照贴图名称,球谐函数名称,纹理坐标索引TEXCOORD8)
DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 8);
float4 positionCS : SV_POSITION;
// REQUIRES_WORLD_SPACE_POS_INTERPOLATOR?????????
#if defined(REQUIRES_WORLD_SPACE_POS_INTERPOLATOR)
float3 positionWS : TEXCOORD1;
#endif
#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
#ifdef DYNAMICLIGHTMAP_ON
float2 dynamicLightmapUV : TEXCOORD9; // Dynamic lightmap UVs
#endif
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
real DegToRad(real deg)//角度转弧度
real RadToDeg(real rad)//弧度转角度
float Length2(float3 v)//返回向量长度的平方
real3 SafeNormalize(float3 inVec)//返回标准化的3维向量
real SafeDiv(real numer, real denom)//返回numer/denom
struct InputData
{
float3 positionWS;
float4 positionCS;
float3 normalWS;
half3 viewDirectionWS;
float4 shadowCoord; //在阴影贴图中的UV
half fogCoord; //雾的等级
half3 vertexLighting; //顶点光照(Additional Light)
half3 bakedGI; //全局光照(光照贴图或球谐函数计算)
float2 normalizedScreenSpaceUV;
half4 shadowMask;
half3x3 tangentToWorld; //法线变换到世界空间的TBN矩阵,与model矩阵有关
#if defined(DEBUG_DISPLAY)
half2 dynamicLightmapUV;
half2 staticLightmapUV;
float3 vertexSH;
half3 brdfDiffuse;
half3 brdfSpecular;
float2 uv;
uint mipCount;
float4 texelSize;
float4 mipInfo;
#endif
};
部分其他变量定义
half4 _GlossyEnvironmentColor;// 环境反射颜色
half4 _SubtractiveShadowColor;// 阴影颜色
half4 _GlossyEnvironmentCubeMap_HDR;
TEXTURECUBE(_GlossyEnvironmentCubeMap);
SAMPLER(sampler_GlossyEnvironmentCubeMap);
#define _InvCameraViewProj unity_MatrixInvVP
float4 _ScaledScreenParams;
float4 _MainLightPosition; // 主光源位置
half4 _MainLightColor; // 主光源颜色
half4 _MainLightOcclusionProbes; // 主光源遮蔽球鞋探针???
uint _MainLightLayerMask; // 主光源层遮罩
half4 _AmbientOcclusionParam;
half4 _AdditionalLightsCount;
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_I_V unity_MatrixInvV
#define UNITY_MATRIX_P OptimizeProjectionMatrix(glstate_matrix_projection)
#define UNITY_MATRIX_I_P unity_MatrixInvP
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_I_VP unity_MatrixInvVP
#define UNITY_MATRIX_MV mul(UNITY_MATRIX_V, UNITY_MATRIX_M)
#define UNITY_MATRIX_T_MV transpose(UNITY_MATRIX_MV)
#define UNITY_MATRIX_IT_MV transpose(mul(UNITY_MATRIX_I_M, UNITY_MATRIX_I_V))
#define UNITY_MATRIX_MVP mul(UNITY_MATRIX_VP, UNITY_MATRIX_M)
#define UNITY_PREV_MATRIX_M unity_MatrixPreviousM
#define UNITY_PREV_MATRIX_I_M unity_MatrixPreviousMI
初始化InputData结构体,函数需要传入Varyings结构体和切线空间法线,然后输出InputData结构体。
void InitializeInputData(Varyings input, half3 normalTS, out InputData inputData)
inputData = (InputData)0;// 初始化 (该写法为语法结构)
包含了所有与空间变换相关的函数
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
初始化输出结构体。
使顶点着色器可以获取到GPU实例的ID
将实例ID传递给输出结构体
针对VR平台进行的顶点操作(VR平台由两个摄像机生成)
声明了两个结构体,并将其初始化。
这两个结构体在Core.hlsl
中定义,初始化函数在ShaderVariablesFunctions.hlsl
中定义,目的是传入一个所需的变量,得到与其相关的所有变量,方便用户在编写代码时可以直接调用。
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);
// Structs
struct VertexPositionInputs
{
float3 positionWS; // World space position
float3 positionVS; // View space position
float4 positionCS; // Homogeneous clip space position
float4 positionNDC;// Homogeneous normalized device coordinates
};
struct VertexNormalInputs
{
real3 tangentWS; // 世界空间切线
real3 bitangentWS; // 世界空间次切线
float3 normalWS; // 世界空间法线
};
VertexPositionInputs GetVertexPositionInputs(float3 positionOS)
VertexNormalInputs GetVertexNormalInputs(float3 normalOS)//只填充normalWS
VertexNormalInputs GetVertexNormalInputs(float3 normalOS, float4 tangentOS)//填充三个切线
...
计算数据:
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
//_FOG_FRAGMENT
half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
//REQUIRES_WORLD_SPACE_TANGENT_INTERPOLATOR || REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR
real sign = input.tangentOS.w * GetOddNegativeScale();
half4 tangentWS = half4(normalInput.tangentWS.xyz, sign);
//REQUIRES_TANGENT_SPACE_VIEW_DIR_INTERPOLATOR
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS);
half3 viewDirTS = GetViewDirectionTangentSpace(tangentWS, output.normalWS, viewDirWS);
VertexLighting()
函数在Lighting.hlsl
中定义。ComputeFogFactor()
函数在ShaderVariablesFunctions.hlsl
文件中定义,函数需要传入深度值Z。保存数据:
output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);//TRANSFORM_TEX==> scale && offset
output.normalWS = normalInput.normalWS;
output.tangentWS = tangentWS;
output.viewDirTS = viewDirTS;
// 光照贴图纹理坐标
OUTPUT_LIGHTMAP_UV(input.staticLightmapUV, unity_LightmapST, output.staticLightmapUV);
// 顶点球谐函数光照
OUTPUT_SH(output.normalWS.xyz, output.vertexSH);
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
output.positionWS = vertexInput.positionWS;
output.shadowCoord = GetShadowCoord(vertexInput);
output.positionCS = vertexInput.positionCS;
half4 LitPassFragment(Varyings input) : SV_Target
{
// 获取实例ID
UNITY_SETUP_INSTANCE_ID(input);
// 用于VR平台
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
//SurfaceData由材质参数决定
SurfaceData surfaceData;
InitializeStandardLitSurfaceData(input.uv, surfaceData);
//InputData由当前顶点的信息得到(需要传入法线贴图信息)
InputData inputData;
InitializeInputData(input, surfaceData.normalTS, inputData);
得到顶点信息和表面材质信息,就可以将这些信息用于PBR着色。
half4 color = UniversalFragmentPBR(inputData, surfaceData);
half4 UniversalFragmentPBR(InputData inputData, SurfaceData surfaceData)
{
BRDFData brdfData;
InitializeBRDFData(surfaceData, brdfData);
// Clear-coat calculation...
BRDFData brdfDataClearCoat = CreateClearCoatBRDFData(surfaceData, brdfData);
half4 shadowMask = CalculateShadowMask(inputData);
AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
uint meshRenderingLayers = GetMeshRenderingLightLayer();
// 得到主光源
Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
// 得到实时光照和光照贴图中的光照,并加到mainLight中
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI);
LightingData lightingData = CreateLightingData(inputData, surfaceData);
// 得到全局光照Color
lightingData.giColor = GlobalIllumination(brdfData, brdfDataClearCoat, surfaceData.clearCoatMask,
inputData.bakedGI, aoFactor.indirectAmbientOcclusion, inputData.positionWS,
inputData.normalWS, inputData.viewDirectionWS);
if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
{
lightingData.mainLightColor = LightingPhysicallyBased(brdfData, brdfDataClearCoat,
mainLight,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
// 额外光照
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
for (uint lightIndex = 0; lightIndex < min(_AdditionalLightsDirectionalCount, MAX_VISIBLE_LIGHTS); lightIndex++)
{
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
}
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
LIGHT_LOOP_END
#endif
return CalculateFinalColor(lightingData, surfaceData.alpha);
}
color.rgb = MixFog(color.rgb, inputData.fogCoord);
Mixfog函数在ShaderVariablesFunctions.hlsl
中,传入 片元颜色值 和 雾效因子
half3 MixFog(half3 fragColor, half fogFactor)
{
return MixFogColor(fragColor, unity_FogColor.rgb, fogFactor);
}
加入雾效颜色值unity_FogColor.rgb
。
half3 MixFogColor(half3 fragColor, half3 fogColor, half fogFactor)
{
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
// 根据 defined 决定雾效的混合值
half fogIntensity = ComputeFogIntensity(fogFactor);
// 混合颜色值
fragColor = lerp(fogColor, fragColor, fogIntensity);
#endif
return fragColor;
}
color.a = OutputAlpha(color.a, _Surface);
定义在ShaderVariablesFunctions.hlsl
中,根据surfaceType
是透明还是不透明决定最终结果color.a
的值。(默认为不透明)
half OutputAlpha(half outputAlpha, half surfaceType = half(0.0))
{
return surfaceType == 1 ? outputAlpha : half(1.0);
}
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
该文章中代码并不完全是URP–LitShader的源代码,而是删除了一些不必要的参数定义,以及不太会用的宏定义内的代码。目的是降低阅读复杂度。