视区和裁剪


从概念上讲,视区是一个二维矩形,三维场景被投影到这个矩形中。在Microsoft® Direct3D®中,这个矩形以Direct3D表面内的坐标的形式存在,该表面被系统用作渲染目标。投影变换把顶点转换到视区所使用的坐标系统。

应用程序用Direct3D中的视区指定以下特性。

·         屏幕空间中的视区,渲染将被限制在该区域内。

·         渲染目标表面的深度值范围(通常是0.0到1.0),在这之间的场景会被渲染。

本节讨论裁剪,即几何流水线的最后一步。讨论被分为以下主题。

·         视区矩形

·         视棱锥

·         裁剪体

·         视区缩放

·         视区的使用

Direct3D通过给设备设置一系列的视区参数来实现裁剪。


视区矩形


C++应用程序通过使用D3DVIEWPORT9结构定义视区矩形。D3DVIEWPORT9结构要配合以下IDirect3DDevice9接口暴露的视区操作方法一起使用。

·         IDirect3DDevice9::GetViewport

·         IDirect3DDevice9::SetViewport

D3DVIEWPORT9结构包含的四个成员——XYWidthHeight——定义了渲染目标表面中的一块区域,场景将被渲染在该区域中。这些值对应于目标矩形,或视区矩形,如下图所示。

应用程序给D3DVIEWPORT9结构的XYWidthHeight成员指定的值是屏幕坐标,相对于渲染目标表面的左上角。该结构定义了两个附加成员(MinZMaxZ),指出在这个深度范围内的场景会被渲染。

Microsoft® Direct3D®假设视区裁剪体在X方向上从-1.0到1.0,在Y方向上从1.0到-1.0。过去,这些是应用程序最常使用的设定。在投影变换过程中,应用程序可以在裁剪前调整纵横比。这项任务被涵盖在投影变换一节中。

注意  D3DVIEWPORT9结构的MinZMaxZ成员指出在这个深度范围内的场景会被渲染,它们不用于裁剪。大多数应用程序把这两个成员设置为0.0和1.0,使系统能渲染深度缓存中全部范围的深度值。在一些情况下,应用程序可以通过使用其它的深度范围实现特殊效果。例如,要在游戏中显示提示(heads-up display),应用程序可以将两个值都设为0.0,强制系统渲染场景中最前面的物体,或者也可以将它们都设为1.0,渲染总是在最后面的物体。


视棱锥


视棱锥是场景中的三维区域,相对于视区的摄像机。区域的形状会影响到建模如何从摄像机空间投影到屏幕。最普通的投影类型,透视投影,是造成离摄像机近的物体显得比远处物体大的原因。对于透视变换,视棱锥可以被想象成一个金字塔,摄像机被放在顶端。这个金字塔分别被前后裁剪平面截取。金字塔中部位于前后裁剪平面之间的区域就是视棱锥。物体只有在这个区域中时才是可见的。

想象我们站在一个黑暗的房间里,从一个方的窗口中看出去,这就是一个直观的视棱锥。在这个类比中,近裁剪平面就是窗口,远裁剪平面是最终妨碍我们视线的任何东西——街上的高楼,远处的山,或什么也不是。我们可以看见从窗口处开始,到任何妨碍我们视线的东西处结束,位于截棱锥内的一切,除此之外我们什么也看不见。

视棱锥由fov(视角)和视点在z轴方向上到前后裁剪平面的距离定义。

在这幅图中,变量D是从摄像机到空间的原点的距离,该空间由几何流水线的上一步——视变换——定义。这就是用于限定视棱锥区域的边界。有关如何用变量D构建一个投影矩阵的信息,请参阅什么是投影矩阵?


裁剪体


投影矩阵的结果决定了投影空间中的裁剪体。Microsoft® Direct3D®将裁剪体定义为以下公式:

在前面的公式中,XYZW表示经过投影变换的顶点坐标。如果裁剪被启用(默认值),任何x-,y-,或z-成员在这些范围之外的顶点将被裁剪掉。

