UnityShader入门——光照

提示

所有的Shader效果图应该以Game窗口为准,Scene窗口很容易出现问题

光照

概述

image-20241030105801988

技术点:BRDF光照模型:次时代渲染 ,SSS材质:和次表面相关

标准光照模型

自发光,高光反射,漫反射,环境光

自发光

物体在摄像机里看起来更亮,在Unity的常规使用中,不对周五其他物体产生影响

高光反射

差异

blinn——phong的 实现效果比较好

Phong模型

推导过程

image-20241030142434006

高光反射公式

1
高光反射颜色 = 入射颜色 * 高光颜色 * max(0,(2(n * l)n - l)v)^光泽度

字母代表向量

实现

知识:Unity的Reflect()方法可以计算入射光线的反射角度,但是使用的是常规物理画图的表达方式,入射光线方向朝着镜面,而Unity的光源方向和这个相反,因此要取一个 负值

注意:在之前的计算中,错误的使用了,该方法实际上返回的是一个向量,而在phong的计算过程中,需要用到的是点

1
2
3
4
5
6
// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
return normalize(mul((float3x3)unity_ObjectToWorld, dir));
}

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
    Shader "Unlit/Phong"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Diffse("Diff",color) = (1,1,1,1)
_Specular("Specular",color) = (1,1,1,1)
_Gloss("Gloss",Range(1,256)) = 20
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc "



struct v2f
{
float4 vertex : SV_POSITION;
float3 wordNormal: TEXCOORD0;
float3 worldPos : TEXCOORD1;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffse;
float4 _Specular;
int _Gloss;

v2f vert(appdata_base v)
{
v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);
o.wordNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);

return o;
}

fixed4 frag (v2f i) : SV_Target
{
float3 environmentLightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));


float3 reflectDir = normalize(reflect(- lightDir,i.wordNormal)); //使用该方法求解出射光线注意 负值
float3 phong = _LightColor0.rgb * _Specular.rbg * pow(max(0,dot(viewDir,reflectDir)),_Gloss);
float3 resColor = environmentLightColor + phong;


float4 col = float4(resColor,1);

return col;
}
ENDCG
}
}
}


image-20241030193432018

Blinn—phong模型

推导过程

image-20241030144440831

在该光照模型中,引入 半角向量 这一概念,该向量是l 和v 的中间向量,(角度的一半或者 (l + v)/2;

实现

在实际中,使用的向量都进行了归一化操作,从而使得半角向量就是入射光线 + 视角

在片元着色器中操作

1
2
float3 halfView = normalize(_WorldSpaceCameraPos + _WorldSpaceLightPos0);
float3 phong = _LightColor0.rgb * _Specular * pow(max(0,dot(i.wordNormal,halfView)),_Gloss);
image-20241031084739812

替换方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//通过给定的方法计算视线向量,返回后需要手动归一化
inline float3 UnityWorldSpaceViewDir( in float3 worldPos )
{
return _WorldSpaceCameraPos.xyz - worldPos;
}

//计算世界坐标下的灯光向量
// Computes world space light direction, from world space position
inline float3 UnityWorldSpaceLightDir( in float3 worldPos )
{
#ifndef USING_LIGHT_MULTI_COMPILE
return _WorldSpaceLightPos0.xyz - worldPos * _WorldSpaceLightPos0.w;
#else
#ifndef USING_DIRECTIONAL_LIGHT
return _WorldSpaceLightPos0.xyz - worldPos;
#else
return _WorldSpaceLightPos0.xyz;
#endif
#endif
}

漫反射

理论

根据入射光线角度的不用,可以分为三种情况

条件准备 入射光线和入射点法线的夹角为a,a不是钝角

a是锐角的情况:漫反射光从入射点法线相对入射光线的那一层射出

a是直角:从入射点向四周发散

a是平角:没有漫反射光

image-20241030112349086

影响漫反射光的角度计算,向量点乘

相关链接

Unity——Shader实现边缘发光效果 | mao的博客 (mao1mao2mao3mao4.github.io)

公式

漫反射颜色计算

漫反射颜色 = (入射光线颜色 * 反射面光滑度 ) * max(0,dot (n ,l));

最终颜色计算

Lambert 光照模型公式: 最终颜色 = 直射光颜色 * 漫反射颜色 * max(0, dot(光源方向, 法线方向))

其中,直射光颜色,漫反射颜色,都是我们自定义的变量。

实现

知识:点乘需要放在同一个坐标系下才有效

顶点漫反射
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
Shader "Unlit/Lambert"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Diffse("Color",color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc "



struct v2f
{
float4 vertex : SV_POSITION;
float3 color : color;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffse;

v2f vert (appdata_base v)
{
v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);
float3 environmentLightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 wordNormal = UnityObjectToWorldNormal(v.normal);
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
normalize(lightDir);

o.color = _LightColor0.rgb * _Diffse.rgb * saturate(dot(wordNormal,lightDir)) + environmentLightColor;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
float4 col = float4(i.color,1);

return col;
}
ENDCG
}
}
}

image-20241030170631794
片元漫反射
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
Shader "Unlit/Lambert"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Diffse("Color",color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert

#pragma fragment frag


#include "UnityCG.cginc"

#include "UnityLightingCommon.cginc "




struct v2f
{
float4 vertex : SV_POSITION;
float3 wordNormal: TEXCOORD0;
};

sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffse;

v2f vert (appdata_base v)
{
v2f o;

o.vertex = UnityObjectToClipPos(v.vertex);
o.wordNormal = UnityObjectToWorldNormal(v.normal);

return o;
}

fixed4 frag (v2f i) : SV_Target
{
float3 environmentLightColor = UNITY_LIGHTMODEL_AMBIENT.xyz;
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
normalize(lightDir);
float3 resColor = _LightColor0.rgb * _Diffse.rgb * saturate(dot(i.wordNormal,lightDir)) + environmentLightColor;


float4 col = float4(resColor,1);

return col;
}
ENDCG
}
}
}
image-20241030171143605
半兰伯特漫反射
1
2
float3 resColor = _LightColor0.rgb * _Diffse.rgb  * (dot(i.wordNormal,lightDir)*0.5 + 0.5)+ environmentLightColor;

将原来的值大小限制直接通过计算归纳到0到1的范围内

image-20241030172345288

环境光

例子:红苹果放在一张白纸旁边,白纸有点微红,描述的是间接光照

纹理采样

属性块中的属性

1
_MainTex ("Texture", 2D) = "white" {}
image-20241031144245470

使用的时候需要设置这两个属性,第二个是第一个的从属,第一个有就默认有第二个,里面保存的是贴图的缩放和位移,分别可以用 xy 和 zw 来进行表示

1
2
sampler2D _MainTex;
float4 _MainTex_ST;//从属于上一个变量

实现后相较于常规的着色器添加的内容

1
2
3
4
5
6
7
8
9
//v2f结构体中的加入元素
float2 uv: TEXCOORD0;

//顶点着色器中的新内容
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
//O.UV = TRANSFORM_TEX(v.texcoord,_MainTex);//Unity官方提供的转换方法,但是在使用的过程中还要定义两个属性
//片元着色器中的新内容
float3 albedo = tex2D(_MainTex,i.uv).rbg;

注意:如果在片元着色器中存在光照模型,需要将 albedo 乘到 漫反射中。

image-20241031144952134

图片设置

知乎上有一篇写的很好的,这里给出链接

纹理滤波,Mipmaps,Unity中图片的导入设置 - 知乎 (zhihu.com)