Unity Build-In管线的SurfaceShader剖析
- 在Unity Build-In 管线(Universal Render Pipeline)
- 新建一个Standard Surface Shader
- 文件里的代码如下:
- 选中"MyPBR.Shader",在Inspector面板,打开"Show generated code"
- 跳转到VScode,这里有1677行代码
- 将其复制到shader文件里。
- PASS
- 删除不用的pass后的大致结构
- FallBack "Diffuse"的作用
- 包含文件
- 判断语句
- 如果/是否定义了(语义)
- 是否使用lightmaps?
- ifdef和ifndef的条件语句的经典应用
- high-precision高精度,half-precision半精度
- 结构体 Struct v2f_surf
- UNITY_POSITION(pos); = float4 pos :SV_POSITION;
- pack0 就是_MainTex的uv
- 在v2f_surf 里声明三个变量用于组成成切线空间下的旋转矩阵
- 最后v2f_surf 结构的完整代码:
- vertex顶点着色器
- 其中appdata_full
- 整理后的vertex——vert_surf顶点着色器如下:
- frag_surf 片段着色器
- 从return c;往前解析
- UNITY_OPAQUE_ALPHA(c.a);
- fixed4 c = LightingStandard (o, worldViewDir, gi);
- LightingStandard (o, worldViewDir, gi)解析
- LightingStandard 的第一个参数"o":
- 不同平台的编译指令区分
- 宏:UNITY_INITIALIZE_OUTPUT(type,name) 解析
- 金属工作流下的SurfaceOutputStandard结构
- 给SurfaceOutputStandard 结构内的参数逐一赋值
- 最后SurfaceOutputStandard 代码和注释
- SurfaceOutputStandard 代码的注释补充:
- o.Normal = worldNormal;
- UnityGI gi;
- UnityGI结构体
- UnityLightingCommon.cginc文件
- UnityLight light; 直接光照包含信息
- UnityIndirect indirect; 间接光照包含信息
- 完整的Shader,保留原始计算和注释的代码如下:
- 新建一个cginc 文件
- 完整的cginc代码,代码如下:
- 最后精简后的Shader
- 最后精简后的cginc代码
在Unity Build-In 管线(Universal Render Pipeline)
新建一个Standard Surface Shader
- 命名为MyPBR
- 双击打开文件
文件里的代码如下:
Shader "Custom/MyPBR"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader{Tags { "RenderType"="Opaque" }LOD 200CGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf Standard fullforwardshadows// Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0sampler2D _MainTex;struct Input{float2 uv_MainTex;};half _Glossiness;half _Metallic;fixed4 _Color;// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.// #pragma instancing_options assumeuniformscalingUNITY_INSTANCING_BUFFER_START(Props)// put more per-instance properties hereUNITY_INSTANCING_BUFFER_END(Props)void surf (Input IN, inout SurfaceOutputStandard o){// Albedo comes from a texture tinted by colorfixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "Diffuse"
}
选中"MyPBR.Shader",在Inspector面板,打开"Show generated code"
跳转到VScode,这里有1677行代码
将文件 Ctrl K 再 Ctrl 0,折叠代码。
里面有Unity的部分详细注释。
将其复制到shader文件里。
- pass 部分的LightMode 注释
- ForwardBase pass 主要是支持最亮主平行灯的逐像素光照。
- ForwardAdd pass 是支持其他等的逐像素光照,这个看项目需求是否需要。
- Deferred pass 是支持延迟渲染,手机上一般很少用这种渲染pass
- Meta 光照烘焙
- 保留ForwardBase pass ,其他的都去除。
// ---- forward rendering base pass:Pass {Name "FORWARD"Tags { "LightMode" = "ForwardBase" }...}// ---- forward rendering additive lights pass:Pass {Name "FORWARD"Tags { "LightMode" = "ForwardAdd" }...}// ---- deferred shading pass:Pass {Name "DEFERRED"Tags { "LightMode" = "Deferred" }...}// ---- meta information extraction pass:Pass {Name "Meta"Tags { "LightMode" = "Meta" }...}
PASS
删除不用的pass后的大致结构
FallBack "Diffuse"的作用
- 是保证不同平台的兜底着色,还有就是支持阴影,如果去掉阴影可能会不显示,
}//这个的作用是保证不同平台的兜底着色,还有就是支持阴影,如果去掉阴影可能会不显示,FallBack "Diffuse"
}
包含文件
- 这些包含文件,内部会相互包含串联应用。
// #include "HLSLSupport.cginc"// #define UNITY_INSTANCED_LOD_FADE// #define UNITY_INSTANCED_SH// #define UNITY_INSTANCED_LIGHTMAPSTS// #include "UnityShaderVariables.cginc"// #include "UnityShaderUtilities.cginc"
判断语句
如果/是否定义了(语义)
- #if !defined(INSTANCIN1G_ON) 表示如果没有定义GPU实例化,那么执行 #if 到 #endif 里面的计算
- #if defined(INSTANCING_ON) 表示如果定义了GPU实例化,那么执行 #if 到 #endif 里面的计算
- -------- variant for: when no other keywords are defined
- #if !defined(INSTANCING_ON)
是否使用lightmaps?
- #ifndef LIGHTMAP_ON 表示如果没有定义LIGHTMAP_ON
- #ifdef LIGHTMAP_ON 表示如果定义了LIGHTMAP_ON
ifdef和ifndef的条件语句的经典应用
- 经典应用链接
high-precision高精度,half-precision半精度
- 精度片段着色器寄存器
结构体 Struct v2f_surf
UNITY_POSITION(pos); = float4 pos :SV_POSITION;
- UNITY_POSITION(pos); 和 float4 pos :SV_POSITION; 是一个东西;
pack0 就是_MainTex的uv
float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uv
在v2f_surf 里声明三个变量用于组成成切线空间下的旋转矩阵
float3 tSpace0:TEXCOORD3;float3 tSpace1:TEXCOORD4;float3 tSpace2:TEXCOORD5;
最后v2f_surf 结构的完整代码:
struct v2f_surf{// UNITY_POSITION(pos); = float4 pos :SV_POSITION;!!!!!!!!!!!!!!!!float4 pos :SV_POSITION;float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uvfloat3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;#if UNITY_SHOULD_SAMPLE_SHhalf3 sh : TEXCOORD3; // SH 球谐#endifUNITY_FOG_COORDS(4)UNITY_SHADOW_COORDS(5)float3 tSpace0:TEXCOORD6;float3 tSpace1:TEXCOORD7;float3 tSpace2:TEXCOORD8;// #if SHADER_TARGET >= 30// float4 lmap : TEXCOORD6;// #endif// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO};
vertex顶点着色器
v2f_surf vert_surf (appdata_full v){UNITY_SETUP_INSTANCE_ID(v);v2f_surf o;// UNITY_INITIALIZE_OUTPUT(v2f_surf,o);// UNITY_TRANSFER_INSTANCE_ID(v,o);// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);o.pos = UnityObjectToClipPos(v.vertex);o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);#if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED)fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;#endif#if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);#endifo.worldPos.xyz = worldPos;o.worldNormal = worldNormal;#ifdef DYNAMICLIGHTMAP_ONo.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;#endif#ifdef LIGHTMAP_ONo.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;#endif// SH/ambient and vertex lights#ifndef LIGHTMAP_ON#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;// Approximated illumination from non-important point lights#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endif#endif // !LIGHTMAP_ONUNITY_TRANSFER_LIGHTING(o,v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader#ifdef FOG_COMBINED_WITH_TSPACEUNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader#elif defined FOG_COMBINED_WITH_WORLD_POSUNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader#elseUNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader#endifreturn o;}
其中appdata_full
v2f_surf vert_surf (appdata_full v){...}
- 在UnityCG.cginc中有具体的结构体数据:
struct appdata_base{...};struct appdata_tan{...};struct appdata_full {float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;fixed4 color : COLOR;UNITY_VERTEX_INPUT_INSTANCE_ID};
- UNITY_SETUP_INSTANCE_ID(v);
仅当您要访问片元着色器中的实例化属性时才需要。
关于实例化的Unity官方介绍
- UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
v2f_surf结构体的初始化。 - LIGHTMAP_ON——烘焙;DIRLIGHTMAP_COMBINED 方向光源
UNITY_SETUP_INSTANCE_ID(v);v2f_surf o;// UNITY_INITIALIZE_OUTPUT(v2f_surf,o);// UNITY_TRANSFER_INSTANCE_ID(v,o);// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
整理后的vertex——vert_surf顶点着色器如下:
v2f_surf vert_surf (appdata_full v){// UNITY_SETUP_INSTANCE_ID(v);v2f_surf o;// UNITY_INITIALIZE_OUTPUT(v2f_surf,o);// UNITY_TRANSFER_INSTANCE_ID(v,o);// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);o.pos = UnityObjectToClipPos(v.vertex);o.pack0.xy = TRANSFORM_TEX(v.texcoord, _MainTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED)// fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);// fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;// fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;// #endif// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);// #endifo.worldPos.xyz = worldPos;o.worldNormal = worldNormal;// // 实时GI// #ifdef DYNAMICLIGHTMAP_ON// o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;// #endif// // o.lmap.xy光照贴图的UV采样// #ifdef LIGHTMAP_ON// o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;// #endif// SH/ambient and vertex lights// #ifndef LIGHTMAP_ON#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;// Approximated illumination from non-important point lights#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endif// #endif // !LIGHTMAP_ON// UNITY_TRANSFER_LIGHTING(o,v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader// #ifdef FOG_COMBINED_WITH_TSPACE// UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader// #elif defined (FOG_COMBINED_WITH_WORLD_POS)// UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader// #elseUNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader// #endifreturn o;}
frag_surf 片段着色器
fixed4 frag_surf (v2f_surf IN) : SV_Target{UNITY_SETUP_INSTANCE_ID(IN);// prepare and unpack dataInput surfIN;#ifdef FOG_COMBINED_WITH_TSPACEUNITY_EXTRACT_FOG_FROM_TSPACE(IN);#elif defined (FOG_COMBINED_WITH_WORLD_POS)UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);#elseUNITY_EXTRACT_FOG(IN);#endifUNITY_INITIALIZE_OUTPUT(Input,surfIN);surfIN.uv_MainTex.x = 1.0;surfIN.uv_MainTex = IN.pack0.xy;float3 worldPos = IN.worldPos.xyz;#ifndef USING_DIRECTIONAL_LIGHTfixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));#elsefixed3 lightDir = _WorldSpaceLightPos0.xyz;#endiffloat3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));#ifdef UNITY_COMPILER_HLSLSurfaceOutputStandard o = (SurfaceOutputStandard)0;#elseSurfaceOutputStandard o;#endifo.Albedo = 0.0;o.Emission = 0.0;o.Alpha = 0.0;o.Occlusion = 1.0;fixed3 normalWorldVertex = fixed3(0,0,1);o.Normal = IN.worldNormal;normalWorldVertex = IN.worldNormal;// call surface functionsurf (surfIN, o);// compute lighting & shadowing factorUNITY_LIGHT_ATTENUATION(atten, IN, worldPos)fixed4 c = 0;// Setup lighting environmentUnityGI gi;UNITY_INITIALIZE_OUTPUT(UnityGI, gi);gi.indirect.diffuse = 0;gi.indirect.specular = 0;gi.light.color = _LightColor0.rgb;gi.light.dir = lightDir;// Call GI (lightmaps/SH/reflections) lighting functionUnityGIInput giInput;UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);giInput.light = gi.light;giInput.worldPos = worldPos;giInput.worldViewDir = worldViewDir;giInput.atten = atten;#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)giInput.lightmapUV = IN.lmap;#elsegiInput.lightmapUV = 0.0;#endif#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELgiInput.ambient = IN.sh;#elsegiInput.ambient.rgb = 0.0;#endifgiInput.probeHDR[0] = unity_SpecCube0_HDR;giInput.probeHDR[1] = unity_SpecCube1_HDR;#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending#endif#ifdef UNITY_SPECCUBE_BOX_PROJECTIONgiInput.boxMax[0] = unity_SpecCube0_BoxMax;giInput.probePosition[0] = unity_SpecCube0_ProbePosition;giInput.boxMax[1] = unity_SpecCube1_BoxMax;giInput.boxMin[1] = unity_SpecCube1_BoxMin;giInput.probePosition[1] = unity_SpecCube1_ProbePosition;#endifLightingStandard_GI(o, giInput, gi);// realtime lighting: call lighting functionc += LightingStandard (o, worldViewDir, gi);UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fogUNITY_OPAQUE_ALPHA(c.a);return c;}
从return c;往前解析
- return c; 返回c。
UNITY_OPAQUE_ALPHA(c.a);
- #define UNITY_OPAQUE_ALPHA(outputAlpha) outputAlpha = 1.0
- Alpha值为1.0
另外一个:
#if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
fixed4 c = LightingStandard (o, worldViewDir, gi);
- fixed4 c = LightingStandard (o, worldViewDir, gi); 其中的LightingStandard 再UnityPBSLighting.cginc文件内
inline half4 LightingStandard (SurfaceOutputStandard s, half3 viewDir, UnityGI gi){s.Normal = normalize(s.Normal);half oneMinusReflectivity;half3 specColor;s.Albedo = DiffuseAndSpecularFromMetallic (s.Albedo, s.Metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);// shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)// this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alphahalf outputAlpha;s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);half4 c = UNITY_BRDF_PBS (s.Albedo, specColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);c.a = outputAlpha;return c;}
LightingStandard (o, worldViewDir, gi)解析
LightingStandard 的第一个参数"o":
传入的第一个"o",o是片段着色段的开头部分: SurfaceOutputStandard o;
不同平台的编译指令区分
#ifdef UNITY_COMPILER_HLSLSurfaceOutputStandard o = (SurfaceOutputStandard)0;#elseSurfaceOutputStandard o;#endif
宏:UNITY_INITIALIZE_OUTPUT(type,name) 解析
- UNITY_INITIALIZE_OUTPUT(type,name)用于把所给结构体里的各个变量初始化为0。
- 在HLSLSupport文件里的定义:
- 用零值初始化任意结构。
- 某些后端不支持(例如,基于Cg,尤其是嵌套结构)。
- hlsl2glsl几乎会支持它,除非有数组的结构——所以也不支持。
- hlsl2glsl:全称High Level Shader Language to OpenGL Shading Language,简写HLSL to GLSL。
// Initialize arbitrary structure with zero values.
// Not supported on some backends (e.g. Cg-based particularly with nested structs).
// hlsl2glsl would almost support it, except with structs that have arrays -- so treat as not supported there either :(
#if defined(UNITY_COMPILER_HLSL) || defined(SHADER_API_PSSL) || defined(UNITY_COMPILER_HLSLCC)
#define UNITY_INITIALIZE_OUTPUT(type,name) name = (type)0;
#else
#define UNITY_INITIALIZE_OUTPUT(type,name)
#endif
金属工作流下的SurfaceOutputStandard结构
给SurfaceOutputStandard 结构内的参数逐一赋值
- 这里的void surf (Input IN, inout SurfaceOutputStandard o)主要作用是给SurfaceOutputStandard 结构内的参数逐一赋值。
void surf (Input IN, inout SurfaceOutputStandard o){// Albedo comes from a texture tinted by colorfixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}
最后SurfaceOutputStandard 代码和注释
SurfaceOutputStandard 代码的注释补充:
o.Normal = worldNormal;
- appdata结构中定义NORMAL与TANGENT语义。
struct appdata{float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;fixed4 color : COLOR;UNITY_VERTEX_INPUT_INSTANCE_ID};
- v2f_surf中新增声明
- float3 tSpace0:TEXCOORD6;
- float3 tSpace1:TEXCOORD7;
- float3 tSpace2:TEXCOORD8;
- float2 normal : TEXCOORD9; // _NormalTex
- 用于组成切线空间下的矩阵。
struct v2f_surf{// UNITY_POSITION(pos); = float4 pos :SV_POSITION;!!!!!!!!!!!!!!!!float4 pos :SV_POSITION;float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uvfloat3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;#if UNITY_SHOULD_SAMPLE_SHhalf3 sh : TEXCOORD3; // SH 球谐#endifUNITY_FOG_COORDS(4)UNITY_SHADOW_COORDS(5)float3 tSpace0:TEXCOORD6;float3 tSpace1:TEXCOORD7;float3 tSpace2:TEXCOORD8;float2 normal : TEXCOORD9; // _NormalTex// #if SHADER_TARGET >= 30// float4 lmap : TEXCOORD6;// #endif// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO};
- 在顶点着色器中:
// vertex shaderv2f_surf vert_surf (appdata v){
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// UNITY_SETUP_INSTANCE_ID(v);v2f_surf o;// UNITY_INITIALIZE_OUTPUT(v2f_surf,o);// UNITY_TRANSFER_INSTANCE_ID(v,o);// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------o.pos = UnityObjectToClipPos(v.vertex);o.pack0 = TRANSFORM_TEX(v.texcoord.xy, _MainTex);o.normal = TRANSFORM_TEX(v.texcoord.zw, _NormalTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED)fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;// #endif// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);// #endifo.worldPos.xyz = worldPos;// o.worldNormal = worldNormal;
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// // 实时GI// #ifdef DYNAMICLIGHTMAP_ON// o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;// #endif// // o.lmap.xy光照贴图的UV采样// #ifdef LIGHTMAP_ON// o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// SH/ambient and vertex lights// #ifndef LIGHTMAP_ON#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;// Approximated illumination from non-important point lights#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endif// #endif // !LIGHTMAP_ON// UNITY_TRANSFER_LIGHTING(o,v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader// #ifdef FOG_COMBINED_WITH_TSPACE// UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader// #elif defined (FOG_COMBINED_WITH_WORLD_POS)// UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader// #elseUNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader// #endifreturn o;}
- 如是在片段着色器的normal填充
half3 normalTex = UnpackNormal(tex2D(_NormalTex,IN.normal));half3 worldNormal = half3(dot(IN.tSpace0,normalTex),dot(IN.tSpace1,normalTex),dot(IN.tSpace2,normalTex));o.Normal = worldNormal;
UnityGI gi;
// compute lighting & shadowing factorUNITY_LIGHT_ATTENUATION(atten, IN, worldPos)// Setup lighting environmentUnityGI gi;UNITY_INITIALIZE_OUTPUT(UnityGI, gi);gi.indirect.diffuse = 0;gi.indirect.specular = 0;gi.light.color = _LightColor0.rgb;gi.light.dir = lightDir;// Call GI (lightmaps/SH/reflections) lighting functionUnityGIInput giInput;UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);giInput.light = gi.light;giInput.worldPos = worldPos;giInput.worldViewDir = worldViewDir;giInput.atten = atten;#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)giInput.lightmapUV = IN.lmap;#elsegiInput.lightmapUV = 0.0;#endif#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELgiInput.ambient = IN.sh;#elsegiInput.ambient.rgb = 0.0;#endifgiInput.probeHDR[0] = unity_SpecCube0_HDR;giInput.probeHDR[1] = unity_SpecCube1_HDR;#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending#endif#ifdef UNITY_SPECCUBE_BOX_PROJECTIONgiInput.boxMax[0] = unity_SpecCube0_BoxMax;giInput.probePosition[0] = unity_SpecCube0_ProbePosition;giInput.boxMax[1] = unity_SpecCube1_BoxMax;giInput.boxMin[1] = unity_SpecCube1_BoxMin;giInput.probePosition[1] = unity_SpecCube1_ProbePosition;#endifLightingStandard_GI(o, giInput, gi);
UnityGI结构体
UnityLightingCommon.cginc文件
fixed4 _LightColor0;
fixed4 _SpecColor;struct UnityLight
{half3 color;half3 dir;half ndotl; // Deprecated: Ndotl is now calculated on the fly and is no longer stored. Do not used it.
};struct UnityIndirect
{half3 diffuse;half3 specular;
};struct UnityGI
{UnityLight light;UnityIndirect indirect;
};struct UnityGIInput
{UnityLight light; // pixel light, sent from the enginefloat3 worldPos;half3 worldViewDir;half atten;half3 ambient;// interpolated lightmap UVs are passed as full float precision data to fragment shaders// so lightmapUV (which is used as a tmp inside of lightmap fragment shaders) should// also be full float precision to avoid data loss before sampling a texture.float4 lightmapUV; // .xy = static lightmap UV, .zw = dynamic lightmap UV#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)float4 boxMin[2];#endif#ifdef UNITY_SPECCUBE_BOX_PROJECTIONfloat4 boxMax[2];float4 probePosition[2];#endif// HDR cubemap properties, use to decompress HDR texturefloat4 probeHDR[2];
};
UnityLight light; 直接光照包含信息
struct UnityLight{half3 color;half3 dir;half ndotl; // Deprecated: Ndotl is now calculated on the fly and is no longer stored. Do not used it.};
UnityIndirect indirect; 间接光照包含信息
struct UnityIndirect{half3 diffuse;half3 specular;};
完整的Shader,保留原始计算和注释的代码如下:
// Upgrade NOTE: replaced 'defined FOG_COMBINED_WITH_WORLD_POS' with 'defined (FOG_COMBINED_WITH_WORLD_POS)'Shader "Custom/MyPBR"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_NormalTex ("NormalTex", 2D) = "bump" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader{Tags { "RenderType"="Opaque" }LOD 200// ------------------------------------------------------------// Surface shader code generated out of a CGPROGRAM block:
// pass 部分的LightMode 注释
//ForwardBase pass 主要是支持最亮主平行灯的逐像素光照
//ForwardAdd pass 是支持其他等的逐像素光照,这个看项目需求是否需要。
// Deferred pass 是支持延迟渲染,手机上一般很少用这种渲染pass
// Meta 光照烘焙// ---- forward rendering base pass:Pass {Name "FORWARD"Tags { "LightMode" = "ForwardBase" }CGPROGRAM// compile directives#pragma vertex vert_surf#pragma fragment frag_surf#pragma target 3.0#pragma multi_compile_instancing#pragma multi_compile_fog#pragma multi_compile_fwdbase// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------//这些包含文件,内部会相互包含串联应用,// #include "HLSLSupport.cginc"// #define UNITY_INSTANCED_LOD_FADE// #define UNITY_INSTANCED_SH// #define UNITY_INSTANCED_LIGHTMAPSTS// #include "UnityShaderVariables.cginc"// #include "UnityShaderUtilities.cginc"// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// 是否定义了(语义)
// #if !defined(INSTANCING_ON) 表示如果没有定义,那么执行 #if 到 #endif 里面的计算
// #if defined(INSTANCING_ON) 表示如果定义了,那么执行 #if 到 #endif 里面的计算
// -------- variant for: <when no other keywords are defined>
// #if !defined(INSTANCING_ON)// Surface shader code generated based on:// writes to per-pixel normal: no// writes to emission: no// writes to occlusion: no// needs world space reflection vector: no// needs world space normal vector: no// needs screen space position: no// needs world space position: no// needs view direction: no// needs world space view direction: no// needs world space position for lighting: YES// needs world space view direction for lighting: YES// needs world space view direction for lightmaps: no// needs vertex color: no// needs VFACE: no// needs SV_IsFrontFace: no// passes tangent-to-world matrix to pixel shader: no// reads from normal: no// 1 texcoords actually used// float2 _MainTex#include "UnityCG.cginc"#include "Lighting.cginc"#include "UnityPBSLighting.cginc"#include "AutoLight.cginc"// #define INTERNAL_DATA// #define WorldReflectionVector(data,normal) data.worldRefl// #define WorldNormalVector(data,normal) normal// // Original surface shader snippet:// #line 13 ""// #ifdef DUMMY_PREPROCESSOR_TO_WORK_AROUND_HLSL_COMPILER_LINE_HANDLING// #endif// /* UNITY: Original start of shader */// // Physically based Standard lighting model, and enable shadows on all light types// //#pragma surface surf Standard fullforwardshadows// // Use shader model 3.0 target, to get nicer looking lighting// //#pragma target 3.0half _Glossiness;half _Metallic;fixed4 _Color;sampler2D _MainTex , _NormalTex;float4 _MainTex_ST , _NormalTex_ST;
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//struct Input 结构 传到void surf (Input IN, ...),所以这里也可以注释掉。// struct Input// {// float2 uv_MainTex;// };
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.// // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.// // //#pragma instancing_options assumeuniformscaling// UNITY_INSTANCING_BUFFER_START(Props)// // put more per-instance properties here// UNITY_INSTANCING_BUFFER_END(Props)
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//这里的void surf (Input IN, inout SurfaceOutputStandard o)主要作用是给SurfaceOutputStandard 结构内的参数逐一赋值。// void surf (Input IN, inout SurfaceOutputStandard o)// {// // Albedo comes from a texture tinted by color// fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;// o.Albedo = c.rgb;// // Metallic and smoothness come from slider variables// o.Metallic = _Metallic;// o.Smoothness = _Glossiness;// o.Alpha = c.a;// }// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// 是否使用lightmaps?
// #ifndef LIGHTMAP_ON 表示如果没有定义LIGHTMAP_ON
// #ifdef LIGHTMAP_ON 表示如果定义了LIGHTMAP_ON
//ifdef和ifndef的条件语句的区别// vertex-to-fragment interpolation data// no lightmaps:// #ifndef LIGHTMAP_ON
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// 半精度// half-precision fragment shader registers:// #ifdef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS// #define FOG_COMBINED_WITH_WORLD_POS// struct v2f_surf {// UNITY_POSITION(pos);// float2 pack0 : TEXCOORD0; // _MainTex// float3 worldNormal : TEXCOORD1;// float4 worldPos : TEXCOORD2;// #if UNITY_SHOULD_SAMPLE_SH// half3 sh : TEXCOORD3; // SH// #endif// UNITY_LIGHTING_COORDS(4,5)// #if SHADER_TARGET >= 30// float4 lmap : TEXCOORD6;// #endif// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO// };// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// 高精度// high-precision fragment shader registers:// #ifndef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERSstruct v2f_surf{// UNITY_POSITION(pos); = float4 pos :SV_POSITION;!!!!!!!!!!!!!!!!float4 pos :SV_POSITION;float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uvfloat3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;#if UNITY_SHOULD_SAMPLE_SHhalf3 sh : TEXCOORD3; // SH 球谐#endifUNITY_FOG_COORDS(4)UNITY_SHADOW_COORDS(5)float3 tSpace0:TEXCOORD6;float3 tSpace1:TEXCOORD7;float3 tSpace2:TEXCOORD8;float2 normal : TEXCOORD9; // _NormalTex// #if SHADER_TARGET >= 30// float4 lmap : TEXCOORD6;// #endif// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO};// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// #endif// // with lightmaps:// #ifdef LIGHTMAP_ON// // half-precision fragment shader registers:// #ifdef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS// #define FOG_COMBINED_WITH_WORLD_POS// struct v2f_surf {// UNITY_POSITION(pos);// float2 pack0 : TEXCOORD0; // _MainTex// float3 worldNormal : TEXCOORD1;// float4 worldPos : TEXCOORD2;// float4 lmap : TEXCOORD3;// UNITY_LIGHTING_COORDS(4,5)// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO// };// #endif// // high-precision fragment shader registers:// #ifndef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS// struct v2f_surf {// UNITY_POSITION(pos);// float2 pack0 : TEXCOORD0; // _MainTex// float3 worldNormal : TEXCOORD1;// float3 worldPos : TEXCOORD2;// float4 lmap : TEXCOORD3;// UNITY_FOG_COORDS(4)// UNITY_SHADOW_COORDS(5)// #ifdef DIRLIGHTMAP_COMBINED// float3 tSpace0 : TEXCOORD6;// float3 tSpace1 : TEXCOORD7;// float3 tSpace2 : TEXCOORD8;// #endif// UNITY_VERTEX_INPUT_INSTANCE_ID// UNITY_VERTEX_OUTPUT_STEREO// };// #endif// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------struct appdata{float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;fixed4 color : COLOR;UNITY_VERTEX_INPUT_INSTANCE_ID};// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// vertex shaderv2f_surf vert_surf (appdata v){
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// UNITY_SETUP_INSTANCE_ID(v);v2f_surf o;// UNITY_INITIALIZE_OUTPUT(v2f_surf,o);// UNITY_TRANSFER_INSTANCE_ID(v,o);// UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------o.pos = UnityObjectToClipPos(v.vertex);o.pack0 = TRANSFORM_TEX(v.texcoord.xy, _MainTex);o.normal = TRANSFORM_TEX(v.texcoord.zw, _NormalTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED)fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;// #endif// #if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);// #endifo.worldPos.xyz = worldPos;// o.worldNormal = worldNormal;
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// // 实时GI// #ifdef DYNAMICLIGHTMAP_ON// o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;// #endif// // o.lmap.xy光照贴图的UV采样// #ifdef LIGHTMAP_ON// o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// SH/ambient and vertex lights// #ifndef LIGHTMAP_ON#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;// Approximated illumination from non-important point lights#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endif// #endif // !LIGHTMAP_ON// UNITY_TRANSFER_LIGHTING(o,v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader// #ifdef FOG_COMBINED_WITH_TSPACE// UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o,o.pos); // pass fog coordinates to pixel shader// #elif defined (FOG_COMBINED_WITH_WORLD_POS)// UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o,o.pos); // pass fog coordinates to pixel shader// #elseUNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader// #endifreturn o;}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// fragment shaderfixed4 frag_surf (v2f_surf IN) : SV_Target{
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// UNITY_SETUP_INSTANCE_ID(IN);// prepare and unpack data// Input surfIN;UNITY_EXTRACT_FOG(IN);// UNITY_INITIALIZE_OUTPUT(Input,surfIN);// #ifdef FOG_COMBINED_WITH_TSPACE// UNITY_EXTRACT_FOG_FROM_TSPACE(IN);// #elif defined (FOG_COMBINED_WITH_WORLD_POS)// UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);// #else// UNITY_EXTRACT_FOG(IN);// #endif
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// surfIN.uv_MainTex.x = 1.0;// IN.uv_MainTex = IN.pack0.xy;float3 worldPos = IN.worldPos.xyz;// #ifndef USING_DIRECTIONAL_LIGHT// fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));// #else// fixed3 lightDir = _WorldSpaceLightPos0.xyz;// #endiffloat3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//将SurfaceOutputStandard的数据填充完整。SurfaceOutputStandard o;UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard,o);// #ifdef UNITY_COMPILER_HLSL// SurfaceOutputStandard o = (SurfaceOutputStandard)0;// #else// SurfaceOutputStandard o;// #endiffixed4 mainTex = tex2D (_MainTex, IN.pack0) * _Color;o.Albedo = mainTex.rgb;// Metallic and smoothness come from slider variables// fixed4 metallic = tex2D (_MetallicTex, IN.uv_MainTex);//如果要用金属度贴图或者金属度遮罩,可以采样2D贴图,然后填充。o.Metallic = _Metallic; // 0=non-metal, 1=metal// 平滑是面向用户的名称,它应该是感知平滑,但用户不应该处理它。// 在代码的任何地方,你都会遇到平滑,这就是感知平滑。o.Smoothness = _Glossiness;o.Alpha = mainTex.a;o.Emission = 0.0;//如果有AO贴图,o.Occlusion = 1.0;// fixed3 normalWorldVertex = fixed3(0,0,1);half3 normalTex = UnpackNormal(tex2D(_NormalTex,IN.normal));half3 worldNormal = half3(dot(IN.tSpace0,normalTex),dot(IN.tSpace1,normalTex),dot(IN.tSpace2,normalTex));o.Normal = worldNormal;// normalWorldVertex = IN.worldNormal;
//检索 void surf (Input IN, inout SurfaceOutputStandard o),这里有注释// // call surface function// surf (surfIN, o);
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// compute lighting & shadowing factorUNITY_LIGHT_ATTENUATION(atten, IN, worldPos)// Setup lighting environmentUnityGI gi;UNITY_INITIALIZE_OUTPUT(UnityGI, gi);gi.indirect.diffuse = 0;gi.indirect.specular = 0;gi.light.color = _LightColor0.rgb;gi.light.dir = _WorldSpaceLightPos0.xyz;// Call GI (lightmaps/SH/reflections) lighting functionUnityGIInput giInput;UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);giInput.light = gi.light;giInput.worldPos = worldPos;giInput.worldViewDir = worldViewDir;giInput.atten = atten;#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)giInput.lightmapUV = IN.lmap;#elsegiInput.lightmapUV = 0.0;#endif#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELgiInput.ambient = IN.sh;#elsegiInput.ambient.rgb = 0.0;#endifgiInput.probeHDR[0] = unity_SpecCube0_HDR;giInput.probeHDR[1] = unity_SpecCube1_HDR;#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending#endif#ifdef UNITY_SPECCUBE_BOX_PROJECTIONgiInput.boxMax[0] = unity_SpecCube0_BoxMax;giInput.probePosition[0] = unity_SpecCube0_ProbePosition;giInput.boxMax[1] = unity_SpecCube1_BoxMax;giInput.boxMin[1] = unity_SpecCube1_BoxMin;giInput.probePosition[1] = unity_SpecCube1_ProbePosition;#endifLightingStandard_GI(o, giInput, gi);
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------//PBR的核心计算,基于物理着色,BRDF的相关计算// realtime lighting: call lighting functionfixed4 c = LightingStandard (o, worldViewDir, gi);UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog// Alpha值为1.0UNITY_OPAQUE_ALPHA(c.a);return c;}// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// #endif// // -------- variant for: INSTANCING_ON // #if defined(INSTANCING_ON)// ...// #endif// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------ENDCG}}//这个的作用是保证不同平台的兜底着色,还有就是支持阴影,如果去掉阴影可能会不显示,FallBack "Diffuse"
}
新建一个cginc 文件
#ifndef MYPBRCGINC_CGINC#define MYPBRCGINC_CGINC#endif
完整的cginc代码,代码如下:
#ifndef MYPBRCGINC_CGINC#define MYPBRCGINC_CGINC
// ----------------------------------------------------------------------------
half3 Unity_GlossyEnvironment_MY (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
// For now disabled
#if 0float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameterconst float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)float n = (2.0/max(fEps, m*m))-2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdfn /= 4; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.htmlperceptualRoughness = pow( 2/(n+2), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
#else// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
#endif
// #endif
// #ifndef UNITY_SPECCUBE_LOD_STEPS
// UNITY_SPECCUBE_LOD_STEPS <6
//mip 是一个非线性函数,perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
// perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness); 简化 x = x*(1.7 - 0.7*x) 在0-1的范围是一个上拱非线性函数
// #define UNITY_SPECCUBE_LOD_STEPS (6)
// #endif
// perceptualRoughnessToMipmapLevel = perceptualRoughness * UNITY_SPECCUBE_LOD_STEPS;half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);half3 R = glossIn.reflUVW;half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);//tex2Dlodreturn DecodeHDR(rgbm, hdr);
}// ----------------------------------------------------------------------------//
// // ---------------------------------------------------------------------------- //
// // GlossyEnvironment - Function to integrate the specular lighting with default sky or reflection probes //
// // ---------------------------------------------------------------------------- //
// struct Unity_GlossyEnvironmentData //
// { //
// // - Deferred case have one cubemap //
// // - Forward case can have two blended cubemap (unusual should be deprecated). //
// // Surface properties use for cubemap integration //
// half roughness; // CAUTION: This is perceptualRoughness but because of compatibility this name can't be change :( //
// half3 reflUVW; //
// }; //
//// 计算Gi的镜面反射inline half3 UnityGI_IndirectSpecular_MY(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn){half3 specular;
//如果开启了BoxProjection// #ifdef UNITY_SPECCUBE_BOX_PROJECTION// // we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment_MY twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection// half3 originalReflUVW = glossIn.reflUVW;// glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);// #endif
//如果勾选了Standard材质面板中的禁用反射功能的情况#ifdef _GLOSSYREFLECTIONS_OFFspecular = unity_IndirectSpecColor.rgb;#elsehalf3 env0 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);#ifdef UNITY_SPECCUBE_BLENDINGconst float kBlendFactor = 0.99999;float blendLerp = data.boxMin[0].w;UNITY_BRANCHif (blendLerp < kBlendFactor){#ifdef UNITY_SPECCUBE_BOX_PROJECTIONglossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);#endifhalf3 env1 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);specular = lerp(env1, env0, blendLerp);}else{specular = env0;}#elsespecular = env0;#endif#endifreturn specular * occlusion;}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// UnityGlobalIllumination_MY 重载函数!!!!!!!!!!!!!!!!!!!!!!!!// inline UnityGI UnityGlobalIllumination_MY (UnityGIInput data, half occlusion, half3 normalWorld)// {// return UnityGI_Base(data, occlusion, normalWorld);// }inline UnityGI UnityGlobalIllumination_MY (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn){//UnityGI_Base只计算Gi的漫反射UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld);//UnityGI_IndirectSpecular_MY计算Gi的镜面反射o_gi.indirect.specular = UnityGI_IndirectSpecular_MY(data, occlusion, glossIn);return o_gi;}// //// // Old UnityGlobalIllumination_MY signatures. Kept only for backward compatibility and will be removed soon// //// inline UnityGI UnityGlobalIllumination_MY (UnityGIInput data, half occlusion, half smoothness, half3 normalWorld, bool reflections)// {// if(reflections)// {// Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup_MY(smoothness, data.worldViewDir, normalWorld, float3(0, 0, 0));// return UnityGlobalIllumination_MY(data, occlusion, normalWorld, g);// }// else// {// return UnityGlobalIllumination_MY(data, occlusion, normalWorld);// }// }// inline UnityGI UnityGlobalIllumination_MY (UnityGIInput data, half occlusion, half smoothness, half3 normalWorld)// {// #if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS// // No need to sample reflection probes during deferred G-buffer pass// bool sampleReflections = false;// #else// bool sampleReflections = true;// #endif// return UnityGlobalIllumination_MY (data, occlusion, smoothness, normalWorld, sampleReflections);// }// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//half SmoothnessToPerceptualRoughness(half smoothness){return (1 - smoothness);}
//Unity_GlossyEnvironmentData UnityGlossyEnvironmentSetup_MY(half Smoothness, half3 worldViewDir, half3 Normal, half3 fresnel0){Unity_GlossyEnvironmentData g;// g.roughness /* perceptualRoughness */ = (1 - smoothness);g.roughness /* perceptualRoughness */ = SmoothnessToPerceptualRoughness(Smoothness);//反射球的采样坐标g.reflUVW = reflect(-worldViewDir, Normal);return g;}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------//PBR 光照模型 GIinline void LightingStandard_GI_MY (SurfaceOutputStandard s,UnityGIInput data,inout UnityGI gi){//&& 表示的是两个条件都满足,才会执行下面的计算。否则执行 #else 下面的计算。//UNITY_PASS_DEFERRED 是延迟渲染// 其中UNITY_ENABLE_REFLECTION_BUFFERS - 使用延迟着色时,以延迟方式渲染反射探测#if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERS//UnityGlobalIllumination_MYgi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal);#elseUnity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup_MY(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic));// 返回gigi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal, g);#endif}#endif
最后精简后的Shader
Shader "Custom/MyPBR"
{Properties{_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_NormalTex ("NormalTex", 2D) = "bump" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader{Tags { "RenderType"="Opaque" }LOD 200Pass {Name "FORWARD"Tags { "LightMode" = "ForwardBase" }CGPROGRAM#pragma vertex vert_surf#pragma fragment frag_surf#pragma target 3.0#pragma multi_compile_instancing#pragma multi_compile_fog#pragma multi_compile_fwdbase#include "UnityCG.cginc"#include "Lighting.cginc"#include "UnityPBSLighting.cginc"#include "AutoLight.cginc"#include "MYPBRCGINC.cginc"half _Glossiness;half _Metallic;fixed4 _Color;sampler2D _MainTex , _NormalTex;float4 _MainTex_ST , _NormalTex_ST;struct v2f_surf{float4 pos :SV_POSITION;float2 pack0 : TEXCOORD0;float3 worldNormal : TEXCOORD1;float3 worldPos : TEXCOORD2;#if UNITY_SHOULD_SAMPLE_SHhalf3 sh : TEXCOORD3;#endifUNITY_FOG_COORDS(4)UNITY_SHADOW_COORDS(5)float3 tSpace0:TEXCOORD6;float3 tSpace1:TEXCOORD7;float3 tSpace2:TEXCOORD8;float2 normal : TEXCOORD9;};struct appdata{float4 vertex : POSITION;float4 tangent : TANGENT;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;float4 texcoord1 : TEXCOORD1;float4 texcoord2 : TEXCOORD2;float4 texcoord3 : TEXCOORD3;fixed4 color : COLOR;UNITY_VERTEX_INPUT_INSTANCE_ID};v2f_surf vert_surf (appdata v){v2f_surf o;o.pos = UnityObjectToClipPos(v.vertex);o.pack0 = TRANSFORM_TEX(v.texcoord.xy, _MainTex);o.normal = TRANSFORM_TEX(v.texcoord.zw, _NormalTex);float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;float3 worldNormal = UnityObjectToWorldNormal(v.normal);fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);o.worldPos.xyz = worldPos;#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELo.sh = 0;#ifdef VERTEXLIGHT_ONo.sh += Shade4PointLights (unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,unity_4LightAtten0, worldPos, worldNormal);#endifo.sh = ShadeSHPerVertex (worldNormal, o.sh);#endifUNITY_TRANSFER_FOG(o,o.pos);return o;}fixed4 frag_surf (v2f_surf IN) : SV_Target{UNITY_EXTRACT_FOG(IN);float3 worldPos = IN.worldPos.xyz;float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));SurfaceOutputStandard o;UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard,o);fixed4 mainTex = tex2D (_MainTex, IN.pack0) * _Color;o.Albedo = mainTex.rgb;o.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = mainTex.a;o.Emission = 0.0;o.Occlusion = 1.0;half3 normalTex = UnpackNormal(tex2D(_NormalTex,IN.normal));half3 worldNormal = half3(dot(IN.tSpace0,normalTex),dot(IN.tSpace1,normalTex),dot(IN.tSpace2,normalTex));o.Normal = worldNormal;UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)UnityGI gi;UNITY_INITIALIZE_OUTPUT(UnityGI, gi);gi.indirect.diffuse = 0;gi.indirect.specular = 0;gi.light.color = _LightColor0.rgb;gi.light.dir = _WorldSpaceLightPos0.xyz;UnityGIInput giInput;UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);giInput.light = gi.light;giInput.worldPos = worldPos;giInput.worldViewDir = worldViewDir;giInput.atten = atten;giInput.lightmapUV = 0.0;#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXELgiInput.ambient = IN.sh;#elsegiInput.ambient.rgb = 0.0;#endifgiInput.probeHDR[0] = unity_SpecCube0_HDR;LightingStandard_GI_MY(o, giInput, gi);fixed4 c = LightingStandard (o, worldViewDir, gi);UNITY_APPLY_FOG(_unity_fogCoord, c);UNITY_OPAQUE_ALPHA(c.a);return c;}ENDCG}}FallBack "Diffuse"
}
最后精简后的cginc代码
#ifndef MYPBRCGINC_CGINC#define MYPBRCGINC_CGINChalf3 Unity_GlossyEnvironment_MY (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn){half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;#if 0float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameterconst float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)float n = (2.0/max(fEps, m*m))-2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdfn /= 4; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.htmlperceptualRoughness = pow( 2/(n+2), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)#elseperceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);#endifhalf mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);half3 R = glossIn.reflUVW;half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);//tex2Dlodreturn DecodeHDR(rgbm, hdr);}inline half3 UnityGI_IndirectSpecular_MY(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn){half3 specular;#ifdef _GLOSSYREFLECTIONS_OFFspecular = unity_IndirectSpecColor.rgb;#elsehalf3 env0 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);#ifdef UNITY_SPECCUBE_BLENDINGconst float kBlendFactor = 0.99999;float blendLerp = data.boxMin[0].w;UNITY_BRANCHif (blendLerp < kBlendFactor){#ifdef UNITY_SPECCUBE_BOX_PROJECTIONglossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);#endifhalf3 env1 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);specular = lerp(env1, env0, blendLerp);}else{specular = env0;}#elsespecular = env0;#endif#endifreturn specular * occlusion;}inline UnityGI UnityGlobalIllumination_MY (UnityGIInput data, half occlusion, half3 normalWorld, Unity_GlossyEnvironmentData glossIn){UnityGI o_gi = UnityGI_Base(data, occlusion, normalWorld);o_gi.indirect.specular = UnityGI_IndirectSpecular_MY(data, occlusion, glossIn);return o_gi;}half SmoothnessToPerceptualRoughness(half smoothness){return (1 - smoothness);}Unity_GlossyEnvironmentData UnityGlossyEnvironmentSetup_MY(half Smoothness, half3 worldViewDir, half3 Normal, half3 fresnel0){Unity_GlossyEnvironmentData g;g.roughness = SmoothnessToPerceptualRoughness(Smoothness);g.reflUVW = reflect(-worldViewDir, Normal);return g;}inline void LightingStandard_GI_MY (SurfaceOutputStandard s,UnityGIInput data,inout UnityGI gi){#if defined(UNITY_PASS_DEFERRED) && UNITY_ENABLE_REFLECTION_BUFFERSgi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal);#elseUnity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup_MY(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic));gi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal, g);#endif}
#endif