除了顶点缓存,应用程序通过D3DRS_CLIPPING渲染状态启用或禁用裁剪。顶点缓存的裁剪信息在处理过程中产生,更多信息请参阅固定功能顶点处理可编程顶点处理

Direct3D不裁剪顶点缓存中经过变换的顶点,除非顶点是由IDirect3DDevice9::ProcessVertices得到的。如果应用程序自己进行变换并需要Direct3D进行裁剪,那么不应该使用顶点缓存。在这种情况下,应用程序遍历顶点数据进行变换,Direct3D再次遍历顶点数据进行裁剪,然后驱动程序渲染顶点数据,这是效率很低的。因此,如果应用程序自己变换顶点数据那么它同时也应该裁剪顶点数据。

当设备需要对收到的经过变换且经过光照的顶点(TL顶点)进行裁剪时,为了执行裁剪操作,Direct3D会根据顶点的rhw和视区信息,把顶点变换回裁剪空间。然后执行裁剪操作。并不是所有设备都能执行这种裁剪TL顶点所需的反向变换操作。

D3DPMISCCAPS_CLIPTLVERTS设备能力标志指出设备是否能裁剪TL顶点。如果没有设置这个能力标志,那么应用程序应该负责对准备送到设备进行渲染的TL顶点进行裁剪。在软件顶点处理模式(无论设备自身是在软件顶点处理模式下创建,还是用D3DRS_SOFTWAREVERTEXPROCESSING渲染状态作为混合模式顶点处理设备创建然后切换到软件顶点处理模式)下,设备总是能裁剪TL顶点。


视区缩放


用于视区的D3DVIEWPORT9结构的XYWidthHeight成员定义了视区在渲染目标表面中的位置和大小。这些值是屏幕坐标,相对于表面的左上角。

Microsoft® Direct3D®使用视区的位置和大小缩放顶点使渲染后的场景正好在渲染目标表面的相应位置。在内部,Direct3D会把这些值插入到应用于每个顶点的矩阵中。

这个矩阵根据视区的大小和希望的深度范围缩放顶点,并把它们转换到渲染目标表面的相应位置。矩阵同时翻转了y-坐标,使屏幕原点在左上角,y值向下增长。在应用这个矩阵之后,顶点仍然在齐次空间中——也就是说,它们仍然以[x,y,z,w]顶点的形式存在——并且它们在被送到光栅化器之前必须被转换到非齐次坐标。

注意 视区缩放矩阵合并了D3DVIEWPORT9结构的MinZMaxZ成员,使缩放后的顶点在深度范围[MinZ, MaxZ]内。这和以前版本的Microsoft DirectX®具有不同的语义,以前这些成员曾被用于裁剪。

注意  更多信息,请参阅视区矩形裁剪体。应用程序一般将MinZMaxZ设为0.0和1.0,使系统渲染整个深度范围。应用程序可以把两个值都设为0.0,强制渲染所有最前面的物体,或都设为1.0,渲染所有最后面的物体。


视区的使用


本节提供有关使用视区的细节,信息被分为以下主题。

·         设置视区裁剪体

·         清除视区

·         手工变换顶点


设置视区裁剪体


要给一个渲染设备配置视区参数的唯一要求是设置视区的裁剪体。要完成这个任务,首先初始化裁剪体和渲染目标表面,并给它们设置裁剪值。视区一般被设置成渲染目标表面的整个区域,但这不是必需的。

C++应用程序可以对D3DVIEWPORT9结构的成员使用以下设置。

D3DVIEWPORT9 viewData = { 0, 0, width, height, 0.0f, 1.0f };

D3DVIEWPORT9结构赋值之后,通过调用IDirect3DDevice9::SetViewport方法会把视区参数应用于设备。以下示例代码显示了这个调用。

HRESULT hr;

 

hr = pd3dDevice->SetViewport(&viewData);

if(FAILED(hr))

    return hr;

如果调用成功,视区参数就设置完成并会在下一次渲染方法被调用时生效。要改变视区的参数,只要更改D3DVIEWPORT9结构的值并再次调用IDirect3DDevice9::SetViewport方法。

