引擎设计跟踪(九.14.3.1) deferred shading: Depthstencil as GBuffer depth

问题汇总

1.Light support for Editor
编辑器加入了灯光工具, 可以添加和修改灯光.

问题1. light object的用户互交.
point light可以把对应的volume (wireframe sphere/cone)画出来用于用户选中, 但是光源太多的时候, 球就有点凌乱了. 所以使用了HUD, 只要选中HUD就会选中灯光, 只有灯光被选中的时候才显示volume. 另外, 编辑器里面的很多"不可见"的逻辑对象都有这种需求, 虽然现在还没有. 另外, 可以直接拿deferred shading中的geometry复用, 来显示bounding volume helper, 对于方向光, 需要额外加一个箭头作为helper.

问题2: HUD的picking. 之前的物体picking, 是用3D方法, 拿screen sapce的x,y, 取任意的z(不同的z都投影到x,y), unproject到world space, 再拿world sapce的相机坐标, 构建一个ray (这个ray上的所有点投影到屏幕上都是x,y), 然后用3D空间算法拾取bounding box.
而HUD比较特殊, 如果复用之前的3D方法, 反而很麻烦. 需要根据screen rect反算bounding box, 而且world bounding并不能完全紧凑包围screen rect, 需要使用view space的bounding, 用view sapce ray计算.

考虑之后选择直接在screen space做picking, 这样做的结果是多加了一两个接口, 但是实现起来变得很轻松. 需要注意的是, screen space的z也需要保留, 用来排序出最靠前的对象.

问题3: HUD的icon, 直接使用的是编辑器的资源(64x64 png), 并没有离线压缩, 而是实时压缩为BC3 (DXT5). 另外这个icon需要背景, 用美术资源重复合成的话冗余太多, 同时又不想把贴图个数写死(必须是2?), 所以加了一个新的senmatic来获取texture的个数, 这样在shader里面就可以迭代采样了.

void BladeFSMain(
    in float4 pos : POSITION,
    in float2 uv  : TEXCOORD0,
    uniform float4 texutreCount : SAMPLER_COUNT,
    uniform sampler2D hudDiffuse[MAX_DYNAMIC_TEXTURE_COUNT],

    out float4 outColor : COLOR0
    )
{
    outColor = float4(0,0,0,0);
    for(int i = 0; i < texutreCount.x; ++i)
    {
        float4 color = tex2D(hudDiffuse[i],uv.xy);
        outColor = lerp(outColor, color, color.a);
    }
}

3: View Distance v.s. View Depth 
(view distance v.s. view space Z)

View space Z 就是camera distance吗? 两者很多时候可以几乎视为等价, 但并不相同.

首先两者都是线性的, 不同的是, distance相同的点构成了一个球面, 而Z相同的点构成了一个与view dir垂直的plane.

由于view space Z由于是z值, 所以可以用于Z buffering(depth write/test), 即可以写入depth stencil; 而view distance不能用于depth test.

Normalized Linear Depth  Can Be Used As ZBuffer(Depth Testing) Geometry Points of  The Same Value
view Distance  NO sphere
view space Z YES plane

而之前的计划为了节省GBuffer, 是准备用INTZ写入深度, 并作为GBufer的depth来采样, 所以Blade使用的View space Z.

然而, deferred shading 里面:

pos = eye_pos + dir * "depth", 实际上这里的depth是viewDist, 即

pos = eye_pos + dir * viewDistance.

对于直接读取depthstencil来说, 拿到的是view Z而不是view Dist. 这个问题之前没有发现, 因为只实现了方向光, 没有用到position.

简单分析可以得出|viewDistance| = |ViewZ| / cos(θ) = |viewZ| / dot(viewDir, dir).

Construct position In View space:
right handed: viewDir = (0,0,-1) 
viewZ = tex2D(depthINTZ, uv).r; //normalized linear depth
viewPos = viewDir * (viewZ * farClipDist) / -viewDir.z ;

Construct position In World space:
viewZ = tex2D(depthINTZ, uv).r;
worldPos = worldEyePos + worldDir * (viewZ*farClipDist) / dot( worldLookatDir, worldDir);

这样就可以使用depthstencil作为Gbuffer的depth了. 既可以做depth test, 又能用来构建顶点位置坐标. 这样就不用把normal和depth压缩后挤在同一个buffer,

