Direct3D 10教程3:Shaders和Effect系统

来源:互联网 发布:ipv6网络电视直播 编辑:程序博客网 时间:2024/06/05 22:55

概览

在前面的教程中,我们设置一个顶点缓存并将一个三角形传递到GPU。现在,我们将看一下图形管线的各个阶段是如何工作的,我们会解释shader的概念和effect系统。

注意:这个教程的共享前一个教程的代码,但强调不同的部分。

程序截图

源代码

(SDK root)\Samples\C++\Direct3D10\Tutorials\Tutorial03

图形管线

在前面的教程中,我们创建了顶点缓存,将一个顶点格式(vertex layout)连接到一个technique对象。现在,我们解释一下technique对象和组成它的shader。要完全理解shader,我们需要看一下图形管线的整个流程。

在教程2中,当我们从technique中调用Apply方法,实际上是将shader绑定到管线的一个阶段中。然后,当我们调用Draw时,就开始访问传递到图形管线的顶点数据。下面的几个部分介绍了Draw命令执行之后的过程细节。

Shader

在Direct3D 10中,shader存在于图形管线的不同阶段。它们是由GPU执行的短小代码程序,获取特定的输入数据,处理这些数据,然后将结果输出到管线的下一阶段。Direct3D 10支持三种类型的shader:顶点着色器(vertex shader)几何着色器(geometry shader)像素着色器(pixel shader)。顶点着色器以顶点作为输入,当每个顶点通过顶点缓存传递到GPU时顶点着色器会执行一次。几何着色器以图元作为输入,当每个图元传递到GPU时执行一次。图元可以是一个点、一条线或一个三角形。像素着色器以像素(有时又被称为片段fragment)作为输入,在你想要绘制的图元上的每个像素上执行一次。当使用Direct3D 10进行绘制时,GPU必须要有一个正确的顶点着色器和像素着色器。几何着色器是Direct3D 10的高级功能,是可选的,所以本教程不讨论几何着色器。

顶点着色器

顶点着色器是执行在GPU上的处理顶点的小程序。可以将顶点着色器看出一个以顶点为参数的C函数,它处理出入的数据,输出为经过修改的顶点。在应用程序以顶点缓存的形式将顶点数据传输入GPU之后,会对每个顶点执行顶点着色器一次,将每个顶点数据作为顶点着色器的输入参数。

顶点着色器可以完成很多任务,最重要的就是变换。变换就是将顶点从一个坐标系统转换为另一个的处理过程。例如,一个3D场景中的三角形顶点的位置为(0, 0, 0) (1, 0, 0) (0, 1, 0)。当三角形绘制在一张2D纹理缓存时,GPU必须知道在一张2D纹理缓存何处绘制三角形,GPU必须知道点的2D坐标,正是变换帮助我们完成了上述工作。变换的细节将在下一个教程中加以讨论。本教程中我们会使用一个简单的顶点着色器,它只是将输入数据直接输出。

在教程中,我们使用高级着色语言(High-Level Shading Language,HLSL)编写shader,应用程序会在effect系统中使用这些shader。我们的顶点数据具有一个3D位置元素,本教程中的顶点着色器对输入数据不进行任何操作。顶点着色器的代码如下:


1
2
3
4
float4 VS( float4 Pos : POSITION ) : SV_POSITION
{
    returnPos;
}

顶点着色器看起来很像C函数,HLSL使用与C类似的语法,这样C/C++程序员学起来会更容易。我们看到这个名为VS的顶点着色器使用一个float4类型的参数,返回值也是一个float4值。在HLSL中,一个float4类型的数据是一个有4个分量的矢量。每个分量都是浮点数。冒号定义参数的语义和返回值。前面已经提到过,HLSL中的语义表示数据的性质。在上面的shader中,我们选择POSITION作为Pos输入参数的语义,因为这个参数包含顶点位置。返回值的语义,SV_POSITION,是一个具有特殊含义的预定义语义。这个语义告诉图形管线与这个语义对应的数据定义了剪裁空间的位置,而GPU需要这个位置数据在屏幕上绘制像素。(我们会在下一个教程中讨论剪裁空间。)在上面的shader中,我们获取输入的位置数据,然后将相同的数据返回到管线中。

像素着色器

现代计算机显示器通常是光栅显示的,即屏幕是一个由小点构成的二维网格,这些小点称为像素。每个像素包含一个独立于其他像素的颜色。当在屏幕上绘制三角形时,我们实际上并没有绘制一个完整的三角形,只是点亮被三角形区域覆盖的一组像素,如图2所示。

图2

图2. 左图:我们想绘制的三角形。右图:屏幕上的实际显示。

将三角形转换为一组像素的过程称为光栅化(rasterization)。GPU首先判断哪些像素被三角形覆盖,然后调用这些像素的像素着色器。像素着色器的首要目的就是计算每个像素的颜色。像素着色器获取要着色的像素作为输入数据,计算像素的颜色,然后将输出结果返回到图形管线。输入数据来自于几何着色器,如果几何着色器不存在,就像本教程一样,则直接来自于顶点着色器。

上面的顶点着色器输出了一个语义为SV_POSITION的float4数据,这个数据就是像素着色器的输入。因为像素着色器输出颜色值,所以输出类型为float4。输出的语义为SV_TARGET 表示输出到渲染目标格式。代码如下:


1
2
3
4
float4 PS( float4 Pos : SV_POSITION ) : SV_Target
{
    returnfloat4( 1.0f, 1.0f, 0.0f, 1.0f );// Yellow, with Alpha = 1
}

Effect系统

我们的effect文件由两个shader组成,顶点着色器和像素着色器,还有technique定义。Technique定义设置对应的着色器。另外还有语义用于编译着色器。注意:本教程中几何着色器为NULL,因为此处不需要。


1
2
3
4
5
6
7
8
9
10
// Technique Definition
technique10 Render
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS() ) );
    }
}

创建Effect和Effect Technique

在应用程序代码中,我们需要创建一个effect对象。Effect对象代表effect文件,它是通过调用D3D10CreateEffectFromFile()创建的。创建了effect对象之后,我们就可以调用ID3D10Effect::GetTechniqueByName()方法,传递“Render”作为名称来获取technique对象,然后使用这个对象进行实际的绘制工作。代码如下:


1
2
3
4
5
6
7
// 创建effect
if( FAILED( D3DX10CreateEffectFromFile( L"Tutorial03.fx", NULL, NULL, D3D10_SHADER_ENABLE_STRICTNESS, 0,
    g_pd3dDevice, NULL, NULL, &g_pEffect, NULL ) ) )
        returnFALSE;
 
// 获取technique
g_pTechnique = g_pEffect->GetTechniqueByName("Render");

整合在一起

在分析了图形管线之后,我们就会理解绘制一个三角形的过程。创建一个Direct3D程序需要两个步骤。一是创建顶点数据,我们已经在教程2中讨论过了。二是创建着色器转换要绘制的数据,这是在本教程中讨论的。

0 0
原创粉丝点击