【寒江雪】模板技术实现镜面特效

来源:互联网 发布:excel怎么对比数据 编辑:程序博客网 时间:2024/06/05 02:58

  在Direct3D中使用模板缓存,可以实现很多特效。比如镜面特效,阴影等.
  关于模板缓存的概念,我在Direct3D9的学习笔记中有记录。这里直接切入正题,讲讲如何在Direct3D11中创建并使用模板缓存

创建模板缓存

  模板缓存的创建是跟随者深度缓存一并创建的。也就是说,创建深度缓存的时候顺便创建模板缓存。
  创建深度缓存,首先需要描述它,描述深度缓存的同时,顺便描述一下模板缓存。描述深度缓存使用D3D11_TEXTURE2D_DESC结构体。然后使用设备指针调用CreateTexture2D方法即可完成深度,模板缓存的创建。

创建深度模板视图

  深度模板缓存创建好之后保存在ID3D11Texture2D指向的内存区。要创建深度模板视图,需要调用设备接口的CreateDepthStencilView方法,将创建好的视图存在内存中,通过接口ID3D11DepthStencilView操作。

设置深度模板视图

  深度模板视图创建好之后,我们在设置目标渲染视图的同时,顺便设置深度模板视图,设置视图是通过调用立即执行上下文接口的OMSetRenderTargets方法完成的。

创建深度模板视图的具体步骤

  1. 描述深度模板缓存
      描述深度模板缓存使用D3D11_TEXTURE2D_DESC结构体完成。该结构体的组成如下:
typedef struct D3D11_TEXTURE2D_DESC {  UINT             Width;  UINT             Height;  UINT             MipLevels;  UINT             ArraySize;  DXGI_FORMAT      Format;  DXGI_SAMPLE_DESC SampleDesc;  D3D11_USAGE      Usage;  UINT             BindFlags;  UINT             CPUAccessFlags;  UINT             MiscFlags;} D3D11_TEXTURE2D_DESC;
  • UINT Width:指纹理宽度,不过这里指窗口宽度
  • UINT Height:指纹理高度,这里指窗口高度
  • UINT MipLevels:纹理映射级数,多重采样则填1。要生成子纹理的全集,填0。这里设置为1即可
  • UINT ArraySize:纹理数组中纹理的数目,至少为1
  • DXGI_FORMAT Format:设置数据格式,在这里可以选择的数据格式包括:DXGI_FORMAT_D32_FLOAT_S8X24_UINT和 DXGI_FORMAT_D24_UNORM_S8_UINT。
    • DXGI_FORMAT_D32_FLOAT_S8X24_UINT:该格式中,每个像素为8字节(64位),其中深度值占32位,为float型。模板值为8位,为位于[0,255]中的整型,后面24位无任何用途,纯对齐用;
    • DXGI_FORMAT_D24_UNORM_S8_UINT:该格式中,每个像素为4字节(32位),其中深度值占24位,并映射到[0,1]之间。模板值为8位,为位于[0,255]中的整型;
  • DXGI_SAMPLE_DESC SampleDesc:对纹理多重采样的描述。这里不使用多重纹理。
  • D3D11_USAGE Usage:定义纹理读写方式,填D3D11_USAGE_DEFAULT即可
  • UINT BindFlags:该标识符定义如何将纹理绑定到渲染管线中。这里填D3D11_BIND_DEPTH_STENCIL。表示将纹理绑定为深度模板的输出目标
  • UINT CPUAccessFlags:定义如何访问CPU,用不到则置零
  • UINT MiscFlags:定义附加选项的标识,没有则置零
      下面是一个使用示例:
D3D11_TEXTURE2D_DESC dsDesc;    dsDesc.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;  //这里表示24位用于深度缓存,8位用于模板缓存    dsDesc.Width = 800;                             //深度模板缓存的宽度    dsDesc.Height = 600;                            //深度模板缓存的高度    dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;    //绑定标识符    dsDesc.MipLevels = 1;    dsDesc.ArraySize = 1;    dsDesc.CPUAccessFlags = 0;                      //CPU访问标识符,0为默认值    dsDesc.SampleDesc.Count = 1;                    //多重采样的属性,本例中不采用多重采样即,    dsDesc.SampleDesc.Quality = 0;                  //所以Count=1,Quality=0    dsDesc.MiscFlags = 0;    dsDesc.Usage = D3D11_USAGE_DEFAULT;
  1. 创建缓存
      描述完成后,通过设备接口指针调用CreateTexture2D方法创建缓存。该方法的原型如下:
