JefferyZhou

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Shader Model 已经有五个版本了:分别是Shader Model 1.0(DirectX8.0)、Shader Model 2.0(DirectX9.0b)、Shader Model 3.0(DirectX9.0c)、Shader Model 4.0(DirectX10)、Shader Model 4.1(DirectX10.1)和Shader Model 5.0(DirectX11)。N3的版本是 DirectX 9.0c, 也就没有赶上 GS (Geomtry Shader)。

N3 中涉及这个系统的文件主要是如下:Shader, ShaderInstance, ShaderVariable, ShaderVariableInstance, ShaderServer, ShaderVariation,shadersemantics,ShaderFeature, 这些基本是对ID3DXEffect一个操作封装,如果要理解工作原理,需要先熟悉DX的Shader Model. 同时N3还设计了一个编译系统,用来生成.fx文件,也就是DX的shader代码文件,这些文件通过一组规则,用XML配置产生。

所以本文,打算,分三部分,第一步解析DX的Shader系统,第二步,解析N3的工具,第三步解析N3的Shader以及这个系统与渲染的整合过程。

着色器


着色程序,分为顶点着色和像素着色,到4.0版本出现了 几何着色,但是由于效率问题,在5.0版本出现一个新技术Tesselltion。简单的回顾一下概念,SM 4.0 中的GE这里就不深入了。

vetex着色: 给定顶点,顶点着色接收渲染管线传送过来的顶点,颜色,纹理坐标等数据,结合Shader变量,给出当前顶点的坐标,颜色值

pixel 着色: 给定像素,像素着色接收顶点着色的输出作为输入,综合设置的Shader 变量,给出当前顶点的颜色值 , 这里颜色值

着色变量,可以是各种类型,包括自定义结构体,通常会包括,各种矩阵以及逆矩阵,相机的坐标,采样纹理等数据,

DX 8.0 取消了固定流水线,取代的是一个可编程的流水线管道 : 一个shader,可以是多个 tench,  一个 tench 可以是 多个 pass, 每一个 pass 都有定义的 vetex shader 和 pixel shader 。

N3的着色器


N3的着色器设计主要是: N3的Shader的导出工具 , N3着色与渲染的整合

要理解N3的设计,需要先知道N3中的Shader的导出工具,具体工程是 nsc3:

其中有几个比较重要的概念 Shader , ShaderNode, ShaderSlot

一个Shadr 由 几个 ShaderNode组成,

ShaderNode 代表的是 ShaderFragment,  而一个ShaderFragment,可以包含片段程序,包括 VertexShader 和 PixelShader

而一个ShaderSlot 代表一个输入或者输出的参数,这些参数链接,不同的ShaderNode

这样就构建了Shader的编辑框架,类似U3中的材质编辑器,用线条链接各种Shader片段

先来结构一下这个框架的设计

一个顶点程序 :

vsMain (vsInput , vsOutput)

{

          vsSourceCode;

}

类似上面的伪代码,是由三部分构成,vsInput, vsOut, vsSourceCode, 所以在xml中配置好这三部分就可以生成一个顶点Shader。

一个像素程序:

psMain(psInput, psOutput)

{

     psSourceCode;

}

如上面的伪代码,也是由三部分构成,psInput, psOutput, psSourceCode, 所以在xml中配置好这三部分就可以生成一个像素Shader。

刚刚上面提到的是基本的着色程序片段。

要生成一个可执行的着色程序,需要一个连贯性。

顶点程序,从图形管道获取原始顶点数据,生成变换后的顶点,UV坐标,纹理等顶点相关的数据,供给像素着色程序使用,像素着色程序结合顶点程序的数据以及其他常量,计算像素的最后颜色数据。

所以构建一个Shader : 需要从外部获取数据vsInput,顶点着色生成一个 vsOutPut, 然后像素着色根据 vsOutput (= psInput) , 生成一个 psOutput, 然后显示设备拿这个psOutput的颜色数据放入帧缓冲区。

有了这些基础的信息。

我们接着说N3的事情,

N3把 着色程序,分解到各种片段,对片段的输入输出重定向配置各种Node,然后把各个Node 通过输入和输出Slot链接起来。为了统一执行上述链接过程。

N3引入了五个标准Node: Vertex,  Interpolator, Sampler, Constant, Shared, Result

其中 Vertex 代表的是 从图形设备的数据管道流入的数据,也就是外部输入数据

而 Interpolater 代表的是  顶点着色程序的输出数据,同时也是 像素程序的输入数据

Sampler 代表的 对各种纹理贴图的采样

Constant 代表的是 常量

Shared 代表的共享变量

Result 代表的是 像素程序的输入数据

所以VertexNode 只有输出没有输入, Result 值有输入没有输出, 而 Interpolater 的输入和输出是一致的,如果Interpolater的某一个变量有输出,但是没有输入,也就是没有顶点着色程序生成这个数据,则需要在Vertex中补充一个输出,这样后面的像素着色才可以从Interpolater中获取相应的数据。

图形分解


下面用图形来展示整个过程:

Image(1)

上图展示的整个着色过程。 其中我们先分解成两部分,顶点着色和像素着色:

Image(2)

