简介
千等万等终于等到了《耻辱2》打折,本以为可以爽一发了,然而各种出问题,先是steam下载速度奇慢无比,下了三天晚上好不容易下完的游戏,第一次打开给弹了个3D11CreateDeviceAndSwapChain Failed,折腾半天装了个补丁算是能打开游戏了,然而过完新手教学显卡驱动就崩了,崩了!崩了,连崩三回,差点想把坑爹的A卡从机箱掏出来顺着窗户扔出去,后来想想,为了楼下同学的生命安全,我还是忍了。好在AMD有专门为《耻辱2》R9380崩溃打了个补丁,算是拯救我于水火之中了。《耻辱2》用了ID Tech5衍生的Void引擎,看起来画面比《耻辱1》用的虚幻3好了不少。先来张帅帅哒截图,最近每天沉迷于杀杀杀,感觉自己好颓废:
一时间差点忘了自己是个程序员,差点变成游戏鉴赏博客,尴尬...下面步入正题,今天打游戏的时候路过了一个火炉,看到了火炉旁边的热空气扭曲的效果,感觉做的还是蛮逼真的,今天打算自己实现一发玩一玩:
实现原理
扭曲效果是游戏里面经常有的一个效果,说道扭曲效果,一般就是当前的画面发生了扭曲,在现实世界中一般是折射导致的,但是在图形学中,我们要模拟这种效果,原理就大不一样了。首先,我们并不会真正影响光线的传播,只是用uv的偏移来模拟扭曲的效果。有一种全屏的扭曲效果,这种是基于屏幕后处理的,可以参考前面的一篇文章
屏幕水波纹效果,但是,往往我们并不希望全屏幕都发生扭曲,而是只希望某些地方发生了扭曲,比如上面的火炉的做法,拼关的同学肯定是希望在火炉的上方放一个特效片,就能够出扭曲的效果。那么,我们的这个片就需要是一个可以显示后面所有物体的片,换句话说,我们需要在这个面片上渲染面片后面所有的东西,这样,面片看起来就是透明的了。然后我们在采样uv的时候将uv进行偏移,就能够得到扭曲的效果了。恩,听起来很简单的样子,但是我们要怎么得到面片后面的所有东西呢?其实Unity已经为我们提供了这样的一个功能,GrabPass。下面看一下Grabpass的使用。
GrabPass
GrabPass是Unity为我们提供的一个很方便的功能,可以直接将当前屏幕内容渲染到一张贴图上,我们可以直接在shader中使用这张贴图而不用自己去实现渲染到贴图这样的一个过程,大大的方便了我们的shader编写。GrabPass的使用非常简单,我们在写vertex fragment shader的时候都需要写一个pass,GrabPass也是一个pass,只不过是Unity为我们实现好的一个pass。我们只需要在我们正常的Pass前面加一个GrabPass{}就可以了。
官方文档上有两种GrabPass的写法,第一种是直接GrabPass{}的写法,这种写法抓屏的图片就直接存到_GrabTexture这个系统预定义的贴图变量中了,我们可以直接访问该贴图,但是这种写法会导致每个使用GrabPass的物体进行一次这种旷日持久的抓屏操作!如果用这种shader的物体多了的话,想想就很可怕。另一种是GrabPass{"TextureName"}的写法,其中TextureName是我们自定义的一个贴图名称,这种写法,Unity每帧只会为第一个使用了该名称的物体进行抓屏操作,之后的就可以复用这张贴图了。所以,我们还是使用第二种方式更好一点。下面附上一份最简单的抓屏代码:
//Grabpass shader
//by: puppet_master
//2017.4.23
Shader "ApcShader/GrabPass"
{
SubShader
{
ZWrite Off
//GrabPass
GrabPass
{
//此处给出一个抓屏贴图的名称,抓屏的贴图就可以通过这张贴图来获取,而且每一帧不管有多个物体使用了该shader,只会有一个进行抓屏操作
//如果此处为空,则默认抓屏到_GrabTexture中,但是据说每个用了这个shader的都会进行一次抓屏!
"_GrabTempTex"
}
Pass
{
Tags
{
"RenderType" = "Transparent"
"Queue" = "Transparent+1"
}
CGPROGRAM
sampler2D _GrabTempTex;
float4 _GrabTempTex_ST;
#include "UnityCG.cginc"
struct v2f
{
float4 pos : SV_POSITION;
float4 grabPos : TEXCOORD0;
};
v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
//计算抓屏的位置,其中主要是将坐标从(-1,1)转化到(0,1)空间并处理DX和GL纹理反向的问题
o.grabPos = ComputeGrabScreenPos(o.pos);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
//根据抓屏位置采样Grab贴图,tex2Dproj等同于tex2D(grabPos.xy / grabPos.w)
fixed4 color = tex2Dproj(_GrabTempTex, i.grabPos);
return 1 - color;
}
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
我们找个面片,附上这个shader的材质。为了更方便的看一下效果,我们就参照官网的写法,直接将最终输出的颜色反向,也就是1-原颜色作为输出(这个颜色不禁让我想起了宇智波鼬的月读........)