Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

6.1 VERTICES AND INPUT LAYOUTS

解释VS的输入input结构体 D3D11_INPUT_ELEMENT_DESC

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

其中要解释一下的是

1.inoutslot 根据以前的方法,一个Direct3D 11应用程序通过使用顶点缓冲区的方式把顶点数据传给GPU,Direct3D 11在中,多种顶点缓冲区能够同时被传入GPU,确切的说是16种。每种顶点缓冲区将有一个数值在[0,15]绑定到Input slot。这个InputSlot告诉GPU可以从哪个顶点缓冲区获取这个元素。

2.注意SemanticIndex 的对应关系,同一类型的元素有多个时,用Index下标标记,比如tex1,tex2

中间有一些配置元素是用于instance的

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

有了input数组,使用ID3D11Device::CreateInputLayout 构建 InputLayout。

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

这中间,一个是会去验证实际vertex input 和vertex shader实际声明是否对应,这样,一个input也可以对应到多个vertex shader上

另一个是获取pShaderBytecodeWithInputSignature, 这是实际的bind的代码,一般的流程是从effect获取对应的tech,获取对应的pass,获取对应的shader入口

6.2 VERTEX BUFFERS

GPU为了访问所有顶点,需要为所有顶点构建一个缓冲区来存储,这就是顶点缓冲,顶点缓冲除了存储顶点数据,还声明了该如何访问这个缓冲区以及这个缓冲区bind到了pipeline的什么地方。

创建buffer需要以下几步:

1. Fill out a D3D11_BUFFER_DESC structure describing the buffer we are going to create.

2. Fill out a D3D11_SUBRESOURCE_DATA structure which specifies the data we want to initialize the buffer contents with.

3. Call ID3D11Device::CreateBuffer to create the buffer.

Set buffer则需要调用void ID3D11DeviceContext::IASetVertexBuffers

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

这里注意需要指定缓冲区的slotindex,和之前声明vertex input是对应的。

填充vertex buffer,并不意味着这会绘制他们,而是表示这些verteices已经准备好填入pipelien中。

最终绘制还要调用void ID3D11DeviceContext::Draw(UINT VertexCount, UINT StartVertexLocation);

6.3 INDICES AND INDEX BUFFERS

创建indices buffer和vertex buffer过程类似,只不过数据类型不同,以及方法接口不同。

set buffer需要

md3dImmediateContext->IASetIndexBuffer(mIB, DXGI_FORMAT_R32_UINT, 0);

最终绘制需要

void ID3D11DeviceContext::DrawIndexed(

UINT IndexCount,

UINT StartIndexLocation,

INT BaseVertexLocation);

6.4 EXAMPLE VERTEX SHADER

SV_POSITION: 表示system value,由于position的特殊作用,属于vertex shader 必须处理的东西,因此特殊标记

在constant buffer中 存储了worldviewproj,把顶点坐标从本地转换到world,到view,到proj齐次裁剪空间。

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

这里如果之后由 geometry shader,proj可以移到那时候在做,否则需要在vertex shader阶段处理

另外,这里proj只做了透视矩阵的转换,并没有进行透视除法,除法会在硬件中进行。

6.5 CONSTANT BUFFERS

constant buffer用于存储shader运行时可能会访问的数据块。

这提供了一个方式让 c++逻辑应用和 effect shader framework去交换信息。

一般如何划分 constant buffer的建议是通过属性的修改频率来分块,如

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

gWorldViewProj:每个object都不一样,都在变

光源信息,位置,颜色:每一帧改变一次,一帧内是一样的

雾的颜色,距离:改变频率不固定,可能很少改变

6.6 EXAMPLE PIXEL SHADER

之所以叫 pixel fragment 而不单是 pixel,是因为一个pixel,可能会计算多次,之后再因为depth,stencil进行discard或者blend操作,因此用fragment更为合适,但实际使用时这两者经常混用。

还有一种技术是 early-z test,提前进行深度测试,不合格可以直接丢弃,但是有一种情况是如果pixel shader中改变了深度,那提前进行的深度测试就是错的,因此有部份硬件的实现是如果在ps中由改变深度的操作,就会禁用 early-z culling。

unity中实现:

