DirectX11 With Windows SDK 24--Render-To-Texture(RTT)技术的应用、使用ScreenGrab保存纹理到文件
2021-06-30 16:08
标签:spec uuid 源码实现 vat normal scree nim black 还需要 尽管在上一章的动态天空盒中用到了Render-To-Texture技术,但那是针对纹理立方体的特化实现。考虑到该技术的应用层面非常广,在这里抽出独立的一章专门来讲有关它的通用实现以及各种应用。此外,这里还会讲到如何使用DirectXTex的ScreenGrab来保存纹理,可以说是干货满满了。 如果想要看Render-To-Texture在动态天空盒的应用,可以点此回顾: DirectX11 With Windows SDK完整目录 Github项目源码 欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。 在前面的章节中,我们默认的渲染目标是来自DXGI后备缓冲区,它是一个2D纹理。而Render-To-Texture技术,实际上就是使用一张2D纹理作为渲染目标,但一般是自己新建的2D纹理。与此同时,这个纹理还能够绑定到着色器资源视图(SRV)供着色器所使用,即原本用作输出的纹理现在用作输入。 它可以用于: 在这一章,我们将展示下面这三种应用: 该类借鉴了上一章 它具有如下特点: 但如果你想要复制出一份渲染好的纹理,请使用 该方法通过GPU将一份完整的源资源复制到目标资源: 但是需要注意: 现在我们需要完成下面5个步骤: 具体代码如下: 该方法缓存当前渲染管线绑定的渲染目标视图、深度/模板视图以及视口,并替换初始化好的这些资源。注意还需要清空一遍缓冲区: 在对当前纹理的所有绘制方法调用完毕后,就需要调用该方法以恢复到原来的渲染目标视图、深度/模板视图以及视口。若在初始化时还指定了 最后就可以通过 注意:不要将纹理既作为渲染目标,又作为着色器资源,虽然不会报错,但这样会导致程序运行速度被拖累。在VS的输出窗口你可以看到它会将该资源强制从着色器中撤离,置其为NULL,以保证不会同时绑定在输入和输出端。 该效果对应的特效文件为 首先是 然后分别是对于的顶点着色器和像素着色器实现: 该套着色器通过gFadeAmount来控制最终输出的颜色,我们可以通过对其进行动态调整来实现一些效果。当 一开始像素着色器的返回值采用的是和Rastertek一样的 原本以为是输出的文件格式乱了,但当我把Alpha通道关闭后,图片却一切正常了 故这里应该让Alpha通道的值乘上1.0f以保持Alpha通道的一致性 为了实现屏幕的淡入淡出效果,我们需要一张渲染好的场景纹理,即通过 首先我们看 启动程序的时候, 而用户按下 然后在 对了,如果窗口被拉伸,那我们之前创建的纹理宽高就不适用了,需要重新创建一个。在 由于屏幕淡入淡出效果需要先绘制主场景到纹理,然后再用该纹理完整地绘制到屏幕上,就不说前面还进行了大量的深度测试了,两次绘制下来使得在渲染淡入淡出效果的时候帧数下降比较明显。因此不建议经常这么做。 关于小地图的实现,有许多种方式。常见的如下: 可以看出,性能的消耗越往后要求越高。 因为本项目的场景是在夜间森林,并且树是随机生成的,因此采用第二种方式,但是地图可视范围为摄像机可视区域,并且不考虑额外绘制任何2D物件。 小地图对应的特效文件为 首先是 为了能在小地图中绘制出局部区域可视的效果,还需要依赖3D世界中的一些参数。其中 然后是顶点着色器和像素着色器的实现: 接下来我们需要通过Render-To-Texture技术,捕获整个场景的俯视图。关于小地图的绘制放在了 通常小地图的制作,建议是使用正交投影矩阵, 然后如果窗口大小调整,为了保证小地图在屏幕的显示是在右下角,并且保持200x200,需要在 最后是 在这一章的项目新增了来自DirectXTex的 下表给出了常用的GUID: 这里演示了如何保存后备缓冲区纹理到文件: 本项目的场景沿用了第20章的森林场景,并搭配了夜晚雾效,在打开程序后可以看到屏幕淡入的效果,按下Esc后则屏幕淡出后退出。 然后人物在移动的时候,小地图的可视范围也会跟着移动。 DirectX11 With Windows SDK完整目录 Github项目源码 欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。 DirectX11 With Windows SDK 24--Render-To-Texture(RTT)技术的应用、使用ScreenGrab保存纹理到文件 标签:spec uuid 源码实现 vat normal scree nim black 还需要 原文地址:https://www.cnblogs.com/X-Jun/p/9977460.html前言
章节
23 立方体映射:动态天空盒的实现
再述Render-To-Texture技术
TextureRender类
DynamicSkyEffect
的实现,因此也继承了它简单易用的特性:class TextureRender
{
public:
template
Begin
和End
方法,确保在这两个方法调用之间的所有绘制都将输出到该纹理Begin
方法会临时缓存后备缓冲区、深度/模板缓冲区和视口,并在End
方法恢复,因此无需自己去重新设置这些东西ID3D11DeviceContext::CopyResource
方法。不过既然都讲到这份上了,那让我们看下该方法的介绍。ID3D11DeviceContext::CopyResource方法--复制一份资源
void ID3D11DeviceContext::CopyResource(
ID3D11Resource *pDstResource, // [InOut]目标资源
ID3D11Resource *pSrcResource // [In]源资源
);
D3D11_USAGE_IMMUTABLE
创建的目标资源DXGI_FORMAT_R32G32B32_FLOAT
,DXGI_FORMAT_R32G32B32_UINT
和DXGI_FORMAT_R32G32B32_TYPELESS
相互间就可以复制。ID3D11DeviceContext::Map
方法又没有Unmap
)TextureRender初始化
TextureRender::TextureRender(ComPtr
TextureRender::Begin方法--开始对当前纹理进行渲染
void TextureRender::Begin(ComPtr
TextureRender::End方法--结束对当前纹理的渲染,还原状态
generateMips
为true
,还会给该纹理生成mipmap链:void TextureRender::End(ComPtr
TextureRender::GetOutputTexture
方法获取渲染好的纹理了。
屏幕淡入/淡出效果的实现
ScreenFadeEffect.cpp
,着色器文件为ScreenFade_VS.hlsl
和ScreenFade_PS.hlsl
。ScreenFadeEffect
类在这不做讲解,有兴趣的可以查看第13章的自定义Effects管理类实现教程,或者去翻看ScreenFadeEffect
类的源码实现。ScreenFade.hlsli
// ScreenFade.hlsli
Texture2D tex : register(t0);
SamplerState sam : register(s0);
cbuffer CBChangesEveryFrame : register(b0)
{
float gFadeAmount; // 颜色程度控制(0.0f-1.0f)
float3 gPad;
}
cbuffer CBChangesRarely : register(b1)
{
matrix gWorldViewProj;
}
struct VertexPosNormalTex
{
float3 PosL : POSITION;
float3 NormalW : NORMAL;
float2 Tex : TEXCOORD;
};
struct VertexPosHTex
{
float4 PosH : SV_POSITION;
float2 Tex : TEXCOORD;
};
// ScreenFade_VS.hlsl
#include "ScreenFade.hlsli"
// 顶点着色器
VertexPosHTex VS(VertexPosNormalTex vIn)
{
VertexPosHTex vOut;
vOut.PosH = mul(float4(vIn.PosL, 1.0f), gWorldViewProj);
vOut.Tex = vIn.Tex;
return vOut;
}
// ScreenFade_PS.hlsl
#include "ScreenFade.hlsli"
// 像素着色器
float4 PS(VertexPosHTex pIn) : SV_Target
{
return tex.Sample(sam, pIn.Tex) * float4(gFadeAmount, gFadeAmount, gFadeAmount, 1.0f);
}
gFadeAmount
从0到1时,屏幕从黑到正常显示,即淡入效果;而当gFadeAmount
从1到0时,平面从正常显示到变暗,即淡出效果。tex.Sample(sam, pIn.Tex) * gFadeAmount
,但是在截屏出来的.dds文件观看的时候颜色变得很奇怪TextureRender
来实现。GameApp::UpdateScene
方法中用于控制屏幕淡入淡出的部分:// 更新淡入淡出值
if (mFadeUsed)
{
mFadeAmount += mFadeSign * dt / 2.0f; // 2s时间淡入/淡出
if (mFadeSign > 0.0f && mFadeAmount > 1.0f)
{
mFadeAmount = 1.0f;
mFadeUsed = false; // 结束淡入
}
else if (mFadeSign
mFadeSign
的初始值是1.0f
,这样就使得打开程序的时候就在进行屏幕淡入。Esc
键退出的话,则先触发屏幕淡出效果,等屏幕变黑后再发送关闭程序的消息给窗口。注意发送消息到真正关闭还相隔一段时间,在这段时间内也不要关闭淡出效果的绘制,否则最后那一瞬间又突然看到场景了。GameApp::DrawScene
方法中,我们可以将绘制过程简化成这样:// ******************
// 绘制Direct3D部分
//
// 预先清空后备缓冲区
md3dImmediateContext->ClearRenderTargetView(mRenderTargetView.Get(), reinterpret_cast
GameApp::OnResize
方法可以看到:void GameApp::OnResize()
{
// ...
// 摄像机变更显示
if (mCamera != nullptr)
{
// ...
// 屏幕淡入淡出纹理大小重设
mScreenFadeRender = std::make_unique
小地图的实现
MinimapEffect.cpp
,着色器文件为Minimap_VS.hlsl
和Minimap_PS.hlsl
。同样这里只关注HLSL实现。Minimap.hlsli
:// Minimap.hlsli
Texture2D tex : register(t0);
SamplerState sam : register(s0);
cbuffer CBChangesEveryFrame : register(b0)
{
float3 gEyePosW; // 摄像机位置
float gPad;
}
cbuffer CBDrawingStates : register(b1)
{
int gFogEnabled; // 是否范围可视
float gVisibleRange; // 3D世界可视范围
float2 gPad2;
float4 gRectW; // 小地图xOz平面对应3D世界矩形区域(Left, Front, Right, Back)
float4 gInvisibleColor; // 不可视情况下的颜色
}
struct VertexPosNormalTex
{
float3 PosL : POSITION;
float3 NormalL : NORMAL;
float2 Tex : TEXCOORD;
};
struct VertexPosHTex
{
float4 PosH : SV_POSITION;
float2 Tex : TEXCOORD;
};
gRectW
对应的是3D世界中矩形区域(即x最小值, z最大值, x最大值, z最小值)。// Minimap_VS.hlsl
#include "Minimap.hlsli"
// 顶点着色器
VertexPosHTex VS(VertexPosNormalTex vIn)
{
VertexPosHTex vOut;
vOut.PosH = float4(vIn.PosL, 1.0f);
vOut.Tex = vIn.Tex;
return vOut;
}
// Minimap_PS.hlsl
#include "Minimap.hlsli"
// 像素着色器
float4 PS(VertexPosHTex pIn) : SV_Target
{
// 要求Tex的取值范围都在[0.0f, 1.0f], y值对应世界坐标z轴
float2 PosW = pIn.Tex * float2(gRectW.zw - gRectW.xy) + gRectW.xy;
float4 color = tex.Sample(sam, pIn.Tex);
[flatten]
if (gFogEnabled && length(PosW - gEyePosW.xz) / gVisibleRange > 1.0f)
{
return gInvisibleColor;
}
return color;
}
GameApp::InitResource
中:bool GameApp::InitResource()
{
// ...
mMinimapRender = std::make_unique
XMMatrixOrthographicLH
函数的中心在摄像机位置,不以摄像机为中心的话可以用XMMatrixOrthographicOffCenterLH
函数。GameApp::OnResize
重新调整网格模型:void GameApp::OnResize()
{
// ...
// 摄像机变更显示
if (mCamera != nullptr)
{
// ...
// 小地图网格模型重设
mMinimap.SetMesh(md3dDevice, Geometry::Create2DShow(1.0f - 100.0f / mClientWidth * 2, -1.0f + 100.0f / mClientHeight * 2,
100.0f / mClientWidth * 2, 100.0f / mClientHeight * 2));
}
}
GameApp::DrawScene
方法将小地图纹理绘制到屏幕的部分:
UINT strides[1] = { sizeof(VertexPosNormalTex) };
UINT offsets[1] = { 0 };
// 小地图特效应用
mMinimapEffect.SetRenderDefault(md3dImmediateContext);
mMinimapEffect.Apply(md3dImmediateContext);
// 最后绘制小地图
md3dImmediateContext->IASetVertexBuffers(0, 1, mMinimap.modelParts[0].vertexBuffer.GetAddressOf(), strides, offsets);
md3dImmediateContext->IASetIndexBuffer(mMinimap.modelParts[0].indexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
md3dImmediateContext->DrawIndexed(6, 0, 0);
使用ScreenGrab将纹理保存到文件
ScreenGrab.h
和ScreenGrab.cpp
。但为了能保存WIC类别的位图,还需要包含头文件wincodec.h
以使用里面一些关于WIC控件的GUID。ScreenGrab
的函数位于名称空间DirectX
内。SaveDDSTextureToFile函数--以.dds格式保存纹理
HRESULT SaveDDSTextureToFile(
ID3D11DeviceContext* pContext, // [In]设备上下文
ID3D11Resource* pSource, // [In]必须为包含ID3D11Texture2D接口类的指针
const wchar_t* fileName ); // [In]输出文件名
SaveWICTextureToFile函数--以指定WIC型别的格式保存纹理
HRESULT SaveWICTextureToFile(
ID3D11DeviceContext* pContext, // [In]设备上下文
ID3D11Resource* pSource, // [In]必须为包含ID3D11Texture2D接口类的指针
REFGUID guidContainerFormat, // [In]需要转换的图片格式对应的GUID引用
const wchar_t* fileName, // [In]输出文件名
const GUID* targetFormat = nullptr, // [In]忽略
std::function
GUID
文件格式
GUID_ContainerFormatPng
png
GUID_ContainerFormatJpeg
jpg
GUID_ContainerFormatBmp
bmp
GUID_ContainerFormatTiff
tif
// 截屏
if (mKeyboardTracker.IsKeyPressed(Keyboard::Q))
mPrintScreenStarted = true;
// ...
// 若截屏键按下,则分别保存到output.dds和output.png中
if (mPrintScreenStarted)
{
ComPtr
项目演示
上一篇:Windows系统的高效使用
文章标题:DirectX11 With Windows SDK 24--Render-To-Texture(RTT)技术的应用、使用ScreenGrab保存纹理到文件
文章链接:http://soscw.com/essay/99920.html