SurfaceShader Analysis of Unity Build-In Pipeline
- In Unity Build-In Pipeline (Universal Render Pipeline)
-
-
- Create a new Standard Surface Shader
- PASS
- include files
- Judge sentences
- Structure Struct v2f_surf
- vertex shader
- frag_surf fragment shader
- LightingStandard ( o , worldViewDir , gi ) 解析
- UnityGI gi;
- The complete Shader, the code retaining the original calculation and comments is as follows:
- The complete cginc code is as follows:
-
- The final streamlined Shader
- The final streamlined cginc code
In Unity Build-In Pipeline (Universal Render Pipeline)
Create a new Standard Surface Shader
- Named MyPBR
- Double click to open file
The code in the file is as follows:
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"
}
Select "MyPBR.Shader", in the Inspector panel, open "Show generated code"
Jump to VScode, there are 1677 lines of code here
Ctrl K then Ctrl 0 the file to collapse the code.
There are some detailed notes on Unity.
Copy it to the shader file.
- LightMode annotation for pass section
- ForwardBase pass mainly supports pixel-by-pixel lighting of the brightest main parallel light.
- ForwardAdd pass supports other pixel-by-pixel lighting. This depends on whether the project requirements require it.
- Deferred pass supports delayed rendering. This rendering pass is rarely used on mobile phones.
- Meta lighting baking
- Keep ForwardBase pass and remove all others.
// ---- 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
The general structure after deleting unused passes
The role of FallBack "Diffuse"
- It ensures the bottom coloring of different platforms, and also supports shadows. If the shadows are removed, they may not be displayed.
}
//这个的作用是保证不同平台的兜底着色,还有就是支持阴影,如果去掉阴影可能会不显示,
FallBack "Diffuse"
}
include files
- These include files will contain concatenated applications internally.
// #include "HLSLSupport.cginc"
// #define UNITY_INSTANCED_LOD_FADE
// #define UNITY_INSTANCED_SH
// #define UNITY_INSTANCED_LIGHTMAPSTS
// #include "UnityShaderVariables.cginc"
// #include "UnityShaderUtilities.cginc"
Judge sentences
if/whether defined (semantics)
- #if !defined(INSTANCIN1G_ON) means that if GPU instantiation is not defined, then the calculations in #if to #endif are performed
- #if defined(INSTANCING_ON) means that if GPU instantiation is defined, then perform the calculations from #if to #endif
- -------- variant for: when no other keywords are defined
- #if !defined(INSTANCING_ON)
Do you want to use lightmaps?
- #ifndef LIGHTMAP_ON means if LIGHTMAP_ON is not defined
- #ifdef LIGHTMAP_ON means if LIGHTMAP_ON is defined
Classic application of conditional statements of ifdef and ifndef
high-precision high-precision, half-precision half-precision
- Precision fragment shader registers
Structure Struct v2f_surf
UNITY_POSITION(pos); = float4 pos :SV_POSITION;
- UNITY_POSITION(pos);andfloat4 pos :SV_POSITION;is a thing;
pack0 is the uv of _MainTex
float2 pack0 : TEXCOORD0; // _MainTex ,其中pack0 就是_MainTex的uv
Declare three variables in v2f_surf to form a rotation matrix in tangent space
float3 tSpace0:TEXCOORD3;
float3 tSpace1:TEXCOORD4;
float3 tSpace2:TEXCOORD5;
The complete code of the final v2f_surf structure:
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 shader
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;
}
where appdata_full
v2f_surf vert_surf (appdata_full v)
{
...
}
- There is specific structure data in 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);
Only required if you want to access instanced properties in the fragment shader. An official introduction to Unity
about instantiation
- UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
Initialization of v2f_surf structure. - LIGHTMAP_ON - baking; DIRLIGHTMAP_COMBINED directional light source
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);
The organized vertex-vert_surf vertex shader is as follows:
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 fragment shader
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;
}
Parse forward from return c;
- return c; Return c.
UNITY_OPAQUE_ALPHA(c.a);
- #define UNITY_OPAQUE_ALPHA(outputAlpha) outputAlpha = 1.0
- Alpha value is 1.0
and another one:
#if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
fixed4 c = LightingStandard (o, worldViewDir, gi);
- fixed4 c = LightingStandard (o, worldViewDir, gi); The LightingStandard is in the UnityPBSLighting.cginc file
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 ) 解析
The first parameter "o" of LightingStandard:
The first "o" passed in, o is the beginning of the fragment shading segment:SurfaceOutputStandard o;
Differentiation of compilation instructions for different platforms
#ifdef UNITY_COMPILER_HLSL
SurfaceOutputStandard o = (SurfaceOutputStandard)0;
#else
SurfaceOutputStandard o;
#endif
Hiroshi: UNITY_INITIALIZE_OUTPUT(type,name) analysis
- UNITY_INITIALIZE_OUTPUT(type,name) is used to initialize each variable in the given structure to 0.
- Definition in HLSLSupport file:
- Initialize any structure with a zero value.
- Some backends don't support it (e.g. Cg-based, especially nested structures).
- hlsl2glsl will almost never support it unless there is an array of structures - so that's not supported either.
- hlsl2glsl: The full name is High Level Shader Language to OpenGL Shading Language, and the abbreviation is 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 structure under metal workflow
Assign values to the parameters in the SurfaceOutputStandard structure one by one
- The main function of void surf (Input IN, inout SurfaceOutputStandard o) here is to assign values to the parameters in the SurfaceOutputStandard structure one by one.
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;
}
Final SurfaceOutputStandard code and comments
Comments added to SurfaceOutputStandard code:
o.Normal = worldNormal;
- NORMAL and TANGENT semantics are defined in the appdata structure.
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
};
- New statement in v2f_surf
- float3 tSpace0:TEXCOORD6;
- float3 tSpace1:TEXCOORD7;
- float3 tSpace2:TEXCOORD8;
- float2 normal : TEXCOORD9; // _NormalTex
- Used to form matrices in tangent space.
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
};
- In the vertex shader:
// 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;
}
- As in the normal fill in the fragment shader
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 structure
UnityLightingCommon.cginc file
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; direct lighting contains information
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; indirect lighting contains information
struct UnityIndirect
{
half3 diffuse;
half3 specular;
};
The complete Shader, the code retaining the original calculation and comments is as follows:
// 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"
}
Create a new cginc file
#ifndef MYPBRCGINC_CGINC
#define MYPBRCGINC_CGINC
#endif
The complete cginc code is as follows:
#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
The final streamlined 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"
}
The final streamlined cginc code
#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