前言
上一章的静态天空盒已经可以满足绝大部分日常使用了。但对于自带反射/折射属性的物体来说,它需要依赖天空盒进行绘制,但静态天空盒并不会记录周边的物体,更不用说正在其周围运动的物体了。因此我们需要在运行期间构建动态天空盒,将周边物体绘制入当前的动态天空盒。
没了解过静态天空盒的读者请先移步到下面的链接:
章节回顾 |
---|
22 立方体映射:静态天空盒的读取与实现 |
DirectX11 With Windows SDK完整目录
欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。
动态天空盒
现在如果我们要让拥有反射/折射属性的物体映射其周围的物体和天空盒的话,就需要在每一帧重建动态天空盒,具体做法为:在每一帧将摄像机放置在待反射/折射物体中心,然后沿着各个坐标轴渲染除了自己以外的所有物体及静态天空盒共六次,一次对应纹理立方体的一个面。这样绘制好的动态天空盒就会记录下当前帧各物体所在的位置了。
但是这样做会带来非常大的性能开销,加上动态天空盒后,现在一个场景就要渲染七次,对应七个不同的渲染目标!如果要使用的话,尽可能减少所需要用到的动态天空盒数目。对于多个物体来说,你可以只对比较重要,关注度较高的反射/折射物体使用动态天空盒,其余的仍使用静态天空盒,甚至不用。毕竟动态天空盒也不是用在场景绘制,而是在物体上,可以不需要跟静态天空盒那样大的分辨率,通常情况下设置到256x256即可.
资源视图(Resource Views)回顾
由于动态天空盒的实现同时要用到渲染目标视图(Render Target View)、深度模板视图(Depth Stencil View)和着色器资源视图(Shader Resource View),这里再进行一次回顾。
由于资源(ID3D11Resource
)本身的类型十分复杂,比如一个ID3D11Texture2D
本身既可以是一个纹理,也可以是一个纹理数组,但纹理数组在元素个数为6时有可能会被用作立方体纹理,就这样直接绑定到渲染管线上是无法确定它本身究竟要被用作什么样的类型的。比如说作为着色器资源,它可以是Texture2D
, Texture2DArray
, TextureCube
的任意一种。
因此,我们需要用到一种叫资源视图(Resource Views)的类型,它主要有下面4种功能:
- 绑定要使用的资源
- 解释该资源具体会被用作什么类型
- 指定该资源的元素范围,以及纹理的子资源范围
- 说明该资源最终在渲染管线上的用途
渲染目标视图用于将渲染管线的运行结果输出给其绑定的资源,即仅能设置给输出合并阶段。这意味着该资源主要用于写入,但是在进行混合操作时还需要读取该资源。通常渲染目标是一个二维的纹理,但它依旧可能会绑定其余类型的资源。这里不做讨论。
深度/模板视图同样用于设置给输出合并阶段,但是它用于深度测试和模板测试,决定了当前像素是通过还是会被抛弃,并更新深度/模板值。它允许一个资源同时绑定到深度模板视图和着色器资源视图,但是两个资源视图此时都是只读的,深度/模板视图也无法对其进行修改,这样该纹理就还可以绑定到任意允许的可编程着色器阶段上。如果要允许深度/模板缓冲区进行写入,则应该取消绑定在着色器的资源视图。
着色器资源视图提供了资源的读取权限,可以用于渲染管线的所有可编程着色器阶段中。通常该视图多用于像素着色器阶段,但要注意无法通过着色器写入该资源。
DynamicSkyRender类
该类继承自上一章的SkyRender类,用以支持动态天空盒的相关操作。
class DynamicSkyRender : public SkyRender
{
public:
DynamicSkyRender(ComPtr<ID3D11Device> device,
ComPtr<ID3D11DeviceContext> deviceContext,
const std::wstring& cubemapFilename,
float skySphereRadius, // 天空球半径
int dynamicCubeSize, // 立方体棱长
bool generateMips = false); // 默认不为静态天空盒生成mipmaps
// 动态天空盒必然生成mipmaps
DynamicSkyRender(ComPtr<ID3D11Device> device,
ComPtr<ID3D11DeviceContext> deviceContext,
const std::vector<std::wstring>& cubemapFilenames,
float skySphereRadius, // 天空球半径
int dynamicCubeSize, // 立方体棱长
bool generateMips = false); // 默认不为静态天空盒生成mipmaps
// 动态天空盒必然生成mipmaps
// 缓存当前渲染目标视图
void Cache(ComPtr<ID3D11DeviceContext> deviceContext, BasicEffect& effect);
// 指定天空盒某一面开始绘制,需要先调用Cache方法
void BeginCapture(ComPtr<ID3D11DeviceContext> deviceContext, BasicEffect& effect, D3D11_TEXTURECUBE_FACE face,
const DirectX::XMFLOAT3& pos, float nearZ = 1e-3f, float farZ = 1e3f);
// 恢复渲染目标视图及摄像机,并绑定当前动态天空盒
void Restore(ComPtr<ID3D11DeviceContext> deviceContext, BasicEffect& effect, const Camera& camera);
// 获取动态天空盒
// 注意:该方法只能在
ComPtr<ID3D11ShaderResourceView> GetDynamicTextureCube();
// 获取当前用于捕获的天空盒
const Camera& GetCamera() const;
private:
void InitResource(ComPtr<ID3D11Device> device, int dynamicCubeSize);
private:
ComPtr<ID3D11RenderTargetView> mCacheRTV; // 临时缓存的后备缓冲区
ComPtr<ID3D11DepthStencilView> mCacheDSV; // 临时缓存的深度/模板缓冲区
FirstPersonCamera mCamera; // 捕获当前天空盒其中一面的摄像机
ComPtr<ID3D11DepthStencilView> mDynamicCubeMapDSV; // 动态天空盒渲染对应的深度/模板视图
ComPtr<ID3D11Shad