HRESULT CreateTexture2D(  [in]            const D3D11_TEXTURE2D_DESC   *pDesc,  [in, optional]  const D3D11_SUBRESOURCE_DATA *pInitialData,  [out, optional]       ID3D11Texture2D        **ppTexture2D);
  • const D3D11_TEXTURE2D_DESC *pDesc:很明显就是刚才我们填的结构体的指针.
  • const D3D11_SUBRESOURCE_DATA *pInitialData:该指针指向一个D3D11_SUBRESOURCE_DATA的数组。关于D3D11_SUBRESOURCE_DATA的描述,可以参考MSDN。这里设为NULL即可。
  • ID3D11Texture2D **ppTexture2D:最终输出的缓存。
      调用示例:
ID3D11Texture2D depthStencilBuffer;hr = (*device)->CreateTexture2D(&dsDesc, 0, depthStencilBuffer);
  1. 创建深度模板视图
      创建深度模板视图需要调用设备接口中的CreateDepthStencilView方法。该方法的原型如下:
HRESULT CreateDepthStencilView(  [in]                  ID3D11Resource                *pResource,  [in, optional]  const D3D11_DEPTH_STENCIL_VIEW_DESC *pDesc,  [out, optional]       ID3D11DepthStencilView        **ppDepthStencilView);
  • ID3D11Resource *pResource:该指针指向将要被保存为深度模板视图的资源,就是刚才我们创建的ID3D11Texture2D接口
  • const D3D11_DEPTH_STENCIL_VIEW_DESC *pDesc:该指针是一个指向D3D11_DEPTH_STENCIL_VIEW_DESC结构体的指针,该结构体描述了深度缓存视图中可访问的纹理子资源。在这里不需要,设为NULL
  • ID3D11DepthStencilView **ppDepthStencilView:指向深度模板缓存视图接口的指针,我们调用这个方法就是为了初始化这个参数。
      下面是一个调用示例:
ID3D11DepthStencilView** depthStencilView;hr = (*device)->CreateDepthStencilView(*depthStencilBuffer, 0, depthStencilView);
  1. 设置视图
      设置视图就是调用立即执行上下文接口的OMSetRenderTargets方法就可以了。下面是调用示例:
