Chapter6 Drawing in Direct3D (Introduction to 3D Game Programming with DirectX 11)笔记

6.1 VERTICES AND INPUT LAYOUTS

解释VS的输入input结构体 D3D11_INPUT_ELEMENT_DESC

其中要解释一下的是

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

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

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

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

 

这中间,一个是会去验证实际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

这里注意需要指定缓冲区的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齐次裁剪空间。

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

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

6.5 CONSTANT BUFFERS

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

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

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

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

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

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

6.6 EXAMPLE PIXEL SHADER

之所以叫 pixel fragment 而不单是 pixel,是因为一个pixel,可能会计算多次(如果没有early-z,那就会渲染之后才比较深度剔除),之后再因为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为例,前三个属性比较常用,分别指定了,渲染模式如线框还是实体,裁剪模式如不裁剪或者背面剔除,三角形判断正反的旋转方向。

调用HRESULT ID3D11Device::CreateRasterizerState(

const D3D11_RASTERIZER_DESC *pRasterizerDesc,

ID3D11RasterizerState **ppRasterizerState); 创建

调用void ID3D11DeviceContext::RSSetState(

ID3D11RasterizerState *pRasterizerState); 配置

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

6.8 EFFECTS

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

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

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

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可以来做这件事。

We can use compile time parameters to use the effects framework to generate shader variations for us based on the given compile time arguments.

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

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

6.9 BOX DEMO

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

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

6.10 HILLS DEMO

在x z平面构建网格平面,y值高度由 f(x,z)得到,所以点为 (x, f(x, z), z)。

长m个顶点,宽n个顶点

长 m-1格,宽n-1格,总格数(m-1)*(n-1),一个网格两个三角形,所以三角形数2*(m-1)*(n-1)

假设width 为w,depth为d,所以 单格长 dx=w/(m-1),宽 dz=d/(n-1)

xz平面上,ij格表示为 vij = [–0.5w + j ·dx, 0.0, 0.5d – i ·dz)],有0.5是因为是以格子中心表示坐标的

构建indices数组,即构建三角形

ΔABC = (i · n + j, i · n + j + 1, (i + 1) · n + j)

ΔCBD = ((i + 1) ·n + j, i ·n + j + 1, (i + 1) · n + j + 1)

6.11 SHAPES DEMO

Cylinder

origin在cylinder中心,平行于y轴,y轴分为stack层,一层有slice个三角形,总高度h

那ring有stackcount +1个,每个ring上有slicecount+1个顶点

顶面半径topradius,底面半径bottomradius,

每层stack的半径间距Δr = (topRadius – bottomRadius)/stackCount

每层stack高度 h/stackcount,第i层stack高度 -h/2+i*h/stackcount

注意这里,每个ring的第一个和最后一个vertex是重复的,但是texture的coordinate不重复,因此为了texture正确需要注意

indiceslist计算如下

ΔABC = (i · n + j, (i + 1) · n + j), (i + 1) · n + j + 1)

ΔACD = (i · n + j, (i + 1) · n + j + 1, i · n + j + 1 。 n=slicecount+1,每行ring的顶点个数

之后,top和botom面很类似

Sphere 和 Geosphere

sphere和cylinder区别就是边的模拟不是线性的而是球形的

geosphere相比sphere,是一般sphere的三角形不是相等大小的三角形,不够近似圆形,geosphere将每个三角形都拆分成四个同样大小的三角形(三条边的中点互连),曲面镶嵌更平滑。

6.12 LOADING GEOMETRY FROM THE FILE

从geometry file构建图形,比如obj文件什么的

6.13 DYNAMIC VERTEX BUFFERS

动态顶点缓冲,需要使用usage D3D11_USAGE_DYNAMIC,因为需要写入改变,因此CPU access flag D3D11_CPU_ACCESS_WRITE。

然后使用 ID3D11DeviceContext::Map 获取buffer memory的起始地址,然后修改,修改之后,调用ID3D11DeviceContext::Unmap释放。

注意,由于dynamic buffer需要cpu动态修改之后再传输到gpu,因此比较慢,尽量少使用

dx中为了尽量减少使用,可以尽量使用一下feature

1. 简单的animation可以通过 vertex shader来做

2.简单的wave,可以通过render to texture 或者 compute shader 或者 vertex texture fecth来做

3.可以考虑用geometry shader来做primitive的 create delete等等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值