本文是视频课程《Unity技术美术TA:Shader篇》,算是对自己学习的总结,也希望分享下所学知识~~
(原课程链接传送门)
扭曲的作用
1.热扭曲
2.水体等特殊表现
(手机端比较耗费性能)
热扭曲如何实现?
1.扭曲材质赋予面片
2.抓取当前一帧的图片内容
3.获取屏幕坐标
4.利用屏幕坐标对抓取的图片进行采样
5.采样扰动贴图做扭曲
屏幕坐标不是屏幕分辨率坐标,是限制在 0-1 的
归一化坐标 = 当前像素 / 总像素
如何得到屏幕坐标?
1.使用 UNITY_VPOS_TYPE screenPos: VPOS
// 在使用VPOS时,就不能在v2f中定义SV_POSITION,这样会冲突,
// 需要把顶点着色器的输入放在()的参数中,并且SV_POSITION添加out.
v2f vert(
float4 vertex : POSITION,
out float4 pos: SV_POSITION
)
{
v2f o;
pos = UnityObjectToClipPos(vertex);
return o;
}
//当前片断在屏幕上的位置(单位是像素,可除以_ScreenParams.xy来做归一化)
// 大部分平台下VPOS返回的是一个四维向量,部分平台是二维向量,所以需要用UNITY_VPOS_TYPE来统一区分.
fixed4 frag(v2f i, UNITY_VPOS_TYPE screenPos: VPOS) : SV_Target
{
fixed2 screenUV = screenPos.xy / _ScreenParams.xy;
return screenUV.x or y;
}
2.自己计算齐次坐标
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _DistortTex) + _Distort.xy * _Time.y;
// 注意范围转换: [-1,1] -> [0,1]
// 注意 openGL 和DX 坐标不一样的问题
o.screenUV.xyz = (o.pos.xyz / o.pos.w) * 0.5 + 0.5;
/也可以用内置函数: pos为裁剪空间下的坐标位置,返回的是某个投影点下的屏幕坐标位置(未除以齐次坐标)
// o.screenUV = ComputeGrabScreenPos(o.pos);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed2 screenUV= i.screenUV;
// DX 左上角是0
screenUV.y = 1 - uv.y;
// 内置函数的用法
// fixed2 screenUV = i.screenUV.xy / i.screenUV.w;
// 或者采样使用用 proj tex2Dproj(_GrabTex, i.screenUV);
return screenUV.x or y;
}
3.直接用引擎转换的 SV_POSITION
v2f vert(appdata v)
{
v2f o;
//从本地空间变换到裁剪空间(没做透视除法)
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _DistortTex) + _Distort.xy * _Time.y;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// 顶点着色器在裁剪空间下返回的 SV_POSITION 会自动变成 屏幕像素坐标,自己定义的不会
fixed2 screenUV = i.pos.xy / _ScreenParams.xy;
return screenUV.x or y;
}
扰动贴图实现
核心代码如下:
使用原屏幕uv 和 扰动贴图采样的UV 做 lerp
fixed4 distortTex = tex2D(_DistortTex, i.uv);
fixed2 uv = lerp(screenUV, distortTex, _Distort);
效果如图: