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 200
CGPROGRAM
// 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.0
sampler2D _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 assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
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;
}
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的uv
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD3; // SH 球谐
#endif
UNITY_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);
#endif
o.worldPos.xyz = worldPos;
o.worldNormal = worldNormal;
#ifdef DYNAMICLIGHTMAP_ON
o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
#endif
#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_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.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);
#endif
o.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
#else
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
#endif
return 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);
// #endif
o.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_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.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);
#endif
o.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
// #else
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
// #endif
return o;
}
frag_surf 片段着色器
fixed4 frag_surf (v2f_surf IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
// prepare and unpack data
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
UNITY_INITIALIZE_OUTPUT(Input,surfIN);
surfIN.uv_MainTex.x = 1.0;
surfIN.uv_MainTex = IN.pack0.xy;
float3 worldPos = IN.worldPos.xyz;
#ifndef USING_DIRECTIONAL_LIGHT
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
#else
fixed3 lightDir = _WorldSpaceLightPos0.xyz;
#endif
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard o;
#endif
o.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 function
surf (surfIN, o);
// compute lighting & shadowing factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
fixed4 c = 0;
// Setup lighting environment
UnityGI 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 function
UnityGIInput 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;
#else
giInput.lightmapUV = 0.0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.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_PROJECTION
giInput.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;
#endif
LightingStandard_GI(o, giInput, gi);
// realtime lighting: call lighting function
c += LightingStandard (o, worldViewDir, gi);
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
UNITY_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 alpha
half 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_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard 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 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;
}
最后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的uv
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD3; // SH 球谐
#endif
UNITY_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 shader
v2f_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);
// #endif
o.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_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.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);
#endif
o.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
// #else
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
// #endif
return 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 factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
// Setup lighting environment
UnityGI 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 function
UnityGIInput 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;
#else
giInput.lightmapUV = 0.0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.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_PROJECTION
giInput.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;
#endif
LightingStandard_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 engine
float3 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_PROJECTION
float4 boxMax[2];
float4 probePosition[2];
#endif
// HDR cubemap properties, use to decompress HDR texture
float4 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.0
half _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_REGISTERS
struct v2f_surf
{
// UNITY_POSITION(pos); = float4 pos :SV_POSITION;!!!!!!!!!!!!!!!!
float4 pos :SV_POSITION;
float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uv
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD3; // SH 球谐
#endif
UNITY_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 shader
v2f_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);
// #endif
o.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_PIXEL
o.sh = 0;
// Approximated illumination from non-important point lights
#ifdef VERTEXLIGHT_ON
o.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);
#endif
o.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
// #else
UNITY_TRANSFER_FOG(o,o.pos); // pass fog coordinates to pixel shader
// #endif
return o;
}
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// fragment shader
fixed4 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;
// #endif
float3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//将SurfaceOutputStandard的数据填充完整。
SurfaceOutputStandard o;
UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard,o);
// #ifdef UNITY_COMPILER_HLSL
// SurfaceOutputStandard o = (SurfaceOutputStandard)0;
// #else
// SurfaceOutputStandard o;
// #endif
fixed4 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 factor
UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
// Setup lighting environment
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;
// Call GI (lightmaps/SH/reflections) lighting function
UnityGIInput 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;
#else
giInput.lightmapUV = 0.0;
#endif
#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.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_PROJECTION
giInput.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;
#endif
LightingStandard_GI(o, giInput, gi);
// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//PBR的核心计算,基于物理着色,BRDF的相关计算
// realtime lighting: call lighting function
fixed4 c = LightingStandard (o, worldViewDir, gi);
UNITY_APPLY_FOG(_unity_fogCoord, c); // apply fog
// Alpha值为1.0
UNITY_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 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const 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.pdf
n /= 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.html
perceptualRoughness = 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);//tex2Dlod
return 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_OFF
specular = unity_IndirectSpecColor.rgb;
#else
half3 env0 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
#ifdef UNITY_SPECCUBE_BLENDING
const float kBlendFactor = 0.99999;
float blendLerp = data.boxMin[0].w;
UNITY_BRANCH
if (blendLerp < kBlendFactor)
{
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);
#endif
half3 env1 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);
specular = lerp(env1, env0, blendLerp);
}
else
{
specular = env0;
}
#else
specular = env0;
#endif
#endif
return 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 光照模型 GI
inline 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_MY
gi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal);
#else
Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup_MY(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic));
// 返回gi
gi = 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 200
Pass {
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_SH
half3 sh : TEXCOORD3;
#endif
UNITY_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_PIXEL
o.sh = 0;
#ifdef VERTEXLIGHT_ON
o.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);
#endif
o.sh = ShadeSHPerVertex (worldNormal, o.sh);
#endif
UNITY_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_PIXEL
giInput.ambient = IN.sh;
#else
giInput.ambient.rgb = 0.0;
#endif
giInput.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_CGINC
half3 Unity_GlossyEnvironment_MY (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
{
half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;
#if 0
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
const 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.pdf
n /= 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.html
perceptualRoughness = 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
perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
#endif
half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
half3 R = glossIn.reflUVW;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);//tex2Dlod
return DecodeHDR(rgbm, hdr);
}
inline half3 UnityGI_IndirectSpecular_MY(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
{
half3 specular;
#ifdef _GLOSSYREFLECTIONS_OFF
specular = unity_IndirectSpecColor.rgb;
#else
half3 env0 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
#ifdef UNITY_SPECCUBE_BLENDING
const float kBlendFactor = 0.99999;
float blendLerp = data.boxMin[0].w;
UNITY_BRANCH
if (blendLerp < kBlendFactor)
{
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[1], data.boxMin[1], data.boxMax[1]);
#endif
half3 env1 = Unity_GlossyEnvironment_MY (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn);
specular = lerp(env1, env0, blendLerp);
}
else
{
specular = env0;
}
#else
specular = env0;
#endif
#endif
return 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_BUFFERS
gi = UnityGlobalIllumination_MY(data, s.Occlusion, s.Normal);
#else
Unity_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