Shader系列4:材质和光源

概览

所有shader编程的关键是它的可伸缩性。在GPU上执行的快速的、并行的浮点计算可用于任意更加复杂和有趣的绘制。但是,要真正从这种力量中获益,需要一些组织层次将这些元素组合到一个3D场景中。

目前为止,shader系列教程只关注一个模型,一个光源和一个材质。但是,真实的游戏中需要多个technique,并且还需要一些更加有效率的操作避免丧失灵活性和可用性。本示例展示了开发者如何将technique组合在一起成为一个可用的系统。在这个意义上,现代的shader需要努力将任何3D引擎构架和引擎设计在shader中实现。

程序截图

最低Shader配置

Vertex Shader Model 2.0

Pixel Shader Model 2.0

示例的控制

这个例子使用下列键盘和手柄控制。

动作 键盘控制 手柄控制
旋转相机 W, A, S和D 右模拟摇杆
旋转模型 UP,DOWN,LEFT和RIGHT箭头 左模拟摇杆
旋转光源 PAGE UP, PAGE DOWN 左或右triggers
在场景中添加光源 小键盘+ 右shoulder键
在场景中移除光源 小键盘- 左shoulder键
循环材质 TAB X
生成随机光源属性 空格键 Y
退出游戏 ESC或ALT+F4 BACK

材质、光源和几何体

这个示例实现了以下功能:

注意:这里的解决方案定义地很好。在任何材质系统中,目标是明确的,避免不必要的状态和代码。状态设置和多余的shader操作会拖慢绘制过程,需要多做权衡让帧率稳定在30Hz或60Hz。

本例中,使用类表示点光源和材质。PointLight类(PointLight.cs)是一个C#类表示场景中的光源。光源数据被HLSL函数使用照亮场景。Material类表示一个可以施加在几何体上的材质。材质参数定义在C#代码中(Example 1.3, MaterialsAndLights.cs)。

使用颜色、纹理和Phong高光的不同组合,场景中的材质可以看起来暗淡、闪亮、粗糙或光滑。通过采用一张高光纹理(specular texture) (也可以叫做高光贴图specular map), 你可以减少几何体上某些部位的粗糙感。

光源中添加了一个新的属性让它看起来更真实。首先,不使用单向光,而是使用一个有真实位置的点光源。点光源还包含一个范围(range)和衰减(falloff)值。点光源是一个全向光并有一个有限的范围。结果是点光源发射出围绕在它周围的球面光。点光源还有一个衰减falloff值,这个因子在一条指数曲线上,让光线变得“更锐利”或“更柔和”。

HLSL概念

在Windows PC平台上,shader硬件有好几代。每次更新换代都会让shader编程有更少的限制和更多的选择。但是,为了适用于不同的PC硬件,有时需要拥有多个绘制版本。这个示例包含shader model 2.0和3.0两个版本。Example 1.1展示了如何判断shader版本和如何选择正确的effect。

要让代码分离易于使用,本示例使用一个include 文件共享了两个effect的许多参数和方法。可以将一个include文件看成一个可以使用 include声明插入的代码段。(MaterialShader30.fx中的Example 5.1)。两个版本的材质shader代码都包含一个叫做Includes.inc的文件。这个文件包含所有被这两个effect使用的参数和方法。结果是代码被共享,减少了冗余和复制粘贴时的潜在错误。注意这个文件虽然是Game Studio项目文件的一部分,但它不是content项目的一部分。因为它被effect引用,所以会自动被生成,所以无需将它复制到content项目中。本例中包含在content项目中是为了可以容易地找到这个文件进行编辑。

本示例的一个重要的特点是光源可以打开或关闭。要实现这点,本例介绍了HLSL中循环的概念。你可以在MaterialShader30.fx中的Example 5.2找到循环结构。注意,当场景中可用光源的数量改变时,循环可以迭代的次数是由一个共享参数决定的。

但是,MaterialShader20.fx中的循环是一个硬编码的迭代,这是因为2.0 pixel shader model无法进行真正的循环。作为替代,GPU将循环结构看成一个静态循环并计算所有的迭代,而不管需要绘制多少个光源。要打开或关闭光源,每个光源的颜色值要与一个comparison结果相乘,这个布尔值的comparison会设置为1或0,表示“打开”或“关闭”状态。

DirectX SDK中包含了各个不同版本shader的功能和限制的详细信息。Xbox 360总支持shader model 3.0。

复杂Effect参数

本系列还有一个概念就是复杂参数类型。本例中,光源包含一个结构数组。从C#代码中访问这些参数与查找一个简单的名称有一点不同,但其他行为都是一样的。

MaterialsAndLights.cs中的Example 1.4展示了如何使用一个EffectParameter代表数组中的一个元素。PointLight.cs 中的Example 3.2,HLSL Light结构中的单独成员可以直接访问。记住EffectParameters指向一个实例而不是HLSL中定义的结构类型。你也可以将它看成递归结构,它可以用于任意数量的结构封装。

Effects和性能

在Material.cs中,Example 2.1介绍了一种有趣的组织effect的方式。本例中的每个材质都有自己的材质shader effect副本。effect实例在初始化时克隆。这比使用SetValue函数有明显的性能提升。

要理解工作原理,需要在低层次看一下effect的功能。Effect是两个核心图形构造——shader程序和shader常量的软件包装。一个Effect实例通过Effect参数包含指向shader程序和shader常量的引用。一个Effect实例在主内存中存储了参数值直至它们需要被渲染器调用。当在C#代码中调用Effect.Begin()或Effect.Commit()时,Effect将这些值设置到GPU上的shader常量寄存器。它还要通过一个常量表确认这些常量寄存器是否映射到正确的HLSL变量中。

在本例中,每帧调用SetValue()次数极少。每次函数调用都会带来开销,通过简化调用Effect.Begin(),本示例避免了每帧上百次调用SetValue() 的开销。用相对来说较快的底层shader代码处理状态设置,这会在Xbox360上带来显著的性能提升。

使用一个effect示例表示一个Material还有另外一个好处:它是对一个具体材质的优雅的抽象。为了可读性和易于调试,Material.cs文件隐藏了所有变量,但effect实例可以制作一组简洁的参数,这对通过内容管道导入材质是很有用的。

扩展

这个示例下面是扩展这个示例的一些想法。

译者注:代码的具体原理要看源代码4.Materials and Multiple Light Sources,我把注释也翻译了一下,有点东西自己也没吃透,翻得有点晦涩。

文件下载(已下载 1526 次)

发布时间:2010/4/2 上午10:15:50  阅读次数:9699

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号