咳咳,有段时间没有更新了,最近有点懒!把不少精力都放在C++身上了。闲言少叙,今天要讲的可和之前的几篇有所不同了,这次是一个次综合应用.这篇内容中与之前不同主要体现在下面几点上.
1.之前我们写的都是只用一个Shader来实现某些效果,而这次我们要使用多个Shader结合起来发挥作用。
2.之前我们只是写的都是纯Shader代码,没有涉及到客户端的C#脚本(你爱用JS也可).而这次也要使用到。
3.这篇教程涉及到的代码量也是之前是之前的几倍了.
4.总的来说之前的都是比较简单的,而这篇就有了些难度了。
不过不要怕,我们先讲解实现的原理,因为这个教程内容比较多,所以只能抽出一篇来单独讲原理了,建议看这篇教程的同学,最好能有基本的UnityShader基础,不妨去看看我的前几篇教程,在后面讲解代码中我不会事无巨细的都讲到,直跳比较重点的地方进行说明。
好了进入正题,什么是遮挡描边呢?直接上图吧。
如上图, 在很多游戏中,特别是3D游戏中,当我们的角色被一些墙体或者其它物体挡着的时候,为了让玩家清楚的看到角色当前身处的位置,就需要把角色被遮挡部分的外轮廓描出来(上图中的绿色边框)。这就是我们要实现的"遮挡描边"。大家可以联想一下自己玩过的游戏看看有没有这种情况。
重要概念
在讲解具体原理之前,我们要先了解几个比较重要的概念:
1.后期处理:有点类似于影音方面后期处理的意思,在这里指的就是,当摄像机把当前帧渲染完毕之后,我们不是直接把图像映射到屏幕上,而是利用我们的代码来对原图像进行处理,把处理后的显示在屏幕上。游戏中非常多的效果都通过后期处理实现的,比如Bloom效果,运动模糊效果等等。我们这篇说的"遮挡描边"也算作是后期处理。
2.深度缓冲:对于一款3D游戏,要将三维世界里摄像机看到的部分展示到二维的屏幕上,除了要展示物体本身的颜色,还要处理物体的前后关系。而在我们的显存里有两个缓冲区(和具体显卡架构有关,这里只是举个例子)来分别用来保存游戏的画面和物体的前后关系。我们把它们分别称作颜色缓冲区和深度缓冲区。颜色缓冲区你可以理解为他是一个二维数组,里面的一个元素对应着屏幕的一个像素点的颜色(还会附带一些其他信息)。深度缓冲区也可以理解为一个二维数组,里面存放着屏幕像素上每一个点所对应场景中物体上的点距离相机的距离。在渲染流程中深度缓冲非常重要的,如果没有它,也就没有了物体间的前后关系,我们在屏幕上看到的可能是杂乱的图像了。
3.深度图:深度图适合深度缓冲相关联的一个概念,深度缓冲是由操作系统通过显卡API来控制的,而我们使用引擎的时候也只能通过引擎提供的有限的API来读写利用深度缓冲来实现一些效果。那当我们需要利用深度缓冲来做一些特殊效果的时候就要抓狂了,别急我们可以通过制定一个相机,让它把渲染的图像不是显示到屏幕上,而是显示到一张我们设定好的二维图片上,我们在通过我们写的Shader来让它把这个相机所拍摄到物体的深度信息保存到这个二维图片(它现在就是深度图了)上,而不是颜色信息。这样在接下来的处理用我们就可以利用这个深度图来做一下事情了。
原理
有了上面的一些概念我们就来了解一下实现的原理(当然了,实现遮挡描边的方法很多,而每一款游戏需要的效果也不尽相同,这里的方法仅供参考):
大体上可以分四个大步骤:(下面的两张图片,在游戏中玩家是看不到的,开发人员也是看不到的,全部都是在代码中处理的,这里显示出来只是为了让大家能更好的理解一下)
1.获得一共只包含我们要进行遮挡描边处理的物体(在我们的例子中,就是上图的那个士兵)深度信息的深度图。对于我们的例子,这一步的结果如下图,粗看起来是一片红,但是大家细看右边你会看到那个士兵的.至于为什么图像这么偏红,大家可以想想,我在下篇中会说明。
2.通过比较判断主摄像机(就是你在Game窗口看到的图像所用到的摄像机)的深度信息和我们第一步中获得到的深度图信息,来界定出我们要处理的物体的哪些部分被挡住了。并对被遮挡全部涂上描边颜色。如下图
3.把被遮挡部分分别进行一次左右,上下方向的拉伸一像素.这个就不用图了基本和上面的图一样,只是上下左右都多了一个像素,所以看起来不明显。
4.最后把除了我们在第三步中额外绘制的一像素外轮廓以外,对角色的被遮挡部分还使用原来的颜色(就是墙本身的颜色)。效果就是本文一开始放的那个图片了。
涉及到的一些API
这样我们就实现了遮挡描边,不过上面只是比较笼统的概括,有很多细节需要注意。具体的代码实现我会在下次的教程中进行具体讲解,这里我先把需要设计到的一些Unity知识列出来,很多内容都可以通过Unity的官方API文档查到,大家可以自己先了解一下然后试着实现一下遮挡描边。这样看下一次教程的时候会效果更好:
1.在Unity进行后处理需要涉及到的一些API,有 OnRenderImage(...),RenderTexture.GetTemporary,RenderTexture.ReleaseTemporary,Graphics.Blit,RenderWithShader.
2.在C#脚本中对Shader进行传值设计到的一些API,如Shader.SetGlobalXXX(),Shader.Find,Material.SetXXX().
3.相机的深度模式,和Unity提供给我们的一个默认深度图.Camera.depthTextureMode,_CameraDepthTexture等.
好了关于遮挡描边的原理篇基本就讲完了,怎么样是不是和之前几篇的内容有很多不同,这篇内容也只是做了一个抛砖引玉,只是希望大家能了解一下渲染中的几个重要概念,然后自己去做一些扩展,比如说深度缓冲和深度图,他们两个能够实现的效果远远不只是"遮挡描边"。更多神奇的功能还等待大家自己一起去探索。另外我会在近期完成"实现篇"的,这段时间大家不妨自己动手试着写写看,没准在我发下一篇之前,聪明的你啊已经搞定了呢!!!
尊重他人智慧成果,欢迎转载,请注明作者esfog,原文地址