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

反射

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

折射模拟了光线通过透明或者半透明材质时的弯曲效果 一般用来模拟睡眠、透明玻璃球、钻石、水晶球、空气扰动等效果 会配合其他效果使用 2024-10-20 114443.png 2024-10-20 114439.png

二、基本原理

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

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

三、基础实现

1.实现思路

  1. 属性声明 我们将声明四个属性 注意: 这里的折射率的声明只是为了学习需要,一般这个折射率都是 n1/n2 计算好后,在传进来
    1. 介质的折射率 A
    2. 介质的反射率 B
    3. 折射的程度
    4. 立方体纹理
  2. 编译指令、内置文件、属性映射、结构体声明
  3. 顶点着色器
    1. 顶点坐标转裁剪空间
    2. 顶点法线向量转世界空间
    3. 顶点坐标转世界空间
    4. 世界空间下视角方向
    5. 利用折射函数 refract() 计算折射向量
      • 参数一:入射光线方向
      • 参数二:法线方向
      • 参数三:折射率 A / 折射率 B
  4. 片元着色器
    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/RefractionBase"
{
Properties
{
//介质A折射率
_RefractiveIndexA("RefractiveIndexA", Range(1, 2)) = 1
//介质B折射率
_RefractiveIndexB("RefractiveIndexB", Range(1, 2)) = 1.3
//立方体纹理贴图
_Cube("Cubemap", Cube) = ""{}
//折射程度
_RefractAmount("RefractAmount", Range(0, 1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" "Queue"="Geometry" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

fixed _RefractiveIndexA;
fixed _RefractiveIndexB;
samplerCUBE _Cube;
fixed _RefractAmount;
struct v2f
{
//裁剪空间下的顶点位置
float4 pos : SV_POSITION;
//折射向量
float3 worldRefr:TEXCOORD0;
};

v2f vert (appdata_base v)
{
v2f o;
//顶点空间转裁剪空间
o.pos = UnityObjectToClipPos(v.vertex);
//法线转世界空间
fixed3 worldNorma = UnityObjectToWorldNormal(v.normal);
//顶点转世界空间
fixed3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//视角方向获取 得到是 摄像机-顶点
fixed3 viewDir = UnityWorldSpaceViewDir(worldPos);
//计算折射向量
o.worldRefr = refract(normalize(-viewDir),normalize(worldNorma), _RefractiveIndexA/_RefractiveIndexB);
return o;
}
fixed4 frag(v2f i):SV_TARGET
{
//立方体纹理采样
fixed4 color = texCUBE(_Cube, i.worldRefr);
//结合折射程度计算返回
return color * _RefractAmount;
}
ENDCG
}
}
}

四、结合漫反射实现折射

1.实现思路

  1. 将折射率 A 和折射率 B 合并为一个 折射率比值 计算好后 从外部传入节约性能
  2. 折射效果结合漫反射 和 阴影结合漫反射 的做法几乎一致
  3. 属性声明
    1. 漫反射的颜色
    2. 折射颜色
  4. v2f 结构体 因为要在片元着色器中处理光和阴影
    1. 世界空间的法线坐标
    2. 世界空间的顶点位置
    3. 阴影宏
  5. 顶点着色器
    1. 顶点坐标转换到裁剪空间下
    2. 顶点法线转世界坐标
    3. 顶点坐标转世界坐标
    4. 世界空间下 计算视角的方向(要求从摄相机到顶点的方向)
    5. 计算得到反射向量
    6. 阴影计算相关
  6. 片元着色器
    1. 得到光的方向
    2. lambert 漫反射的计算
    3. 立方体纹理采样
    4. 利用宏计算阴影衰减和光衰减系数
    5. 使用 lerp 函数 控制颜色叠加
  7. 添加阴影投射 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
95
96
Shader "Unlit/Refraction"
{
Properties
{
//折射的比值 折射率A/B 在外部计算好传入来优化性能
_RefractRatio("RefractRatio", Range(0.1, 1)) = 0.5
//介质A折射率
_RefractiveIndexA("RefractiveIndexA", Range(1, 2)) = 1
//介质B折射率
_RefractiveIndexB("RefractiveIndexB", Range(1, 2)) = 1.3
//立方体纹理贴图
_Cube("Cubemap", Cube) = ""{}
//折射程度
_RefractAmount("RefractAmount", Range(0, 1)) = 1

//漫反射颜色
_Color("Color",Color) = (1,1,1,1)
//折射颜色
_RefractColor("_RefractColor",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"

fixed _RefractRatio;
samplerCUBE _Cube;
fixed _RefractAmount;
fixed4 _Color;
fixed4 _RefractColor;
struct v2f
{
//裁剪空间下的顶点位置
float4 pos : SV_POSITION;
//折射向量
float3 worldRefr:TEXCOORD0;
//世界空间下的法线
float3 worldNormal:NORMAL;
//世界坐标下的顶点
float3 worldPos:TEXCOORD1;

//阴影相关
SHADOW_COORDS(2)
};

v2f vert (appdata_base v)
{
v2f o;
//顶点空间转裁剪空间
o.pos = UnityObjectToClipPos(v.vertex);
//法线转世界空间
o.worldNormal = UnityObjectToWorldNormal(v.normal);
//顶点转世界空间
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//视角方向获取 得到是 摄像机-顶点
fixed3 viewDir = UnityWorldSpaceViewDir(o.worldPos);
//计算折射向量
o.worldRefr = refract(normalize(-viewDir),normalize(o.worldNormal), _RefractRatio);
//阴影相关处理
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.worldRefr).rgb * _RefractColor.rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
//我们在利用lerp 在漫反射和折射颜色之间 进行插值
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse,cubemapColor,_RefractAmount) * atten;
//结合折射程度计算返回
return fixed4(color,1);
}
ENDCG
}

}
FallBack "Reflective/VertexLit"
}

评论