|
我们在游戏中经常用需要用到屏幕的后处理效果(类似:泛光效果,热浪扭曲,景深模糊等等),需要实现这些效果都避免不了的要拿到当前相机渲染的画面,然后当做纹理贴图去传递给shader做一系列的计算。首先要了解下RenderTexture,这里有篇文章讲述的比较清楚了。
在Unity中我们主要通过以下方法能够拿到当前相机渲染的画面:
1.GrabPass,这个是Shader中的一个特殊Pass
2.OnRenderImage,常见的后处理效果都是在这里面去执行的
3.TargetTexture,直接设置相机的TargetTexture
接下来我们在真机上测试下以上实现方法的性能,测试机选用的是ViVO X5ProV。测试的场景也很简单,就是渲染一个九宫格的贴图,没有任何光照计算,把相机渲染的九宫格画面当做贴图传递给一个面片来显示。RenderTexture的分辨率为1920x1080,16位Depth Buffer。
1.GrabPass
在Shader中加上GrabPass,每帧都会抓取一张当前屏幕的画面存储在“_GrabTexture”里面,也可以通过GrabPass{“_GrabTempTex”}来自定义名称。没有优化过的特效实现扭曲效果的Shader用的就是这个方法。
我们可以看到,只是通过GrabPass做一个单纯的截屏,在低端的移动设备上就已经开销很大了,所以在很多游戏做机型适配的时候,低端机都是直接阉割掉扭曲特效的。猜想可能造成的原因是把Back-Buffer拷贝到_GrabTexture这个过程开销很大。
2.OnRenderImage
OnRenderImage是Camera的一个回调,相机每次渲染后都会调用,官方给的后处理方法都是用这个来实现的。这里我们新建一个C#脚本,然后挂在相机上,只做一个单纯的Graphic.Blit操作。
private void OnRenderImage(RenderTexture source , RenderTexture destination)
{
Graphics.Blit(source , destination);
}
source就是当前相机渲染的画面,destination是最后输出到屏幕的画面。这里省略了后面的material参数,只是单纯的把source拷贝到destination作为输出。
可以看到这种操作会比GrabPass的性能好很多,但是只能固定在相机渲染完成之后调用,使用起来很不灵活。
3.TargetTexture
申请一个新的RenderTexture,然后把Camera.targetTexture设置为新的RenderTexture,再把这个RenderTexture作为贴图传递给shader进行计算。
private void OnEnable()
{
cam = GetComponent<Camera>();
RenderTexture.ReleaseTemporary(screenCopyRT);
screenCopyRT = RenderTexture.GetTemporary(cam.pixelWidth , cam.pixelHeight , 16);
Shader.SetGlobalTexture(&#34;_GrabTempTex&#34; , screenCopyRT);
cam.targetTexture = screenCopyRT;
}
这里的Shader还是用之前GrabPass时的,所以传递的贴图名称&#34;_GrabTempTex&#34;是一样的,但是要在Shader中把GrabPass这段注释掉,不要调用这个方法,其他的都不变。
如果考虑到实际项目中的应用,应该只有这种方案在低端机上能够勉强接受了,但是这种方案需要多一个相机,因为当前相机设置了TargetTexture后,在屏幕窗口就是全黑色的画面了。可能是设置了TargetTexture后,渲染的内容就存在了这张RenderTexture上面,而Back-Buffer中就没有内容显示了,要加多一个相机把处理后的内容在输出到Back-Buffer。
在Unity的论坛里面,也有看到一种不需要两个相机的实现方案,在OnPreRender的时候设置Camera的targetTexture,然后在OnPostRender中又把targetTexture设置为null,还要加上Graphics.Blit(myRenderTexture,null as RenderTexture),也就是说如果Blit的目标为null就会直接输出到Back-Buffer。
RenderTexture myRenderTexture;
void OnPreRender()
{
myRenderTexture = RenderTexture.GetTemporary(width,height,24);
camera.targetTexture = myRenderTexture;
}
void OnPostRender()
{
camera.targetTexture = null;
Graphics.Blit(myRenderTexture,null as RenderTexture, postMat, postPassNo);
// Whatever other blits you may need
RenderTexture.ReleaseTemporary(myRenderTexture);
}
这种方案我在PC端测试过是可以实现的,但是在移动端不同的机型上会有不同的显示Bug,所以不建议在移动端这样使用。
最后说一下CommandBuffer,这个用起来非常的灵活,可以在指定的时间去执行一系列的渲染指令。CommandBuffer也可以将当前相机渲染的内容拷贝到一张RenderTexture上,但是性能开销也很大,跟GrabPass差不多了,所以也不建议通过CommandBuffer.Blit(BuiltinRenderTextureType.CurrentActive , RenderTexture)这样来保存屏幕画面的内容。
后面的工作就是要通过TargetTexture和CommandBuffer,来整合一套屏幕后处理效果的实现方案了。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|