前面我们学习了Directx 11如何在屏幕上绘制一个图形(三角形),其中涉及到着色器,我们只是使用了其中的方法,而没有讲解着色器是如何工作的,到底什么是着色器等等,今天将来了解一下着色器到底是什么!!!由于在Directx 11中包含了多种着色器,而有些着色器在一些高级的应用中才会用到,因此作为一个初学者,先了解顶点着色器和像素着色器就OK了,其它的在碰到的时候再进行深入了解。
着色器:
在以前的显卡中,图像的呈现就像流水线一样,不能够进行编程。不过后来GPU出现了,那样就可以计算很多东东,也就可以进行编程了,着色器就是一个可编程的例子,他通过代码传给GPU进行计算,然后通过屏幕显示出来。就像在前面的例子中,我们创建了一个缓存数据(Vertex Buffer)将三角形的坐标传给GPU。在Directx 11 SDK中,支持三种基础着色器:顶点着色器(Vertex Shader),像素着色器(Pixel Shader)【注:有些地方也就叫Fragment Shader】,以及几何着色器(Geometry Shader)。顶点着色器通过顶点作为输入数据,只要将每个顶点缓存数据传入GPU就会执行;像素着色器使用一个像素作为输入数据;而几何着色器使用基元(primitive)作为输入,基元可以是一个点,一条线或一个三角形。这三个基础着色器在呈现时都会遇到,在Direct3D 11中,GPU必须包含一个正确的顶点和像素着色器,而几何着色器是可选的,因此这也是为什么我们先了解顶点着色器和像素着色器的原因。当然在DirectX中,还包含了其他着色器,如Hull Shader,Domain Shader(域着色器)用于曲面细分(有些地方叫做镶嵌tessellation),Compute Shader用于计算。
顶点着色器:
顶点着色器支持编程,可以把顶点着色器看出是C语言中的一个函数,通过顶点作为参数输入这个函数,并且返回编程后的顶点信息。当程序通过顶点着色器传入顶点缓冲数据时,GPU就会迭代顶点缓冲数据,并为每一个顶点执行一次着色器函数。
顶点着色器可以用了做非常多的事情,最主要的是用了进行坐标变换等等,在Direct3D编程中会涉及到很多坐标的变换,如:将世界坐标转换为屏幕坐标等等。比如一个3D的三角形具有(0, 0, 0) (1, 0, 0) (0, 1, 0)坐标,当被绘制到2D纹理缓冲时GPU就需要知道在2D坐标系里的坐标,那样才知道需要画在什么地方。关于坐标的转换,我们后续还会遇到,这里就不进行讨论,对于前面的例子我们只需要知道传入一个顶点信息,返回一个顶点信息就OK了,具体代码如下:
2 {
3 return Pos;
4 }
上面的代码就是HLSL,在前面我们也介绍了一下,他的语言像C一样,传入一个float4数据,POSITION是一个声明性字符串,而SV_POSITION具有特殊的声明意义,在HLSL中可以查询到,即是告诉绘图管线这是定义了一个坐标数据。而这个坐标就是GPU需要的知道的,也就是说在哪里绘制。
像素着色器:
在现代的显示器中,屏幕是有很多个正方形格子组成的,这些格式很小,我们把它叫做像素,每一个像素都包含着自己的颜色,他们之间不用相互依赖。其实当我们需要在屏幕上绘制一个三角形时,在屏幕上呈现的不切切是一个三角形,如下图所示,我们将会更好的理解。
一个三角形,包含了三个顶点,通过这三个顶点连在一起,即叫做光栅化。GPU首先需要知道哪些像素需要被呈现,然后将这些需要呈现(三角形内部)像素激活并赋予颜色值,像素着色器就是为了计算哪些像素需要的颜色。像素着色器通过输入的像素颜色值,然后计算颜色并且将其返回给绘图管线。像素管线参数一般由几何着色器返回,假如没有几何着色器,比如我们上一节说说的例子,那么就通过顶点着色器返回。
顶点着色器中通过SV_POSITION声明描述创建了一个float4的值用于返回像素的坐标位置,这将作为像素着色器的输入参数,这样就告之了GPU当前坐标,而像素着色器返回的一个颜色值,即也为float4数值,并且使用SV_TARGET声明描述,以表示将用于目标的呈现格式。其代码如下所示:
2 {
3 return float4( 1.0f , 1.0f , 0.0f , 1.0f ); // Yellow, with Alpha = 1
4 }
编译着色器:
着色器编写的代码即HLSL保存在一个文本文件中,可以通过Direct3D 11中的D3DX11CompileFromFile()进行编译,其代码如下所示:
2 if ( FAILED( D3DX11CompileFromFile( " Tutorial03.fx " , NULL, NULL, " VS " , " vs_4_0 " , D3DCOMPILE_ENABLE_STRICTNESS, NULL, NULL, & pVSBlob, & pErrorBlob, NULL ) ) )
3 return FALSE;
4
5 // Create the pixel shader
6 if ( FAILED( D3DX11CompileFromFile( " Tutorial03.fx " , NULL, NULL, " PS " , " ps_4_0 " , D3DCOMPILE_ENABLE_STRICTNESS, NULL, NULL, & pPSBlob, & pErrorBlob, NULL ) ) )
7 return FALSE;
绑定着色器:
这样我们就可以在C++代码中通过VSSetShader() 和 PSSetShader()两个方法将顶点着色器和像素着色器绑定到管线上,当我们使用Draw方法时,将顶点缓存信息Vertex Buffer传入到绘图管线中进行绘制,这样关于顶点着色器和像素着色器就了解完毕了。
通过这篇文章其实就是了解到两个东东,顶点着色器是告诉管线坐标,而像素着色器是告诉管线颜色,有了这两项东西就可以绘制图形了。当然在实际需求中不可能这么便宜,书写的代码还是挺多的,最主要希望微软也能够将HLSL的言语智能提示继承到VS中,那样我想像我这种写代码的人将会带来很爽的感觉。
附:
Directx11管道流水线
曲面细分(Tessellator):
从上面这张技术解析图,我们可以很了解到Hull Shader、Tessellator、Domain Shader这3个新单元的具体作用:Hull Shader主要负责定义细分等级(LOD)和相关控制点在细分中的“形变”趋势,需要说明的是这种形变仅仅是类似于曲率改变等小幅度的变化,而非大幅度的多边形位移;Tessellator则负责根据Hull Shader传输下来的信息,通过“暴力”增加多边形去实现Hull Shader的要求;Domain Shader负责的最重要的功能就是通过贴图控制的方式,实现模型的形变,也就是我们大家在DX11的细分曲面中看到的高细节画面。
转自:http://www.cnblogs.com/xdotnet/archive/2011/07/31/directx_direct3d11_shaders.html