甚至在使用24位normal时, 还空出一个通道. 目前这个通道用来保存specular的power.

目前Blade的GBuffer如下:

Component Format Attachment Usage
Color A8R8G8B8 MRT color 0 Diffuse:rgb, SpecularLevel:a
Normal A8R8G8B8  MRT color 1 WorldNormal:rgb, Specular exponent:a
Depth INTZ  depthstencil normalized View space Z ZBuffer depth, converted to view space

Blade 一开始在pixel shader里将normalized view space z输出, 后来因为效率不高, 所以改成了vertex shader里输出常规的z, 然后在deffered shading里根据zbuffer解算view sapce z:

viewZ = convertDepthToViewSpace( tex2D(depthINTZ, uv).r );
worldPos = worldEyePos + worldDir * (viewZ*farClipDist) / dot( worldLookatDir, worldDir);

关于convertDepthToViewSpace的计算:

前面已经记录了一个链接, 这里简单记录一下思路. 根据projection matrix
projectedZ = f(viewZ) (linear conversion to [0, zfar] )
projectedW = -viewZ (right handed)

在perspective divide (z/w) 以后:
NDCz = -f(viewZ)/viewZ    ranges [-1,1] (OGL) or [0,1] (D3D)

即NDCz = g(z) = a/z + b  (non-linear)

其中b = prjoectionMatrix33 = projectionMatrix[2][2], a = projectMatrix34 = projectionMatrix[3][2]

如果忽略viewport 的depth range, 可以认为最终depth buffer里面存储的是NDC space的z,

现在带入projection matrix的参数, 反向计算viewZ = g-1(zbuffer) = a / (zbuffer + b)

只要在CPU端计算好a,b, 在shader里面计算出view space Z,就可以了. 也可以使用invertedProjectionMatrix来直接求出viewSpaceZ.

目前只是尝试阶段, 还没有发现问题, 比如可能的精度不够等等的可能情况(之前使用linear depth就是为了精度问题), 后面再继续完善.

如果精度不够用的话, 可能就需要将view distance直接渲染到MRT里, 不采样depthstencil了.

需要注意的是, 并不是所有SM3.0都支持INTZ, 只有G80以上才支持. 为了简化管线, 在不支持INTZ的显卡上, 直接使用forward shading.

目前Blade支持的shader model有2_0, 2_x, 3_0. 在shader model为3.0的时候, 检测INTZ的支持, 如果不支持,则设置为2_x, 这个细节备忘下, 后面再完善.

其他问题

Light Volume: Calculate Screen UV in Vertex Shader:

只有方向光的quad比较特殊, 可以这么做, shpere和cone是不行的.尝试过了不对. 详细见这里: http://gamedev.stackexchange.com/questions/63870/computing-pixels-screen-position-in-a-vertex-shader-right-or-wrong

UV scale

由于默认backbuffer是desktop的大小, 所以depthstencil创建时也是这么大. 然而使用时是实际窗口的大小, 需要根据viewport的pixel size(或window size)和depthstencil的size, 计算UV的scale, 只采样部分区域, 同时apply half pixel offset, 这个UV的scale和offset在CPU计算并传入pxiel shader.

后面准备完善spot light, 并加上stencil mask.

时间: 2024-10-05 23:14:02

引擎设计跟踪(九.14.3.1) deferred shading: Depthstencil as GBuffer depth的相关文章

引擎设计跟踪(九.14.3.2) Deferred shading的后续实现和优化

最近完成了deferred shading和spot light的支持, 并作了一部分优化. 之前forward shading也只支持方向光, 现在也支持了点光源和探照光. 对于forward shading, 可以在渲染每个对象之前, 用对象的包围盒, 查询空间内的光源, 然后填入shader cosntant里. 因为空间一般是基于四叉树或者八叉树的划分, 所以查询不会慢.现在透明物体也能通过forward shading 正常光照了. Deferred shading optimizat

引擎设计跟踪(九.14) 更新记录和骨骼动画导出

骨骼动画是去年打算写的部分, 但是中间因为工作太忙, 已经拖了一年了. 期间也加了其他东西, 比如对UI做了部分完善.UI对toolbar button添加了drop down 支持, 一种是dropdown menu, 一种是dropdown property sheet 实现这些控件不难, 但是要做抽象和复用, 接口设计稍微有点复杂. 现在可以把一个IConfig对象绑定到toolbar的button里了. 这样保存这些配置的时候,直接使用IConfig接口就可以了.贴一个编辑器的配置文件,

