Geometry Shader
要点
- 作用于每个片元(primitive)
- each_outPrimitiveList = GeometryShader(each_primitive.vertexList)
- 不论三角形带还是三角形列表,输入都是一个完整三角形顶点列表
- 离开GS的顶点坐标必须是齐次裁剪空间
- 输出必须预先定义最大顶点数N([maxvertexcount(N)]),且越小越好
- 可以使用GS来制作billboard效果,可以节省四分之三的顶点吞吐
- SV_PrimitiveID:面元索引。可以直接在PS里边加入该输入;如果有GS的话,一定要出现在GS的signature中才能使用它。
- SV_VertexID:顶点索引。VS中即可使用。
用法
- 输出流 SteamOutputObject
- 类型:PointStream/LineStream/TriangleStream
- 总是strip的形式
- 可以选择不输出
- 如果一个strip中的顶点数不能够成一个primitive,那么那个strip将被舍去
- 函数
- 追加顶点:void SteamOutputObject::Append(OutputVertexType v)
- 开始新的三角形带:void SteamOutputObject::RestartStrip()
- 其他同vs和ps
Texture Array
要点
- 在PS中进行Sample的时候,对于Texture2D列表的索引必须是const,所以无法临时更改Sample对象
- 但是使用TextureArray,就可以通过制定z值来索引到
用法
- 制作TextureArray的SRV
- 创建每个Texture:
- 填充 D3DX11_IMAGE_LOAD_INFO
- 从File中读取ID3D11Resource:D3DX11CreateTextureFromFile
- 获取 D3D11_TEXTURE2D_DESC
- 创建TextureArray:
- 根据texture的desc填充desc,ArraySize为所需个数
- 创建ID3D11Texture2D:CreateTexture2D
- 拷贝Texture进入TextureArray:
- 遍历每个texture的每个mip level
- 锁定:context->Map
- 拷贝:context->UpdateSubresource
- 解锁:context->Unmap
- 创建TextureArrayView:
- 填充 D3D11_SHADER_RESOURCE_VIEW_DESC
- device->CreateShaderResourceView
- 获取Texture
- textureArray.Sample(samLinear, float3(tex_uv, tex_idx))
Alpha-to-converage
要点
- 应对大量透明物体时如何选择:Alpha Test ? Alpha to converage ? Alpha Blend ?
- Alpha Test: aliasing严重
- Alpha Blend: 计算量大
- 带MSAA的Alpha Test: 只能对depth stencil引起的边缘进行反走样,不能对alpha test引起的边缘反走样。注意MSAA计算四个子片元时,是对depth stencil引起的显隐进行的平均。
- Alpha to converage解决上述问题
- 纱门透明
- 多种pattern
- 虽然效果肯定没有blend那么好~
用法
- 填充 D3D11_BLEND_DESC: AlphaToConverageEnable=True; BlendEnable=False
- 创建blend state:device->CreateBlendState
Compute Shader
要点
- GPGPU(general purpose GPU)
-
流操作:GPU被设计为对于从单个或者连续位置(流操作streaming operation)读取大量内存的情况有优化,这不同于CPU被设计为针对随机内存读取。
-
大量并行的:而且,因为顶点和像素的处理是互相独立的,所以GPU被架构为大量并行的。例如NVIDIA的Fermi架构支持16个流多处理器(streaming multiprocessor),每个有32个CUDA核心,亦即总共有512个CUDA核心。
- GPGPU(general purpose GPU):使用GPU的以上特性,对非图形的数据并行算法的工作也能很好的运算。
- 数据交换速度:GPU与VRAM > CPU与RAM > CPU与GPU。瓶颈往往在CPU与GPU
- Thread Group:
- 每个multiprocessor中有多个thread group,每个thread group中是多个thread
- thread group中的thread可以使用需要同步(synchronization)操作(operation)来共享内存,不同thread group的thread不能共享内存。
- GPU会把thread group中的多个thread打进包(wrap)中,每个wrap固定容纳32(NV)或64(ATI)个thread,然后交给multiprocessor。为了效率,最好还是把一个thread group里的thread的数量设置为32或64的倍数。
- CS
- 独立于管线之外,它可以和管线混合起来,也可以独立去做GPGPU工作:
- CS中的越界行为是被定义好了的:读取时返回0,写入时无操作。
- 依赖于thread的系统标志值(thread identification system values)
- SV_GroupID(int3): 被声明出的一系列thread group的其中一个的id
- SV_GroupThreadID(int3): 每个group中的一个thread的id
- SV_DispatchThreadID: 在所有group的范围内,每个thread有完全唯一的此dispatch id。即:DispatchThreadID.xyz = groupID.xyz * ThreadGroupSize.xyz + groupThreadID.xyz
- SV_GroupThreadID: 在thread的局部存储中索引
- 线性版本的索引:idx = id.z * size_x * size_y + id.y * size_x + id.z。注意这与CPU中的matrix的索引方式相反。
- 我们可以使用这些id来作为texture的索引用
用法
- 建立一个3D格子的thread group:
- context->Dispatch(UINT threadGroupCountX, ..Y, ..Z)
- 输入类型(SRV):
- Shader Resource View
- Texture2D inputA;
- bindFlag: D3D11_BIND_CONSTANT_BUFFER
- 类型:ID3D11ShaderResourceView
- 创建:ID3DX11EffectShaderResourceVariable
- 绑定:ID3DX11EffectShaderResourceVariable->SetResource
- 输出类型RW(UAV):
- Unordered Access View
- RWTexture2D output;
- bindFlag: D3D11_BIND_UNORDERED_ACCESS|D3D11_BIND_SHADER_RESOURCE
- 类型:ID3D11ShaderResourceView + ID3D11UnorderedAccessView
- 创建:CreateShaderResourceView + CreateUnorderedAccessView
- 绑定:ID3DX11EffectUnorderedAccessViewVariable->SetUnorderedAccessView
- 在HLSL中使用自定义类型的buffer:
- 在CPP中声明一个同类型的Data
- buffer desc:
- ByteWidth = sizeof(Data) * length
- StructureByteStride = sizeof(Data)
- MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
- view desc:
- Format = DXGI_FORMAT_UNKNOWN
- 其他不变
- 指定每个group中的thread个数(3D格子):
- 函数:
- 使用 SV_DispatchThreadID 作为要处理的 texture 的 sample 的 uv,在thread足够多的情况下,即可处理texture上的所有pixel
- 使用 [] 对texture进行索引,或者使用 SampleLevel(而非Sample)来代替。SampleLevel具有mipmap及其插值功能,但要注意它使用的是归一化的坐标范围
- 使用 Consume 和 Append 来从 input texture 中pop一个pixel和向 output texture写入一个pixel
- ComsumeStructuredBuffer / AppendStructuredBuffer
- 注意在创建UAV的view desc时,要指定buffer.Flags 为 D3D11_BUFFER_UAV_FLAG_APPEND
- 注意 Consume 是一次性的。
- 注意 Append 不是自动扩容的。
- 但是因为thread的顺序是无需的,所以这只在当vertex的互相顺序无关紧要时能用。例如粒子。
- 使用 group 内共享的内存 (Shader memory / thread local storage)
- 声明:groupshared float4 gCache[256]
- 最大空间为32kb
- 使用SV_ThreadGroupID来索引
- 过多的使用会导致一个multiprocessor中不能放入多个Thread group,导致并行出现困难,从而降低效率
- 通常的使用情形是存储texture值。比如blur这种需要对一个pixel进行获取(比较慢)很多次的操作。把值全都存入shared memory就可以快速访问pixel了。
- 使用synchronization操作来等待所有thread都加载进shared memory:在load之后加上一句GroupMemoryBarrierWithGroupSync()
- 读取结果:
- 填充 buffer desc 时:
- Usage: D3D11_USAGE_STAGING
- BindFlags: 0
- CPU access: D3D11_CPU_ACCESS_READ
- context->CopyResource:把结果拷贝到 buffer (CPU)
- 声明 D3D11_MAPPED_SUBRESOURCE b
- context->Map(ID3D11Buffer*, 0, D3D11_MAP_READ, 0, &b)
- context->Unmap
Blur例子
除了主线之外也值得注意的几个点
- 高斯模糊是separable的,也就是x和y方向独立,据此,分别做x和y方向的,可以把一次模糊的访问量从n平方减少到n,大大减少了shared memory用量
- 离屏渲染(render-to-off-screen-texture or render-to-texture)
- 先渲染到一个非back buffer的target中,再渲染到back buffer
- 如此一来,在CS中所要用到的CPU access就变为可能了。
- bindFlag: D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS
- 例如:
- 阴影映射(Shadow mapping)
- 空间环境光遮蔽 (Screen Space Ambient Occlusion)
- 使用cube map进行动态反射 (Dynamic reflection with cube maps)
- context的前后缓存切换有间接费用(overhead),应尽可能最小化
- 对于blur,可以使离屏渲染的buffer变为1/4,以减少计算。之后用magnification filter放大回去就是了。
- 如果texture的宽高和Thread的个数不成倍数,那么会有一些不相关的thread混进末尾。我们只能在shader中做好越界检查和钳位了。
- shared memory: 例如横向n像素r半径的blur,需要n+2r的memory。
流程
- 绑定 A 为 SRV (作为input)
- 绑定 B 为 UAV(作为output)
- 设置好thread,进行水平方向的blur
- numGroupX = ceil( mWidth / 256.f )
- device->Dispatch(numGroupX, mHeight, 1)
- device->CSSetShaderResources
- device->CSSetUnorderedAccessViews
- fx->Tech->GetPassByIndex->Apply
- 最后要清空:
- CSSetShaderResources(0,1,nullSRV)
- CSSetUnorderedAccessViews(0,1,nummUAV,0)
- CSSetShader(0,0,0)
- 绑定 B 为 SRV(作为input)
- 绑定 A 为 UAV(作为output)
- 进行垂直方向的blur
- 于是最终A中存储的就是blur后的结果