unity-shader-tutorial-pbr

buildin效果图

PBR.shader

PBR.shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
Shader "Unlit/PBR"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_NormalTex("Normal",2D) ="bump"{}
_MetallicTex ("Metallic (R) Smoothness (G) AO (B)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_AO ("Ambient Occlusion", Range(0,1)) = 1
}

SubShader
{
Tags { "RenderType"="Opaque" }

Pass
{
Name "FORWARD"
Tags { "LightMode" = "ForwardBase" }

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#pragma multi_compile_fog
#pragma multi_compile_fwdbase
#pragma skip_variants DIRLIGHTMAP_COMBINED DYNAMICLIGHTMAP_ON LIGHTMAP_ON LIGHTMAP_SHADOW_MIXING
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "UnityPBSLighting.cginc"
#include "AutoLight.cginc"
#include "../CGIncludes/PBR.cginc"

sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NormalTex;
sampler2D _MetallicTex;
half _Glossiness;
half _Metallic;
half _AO;
fixed4 _Color;

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;
};

struct v2f
{
float4 pos:SV_POSITION;
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;

#if UNITY_SHOULD_SAMPLE_SH
half3 sh : TEXCOORD3;
#endif

float3 tSpace0:TEXCOORD4;
float3 tSpace1:TEXCOORD5;
float3 tSpace2:TEXCOORD6;

UNITY_FOG_COORDS(7)
UNITY_SHADOW_COORDS(8)
};

v2f vert (appdata v)
{
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f,o);
// 将模型本地空间转换到齐次裁剪空间
o.pos = UnityObjectToClipPos(v.vertex);
// 对_MainTex纹理进行UV变换
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
// 世界空间下的顶点坐标
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
// 世界空间下的顶点法线
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
// 世界空间下的切线
half3 worldTangent = UnityObjectToWorldDir(v.tangent);
// 切线方向
fixed tangentSign = v.tangent.w * unity_WorldTransformParams.w;
// 世界空间下的副切线
half3 worldBinormal = cross(o.worldNormal, worldTangent) * tangentSign;
// 切线矩阵
o.tSpace0 = float3(worldTangent.x,worldBinormal.x,worldNormal.x);
o.tSpace1 = float3(worldTangent.y,worldBinormal.y,worldNormal.y);
o.tSpace2 = float3(worldTangent.z,worldBinormal.z,worldNormal.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_LIGHTING(o,v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
UNITY_EXTRACT_FOG(i);

float3 worldPos = i.worldPos.xyz;

SurfaceOutputStandard o;
UNITY_INITIALIZE_OUTPUT(SurfaceOutputStandard,o);
fixed4 mainTex = tex2D(_MainTex,i.uv);
o.Albedo = mainTex.rgb * _Color;
half3 normalTex = UnpackNormal(tex2D(_NormalTex,i.uv));
half3 worldNormal = half3(dot(i.tSpace0,normalTex),dot(i.tSpace1,normalTex),dot(i.tSpace2,normalTex));
o.Normal = worldNormal;
o.Emission = 0;
fixed4 metallicTex = tex2D(_MetallicTex, i.uv);
o.Metallic = metallicTex.r * _Metallic;
o.Smoothness = metallicTex.g * _Glossiness;
o.Occlusion = metallicTex.b * _AO;
o.Alpha = 1;

// 计算光照和阴影系数
UNITY_LIGHT_ATTENUATION(atten, i, 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 = normalize(UnityWorldSpaceViewDir(worldPos));
giInput.atten = atten;
giInput.lightmapUV = 0.0;

#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
giInput.ambient = i.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);
// return fixed4(gi.indirect.specular,1);

// PBS的核心计算>LightingStandard
fixed4 c = LightingStandard_ (o, giInput.worldViewDir, gi);

// 雾效
UNITY_APPLY_FOG(_unity_fogCoord, c);
return c;
}

ENDCG
}
}
}

GI.cginc

GI.cginc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#ifndef __GI_CGINCLUDE__
#define __GI_CGINCLUDE__

// Lambert光照模型
inline fixed4 UnityLambertLight_ (SurfaceOutput s, UnityLight light)
{
fixed diff = max (0, dot (s.Normal, light.dir));

fixed4 c;
c.rgb = s.Albedo * light.color * diff;
c.a = s.Alpha;
return c;
}

// 直接光照+间接光照
inline fixed4 LightingLambert_ (SurfaceOutput s, UnityGI gi)
{
fixed4 c;
c = UnityLambertLight_ (s, gi.light);

// 如果是BakedGI或者RealtimeGI的情况下
#ifdef UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
c.rgb += s.Albedo * gi.indirect.diffuse;
#endif

return c;
}

// GI核心内容
inline UnityGI UnityGI_Base_(UnityGIInput data, half occlusion, half3 normalWorld)
{
// 声明一个UnityGI类型的变量o_gi,用于存储最终计算的gi信息
UnityGI o_gi;
// 重置gi内的变量值
ResetUnityGI(o_gi);

// 计算在Distance Shadowmask中实时阴影与烘焙阴影的混合过渡
// Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
#if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
#endif

// 将主平行灯传入gi中
o_gi.light = data.light;
// 将衰减应用于灯光颜色中
o_gi.light.color *= data.atten;

// 是否进行球谐光照
#if UNITY_SHOULD_SAMPLE_SH
o_gi.indirect.diffuse = ShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
#endif

// 当采用Baked GI时
#if defined(LIGHTMAP_ON)
// Baked lightmaps
// 光照图的采样(核心)
half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
half3 bakedColor = DecodeLightmap(bakedColorTex);

// 当开启Directional模式时
#ifdef DIRLIGHTMAP_COMBINED
fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER (unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
o_gi.indirect.diffuse += DecodeDirectionalLightmap (bakedColor, bakedDirTex, normalWorld);

#if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
ResetUnityLight(o_gi.light);
o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap (o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
#endif

#else // not directional lightmap
o_gi.indirect.diffuse += bakedColor;

#if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) && defined(SHADOWS_SCREEN)
ResetUnityLight(o_gi.light);
o_gi.indirect.diffuse = SubtractMainLightWithRealtimeAttenuationFromLightmap(o_gi.indirect.diffuse, data.atten, bakedColorTex, normalWorld);
#endif

#endif
#endif

// 当采用Realtime GI时
#ifdef DYNAMICLIGHTMAP_ON
// Dynamic lightmaps
fixed4 realtimeColorTex = UNITY_SAMPLE_TEX2D(unity_DynamicLightmap, data.lightmapUV.zw);
half3 realtimeColor = DecodeRealtimeLightmap (realtimeColorTex);

#ifdef DIRLIGHTMAP_COMBINED
half4 realtimeDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_DynamicDirectionality, unity_DynamicLightmap, data.lightmapUV.zw);
o_gi.indirect.diffuse += DecodeDirectionalLightmap (realtimeColor, realtimeDirTex, normalWorld);
#else
o_gi.indirect.diffuse += realtimeColor;
#endif
#endif

o_gi.indirect.diffuse *= occlusion;
// 返回计算后的gi
return o_gi;
}

inline UnityGI UnityGlobalIllumination_ (UnityGIInput data, half occlusion, half3 normalWorld)
{
return UnityGI_Base_(data, occlusion, normalWorld);
}

inline void LightingLambert_GI_ (SurfaceOutput s,UnityGIInput data,inout UnityGI gi)
{
gi = UnityGlobalIllumination_ (data, 1.0, s.Normal);
}

#endif // __GI_CGINCLUDE__