引擎设计跟踪(九.14.2a) 导出插件问题修复和 Tangent Space 裂缝修复

由于工作很忙, 近半年的业余时间没空搞了, 不过工作马上忙完了, 趁十一有时间修了一些小问题. 这次更新跟骨骼动画无关, 修复了一个之前的, 关于tangent space裂缝的问题: 引擎设计跟踪(九) 3DS MAX 导出插件 引擎设计跟踪(九.10) Max插件更新,地形问题备忘 这里说明一下修复方法, 并且做一个总结. 之前的做法都不算错, 但是不完善. 这里有缝, 主要是因为那个战争机器3的模型本身已经复制了顶点( 左半部分和右半部分是不同的mesh, 有重合的顶点), 接缝处的顶点虽

引擎设计跟踪(九.14.2 final) Inverse Kinematics: CCD 在Blade中的应用

因为工作忙, 好久没有记笔记了, 但是有时候发现还得翻以前的笔记去看, 所以还是尽量记下来备忘. 关于IK, 读了一些paper, 觉得之前翻译的那篇, welman的paper (http://graphics.ucsd.edu/courses/cse169_w04/welman.pdf  摘译:http://www.cnblogs.com/crazii/p/4662199.html) 非常有用, 入门必读. 入门了以后就可以结合工程来拓展了. 先贴一下CCD里面一个关节的分析: 当Pic的方

引擎设计跟踪(九.14.2f) 最近更新: OpenGL ES &amp; tools

之前骨骼动画的IK暂时放一放, 最近在搞GLES的实现. 之前除了GLES没有实现, Android的代码移植已经完毕: [原]跨平台编程注意事项(三): window 到 android 的 移植 总的来说上次移植的改动不是很大, 主要是DLL与.so之间的调整和适配, 还有些C++标准相关的编译错误. 数据包的加载/初始化/配置文件和插件的加载测试可用了, 但GLES没有实现, 所以上次的移植只能在真机上空跑. 最近想在业余时间抽空把GLES的空白填上, 目前接口调整差不多了, GLES r

引擎设计跟踪(九.14.3.4) mile stone 2 - model和fbx导入的补漏

之前milestone2已经做完的工作, 现在趁有时间记下笔记. 1.设计 这里是指兼容3ds max导出/fbx格式转换等等一系列工作的设计. 最开始, Blade的3dsmax导出插件, 全部代码都是写在导出的DLL里面的, 后来考虑到FBX等等其他格式, 现在把模块分成两部分: Model/Anim Collector: 预定义的接口, 用于收集其他模型的相关数据. 用户负责扩展实现, 比如FBXCollector, MaxCollector, 或者其他格式. Model/Anim Bui

引擎设计跟踪(九.14.2b) 骨骼动画基本完成

首先贴一个介绍max的sdk和骨骼动画的文章, 虽然很早的文章, 但是很有用, 感谢前辈们的贡献: 3Ds MAX骨骼动画导出插件编写 1.Dual Quaternion 关于Dual Quaternion, 这里不做太详细的介绍了,贴出来几个链接吧: http://en.wikipedia.org/wiki/Dual_quaternion http://www.seas.upenn.edu/~ladislav/kavan08geometric/kavan08geometric.pdf http

引擎设计跟踪(九.14.2i) Android GLES 3.0 完善

最近把渲染设备对应的GLES的API填上了. 主要有IRenderDevice/IShader/ITexture/IGraphicsResourceManager/IIndexBuffer/IVertexBuffer.都是体力活, 根据文档(https://www.khronos.org/opengles/sdk/docs/man3/)填上对应的API就可了.遇到的问题纪录在下面: Stick to the standard C++standard并没有要求char必须是unsignedtype

引擎设计跟踪(九.14.2g) 将GNUMake集成到Visual Studio

最近在做纹理压缩工具, 以及数据包的生成. shader编译已经在vs工程里面了, 使用custom build tool, build命令是调用BladeShaderComplier, 并且每个文件对应一个输出, vs会自动检查工程里面文件的依赖, 这样很方便. 纹理压缩如果也要放在visual studio里面, 可以用build event或者custom build step来做, 但是build dependency很难处理, 比如每个原始贴图对应一张目标贴图, 如果像编译shader