本系列文章由@浅墨_毛星云 出品,转载请注明出处。
文章链接: http://hpw123.net/a/C__/kongzhitaichengxu/2014/1222/163.html
作者:毛星云(浅墨) 微博:http://weibo.com/u/1723155442
QQ交流群:330595914
更多文章尽在:http://www.hpw123.net
本文介绍了Unity中Shader书写中会用到的剔除、深度测试、Alpha测试以及基本雾效相关的语法知识,然后以6个Shader的书写作为实战内容,最后创建了一个生机勃勃的热带雨林场景进行了Shader的测试。依旧是国际惯例,先上本文配套程序的截图。
绿色的海洋:
满眼的生机:
竹林:
参天大树:
飘到脸上的树叶:
OK,图先就上这么多。文章末尾有更多的运行截图,并提供了原工程的下载。可运行的exe下载在这里:
好的,我们正式开始。
对于实时交互的3D环境而言,现实的速度和效率是非常重要的。虽然现在的硬件能力非常的快,但是要想保持30FPS的同时处理数十万的三角形,以现在的主流机器来说还是有些困难的。
为了解决这种问题,人们提出了很多方法,其中有LOD,有Culling。这两种方法并不矛盾,而且我们往往需要在culling的基础上再使用LOD进一步解决pipeline的负担。
剔除是一种通过避免渲染背对观察者的几何体面来提高性能的优化措施。所有几何体都包含正面和反面。剔除基于大多数对象都是封闭的事实;如果你有一个立方体,你不会看到背离你的那一面(总是只有一面在你的前方),因此我们不需要绘制出背面。因此也被称做背面剔除。
一言以蔽之,所谓剔除,就是被挡住或视角以外的我们看不到的物体,因为它们无关紧要,所以我们就不去绘制,以节省资源,提高场景的运行效率。
在复杂的场景中,通常有多个物体需要绘制,这些物体之间通常会存在遮挡关系,离观察点较远的物体会因为近处物体的者的遮挡而不可见或只有部分可见,Direct3D图形系统提供了深度测试功能来实现这种效果。深度测试可以简化复杂场景的绘制,确保只有场景内的对象的最靠近的表面参与绘制。
浅墨之前在写DirectX相关博文的时候写过对深度测试的形象化理解,在这边也列出来吧:
把深度测试看做在一口井的井口处向井中观望。把所有物体都赋予一个深度值,放到井中来显示。深度越深的物体,离井口就越远。深度越浅的物体,离井口就越近。井表面的深度值为0。离井口近而深度浅的物体,可能会把离井口远的物体遮挡住。最终显示在屏幕上的开启深度测试后的画面,就如在井口处向井中观望里面物体显示出的遮挡与层次的效果一样。当然,离井口的深度就是每个物体在世界坐标系中的矩阵的Z坐标值了。
此语句用于控制几何体的哪一面会被剔除(也就是不被绘制) 。其中:
此语句用于控制是否将来之对象的像素写入深度缓冲(默认开启),如果需要绘制纯色物体,便将此项打开,也就是写上ZWrite On。如果要绘制半透明效果,关闭深度缓冲,则用ZWrite Off。
此语句用于控制深度测试如何执行。
缺省值是LEqual (绘制和存在的对象一致或是在其中的对象;隐藏其背后的对象),含义列举对应如下:
Greater |
只渲染大于AlphaValue值的像素 |
GEqual |
只渲染大于等于AlphaValue值的像素 |
Less |
只渲染小于AlphaValue值的像素 |
LEqual |
只渲染小于等于AlphaValue值的像素 |
Equal |
只渲染等于AlphaValue值的像素 |
NotEqual |
只渲染不等于AlphaValue值的像素 |
Always |
渲染所有像素,等于关闭透明度测试。等于用AlphaTest Off |
Never |
不渲染任何像素 |
此语句用两个参数(Facto和Units)来定义深度偏移。
于是,我们就可以强制使位于同一位置上的两个集合体中的一个几何体绘制在另一个的上层。例如偏移量Offset 设为0, -1(即Factor=0, Units=-1)的值使得靠近摄像机的几何体忽略几何体的斜率,而偏移量为-1,-1(即Factor =-1, Units=-1)时,则会让几何体偏移一个微小的角度,让观察使看起来更近些。
Unity中,Alpha测试(Alpha Testing)是阻止像素被写到屏幕的最后机会。在Pineline中Alpha测试的位置如下:
在最终渲染出的颜色被计算出来之后,可选择通过将颜色的透明度值和一个固定值比较。如果比较的结果失败,像素将不会被写到显示输出中。
Alpha测试在渲染凹形物体的透明部分时非常有用。显卡上有着每个像素写到屏幕上的深度记录。如果一个新的像素比原来的像素的深度深,那么新的像素就不会被写到屏幕中。
让我们看一幅图:
仔细看上图,会发现:
此语句用于渲染所有像素(缺省)
此语句用于设定透明度测试只渲染在某一确定范围内的透明度值的像素。其中的comparison取值为下表之一:
Greater |
Only render pixels whose alpha is greater than AlphaValue. 大于 |
GEqual |
Only render pixels whose alpha is greater than or equal to AlphaValue. 大于等于 |
Less |
Only render pixels whose alpha value is less than AlphaValue. 小于 |
LEqual |
Only render pixels whose alpha value is less than or equal to from AlphaValue. 小于等于 |
Equal |
Only render pixels whose alpha value equals AlphaValue. 等于 |
NotEqual |
Only render pixels whose alpha value differs from AlphaValue. 不等于 |
Always |
Render all pixels. This is functionally equivalent to AlphaTest Off. |
Never |
Don‘t render any pixels. 不渲染任何像素 |
而AlphaValue为一个范围在0到1之间的浮点值。也可以是一个指向浮点属性或是一个范围属性,在后一种情况下需要使用标准的方括号写法标注索引名字,如([变量名]).
雾效(Fog)参数用于控制雾相关的渲染。
在计算机图形学中,雾化是通过混合已生成的像素的颜色和基于到镜头的距离来确定的最终的颜色来完成的。雾化不会改变已经混合的像素的透明度值,只是改变RGB值。
Unity中的缺省雾设定是基于Edit->RenderSettings中的设置的,且雾模式既可以是Exp2也可以是关闭;密度和颜色完全取自设定。
注意如果使用了片段着色器的话,着色器中的雾设定仍然是有效的。如果平台没有对固定管线的雾功能支持,Unity会实时补上着色器以支持需要的雾模式。
此语句用于设定雾命令的内容,具体的填在大括号之中的内容见下面的语句。
此语句用于定义雾模式。缺省是全局的,依据雾在渲染设定中是否打开确定可从无变化到平方值
此语句用于设定雾的颜色值。
此语句以指数的方式设定雾的密度。
此语句用于为linear类型的雾设定远近距离值。
OK,本文的基本知识就将这么多,接下来看看QianMo‘s Toolkit的更新,然后开始基于上面讲解的基础知识的Shader实战。
如前面三篇文章中所言,QianMo‘s Toolkit是浅墨在Unity中写Shader时会用到的一些脚本小工具,用C#来实现。
此次在QianMo‘s Toolkit Ver1.2版中加入了新的特性——检测当前系统的cpu与显卡的型号和参数,并显示到游戏窗口中。其实现代码如下:
添加此脚本到场景中的任意物体后,填一下其中的x和y坐标的值,用于设置输出在屏幕中的哪个位置。
接着点运行,于是在游戏窗口的指定区域中出现了CPU和显卡的信息:
于是,目前的QianMo‘s Toolkit v1.2拥有的功能如下所示:
ShowFPS:在游戏运行时显示帧率相关信息
ShowObjectInfo:在测试过程里,于场景中和游戏窗口中分别显示添加给任意物体文字标签信息。隐藏和显示可选,基于公告板技术实现。
ShowGameInfo:在游戏运行时显示GUI相关说明
ShowLogo:在游戏运行时显示Logo
ShowUI:在游戏运行时显示简单的镶边UI。
SetMaxFPS :用于突破Unity每秒渲染 60帧的设定,自由设置最大帧率。
ShowObjectInfoInGamePlay:用于发布游戏之后显示文本信息。
ShowSystemInfo:在游戏运行时显示系统CPU、显卡信息
根据上文讲解的剔除、深度测试和Alpha测试操作的句法,我们将完成如下六个Shader的写法:
1.用剔除操作渲染对象背面
2.渲染对象背面v2
3.用剔除实现玻璃效果
4.基本Alpha测试
5.顶点光照+可调透明度
6.简单的植被Shader
让我们通过详细注释的代码一个一个将他们讲清楚。
先写一个非常简单的只会渲染对象的背面的Shader:
我们将此Shader编译后赋给材质,得到如下效果,可以发现比原本默认的材质颜色暗,因为我们绘制的是物体的背面:
我们将材质赋给一个立方体,可以看到,通过此Shader我们可以渲染对象的背面,从而看到一个很奇怪立方体(因为我们看到的是它的内部)。效果如下:
让我们给上面写的这个Shader加上之前我们已经掌握的顶点光照的一些内容,并通过定义出第二个通道,采用亮蓝色来渲染物体的背面。代码如下:
同样是将Shader先赋给一个材质,得到的效果如下:
我们将材质赋给一个立方体,直接看是一个非常正常的立方体:
但一旦镜头靠近,看到物体的内部时,就发现了我们定义的蓝色部分了:
很多时候,控制剔除比背面调试(debugging backfaces)更有用。比如遇到需要渲染透明的物体,经常会想要显示一个对象的背面。如果不做任何剔除操作的话,我们会发现有时常有一部分背面会覆盖在前面的一部分之上。下面就是一个解决这个问题的用于凸物体(球,立方体,车窗)的简单玻璃效果的着色器:
同样是将Shader先赋给一个材质,得到的效果如下,可以发现是透明的黄色玻璃的效果:
再将材质赋给一个物体,就让此物体实现了简单的玻璃效果:
先看一个最简单的能用的例子,使用一张带有透明度通道的纹理。对象只会在透明度大于0.6 时显示:
此Shader编译后赋给材质,材质又赋给物体的效果如下,且因为选取的纹理的透明度除了局部线条以外,其他的都小于了60%,于是就是只剩下这大概的轮廓了:
让我们在上面这个Shader的基础上增加一些光照和并让Alpha的阈值可以调节,于是便写出了如下的Shader:
赋给材质后的效果如下,可以自由调节Alpha透明度的阈值:
通过调节Alpha透明度的阈值,得到了显示效果差异很大的材质:
将此材质赋给物体后的效果如下:
最后一个Shader,让我们实现一个简单的植物渲染Shader的写法。
当渲染树和植物时,透明度测试使许多游戏中出现尖锐的边缘。解决这个问题的方法之一是渲染对象两次。首次通道中,我们只渲染超过50%透明度的像素。在第二次通道中,我们使用透明度混合上次我们切除的部分,而不记录像素的深度。我们可能会使一些树枝覆盖近的其他树枝,以实现逼真的效果。于是,简单的植被Shader写法如下:
我们将一棵树的叶子和树皮的材质替换成刚刚写好的Shader:
然后看看效果:
嗯,还是挺不错的,整体显示效果比用Unity自带的Bumped Diffuse要出色一些(Unity自带的Bumped Diffuse还可以在树叶上看到透过的光影,这部分内容我们以后以后学了再加)。
OK,我们就写如上的这六个Shader作为本次实战的内容吧。最后是构建本文配套的游戏场景,以及一些最终程序运行截图的欣赏。
上一次我们处于神秘的光之城堡,这次的场景,不妨让我们来到充满生机的热带雨林,领略一番不一样的味道。以大师级美工鬼斧神工的场景作品为基础,浅墨调整了场景布局,加入了音乐,并加入了更多高级特效,于是便得到了如此这次生机勃勃的场景。
运行游戏,我们来到充满各种绿色植物的热带雨林之中:
满满的绿色植物:
飘落到脸上的树叶:
竹林:
被椰子树包围:
鹤立鸡群的铁树:
冲破云霄的树:
凸起树根的老树:
最后放一张这次Shader的全家福:
OK,美图就放这么多。游戏场景可运行的exe可以在文章开头中提供的链接下载。
本篇文章的示例程序请点击此处下载:
【浅墨Unity3D Shader编程】之四 热带雨林篇配套Unity工程下载
浅墨已经完成《OpenCV3编程入门》一书的技术修订工作,以及OpenCV2、OpenCV3双版本总计两百多个示例程序的注释和整理工作,书籍预计会在12月底印刷,1月上市。
最近应该可以没什么琐事了,没有特殊情况的话Unity Shader系列博文可以保持每周周一更新到明年2月份过年前。美剧都冬歇了咱们才刚上线:)
所以本周开始,Unity Shader系列博文宣告正式回归~下周一,我们,不见不散。
【浅墨Unity3D Shader编程】之四 热带雨林篇: 剔除、深度测试、Alpha测试以及基本雾效合辑
原文地址:http://blog.csdn.net/u010283694/article/details/42083261