§8.3更多Post-Screen Shaders
改变屏幕边界,添加天空,将场景纹理变成黑白很漂亮,但还有更多更好的Post-Screen Shaders。Rocket Commande通过模拟alpha通道的HDR光线使用了相当复杂的发光效果,通过几个pass,混合上一帧的场景纹理和发光效果,实现了运动模糊。
这个shadre被称为PostScreenGlow,它可以在Rocket Commander(包括了DirectX和xna版本)。图8-13显示了基本功能,shader 比这更复杂,因为它支持多种shader模型(1.1和2.0),并对慢的计算机做了一些优化。在Rocket Commander游戏中的Shader Model可以选择,在速度慢的电脑上也可关闭Post-Screen Shaders,但没有这些效果游戏乐趣会降低。
这看起来有点复杂,要做大量工作,但也有许多很好的shader书籍可供参考,你可以借鉴别人的东西。
Shader的有无效果是相当不同的(见图8-14)。
运动模糊
你已经在PostScreenGlow例子中看到使用Post-Screen Shaders实现运动模糊的一个简单方法,但对你来说可能并不简单,但运动模糊效果是glow中最简单的一部分,它可以扩展成所谓的“Per Pixel Motion Blur”的效果。要实现Per Pixel Motion Blur你必须在一个特殊shader的帮助下,保存场景中每个像素的速度;无需渲染场景中的每个物体,只渲染运动的物体。
接着在shader中你根据玩家的运动方向、每个像素的速度和方向,对每个像素决定采用多大的模糊程度。一些较新的赛车游戏采用了这种技术,但渲染这种特殊纹理很占资源和shader指令。
因为要用重用上一帧的屏幕,所以本章以前使用的运动模糊在一个较大的模糊值下工作。这意味着你只需要增加10%的新的运动模糊而再利用90%上个画面的运动模糊。Per Pixel Motion Blur也能这样做,但如果每个物体都在运动时就很难跟踪,如果这样不如计算整个屏幕的运动模糊。
一个相对简单的诀窍是把你要渲染的屏幕分割成10×10的网格,把它们当成一个格子渲染,但允许允许顶点shader根据运动模糊的强度使用不同的权重,见图8-15比较只有一个格子和5×5格子的区别,采用10×10网格看上去甚至更好一点。
颜色校正
我经常在Post-Screen Shaders中使用颜色校正,因为用起来比较简单而且对最后的画面影响很大。就像室外的光照,颜色修正影响所有的像素,所以它能使纹理更加真实。例如:如果你在一个洞穴里会很暗,只有一点光照进来,但如果你放置一个明亮的石头材质可能看起来会比周围显得更亮,使用颜色修正shader无法修正这个问题(because you should really put better objects in your cave that fit together in a better way),但改变一点颜色能使场景看起来更好一点。例如:如果整个场景很暗,那么增加一点蓝色会使各种材质融合得更好(见图8-16)。此图来自于我一年前写的一个游戏,它本来没有颜色修正,但有一天我想:为什么不能像电影画面处理的那样去实现这个效果呢?几乎每部电影都使用了颜色校正和对比度匹配,效果很好。现在我的每个新游戏都有色彩校正shader。
亮度效果的基本代码只是简单地把颜色值乘以一个常数(如果你想动态地改变亮度也可使用)。例如:乘以0.5使得整个图像的变暗一半,乘以2.0使得亮度加倍,乘以0.0使完全黑色等等。只使用一条shader指令,你还能把它整合到颜色修正中(本章接下去要用到),它仍然只需一个shader指令。图8-17显示了效果。
return brightness * originalColorValue;
改变对比度有点麻烦。使用1.0的值使图像保持不变,0.0使图像完全变灰(即没对比度)和更高的值使图像锐利。要实现这个效果,首先必须从每个颜色通道减去0.5,然后再把结果乘以对比度值,最后再在每种颜色通道加0.5。
当你在颜色通道中加或减超过1.0时会使图像丢失很多颜色而0.0使图像完全变灰(因为只有您最终使用的0.5有效,其余乘0不起效果)。高的对比度让暗的更暗亮的更亮(见图8-18)。
return (originalColorValue-float3(0.5, 0.5, 0.5)) * contrast + float3(0.5, 0.5, 0.5);
为了实现图8-16的效果只需使用接近1的亮度值,我使用了0.92和1.2的对比度值。另外,您还可以改变每个像素的颜色。
当你在颜色通道中加减超过0.1时,你会看到绿色比红蓝色更加明显,看起来会太鲜艳;建议使用非常小的值如0.01和0.05。下面的代码使输出图像更蓝,去掉了点红颜色,这可以用在极地的环境,在沙漠中你可能需要橙色调,而火山环境可以使用更多的红颜色,等等。
inputColor = float3(inputColor.r-0.04f,inputColor.g,inputColor.b+0.05f);
有一个非常简单的方法可以改变颜色:如果你想进行更复杂的操作或在shader中优化色彩校正,您可以使用矩阵预先计算所有组合矩阵的结果,这样只需要修改像素一次就可以了。以下是一些矩阵例子:
brightnessMatrix = float4x4( brightness, 0, 0, 0,
0, brightness, 0, 0,
0, 0, brightness, 0,
0, 0, 0, 1);
contrastMatrix = float4x4( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-0.5, -0.5, -0.5, 1);
contrastMatrix *= float4x4( contrast, 0, 0, 0,
0, contrast, 0, 0,
0, 0, contrast, 0,
0, 0, 0, 1);
contrastMatrix *= float4x4( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
+0.5, +0.5, +0.5, 1);
addColorMatrix = float4x4( 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-0.04, 0.0, +0.05, 1);
你可以通过矩阵做很多事情,比如旋转或缩放等等。旋转对完全改变颜色、切换颜色通道很有用;而缩放矩阵中的一行可以实现你以前看到过的发光效果。你可以试着自己发挥一下。
菜单效果
本书的最后一个游戏你将会用到菜单的Post-Screen效果,它与PostScreenGlow shader很相似。但它的发光效果不强,但仍清晰可见。更重要的是增加了像老电影一样的效果,看起来挺有趣的。它可能不适合每个游戏,但对赛车游戏来说很酷,并和菜单音乐合拍。基本的思路是采用噪声纹理并非常缓慢的遍历它,数值增加地如此之大导致只有最亮的点才有用。效果从顶部扩散到底部,并在顶部增加了点其他效果(见图8-19 )。
Post-Screen shader能实现很多其他效果,有时你并不想在post-screen space中做任何事。储存渲染过程中一些像素的数据,然后用在后面的Post-Screen shader中能让事情变得简单些。这种方法不仅比对每个像素进行运算快得多,而且也能更容易地将临近像素混合在一起、移动像素,改变颜色等。例如,要实现阴影映射shader,你首先要渲染一个根据灯光的角度而生成的阴影贴图,然后再次渲染场景并与阴影贴图进行比较。它工作得很好,但是如果你有很多不同的shader,你需要修改所有这些shader,而shader(尤其是Pixel Shader 1.1)存在指令限制,你没法增加更多的指令。然后,你需再增加另一个pass再次渲染几何数据。
另一种办法再次渲染场景阴影,但是这一次只是渲染一个场景的阴影贴图,这个贴图在后面的Post-Screen Shaders使用,将没有阴影效果的场景贴图与阴影贴图混合在一起。归功于屏幕空间,你也可以容易地实现模糊阴影或改变影子的强度。
关于今天硬件的shader、Post-Screen shader,阴影映射,渲染技术的更多知识,我建议你阅读关于shader的图书(如果你全看过的话,你就是一个shader高手了),如Shader X,GPU Gems, Game Programming Gems等(如果你全看过的话,你就是一个shader高手了),你可以很容易地在诸如亚马逊之类的网站上找到。我还建议下载NVIDIA SDK,学习其中的shader例子。
发布时间:2008/9/22 下午1:02:37 阅读次数:6050