Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

反射

一、反射效果在 UnityShader 中是什么

在 UnityShader 中,反射效果模拟了物体表面反射环境光的特性 使得物体看起来像是镜子或者金属表面,能够反射周围环境的图像

二、基本原理

反射的基本原理就是 利用立方体纹理进行环境映射

利用摄像机看向物体表面顶点的方向作为入射光,结合顶点法线向量可以计算出反射向量,然后利用反射方向向量在立方体纹理中进行采样 2024-10-17 110415.png

三、基础实现

1.实现思路

  1. 属性声明 两个关键属性
    • 立方体纹理
    • 反射率(0~1 之间)
  2. 顶点着色器 关键步骤
    1. 顶点坐标转裁剪坐标
    2. 顶点法线转世界坐标
    3. 顶点坐标转世界坐标
    4. 世界空间下 视角的计算
    5. 计算反射坐标
  3. 片元着色器 关键步骤
    1. 立方体纹理采样(利用 texCUBE 函数)
    2. 结合反射率返回最终的颜色

2.实现示例

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
Shader "Unlit/ReflectBase"
{
Properties
{
//立方体纹理
_Cube("Cubemap",Cube) = ""{}
//反射率
_Reflectivity("Reflectivity",Range(0,1)) = 1
}
SubShader
{
Tags{"RenderType" = "Opaque" "Queue" = "Geometry"}

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


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

samplerCUBE _Cube;
float _Reflectivity;

struct v2f
{
float4 pos:SV_POSITION;//裁剪坐标下的顶点坐标
//世界空间下的顶点坐标
//我们将把反射向量的的计算放在顶点坐标系下 节约性能 表现效果肉眼几乎和片元着色器一致
float3 worldRefl:TEXCOORD0;
};
v2f vert(appdata_base v)
{
v2f o;
//顶点坐标转裁剪坐标
o.pos = UnityObjectToClipPos(v.vertex);
//计算计算反射光向量
//1.计算世界空间下的法线向量
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
//2.计算世界空间下的顶点坐标
fixed3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置-世界坐标位置
fixed3 worldViewDir = -UnityWorldSpaceViewDir(worldPos);
//计算反射向量
o.worldRefl = reflect(worldViewDir,worldNormal);
return o;
}
fixed4 frag(v2f i):SV_TARGET
{
//对立方体纹理利用相应的反射函数进行采样
fixed4 cubemapColor = texCUBE(_Cube,i.worldRefl);
//用采样颜色*反射率 得到最终效果
return cubemapColor * _Reflectivity;
}
ENDCG
}
}
}

四、反射结合漫反射

1.实现思路

在反射实现的基础之上

  1. 属性声明
    1. 漫反射的颜色
    2. 反射颜色
  2. v2f 结构体 因为要在片元着色器中处理光和阴影
    1. 世界空间的法线坐标
    2. 世界空间的顶点位置
    3. 阴影宏
  3. 顶点着色器
    1. 顶点坐标转换到裁剪空间下
    2. 顶点法线转世界坐标
    3. 顶点坐标转世界坐标
    4. 世界空间下 计算视角的方向(要求从摄相机到顶点的方向)
    5. 计算得到反射向量
    6. 阴影计算相关
  4. 片元着色器
    1. 得到光的方向
    2. lambert 漫反射的计算
    3. 立方体纹理采样
    4. 利用宏计算阴影衰减和光衰减系数
    5. 使用 lerp 函数 控制颜色叠加
  5. 添加阴影投射 SubShader(FallBack "Reflective/VertexLit")

2.实现示例

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
Shader "Unlit/Reflection"
{
Properties
{
//立方体纹理
_Cube("Cubemap",Cube) = ""{}
//反射率
_Reflectivity("Reflectivity",Range(0,1)) = 1

//漫反射颜色
_Color("Color",Color) = (1,1,1,1)
//反射颜色
_ReflectColor("ReflectColor",Color) = (1,1,1,1)

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

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


CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase

#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"

samplerCUBE _Cube;
float _Reflectivity;
fixed4 _Color;
fixed4 _ReflectColor;

struct v2f
{
float4 pos:SV_POSITION;//裁剪坐标下的顶点坐标
//世界空间下的法线
float3 worldNormal:NORMAL;
//世界坐标下的顶点
float3 worldPos:TEXCOORD0;
//世界空间下的顶点坐标
//我们将把反射向量的的计算放在顶点坐标系下 节约性能 表现效果肉眼几乎和片元着色器一致
float3 worldRefl:TEXCOORD1;

//阴影相关
SHADOW_COORDS(2)

};
v2f vert(appdata_base v)
{
v2f o;
//顶点坐标转裁剪坐标
o.pos = UnityObjectToClipPos(v.vertex);
//计算计算反射光向量
//1.计算世界空间下的法线向量
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//2.计算世界空间下的顶点坐标
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置-世界坐标位置
fixed3 worldViewDir = -UnityWorldSpaceViewDir(o.worldPos);
//计算反射向量
o.worldRefl = reflect(worldViewDir,o.worldNormal);
//阴影相关处理
TRANSFER_SHADOW(o)
return o;
}
fixed4 frag(v2f i):SV_TARGET
{
//进行漫反射光照相关计算
//得到光的方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//计算漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0,dot(normalize(i.worldNormal),worldLightDir));

//对立方体纹理利用相应的反射函数进行采样
fixed3 cubemapColor = texCUBE(_Cube,i.worldRefl).rgb * _ReflectColor.rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
//我们在利用lerp 在漫反射和反射颜色之间 进行插值
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse,cubemapColor,_Reflectivity) * atten;
//用采样颜色*反射率 得到最终效果
return fixed4(color,1.0);
}
ENDCG
}
}
Fallback "Reflective/VertexLit"
}

评论