Early-Z的实现,主要是通过一个Z-pre-pass实现,简单来说,对于所有不透明的物体(透明的没有用,本身不会写深度),首先用一个超级简单的shader进行渲染,这个shader不写颜色缓冲区,只写深度缓冲区,第二个pass关闭深度写入,开启深度测试,用正常的shader进行渲染。其实这种技术,我们也可以借鉴,在渲染透明物体时,因为关闭了深度写入,有时候会有其他不透明的部分遮挡住透明的部分,而我们其实不希望他们被遮挡,仅仅希望被遮挡的物体半透,这时我们就可以用两个pass来渲染,第一个pass使用Color Mask屏蔽颜色写入,仅写入深度,第二个pass正常渲染半透,关闭深度写入。 --------------------- 本文来自:https://blog.csdn.net/puppet_master/article/details/53900568?utm_source=copy

还有一些说明 Early Z Culling 优化 (https://blog.csdn.net/arundev/article/details/7895839

另外要注意的是vertex shader 的output和 pixel shader的input的对应关系,以及这两者之间到了pixel,这些值是经过了插值的

6.7 RENDER STATES

DirectX本质上就是一个状态机,在没有下一个指令改变它状态的时候,它会一直维持原样。

另外,DirectX封装了一些状态组,方便使用时统一设置。

1. ID3D11RasterizerState: This interface represents a state group used to configure the rasterization stage of the pipeline.光栅化用的

2. ID3D11BlendState: This interface represents a state group used to configure blending operations.混合阶段用的

3. ID3D11DepthStencilState: This interface represents a state group used to configure the depth and stencil tests。深度和模版测试用的

以D3D11_RASTERIZER_DESC为例,前三个属性比较常用,分别指定了,渲染模式如线框还是实体,裁剪模式如不裁剪或者背面剔除,三角形判断正反的旋转方向。

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

调用HRESULT ID3D11Device::CreateRasterizerState(

const D3D11_RASTERIZER_DESC *pRasterizerDesc,

ID3D11RasterizerState **ppRasterizerState); 创建

调用void ID3D11DeviceContext::RSSetState(

ID3D11RasterizerState *pRasterizerState); 配置

一般来说,状态组不需要运行时创建和改变,因此可以在一开始就创建好,并且可以放在一个static类中,方便使用。

6.8 EFFECTS

1.声明变量带后缀表示空间

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

2.调用create函数创建都是很耗的,因此不要在运行时创建,所有都在初始化时创建好,包括input layout,buffer,render state,effect等等。

3.ID3D10Blob :仅是一段内存区域,因此使用前,必须cast成别的合适的类型,主要有两个方法,一个是返回 void*指针,用于cast,另一个是 获取内存大小

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

4. 应用和shader交互,通过ID3DX11Effect::GetVariableByName 接口,然后通过set方法设置,注意这里的set是 set到一个不哪缓存,然后apply pass时才会统一应用到GPU中,仅提交一次,防止多次提交。

5.获取array类型的shader参数,get和set不需要指定特定类型,比如setrawvalue。

6.compile your effects offline using the fxc tool,这样可能build 时检查 shader错误,而不用等到运行时,不过我这里用renderdoc也可以运行时查看错误,或者直接dx在vs的debug模式直接调试。

7.一般都知道shader中会尽情避免ifelse的分支,因此如果有,编译器可能会对代码flatten,转化成没有分支版本。

如 if(s)a; else b; 会转化成 s==1: a; s==0: b; flatten之后的代码是assemble code ,用fxc工具可能编译成 fxo文件,查看,并且可以直接在代码中使用fxo,直接D3DX11CreateEffectFromMemory(,免去D3DX11CompileFromFile)。

8.多tech,可以做适配,根据显卡能力,同一个效果,可以有多个不同复杂度的实现。

适配时,如果每一个level声明一个ps,会有很多共同的重复代码,而如果声明在一个ps里,然后把level能力作为参数传进去,会有大量if else判断,因此我们真正想做的是运行时branch编译,每一个if else可能的branch变体都生成一份代码,effect framework可以来做这件事。

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

因此要做的就是把branch作为参数传入ps,就会编译时生成副本。

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

9.以上这些不管是 branch statement还是具代码指令,都可以通过 assemble code来查看,instrucment slot表示汇编的代码执行行数。

6.9 BOX DEMO

1.注意这里是左手空间坐标系,前面从左下角顺时针依次0,1,2,3,后面是4,5,6,7

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读

2.indice根据顺序,注意面的正反,如下back face会有一面反掉

Chaptor 6 Drawing in Direct3D (Introduction to 3D Game Programing with DirextX 11)自读