注意  D3DVIEWPORT9结构的MinZMaxZ成员指出在这个深度范围内的场景将被渲染,它们不用于裁剪。大多数应用程序将这两个成员设置为0.0和1.0,使用系统渲染深度缓存中全部范围的深度值。在一些情况下,应用程序可以通过使用其它的深度范围实现特殊效果。例如,要在游戏中显示提示,应用程序可以把两个值都设为0.0,强制系统渲染场景中最前面的物体,或者也可以把它们都设为1.0,渲染总是在最后面的物体。


清除视区


清除视区会重置渲染目标表面中视区矩形内的内容,如果指定的话,还可以重置深度缓存和模板缓存表面中相应矩形区域中的内容。一般来说,为了保证图形和其它数据(渲染目标表面和深度/模板缓存表面)都会接受新的渲染物体而不会显示遗留物,应用程序要在渲染新的一帧前清除视区。

C++开发人员可以用IDirect3DDevice9接口提供的IDirect3DDevice9::Clear方法清除视区。该方法接收一个或多个定义表面中需要被清除区域的矩形。万一正在渲染的场景包含穿越整个视区矩形的运动——例如,在一个第一人称视角的游戏中——应用程序可能希望每帧清除整个视区。在这种情况下,应用程序将Count参数设为1,将pRect参数设为覆盖整个视区的矩形的地址。如果这样更方便,应用程序也可以将pRect参数设为NULL,将Count参数设为0,表示要清除整个视区矩形。

IDirect3DDevice9::Clear方法很灵活,它提供了对清除深度缓存中的模板缓存的支持。Flags参数接收三个标志,确定怎样清除渲染目标及相关的深度缓存和模板缓存。如果Flags参数包含D3DCLEAR_TARGET标志,该方法用Color参数(不是材质颜色)中指定的RGBA颜色值清除视区。如果Flags参数包含D3DCLEAR_ZBUFFER标志,该方法用Z参数中指定的深度值清除深度缓存,0.0是最近的距离,1.0是最远的距离。如果Flags参数包含D3DCLEAR_STENCIL标志,该方法用Stencil参数中指定的值清除模板缓存。应用程序可以使用从0到2的n次方减1范围内的整数,n是模板缓存的位数。

注意  Microsoft DirectX® 5.0允许背景材质具有关联的纹理,这可以将视区清除为一张纹理而不是一个单纯的颜色。这项特性很少使用,实际上效率也不高。从DirectX® 6.0开始及后续的接口不再接受纹理句柄,这意味着应用程序不能再用一张纹理清除视区。更确切地讲,应用程序现在必须手工绘制背景。因此,很少需要清除渲染目标表面上的视区。只要应用程序清除了深度缓存,渲染目标表面上的所有像素都将被覆盖。

在一些情况下,应用程序可能希望只渲染到渲染目标和深度缓存表面的一小部分。清除方法同样允许应用程序在单个调用中清除表面中的多个区域。要完成这个任务,将Count参数设置为希望清除的矩形区域的数量,并把pRects参数设为矩形数组中第一个矩形的地址。


手工变换顶点


Microsoft® Direct3D®应用程序中,可以使用三种顶点。更多有关顶点格式的细节请参阅顶点格式

应用程序可以用顶点缓存从简单顶点类型转到复杂顶点类型。顶点缓存是为了进行高速渲染,用来有效地存储并处理大批量顶点的对象,并经过优化可以充分利用特定处理器的特性。要用IDirect3DDevice9::ProcessVertices方法执行顶点变换。IDirect3DDevice9::ProcessVertices只接收未经变换的顶点,也可以有选择地对顶点执行光照计算和裁剪。光照计算在应用程序调用IDirect3DDevice9::ProcessVertices方法时执行,但裁剪在渲染时执行。

在处理完顶点之后,应用程序可以使用特殊的渲染方法渲染顶点,或者也可以通过锁定顶点缓存直接存取它们。更多有关使用顶点缓存的信息,请参阅顶点缓存