Unity Shader Chapter 6

《Unity Shader 入门精要》Chapter 6 — Reading Note

Standard Lighting Model

Standard lighting model only cares about direct light, emitted from the light source, reflected by the object’s surface and then enters the camera.

These light can be considered into 4 part:

  1. Emissive: Irradiance emitted from a surface to a given direction.
  2. Specular: Irradiance of the completely specular reflection of a surface.
  3. Diffuse: Irradiance of diffusion of a surface.
  4. Ambient: All other indirect light.

Ambient

In standard lighting model, we use a global variable to simulate all indirect light.

cambient = gambient

Emissive

In standard light model, we directly use the emissive color of the material.

cemissive = memissive

Diffuse

The direction of the diffuse light is random, meaning the reflection light’s direction is not important. However, the direction of the incident light matters.

Lambert’s Law: The intensity of the reflection light is proportional to the cosine value of the degree between the surface normal and the source light.

cdiffuse = (clight · mdiffuse) · max(0, n · l);

  • clight: Color of the source light.
  • mdiffuse: Color of the diffuse of the material.
  • n: Surface normal.
  • l: Direction of the source light.

Specular

Here specular is an experienced model, which not totally suit the real world.

We need to know 4 information: surface normal, view direction, source light, reflection direction. However we can calculate one using the other three.

r = 2(n · l) · n - l

Phone Model

cspecular = (clight * mspecular) · max(0, v · r)mgloss

  • mgloss: gloss of the material(shininess). The greater it is, the smaller of the light point.
  • mspecular: Color of the specular of the material.

Blinn Model

A faster model if the distance between camera and light source is enough long.

We introduce a new variable h to help calculation.

h = (v + l) / |v + l|

cspecular = (clight · mspecular) · max(0, n · h)mgloss

Per-pixel Lighting VS Per-vertex Lighting

These are two ways to calculate lighting model.

Per-pixel Lighting (In fragment shader)

Based on each fragment(pixel), retrieve its normal(by interpolation of vertex normal or sample from normal texture), then start the calculation of the lighting model. This is called Phone Shading.

Per-vertex Lighting (In vertex shader)

Based on each vertex, calculate the lighting, then use linear interpolation to render the pixel’s color. This is called Gouraud Shading.

Comparison

Per-vertex lighting is faster than per-pixel lighting because the number of vertexs is much less than the number of pixels. However, because per-vertex lighting relies on linear interpolation, it will make troubles when there exists non-linear calculation (like specular reflection) in the lighting model.

For a high-detailed model, per-vertex lighting is good. But for a low-detailed mode, per-vertex lighting will cause some visual problems. For example, there exists some sawtooth between the frontlight surface and the backlight surface.

Half-Lambert Lighting Model

Even if we are using per-pixel lighting, there exists a problem. In the region that lights cannot reach, usually the appearance of the model is totally dark, without any change in brightness. This makes the backlight region look like a surface, then loses the detials of the model. That’s why we use Half-Lambert Lighting Model.

cdiffuse = (clight · mdiffuse) · (a · (n · l) + b)

  • a: Scale, usually set to 0.5
  • b: Offset, usually set to 0.5

This model maps the result of n · l from [-1, 1] to [0, 1], which gives the backlight side the change of brightness. It can be seen as a visual enhanced technology.

Code

Here we only show the code of Blinn-Phong Lighting Model.

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
Shader "Unity Shaders Book/Chapter 6/Blinn-Phong"{
Properties{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
_Specular("Specular", Color) = (1, 1, 1, 1)
_Gloss("Gloss", Range(8.0, 256)) = 20
}

SubShader{
Pass{
Tags{
"LightMode" = "ForwardBase"
}

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

#include "Lighting.cginc"

fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;

struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
};

struct v2f{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};

v2f vert(a2v v){
v2f o;

o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

return o;
}

fixed4 frag(v2f i) : SV_TARGET{
// Diffuse
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

fixed3 worldNormal = i.worldNormal;
// fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));

// Specular
// fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);

fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);

// Final Light
return fixed4(ambient + diffuse + specular, 1.0);
}

ENDCG
}
}

Fallback "Specular"
}