光照模型与法线贴图

光照模型与法线贴图

二月 14, 2022

渲染路径

前向渲染

对每一个物体每一个光源进行渲染,超出范围的灯光以顶点灯光渲染,适用于灯光较少的情况

ForwardBase

在这个Pass里面,主方向灯以及超出范围的灯光作为顶点灯光传入SH, LightMap,Reflection, Probe等计算均在这个Pass里面完成。

ForwardAdd

数量范围内的灯光,每个灯光的计算,均会调用一次这个pass,计算的结果通过Blend one one 叠加起来。

延迟渲染

使用MRT技术 ,RT0是物体的颜色,RT1是金属反射的颜色,RT2是法线数据,RT4是深度信息。

光源直接渲染一次。有带宽和设备支持的限制,ue4和HDRP默认。

法线贴图

法线

顶点垂直模型的线NORMAL

切线

UV中u递增的线或者v递增的线,引擎帮我们生成的TANGENT

副切线

法线叉乘切线得到的。

unity的无光照shader获得光源信息实战

ForwardBase Pass

关键点

需要将光照模式调为ForwardBase

1
Tags { "LightMode" = "ForwardBase"}

在编写时加入如下引用文件以方便获取光照参数

1
2
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"

appadata

1
2
3
4
5
6
7
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;//法线
float4 tangent:TANGENT;//切线注意是四维向量,w用来矫正平台的区别
};

片元数据

1
2
3
4
5
6
7
8
9
10
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normali_dir:TEXCOORD1;
float3 tangent_dir:TEXCOORD2;
float3 binormal_dir:TEXCOORD3;
float3 pos_world:TEXCOORD4;

};

光源数据

1
half3 light_dir =normalize( _WorldSpaceLightPos0.xyz );//定向光位置无意义所以代表光照方向

代码

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
Pass
{
Tags { "LightMode" = "ForwardBase"}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normali_dir:TEXCOORD1;
float3 tangent_dir:TEXCOORD2;
float3 binormal_dir:TEXCOORD3;
float3 pos_world:TEXCOORD4;

};

sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _AOMap;
float4 _AOMap_ST;
float4 _LightColor0;
float _Shininess;
float _SpecularIntensity;
float4 _AmbientColor;
sampler2D _SpecMask;
float _SpecMask_ST;
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _NormalIntensity;
float __AmbientIntensity;

float3 ACESFilm(float3 x)
{
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
return saturate((x*(a*x + b)) / (x*(c*x + d) + e));
};

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.normali_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
o.tangent_dir = normalize(mul(float4(v.tangent.xyz,0.0),unity_WorldToObject).xyz);
o.binormal_dir = normalize(cross(o.normali_dir,o.tangent_dir))* v.tangent.w;
o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 col=fixed4(0,0,0,0);
half3 AO = tex2D(_AOMap,i.uv).xyz;
half3 spec_mask = tex2D(_SpecMask,i.uv).xyz;
half4 normalmap = tex2D(_NormalMap,i.uv);

half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
half3 light_dir =normalize( _WorldSpaceLightPos0.xyz );

//Normal
half3 normal_dir =normalize(i.normali_dir);
half3 tangent_dir =normalize(i.tangent_dir)*_NormalIntensity;
half3 binormal_dir =normalize(i.binormal_dir)*_NormalIntensity;
half3 normal_data = UnpackNormal(normalmap);
float3x3 TBN = float3x3(tangent_dir,binormal_dir,normal_dir);
normal_dir =normalize(mul(normal_data,TBN));
//normal_dir=tangent_dir*normal_data.x*_NormalIntensity+binormal_dir*normal_data.y*_NormalIntensity+normal_dir*normal_data.z ;

half NotL = max(dot(normal_dir,light_dir),0);
half3 diffuse = NotL*_LightColor0.xyz;
half3 specular = pow(max(dot(reflect(-light_dir,normal_dir),view_dir),0),_Shininess)* _LightColor0.xyz*_SpecularIntensity*spec_mask;
half3 ambient = _AmbientColor.xyz*__AmbientIntensity;

//col.xyz+= AO;

col.xyz += diffuse;
col.xyz += specular;
col.xyz += ambient;