(*immediateContext)->OMSetRenderTargets(1,                   //绑定的目标视图的个数        renderTargetView,    //渲染目标视图,InitD3D函数传递的实参        *depthStencilView);  //绑定模板

创建渲染状态

  在使用模板绘图的时候,可能需要修改一些混合状态。绘制完成后又把状态恢复。所以状态的描述应该提前保存起来。
  创建混合状态之前,都需要对其进行描述,描述混合状态使用D3D11_BLEND_DESC结构体来完成。该结构体在混合技术笔记中有提到.
  除了混合状态之外,还有深度模板状态。该状态定义比较函数,写掩码等等参数。使用D3D11_DEPTH_STENCIL_DESC描述.

禁止颜色写入

  有时候,在渲染的过程中,我们只希望修改深度模板缓冲区部分,而且希望保持后缓冲区中的颜色值,这时候,我们就需要禁止颜色写入。
  有两种方法可以禁止颜色的写入。
  其中一种是把源混合因子设为D3D11_BLEND_ZERO,把目标因子设为D3D11_BLEND_ONE,这样混合方程中源部分相乘结果为0,目标部分相乘后结果仍为原样。
  另外一种方法是直接把描述中的RenderTargetWriteMask设为0,即任何一位都无法写入
  下面是一个示例:

//禁止写颜色    D3D11_BLEND_DESC noColorWriteBlendDesc;               //混合状态的描述    noColorWriteBlendDesc.AlphaToCoverageEnable = false;  //关闭AlphaToCoverage多重采样技术    noColorWriteBlendDesc.IndependentBlendEnable = false; //不针对多个RenderTarget使用不同的混合状态    noColorWriteBlendDesc.RenderTarget[0].BlendEnable = false;  //不开启融合    noColorWriteBlendDesc.RenderTarget[0].RenderTargetWriteMask = 0;   //写掩码为0,即禁止写入颜色

  描述完成后调用设备接口的CreateBlendState方法完成创建.

D3D11_DEPTH_STENCIL_DESC结构体

  该结构体定义如下:

typedef struct D3D11_DEPTH_STENCIL_DESC {  BOOL                       DepthEnable;  D3D11_DEPTH_WRITE_MASK     DepthWriteMask;  D3D11_COMPARISON_FUNC      DepthFunc;  BOOL                       StencilEnable;  UINT8                      StencilReadMask;  UINT8                      StencilWriteMask;  D3D11_DEPTH_STENCILOP_DESC FrontFace;  D3D11_DEPTH_STENCILOP_DESC BackFace;} D3D11_DEPTH_STENCIL_DESC;
  • BOOL DepthEnable:该参数表示是否开启深度测试。需要开启则设为TRUE
  • D3D11_DEPTH_WRITE_MASK DepthWriteMask:深度缓存的写掩码,表示哪些位可以写入深度缓存中,该枚举类型只有D3D11_DEPTH_WRITE_MASK_ZERO和D3D11_DEPTH_WRITE_ALL两个,即要么不写,要写就全写的选择。
  • D3D11_COMPARISON_FUNC DepthFunc:深度值比较函数,该参数定义了深度测试的操作符。具体可以参考MSDN中对该枚举体的描述。
  • BOOL StencilEnable:表示是否开启模板测试
  • UINT8 StencilReadMask:模板的读掩码。这里使用默认的读掩码即可——D3D11_DEFAULT_STENCIL_READ_MASK
  • UINT8 StencilWriteMask:模板的写掩码。这里使用默认的写掩码即可——D3D11_DEFAULT_STENCIL_WRITE_MASK
  • D3D11_DEPTH_STENCILOP_DESC FrontFace:该结构体描述正面模板测试的相关信息。该结构体的内容如下
    • D3D11_DEPTH_STENCILOP_DESC

      typedef struct D3D11_DEPTH_STENCILOP_DESC {
      D3D11_STENCIL_OP StencilFailOp;
      D3D11_STENCIL_OP StencilDepthFailOp;
      D3D11_STENCIL_OP StencilPassOp;
      D3D11_COMPARISON_FUNC StencilFunc;
      } D3D11_DEPTH_STENCILOP_DESC;
    • D3D11_STENCIL_OP StencilFailOp:模板测试失败后的操作
    • D3D11_STENCIL_OP StencilDepthFailOp:模板测试成功后,深度测试失败时的操作
    • D3D11_STENCIL_OP StencilPassOp:模板测试成功且深度测试也成功后的操作
    • D3D11_COMPARISON_FUNC StencilFunc:比较函数
  • D3D11_DEPTH_STENCILOP_DESC BackFace:该结构体描述背面模板测试的相关信息。与正面模板测试相关信息相似。
      下面是一个调用示例:
///填充镜子区域    D3D11_DEPTH_STENCIL_DESC markMirrorDSSDesc;      //深度模板描述    markMirrorDSSDesc.DepthEnable = true;                                 //开启深度测试    markMirrorDSSDesc.DepthFunc = D3D11_COMPARISON_LESS;                  //深度测试比较为“小于”    markMirrorDSSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;       //禁止将深度值写入深度模板缓存    markMirrorDSSDesc.StencilEnable = true;                               //开启模板测试    markMirrorDSSDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;  //设置默认读掩码    markMirrorDSSDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;//设置默认写掩码    markMirrorDSSDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;    //通过模板测试的条件为“总是”    markMirrorDSSDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; //通过测试进行替换操作    markMirrorDSSDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;//模板通过,深度失败则保持不变    markMirrorDSSDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;     //模板测试失败则保持不变    markMirrorDSSDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;      //通过模板测试的条件为“总是”    markMirrorDSSDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;   //通过测试进行替换操作    markMirrorDSSDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; //测试通过,深度失败则保持不变    markMirrorDSSDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;      //模板测试失败则保持不变

  这些渲染状态通过调用设备接口的CreateDepthStencilState方法来完成。

更多的渲染状态描述这里就不阐述了。

绘制镜子中的箱子

  下面通过实例讲解如何绘制镜子中的箱子.
  创建深度模板视图就不写出来了。把主要的步骤写出来即可.

创建 半透明效果 的渲染状态

//半透明效果    D3D11_BLEND_DESC blendDesc;                      ZeroMemory(&blendDesc, sizeof(blendDesc));   //清零操作    blendDesc.AlphaToCoverageEnable = false;     //关闭AlphaToCoverage多重采样技术    blendDesc.IndependentBlendEnable = false;    //不针对多个RenderTarget使用不同的混合状态    //只针对RenderTarget[0]设置绘制混合状态,忽略1-7    blendDesc.RenderTarget[0].BlendEnable = true;                   //开启混合    blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;     //设置源因子    blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;//设置目标因子    blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;         //混合操作    blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;      //源混合百分比因子    blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;    //目标混合百分比因子    blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;    //混合百分比的操作    blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;  //写掩码    //创建ID3D11BlendState接口    device->CreateBlendState(&blendDesc, &blendStateAlpha);

创建 关闭背面消隐 的渲染状态

//关闭背面消隐    D3D11_RASTERIZER_DESC ncDesc;        //光栅器描述    ZeroMemory(&ncDesc,sizeof(ncDesc));  //清零操作    ncDesc.CullMode = D3D11_CULL_NONE;   //剔除特定朝向的三角形,这里不剔除,即全部绘制    ncDesc.FillMode = D3D11_FILL_SOLID;  //填充模式,这里为利用三角形填充    ncDesc.FrontCounterClockwise = false;//是否设置逆时针绕续的三角形为正面    ncDesc.DepthClipEnable = true;       //开启深度裁剪    //创建一个关闭背面消隐的状态,在需要用的时候才设置给设备上下文    if(FAILED(device->CreateRasterizerState(&ncDesc,&NoCullRS)))    {        MessageBox(NULL,L"Create 'NoCull' rasterizer state failed!",L"Error",MB_OK);        return false;    }

创建 描述逆时针为正面 的渲染状态

//设置逆时针为正面    D3D11_RASTERIZER_DESC ccfDesc;         //光栅器描述    ZeroMemory(&ccfDesc,sizeof(ccfDesc));  //清零操作    ccfDesc.CullMode = D3D11_CULL_BACK;    //不绘制背面    ccfDesc.FillMode = D3D11_FILL_SOLID;   //填充模式,这里为利用三角形填充    ccfDesc.FrontCounterClockwise = true;  //设置逆时针绕续的三角形为正面    ccfDesc.DepthClipEnable = true;        //开启深度裁剪    if(FAILED(device->CreateRasterizerState(&ccfDesc,&counterClockFrontRS)))    {        MessageBox(NULL,L"Create 'NoCull' rasterizer state failed!",L"Error",MB_OK);        return false;    }

创建 禁止颜色 的渲染状态

//禁止写颜色    D3D11_BLEND_DESC noColorWriteBlendDesc;               //混合状态的描述    noColorWriteBlendDesc.AlphaToCoverageEnable = false;  //关闭AlphaToCoverage多重采样技术    noColorWriteBlendDesc.IndependentBlendEnable = false; //不针对多个RenderTarget使用不同的混合状态    noColorWriteBlendDesc.RenderTarget[0].BlendEnable = false;  //不开启融合    noColorWriteBlendDesc.RenderTarget[0].RenderTargetWriteMask = 0;   //写掩码为0,即禁止写入颜色    if(FAILED(device->CreateBlendState(&noColorWriteBlendDesc,&noColorWriteBS)))    {        MessageBox(NULL,L"Create 'No Color Write' blend state failed!",L"Error",MB_OK);        return false;    }

创建 镜子区域绘深度模板测试 的渲染状态

//填充镜子区域    D3D11_DEPTH_STENCIL_DESC markMirrorDSSDesc;      //深度模板描述    markMirrorDSSDesc.DepthEnable = true;                                 //开启深度测试    markMirrorDSSDesc.DepthFunc = D3D11_COMPARISON_LESS;                  //深度测试比较为“小于”    markMirrorDSSDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;       //禁止将深度值写入深度模板缓存    markMirrorDSSDesc.StencilEnable = true;                               //开启模板测试    markMirrorDSSDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;  //设置默认读掩码    markMirrorDSSDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;//设置默认写掩码    markMirrorDSSDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;    //通过模板测试的条件为“总是”    markMirrorDSSDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; //通过测试进行替换操作    markMirrorDSSDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;//模板通过,深度失败则保持不变    markMirrorDSSDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;     //模板测试失败则保持不变    markMirrorDSSDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;      //通过模板测试的条件为“总是”    markMirrorDSSDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;   //通过测试进行替换操作    markMirrorDSSDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; //测试通过,深度失败则保持不变    markMirrorDSSDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;      //模板测试失败则保持不变    if(FAILED(device->CreateDepthStencilState(&markMirrorDSSDesc,&markMirrorDSS)))    {        MessageBox(NULL,L"Create 'MarkMirror' depth stencil state failed!",L"Error",MB_OK);        return false;    }

创建 绘制镜子中的物体时深度模板测试 的渲染状态

//绘制镜子中物体    D3D11_DEPTH_STENCIL_DESC drawRefDesc;    drawRefDesc.DepthEnable = true;//开启深度测试    drawRefDesc.DepthFunc = D3D11_COMPARISON_LESS;//深度测试比较为“小于”    drawRefDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;//允许将深度值写入深度模板缓存    drawRefDesc.StencilEnable = true;//开启模板测试    drawRefDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;//设置默认读掩码    drawRefDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;//设置默认写掩码    drawRefDesc.FrontFace.StencilFunc = D3D11_COMPARISON_EQUAL;//通过模板测试的条件为“等于”    drawRefDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;//通过测试进行保持操作    drawRefDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;//模板通过,深度失败则保持不变    drawRefDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;//模板测试失败则保持不变    drawRefDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;//通过模板测试的条件为“总是”    drawRefDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE;//通过测试进行替换操作    drawRefDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;//测试通过,深度失败则保持不变    drawRefDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;//模板测试失败则保持不变    if(FAILED(device->CreateDepthStencilState(&drawRefDesc,&drawReflectionDSS)))    {        MessageBox(NULL,L"Create 'DrawReflection' depth stencil state failed!",L"Error",MB_OK);        return false;    }

完整示例代码


https://coding.net/u/HJXCoding/p/MirrorEffect/git




Copyright© by 寒江雪
Date:2017.1.11

0 0
原创粉丝点击