我们接着细分 顶点着色程序:

Image(3)

和前面的分解是一样的,把整个处理过程分解成三部分 输入参数,着色代码,输出参数。顶点着色的输出参数和像素的输入参数是一致的(除了哪些来自外部的常量)

细分 像素着色程序:

Image(4)

通过前面的图示,我们已经完美的分解了整个着色程序。把着色程序分解成为不同的片段和结构。

N3 设计的材质编辑器的目的就是 通过输入和输出参数的重定向,可以拼接不同的着色片段为一个完整的Shader。N 3的着色片段就是 顶点着色程序和像素着色片段

我们先来看个最简单的例子 , 把一个只有 顶点着色的程序片段和一个只有像素着色的片段连接起来, 如下图所示:

Image(5)

要链接两者组成一个完整的着色程序,只需把  像素着色1 的 VS 输入参数1 链接到 顶点着色1的输出参数 PS输出1, 以及像素着色1的 VS输入2 链接到 顶点着色1的输出参数 PS输出2 两者就链接起来了。这个输入与输出的链接,就是说把其中一个的 输出参数当做 另外一个的输入参数。

链接后如下图所示:

Image(6)

如上图一个标准的shader程序就已经完成了。对于上述情况也会存在一些特别地方:比如把一个像素着色程序链接到一个顶点着色程序的时候,如果像素着色的参数,顶点程序无法满足时,比如像素着色需要一个UV坐标,而顶点着色没有UV坐标的生成,这个时候就需要给顶点程序的输入加一个参数,让这个参数以顶点着色程序的名义流入像素着色程序,当顶点程序的输出大于像素着色的输入的时候,这个时候可以忽略,因为只是一个参数没用到而已,下面用图把这两种情况列举出来

Image(7)                用详细的导向描述图如下: Image(8)

上面的红色框代表一个顶点着色程序,有一个Vertex输入,一个const输入, 有三个PS输出。而橙绿色框代表一个像素着色程序,有三个VS输入,一个PS输入. 要链接两个程序,我们需要链接 VS 输入到 PS输入1 , VS输入2链接到PS输入2,但是 VS输入3和PS输出3是不匹配的,这个时候,我们给顶点程序加一个输入参数,名字叫 Vertex-vS 输入3,让这个参数从顶点着色程序经过,然后再直接输出 Vetex-VS输出3,然后再把VS输入3和Vertex-VS输出3链接起来。这样前面提到的像素着色需要的参数顶点着色没提供,顶点着色输出的参数像素着色么有的情形。

有了前面的分析,我们再深入一步,链接多个像素着色和顶点着色片段。我们已经列举了顶点着色和像素着色的链接,下面我们单独说明顶点着色片段之间怎么链接,以及像素着色片段之间怎么链接,有如下的连个顶点着色片段:

Image(9)  和  Image(10)

现在顶点着色片段2的Vertex输入1 和 顶点着色1的PS输入1是一致的,这样链接两者后的图如下:

Image(11)                        我们把图整理一下 :::           Image(12)    把图 分割一下::::             Image(13)

这其中涉及的规则就是所有的输入来自 const, vertex, shared,  而这些输入都是唯一的,在整理顶点着色的输入参数的时候,就是整理Vertex节点中有多少个输出链接。而在整理输出的时候就是整理 Interpolater 节点中有多少个输入链接。 [这个规则其实就是前面重复过的对于五个特殊Node功能的定位]

这样 两个顶点着色片段链接后,变成一个着色片段。  这个着色片段有 4个输入, 3个输出。在整个着色片段中,会定义2个变量的输入结构体,其余两个为 const 变量,会定义三个变量的输出结构体(前提是三个输出不一样)。详细的结构如右边的框图。

对于像素着色程序的链接和顶点程序是一样的原理。如下有三个像素着色片段:

Image(14) Image(15) Image(16)

他们之间存储如下的链接关系:

Image(17)  把图整理一下:Image(18)

接着把图分解成 完整的 顶点着色程序:

Image(19)

完整的像素着色片段已经完成。

有了前面的理论,我们现在对N3的材质编辑器的工作原理做一个简要说明:

先是顶点着色程序,我们顶点着色程序,分为三部分,输入参数,代码,输出参数。顶点着色程序的输入参数会被绑定到节点Vertex, Const, Shared, Sampler, 其中 Vertex 是特殊节点中的特殊节点,他代表的是渲染管道传送过来的渲染数据,主要包括顶点坐标等。而顶点程序的输出参数绑定到Interpolater 节点。当把几个顶点着色程序连接起来的时候,就产生了一个调用链。比如顶点程序1 的输入绑定到顶点程序2的输出参数。通过这种方式,链接起来的调用链,可以通过节点Interpolater的所有输出slot的逆向绑定关系。然后通过遍历Vertex节点的输出slot就可以知道整个程序的输入参数,而访问Interpolater的输出slot,就可以知道整个顶点着色程序的输出参数。 

有了上述的情况说明,N3的材质工具的原理就已经说明了。对于游戏开发中,遇到的材质编辑器也就有了理论上的相关认识。

posted on 2012-09-24 16:46  JefferyZhou  阅读(707)  评论(0编辑  收藏  举报