标签:二进制 文档 mem 2.0 position gis ice x11 targe
# 前言 从这一部分开始,感觉就像是踏入了无人深空一样,在之前初学DX11的时候,这部分内容都是基本上跳过的,现在打算重新认真地把它给拾回来。 [DirectX11 With Windows SDK完整目录](http://www.cnblogs.com/X-Jun/p/9028764.html) [Github项目源码](https://github.com/MKXJun/DirectX11-With-Windows-SDK) # 几何着色器 首先用一张图来回顾一下渲染管线的各个阶段,目前为止我们接触的着色器有顶点着色器和像素着色器,而渲染管线阶段有:输入装配阶段、顶点着色阶段、光栅化阶段、像素着色阶段、输出合并阶段。 ![](https://images2018.cnblogs.com/blog/1172605/201808/1172605-20180805203259072-78808112.png) 可以看到,几何着色器是我们在将顶点送入光栅化阶段之前,可以操作顶点的最后一个阶段。它同样也允许我们编写自己的着色器代码。几何着色器可以做如下事情: 1. 让程序自动决定如何在渲染管线中插入/移除几何体; 2. 通过流输出阶段将顶点信息再次传递到顶点缓冲区; 3. 改变图元类型(如输入点图元,输出三角形图元); 但它也有缺点,几何着色器输出的顶点数据很可能是有较多重复的,从流输出拿回到顶点缓冲区的话会占用较多的内存空间。它本身无法输出索引数组。 几何着色阶段会收到一系列代表输入几何体类型的顶点,然后我们可以自由选择其中的这些顶点信息,然后交给流输出对象重新解释成新的原始类型(或者不变),传递给流输出阶段或者是光栅化阶段。而几何着色器仅能够接受来自输入装配阶段提供的顶点信息,对每个顶点进行处理,无法自行决定增减顶点。 注意:离开几何着色器的顶点如果要传递给光栅化阶段,需要包含有转换到齐次裁剪坐标系的坐标信息(语义为`SV_POSITION`的`float4`向量) # 可编程的几何着色器 ## 从一个看似没什么用的几何着色器代码入手 若我们直接从VS项目新建一个几何着色器文件,则可以看到下面的代码: ```cpp struct GSOutput { float4 pos : SV_POSITION; }; [maxvertexcount(3)] void main( triangle float4 input[3] : SV_POSITION, inout TriangleStream< GSOutput > output ) { for (uint i = 0; i < 3; i++) { GSOutput element; element.pos = input[i]; output.Append(element); } } ``` ps. 可能有些人会对void main的写法表示不爽,比如说我。不过这不是C语言的主函数...... 若在输入装配阶段指定使用TriangleList图元的话,初步观察该代码,实际上你可以发现其实该着色器只是把输入的顶点按原样输出给流输出对象,即跟什么都没做(咸鱼)有什么区别。。不过从这份代码里面就已经包含了几何着色器所特有的绝大部分语法了。 首先,几何着色器是根据图元类型来进行调用的,若使用的是TriangleList,则每一个三角形的三个顶点都会作为输入,触发几何着色器的调用。这样一个TriangleList解释的30个顶点会触发10次调用。 对于几何着色器,我们必须要指定它每次调用所允许输出的最大顶点数目。我们可以使用属性语法来强行修改着色器行为: `[maxvertexcount(N)]` 这里`N`就是每次调用允许产出的最大顶点数目,然后最终输出的顶点数目不会超过`N`的值。`maxvertexcount`的值应当尽可能的小。 关于性能上的表现,我根据龙书提供的引用找到了对应的说明文档: [NVIDIA08](http://developer.download.nvidia.com/GPU_Programming_Guide/GPU_Programming_Guide_G80.pdf) 虽然是10年前的文档,这里说到:在GeForce 8800 GTX,一个几何着色器的调用在输出1到20个标量的时候可以达到最大运行性能表现,但是当我们指定最大允许输出标量的数目在27到40个时,性能仅达到峰值的50%。比如说,如果顶点的声明如下: ```cpp struct V0 { float3 pos : POSITION; float2 tex : TEXCOORD; }; ``` 这里每个顶点就已经包含了5个标量了,如果以它作为输出类型,则`maxvertexcount`为4的时候就可以达到理论上的峰值性能(20个标量)。 但如果顶点类型中还包含有`float3`类型的法向量,每个顶点就额外包含了3个标量,这样在`maxvertexcount`为4的时候就输出了32个标量,只有50%的峰值性能表现。 这份文档已经将近10年了,对于那时候的显卡来说使用几何着色器可能不是一个很好的选择,不过当初的显卡也早已不能和现在的显卡相提并论了。 > 注意: > 1. `maxvertexcount`的值应当设置到尽可能小的值,因为它将直接决定几何着色器的运行效率。 > 2. 几何着色器的每次调用最多只能处理1024个标量,对于只包含4D位置向量的顶点来说也只能处理256个顶点。 在HLSL编译器里,如果设置的`maxvertexcount`过大,会直接收到编译错误: ![](https://images2018.cnblogs.com/blog/1172605/201808/1172605-20180806192145098-1956370002.png) 然后代码中的`triangle`是用于指定输入的图元类型,具体支持的关键字如下: |图元类型|描述| |--------|----| |point |Point list| |line |Line list or line strip| |triangle|Triangle list or triangle strip| |lineadj |Line list with adjacency or line strip with adjacency| |triangleadj|Triangle list with adjacency or triangle strip with adjacency| 具体的图元类型可以到第2章回顾:[点击此处](https://www.cnblogs.com/X-Jun/p/9031959.html) 而参数类型可以是用户自定义的结构体类型,或者是向量(float4)类型。从顶点着色器传过来的顶点至少会包含一个表示齐次裁剪坐标的向量。 参数名`inupt`实际上用户是可以任意指定的。 对于该输入参数的元素数目,取决于前面声明的图元类型: |图元类型|元素数目| |--------|--------| |point |[1] 每次只能处理1个顶点| |line |[2] 一个线段必须包含2个顶点| |triangle|[3] 一个三角形需要3个顶点| |lineadj |[4] 一个邻接线段需要4个顶点| |triangleadj|[6] 一个邻接三角形需要6个顶点| 而第二个参数必须是一个流输出对象,而且需要被指定为`inout`可读写类型。可以看到,它是一个类模板,模板的形参指定要输出的类型。流输出对象有如下三种: |流输出对象类型|描述| |--------------|----| |PointStream |一系列点的图元| |LineStream |一系列线段的图元| |TriangleStream|一系列三角形的图元| 流输出对象都具有下面两种方法: |方法|描述| |----|----| |Append|向指定的流输出对象添加一个输出的数据| |RestartStrip|在以线段或者三角形作为图元的时候,默认是以strip的形式输出的,如果我们不希望下一个输出的顶点与之前的顶点构成新图元,则需要调用此方法来重新开始新的strip。若希望输出的图元类型也保持和原来一样的TriangleList,则需要每调用3次Append方法后就调用一次RestartStrip。| >注意: > 1. 所谓的删除顶点,实际上就是不将该顶点传递给流输出对象 > 2. 若传入的顶点中多余的部分无法构成对应的图元,则抛弃掉这些多余的顶点 在开始前,先放出`Basic.fx`文件的内容: ``` #include "LightHelper.hlsli" cbuffer CBChangesEveryFrame : register(b0) { row_major matrix gWorld; row_major matrix gWorldInvTranspose; } cbuffer CBChangesOnResize : register(b1) { row_major matrix gProj; } cbuffer CBNeverChange : register(b2) { DirectionalLight gDirLight; Material gMaterial; row_major matrix gView; float3 gEyePosW; float gCylinderHeight; } struct VertexPosColor { float3 PosL : POSITION; float4 Color : COLOR; }; struct VertexPosHColor { float4 PosH : SV_POSITION; float4 Color : COLOR; }; struct VertexPosNormalColor { float3 PosL : POSITION; float3 NormalL : NORMAL; float4 Color : COLOR; }; struct VertexPosHWNormalColor { float4 PosH : SV_POSITION; float3 PosW : POSITION; float3 NormalW : NORMAL; float4 Color : COLOR; }; ``` ## 实战1: 将一个三角形分割成三个三角形 现在我们的目标是把一个三角形分裂成三个三角形: ![](https://images2018.cnblogs.com/blog/1172605/201808/1172605-20180808204508906-886184113.jpg) 这也为以后实现分形做为基础。建议读者可以先自行尝试编写着色器代码再来对比。在编写好着色器代码后, 要给渲染管线绑定好一切所需的资源才能够看到效果。 ### HLSL代码 `Triangle_VS.hlsl`, `Triangle_GS.hlsl`和`Triangle_PS.hlsl`的实现如下: ```cpp // Triangle_VS.hlsl #include "Basic.fx" VertexPosHColor VS(VertexPosColor pIn) { row_major matrix worldViewProj = mul(mul(gWorld, gView), gProj); VertexPosHColor pOut; pOut.Color = pIn.Color; pOut.PosH = mul(float4(pIn.PosL, 1.0f), worldViewProj); return pOut; } ``` ```cpp Triangle_GS.hlsl #include "Basic.fx" [maxvertexcount(9)] void GS(triangle VertexPosHColor input[3], inout TriangleStreamDirectX11 With Windows SDK--15 几何着色器初探
标签:二进制 文档 mem 2.0 position gis ice x11 targe
原文地址:https://www.cnblogs.com/X-Jun/p/9429881.html