// sample the texture
col.xyz *=tex2D(_MainTex, i.uv).xyz;
col.xyz *=AO;
// apply fog
return col;
}
ENDCG
}

ForwardAdd Pass

关键点

需要将光照模式调为ForwardAdd

1
Tags { "LightMode" = "ForwardAdd"}

要加混合模式

1
Blend One One

在编写时加入如下引用文件以方便获取光照参数

1
2
#pragma multi_compile_fwdadd
#include "AutoLight.cginc"

appadata

1
2
3
4
5
6
7
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;//法线
float4 tangent:TANGENT;//切线注意是四维向量,w用来矫正平台的区别
};

片元数据

1
2
3
4
5
6
7
8
9
10
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normali_dir:TEXCOORD1;
float3 tangent_dir:TEXCOORD2;
float3 binormal_dir:TEXCOORD3;
float3 pos_world:TEXCOORD4;

};

光源数据

如果是定向光的话和ForwardBase是一样的,但如果是点光源的话应考虑衰减量和光照方向

1
2
3
4
5
half3 light_dir =normalize( _WorldSpaceLightPos0.xyz-i.pos_world.xyz ); 
half distance=length(_WorldSpaceLightPos0.xyz-i.pos_world.xyz);
float range = 1.0/unity_WorldToLight[0][0];

half attuenation =saturate((range-distance)/range);

代码

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
     Pass
{
Tags { "LightMode" = "ForwardAdd"}
Blend One One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdadd
#include "UnityCG.cginc"
#include "AutoLight.cginc"

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};

struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normali_dir:TEXCOORD1;
float3 tangent_dir:TEXCOORD2;
float3 binormal_dir:TEXCOORD3;
float3 pos_world:TEXCOORD4;

};

sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _AOMap;
float4 _AOMap_ST;
float4 _LightColor0;
float _Shininess;
float _SpecularIntensity;
float4 _AmbientColor;
sampler2D _SpecMask;
float _SpecMask_ST;
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _NormalIntensity;

float3 ACESFilm(float3 x)
{
float a = 2.51f;
float b = 0.03f;
float c = 2.43f;
float d = 0.59f;
float e = 0.14f;
return saturate((x*(a*x + b)) / (x*(c*x + d) + e));
};

v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.normali_dir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
o.tangent_dir = normalize(mul(float4(v.tangent.xyz,0.0),unity_WorldToObject).xyz);
o.binormal_dir = normalize(cross(o.normali_dir,o.tangent_dir))*v.tangent.w;
o.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed4 col=fixed4(0,0,0,0);
half3 spec_mask = tex2D(_SpecMask,i.uv).xyz;
half4 normalmap = tex2D(_NormalMap,i.uv);



half3 normal_dir =normalize(i.normali_dir);
half3 tangent_dir =normalize(i.tangent_dir);
half3 binormal_dir =normalize(i.binormal_dir);
half3 normal_data = UnpackNormal(normalmap);
normal_dir= tangent_dir*normal_data.x *_NormalIntensity+binormal_dir*normal_data.y*_NormalIntensity+normal_dir*normal_data.z ;
half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
#if defined (DIRECTIONAL)
half3 light_dir =normalize( _WorldSpaceLightPos0.xyz );
half attuenation=1.0;
#elif defined (POINT)
half3 light_dir =normalize( _WorldSpaceLightPos0.xyz-i.pos_world.xyz );
half distance=length(_WorldSpaceLightPos0.xyz-i.pos_world.xyz);
float range = 1.0/unity_WorldToLight[0][0];

half attuenation =saturate((range-distance)/range);

#endif
half NotL = max(dot(normal_dir,light_dir),0);
half3 diffuse = NotL*_LightColor0.xyz;
half3 specular = pow(max(dot(reflect(-light_dir,normal_dir),view_dir),0),_Shininess)* _LightColor0.xyz*_SpecularIntensity*spec_mask;
half3 AO = tex2D(_AOMap,i.uv).xyz;

//col.xyz+= AO;

col.xyz += diffuse;
col.xyz += specular;

// sample the texture
col.xyz *=tex2D(_MainTex, i.uv).xyz;
col.xyz *=AO;
col.xyz *=attuenation;
// apply fog
return col;

}
ENDCG
}