DirectX11 With Windows SDK-13 Abandons FX11 and Preliminary Implementation of BasicManager Class

Keywords: C++ SDK github Windows

Preface

DirectX11 With Windows SDK complete directory: http://www.cnblogs.com/X-Jun/p/9028764.html

So far, none of the tutorial projects has used the Effects11 framework class to draw scenes. Because in the D3D Compile API (#47) version, if you try to compile the fx_5_0 effect file, you will receive such a warning:
X4717: Effects deprecated for D3DCompiler_47

In future versions, D3D Compiler may stop supporting FX11, so we need to manage all kinds of special effects ourselves and use the HLSL compiler to compile each shader. Refer to the previous tutorials in this series:

Course
01 DirectX11 initialization
Render a triangle
Render a cube

This tutorial also mentions the use of depth/template state to achieve simple shadowing effects, but does not go into mathematical formulas.

Project source point here: https://github.com/MKXJun/DX11-Without-DirectX-SDK

Review the RenderStates class

The RenderStates class currently stores a variety of commonly used states. In the original framework of Effects11, various rendering states can be initialized in fx files and set to Technique11. But now we can only create all kinds of rendering states at one time in the C++ code layer:

class RenderStates
{
public:
    template <class T>
    using ComPtr = Microsoft::WRL::ComPtr<T>;

    static void InitAll(const ComPtr<ID3D11Device>& device);
    // Using ComPtr does not require manual release

public:
    static ComPtr<ID3D11RasterizerState> RSWireframe;       // Grating state: wireframe mode
    static ComPtr<ID3D11RasterizerState> RSNoCull;          // Grating State: Backless Cutting Mode
    static ComPtr<ID3D11RasterizerState> RSCullClockWise;   // Grating State: Clockwise Cutting Mode

    static ComPtr<ID3D11SamplerState> SSLinearWrap;         // Sampler state: linear filtering
    static ComPtr<ID3D11SamplerState> SSAnistropicWrap;     // Sampler status: anisotropic filtering

    static ComPtr<ID3D11BlendState> BSNoColorWrite;     // Mixed state: Do not write color
    static ComPtr<ID3D11BlendState> BSTransparent;      // Mixing state: transparent mixing
    static ComPtr<ID3D11BlendState> BSAlphaToCoverage;  // Mixed state: Alpha-To-Coverage

    static ComPtr<ID3D11DepthStencilState> DSSWriteStencil;     // Depth/Template State: Write Template Values
    static ComPtr<ID3D11DepthStencilState> DSSDrawWithStencil;  // Depth/Template State: Draws the area of the specified template value
    static ComPtr<ID3D11DepthStencilState> DSSNoDoubleBlend;    // Depth/Template State: No Secondary Mixing Zone
    static ComPtr<ID3D11DepthStencilState> DSSNoDepthTest;      // Depth/Template Status: Close Depth Testing
    static ComPtr<ID3D11DepthStencilState> DSSNoDepthWrite;     // Depth/Template Status: Deep Testing Only, Deep Value Not Written
};

Specific settings can refer to the source code or the content of the previous chapter.

BasicManager class

Now, in order to alleviate the burden of GameApp class, and to achieve some Effects11-like functions, we need to implement a corresponding simple Basic Manager class according to HLSL code.

The structure corresponding to HLSL constant buffer is also included in BasicManager.h.

#ifndef BASICMANAGER_H
#define BASICMANAGER_H

#include <wrl/client.h>
#include <d3d11_1.h>
#include <d3dcompiler.h>
#include <directxmath.h>
#include <vector>
#include "LightHelper.h"
#include "RenderStates.h"
#include "Vertex.h"

// Since constant buffer creation requires a multiple of 16 bytes, the function can return the appropriate byte size
inline UINT Align16Bytes(UINT size)
{
    return (size + 15) & (UINT)(-16);
}

struct CBChangesEveryDrawing
{
    DirectX::XMMATRIX world;
    DirectX::XMMATRIX worldInvTranspose;
    DirectX::XMMATRIX texTransform;
    Material material;
};

struct CBChangesEveryFrame
{
    DirectX::XMMATRIX view;
    DirectX::XMFLOAT4 eyePos;

};

struct CBDrawingState
{
    int isReflection;
    int isShadow;
    DirectX::XMINT2 pad;
};

struct CBChangesOnResize
{
    DirectX::XMMATRIX proj;
};


struct CBNeverChange
{
    DirectX::XMMATRIX reflection;
    DirectX::XMMATRIX shadow;
    DirectX::XMMATRIX refShadow;
    DirectionalLight dirLight[10];
    PointLight pointLight[10];
    SpotLight spotLight[10];
    int numDirLight;
    int numPointLight;
    int numSpotLight;
    float pad;      // Packing guarantees 16-byte alignment
};

// Temporarily omit the BasicManager class
// ...

#endif

Now Basic.fx in HLSL is as follows:

#include "LightHelper.hlsli"

Texture2D tex : register(t0);
SamplerState sam : register(s0);


cbuffer CBChangesEveryDrawing : register(b0)
{
    row_major matrix gWorld;
    row_major matrix gWorldInvTranspose;
    row_major matrix gTexTransform;
    Material gMaterial;
}

cbuffer CBDrawingState : register(b1)
{
    int gIsReflection;
    int gIsShadow;
}

cbuffer CBChangesEveryFrame : register(b2)
{
    row_major matrix gView;
    float3 gEyePosW;
}

cbuffer CBChangesOnResize : register(b3)
{
    row_major matrix gProj;
}

cbuffer CBNeverChange : register(b4)
{
    row_major matrix gReflection;
    row_major matrix gShadow;
    row_major matrix gRefShadow;
    DirectionalLight gDirLight[10];
    PointLight gPointLight[10];
    SpotLight gSpotLight[10];
    int gNumDirLight;
    int gNumPointLight;
    int gNumSpotLight;
    float gPad;
}



struct Vertex3DIn
{
    float3 Pos : POSITION;
    float3 Normal : NORMAL;
    float2 Tex : TEXCOORD;
};

struct Vertex3DOut
{
    float4 PosH : SV_POSITION;
    float3 PosW : POSITION;     // Position in the World
    float3 NormalW : NORMAL;    // The Direction of Normal Vectors in the World
    float2 Tex : TEXCOORD;
};

struct Vertex2DIn
{
    float3 Pos : POSITION;
    float2 Tex : TEXCOORD;
};

struct Vertex2DOut
{
    float4 PosH : SV_POSITION;
    float2 Tex : TEXCOORD;
};

The HLSL codes of each shader are placed in separate files, which will be described in detail later.

A simple Basic Manager has the following functions:

  1. Variables that modify constant buffers (block updates)
  2. Similar to Effects 11, you can set various states and shaders in a single channel by yourself.

However, the management class is not used for drawing. The specific drawing function is handed over to the simple GameObject class for operation. So every time you draw an object, you may need to call the BasicManager setup method to specify the current form of rendering, and then call the GameObject::Draw method to draw.

Because this class has no reflection function, the user needs to decide when to update the constant buffer resources.

class BasicManager
{
public:
    // Simplify type names with template aliases (C++11)
    template <class T>
    using ComPtr = Microsoft::WRL::ComPtr<T>;

    // Initialize the resources required for Basix.fx and initialize the rasterized state
    bool InitAll(ComPtr<ID3D11Device> device);
    // Has it been initialized?
    bool IsInit() const;

    template <class T>
    void UpdateConstantBuffer(const T& cbuffer);

    // Drawing by default
    void SetRenderDefault();
    // Alpha Mixed Rendering
    void SetRenderAlphaBlend();     
    // No secondary mixing
    void SetRenderNoDoubleBlend(UINT stencilRef);
    // Write only template values
    void SetWriteStencilOnly(UINT stencilRef);      
    // Draws the area of the specified template value using the default state
    void SetRenderDefaultWithStencil(UINT stencilRef);      
    // Drawing the area of the specified template value, using Alpha mixing
    void SetRenderAlphaBlendWithStencil(UINT stencilRef);       
    // 2D default state rendering
    void Set2DRenderDefault();
    // 2D Hybrid Rendering
    void Set2DRenderAlphaBlend();


private:
    // Compiling shaders from.fx/.hlsl files
    HRESULT CompileShaderFromFile(const WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);

private:
    ComPtr<ID3D11VertexShader> mVertexShader3D;             // Vertex Shader for 3D
    ComPtr<ID3D11PixelShader> mPixelShader3D;               // Pixel Shader for 3D
    ComPtr<ID3D11VertexShader> mVertexShader2D;             // Vertex Shader for 2D
    ComPtr<ID3D11PixelShader> mPixelShader2D;               // Pixel shader for 2D

    ComPtr<ID3D11InputLayout> mVertexLayout2D;              // Vertex Input Layout for 2D
    ComPtr<ID3D11InputLayout> mVertexLayout3D;              // Vertex Input Layout for 3D

    ComPtr<ID3D11DeviceContext> md3dImmediateContext;       // device context

    std::vector<ComPtr<ID3D11Buffer>> mConstantBuffers;     // Constant Buffer
};

Default state rendering

The linear Wrap sampler is used in this drawing mode and all subsequent drawing modes.

Basic Manager:: The SetRenderDefault method uses the default 3D pixel shader and vertex shader, and the rest of the states remain in the default state:

void BasicManager::SetRenderDefault()
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(nullptr);
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(nullptr, 0);
    md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
}

Alpha Transparent Hybrid Rendering

The rendering mode turns off rasterization clipping and adopts transparent mixing mode.

void BasicManager::SetRenderAlphaBlend()
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get());
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(nullptr, 0);
    md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);
}

No repetitive mixing (single mixing)

The rendering mode is used to draw shadows to prevent excessive mixing. The template value of the drawing area needs to be specified.

void BasicManager::SetRenderNoDoubleBlend(UINT stencilRef)
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get());
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSNoDoubleBlend.Get(), stencilRef);
    md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);
}

Write only template values

This mode is used to write user-specified template values to the template buffer and not to the depth buffer and backup buffer.

void BasicManager::SetWriteStencilOnly(UINT stencilRef)
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(nullptr);
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSWriteStencil.Get(), stencilRef);
    md3dImmediateContext->OMSetBlendState(RenderStates::BSNoColorWrite.Get(), nullptr, 0xFFFFFFFF);
}

Routine rendering of specified template value areas

In this mode, only the template value of the template buffer and the same area specified by the user are drawn routinely.

void BasicManager::SetRenderDefaultWithStencil(UINT stencilRef)
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(RenderStates::RSCullClockWise.Get());
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawWithStencil.Get(), 1);
    md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
}

Alpha Transparent Mixed Drawing for Specified Template Value Area

In this mode, only the template value of the template buffer and the same area specified by the user are rendered by Alpha transparent blending.

void BasicManager::SetRenderAlphaBlendWithStencil(UINT stencilRef)
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout3D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader3D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get());
    md3dImmediateContext->PSSetShader(mPixelShader3D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(RenderStates::DSSDrawWithStencil.Get(), 1);
    md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);
}

Default rendering of 2D

The mode uses a 2D vertex shader and a pixel shader, and is modified to a 2D input layout.

void BasicManager::Set2DRenderDefault()
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout2D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader2D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(nullptr);
    md3dImmediateContext->PSSetShader(mPixelShader2D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(nullptr, 0);
    md3dImmediateContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFFF);
}

2D Transparent Hybrid Rendering

Compared with the above, there is more transparent mixing state.

void BasicManager::Set2DRenderAlphaBlend()
{
    md3dImmediateContext->IASetInputLayout(mVertexLayout2D.Get());
    md3dImmediateContext->VSSetShader(mVertexShader2D.Get(), nullptr, 0);
    md3dImmediateContext->RSSetState(RenderStates::RSNoCull.Get());
    md3dImmediateContext->PSSetShader(mPixelShader2D.Get(), nullptr, 0);
    md3dImmediateContext->PSSetSamplers(0, 1, RenderStates::SSLinearWrap.GetAddressOf());
    md3dImmediateContext->OMSetDepthStencilState(nullptr, 0);
    md3dImmediateContext->OMSetBlendState(RenderStates::BSTransparent.Get(), nullptr, 0xFFFFFFFF);
}

Update Constant Buffer

The member template function is used here to update the constant buffer according to the type of structure passed in:

template<class T>
void BasicManager::UpdateConstantBuffer(const T& cbuffer)
{
}

template<>
void BasicManager::UpdateConstantBuffer<CBChangesEveryDrawing>(const CBChangesEveryDrawing& cbuffer)
{
    md3dImmediateContext->UpdateSubresource(mConstantBuffers[0].Get(), 0, nullptr, &cbuffer, 0, 0);
}

template<>
void BasicManager::UpdateConstantBuffer<CBDrawingState>(const CBDrawingState& cbuffer)
{
    md3dImmediateContext->UpdateSubresource(mConstantBuffers[1].Get(), 0, nullptr, &cbuffer, 0, 0);
}

template<>
void BasicManager::UpdateConstantBuffer<CBChangesEveryFrame>(const CBChangesEveryFrame& cbuffer)
{
    md3dImmediateContext->UpdateSubresource(mConstantBuffers[2].Get(), 0, nullptr, &cbuffer, 0, 0);
}

template<>
void BasicManager::UpdateConstantBuffer<CBChangesOnResize>(const CBChangesOnResize& cbuffer)
{
    md3dImmediateContext->UpdateSubresource(mConstantBuffers[3].Get(), 0, nullptr, &cbuffer, 0, 0);
}

template<>
void BasicManager::UpdateConstantBuffer<CBNeverChange>(const CBNeverChange& cbuffer)
{
    md3dImmediateContext->UpdateSubresource(mConstantBuffers[4].Get(), 0, nullptr, &cbuffer, 0, 0);
}

Prevent GameApp::OnResize from modifying the projection matrix when Basic Manager is not initialized

BasicManager::IsInit method determines whether the camera has been initialized. Since the initialization of Basic Manager precedes GameApp::Init but calls GameApp::OnResize to update one of the constant buffers first, it is necessary to add a protection that should not be operated when not initialized, and initialize the projection matrix of the camera in GameApp::InitResource method.

Of course, what BasicManager can do is still limited, and it needs to be adjusted as the HLSL code changes. More features will be implemented in subsequent tutorials.

Drawing Planar Shadows

Using XMMatrix Shadow, the shadow matrix can be generated, and the geometry can be projected onto the plane according to the type and position of illumination.

XMMATRIX XMMatrixShadow(
    FXMVECTOR ShadowPlane,      // Planar vectors (nx, ny, nz, d)
    FXMVECTOR LightPosition);   // w = 0 indicates the direction of parallel light and w = 1 indicates the position of light source.

Usually the specified plane is slightly higher than the actual plane to avoid the problem of shadow display caused by contention for deep buffer resources.

Use template buffer to prevent over-mixing

When an object is projected onto a plane, some positions of the projection area may be located in multiple triangles, which will result in multiple pixels passing the test and mixing operation. The more times the object is rendered, the darker the color will be displayed.

We can use template buffers to solve this problem.

  1. In the previous example, we used a region with a template value of 0 to represent a non-mirror reflection area, and a region with a template value of 1 to represent a mirror reflection area.
  2. Using RenderStates::DSSNoDoubleBlend's depth template state, when the given template value and the template value of the depth/template buffer are identical, the pixel mixing is drawn by adding 1 to the template value, and then the next time because the given template value is 1 less than the template value of the depth/template buffer, the subsequent pixel drawing is blocked by not passing the template test again.
  3. Shadow areas of the mirror should be drawn first, and then normal shadow areas should be drawn.

Changes in shader code

The Basic_PS_2D.hlsl file changes as follows:

#include "Basic.fx"

// Pixel Shader (2D)
float4 PS_2D(Vertex2DOut pIn) : SV_Target
{
    float4 color = tex.Sample(sam, pIn.Tex);
    clip(color.a - 0.1f);
    return color;
}

The Basic_PS_3D.hlsl file changes as follows:

#include "Basic.fx"

// Pixel Shader (3D)
float4 PS_3D(Vertex3DOut pIn) : SV_Target
{
    // Clipping ahead of time can avoid subsequent operations for unqualified pixels
    float4 texColor = tex.Sample(sam, pIn.Tex);
    clip(texColor.a - 0.1f);

    // Standardized Normal Vector
    pIn.NormalW = normalize(pIn.NormalW);

    // Vector whose vertex points to the eye
    float3 toEyeW = normalize(gEyePosW - pIn.PosW);

    // Initialization is 0 
    float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 A = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 D = float4(0.0f, 0.0f, 0.0f, 0.0f);
    float4 S = float4(0.0f, 0.0f, 0.0f, 0.0f);
    int i;


    // Force unfolding loops to reduce the number of instructions
    [unroll]
    for (i = 0; i < gNumDirLight; ++i)
    {
        ComputeDirectionalLight(gMaterial, gDirLight[i], pIn.NormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }
    
    [unroll]
    for (i = 0; i < gNumPointLight; ++i)
    {
        PointLight pointLight = gPointLight[i];
        // If the reflective object is currently being drawn, it is necessary to transform the reflection matrix of the illumination.
        [flatten]
        if (gIsReflection)
        {
            pointLight.Position = (float3) mul(float4(pointLight.Position, 1.0f), gReflection);
        }

        ComputePointLight(gMaterial, pointLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }
    
    [unroll]
    for (i = 0; i < gNumSpotLight; ++i)
    {
        SpotLight spotLight = gSpotLight[i];
        // If the reflective object is currently being drawn, it is necessary to transform the reflection matrix of the illumination.
        [flatten]
        if (gIsReflection)
        {
            spotLight.Position = (float3) mul(float4(spotLight.Position, 1.0f), gReflection);
        }

        ComputeSpotLight(gMaterial, spotLight, pIn.PosW, pIn.NormalW, toEyeW, A, D, S);
        ambient += A;
        diffuse += D;
        spec += S;
    }
    

    
    float4 litColor = texColor * (ambient + diffuse) + spec;
    litColor.a = texColor.a * gMaterial.Diffuse.a;
    return litColor;
}

Basic_VS_2D.hlsl changes as follows:

#include "Basic.fx"

// Vertex Shader (2D)
Vertex2DOut VS_2D(Vertex2DIn pIn)
{
    Vertex2DOut pOut;
    pOut.PosH = float4(pIn.Pos, 1.0f);
    pOut.Tex = mul(float4(pIn.Tex, 0.0f, 1.0f), gTexTransform).xy;
    return pOut;
}

Basic_VS_3D.hlsl changes as follows:

#include "Basic.fx"

// Vertex Shader (3D)
Vertex3DOut VS_3D(Vertex3DIn pIn)
{
    Vertex3DOut pOut;
    
    float4 posH = mul(float4(pIn.Pos, 1.0f), gWorld);
    // If the reflective object is currently being drawn, the reflective operation is performed first.
    [flatten]
    if (gIsReflection)
    {
        posH = mul(posH, gReflection);
    }
    // If you are currently drawing shadows, do the projection first.
    [flatten]
    if (gIsShadow)
    {
        posH = (gIsReflection ? mul(posH, gRefShadow) : mul(posH, gShadow));
    }

    pOut.PosH = mul(mul(posH, gView), gProj);
    pOut.PosW = mul(float4(pIn.Pos, 1.0f), gWorld).xyz;
    pOut.NormalW = mul(pIn.Normal, (float3x3) gWorldInvTranspose);
    pOut.Tex = mul(float4(pIn.Tex, 0.0f, 1.0f), gTexTransform).xy;
    return pOut;
}

Now the code for each shader is placed in a separate file to participate in compilation.

Scene rendering

Now there are only walls, floors, wooden boxes and mirrors.

Step 1: Write the mirror area to the template buffer

// *********************
// 1. Write a value of 1 to the template buffer for the specular reflection area
// 

mBasicManager.SetWriteStencilOnly(1);
mMirror.Draw(md3dImmediateContext);

Step 2: Draw opaque reflections and shadows

// ***********************
// 2. Drawing opaque reflective objects
//

// Open Reflection Drawing
mDrawingState.isReflection = 1;     // Reflex opening
mBasicManager.UpdateConstantBuffer(mDrawingState);
mBasicManager.SetRenderDefaultWithStencil(1);

mWalls[2].Draw(md3dImmediateContext);
mWalls[3].Draw(md3dImmediateContext);
mWalls[4].Draw(md3dImmediateContext);
mFloor.Draw(md3dImmediateContext);
mWoodCrate.Draw(md3dImmediateContext);

// Shadow rendering
mWoodCrate.SetMaterial(mShadowMat);
mDrawingState.isShadow = 1;         // Reflections turn on, shadows turn on
mBasicManager.UpdateConstantBuffer(mDrawingState);
mBasicManager.SetRenderNoDoubleBlend(1);

mWoodCrate.Draw(md3dImmediateContext);

// Return to the original state
mDrawingState.isShadow = 0;
mBasicManager.UpdateConstantBuffer(mDrawingState);
mWoodCrate.SetMaterial(mWoodCrateMat);

Step 3: Draw a transparent mirror

// ***********************
// 3. Drawing transparent mirror
//

mBasicManager.SetRenderAlphaBlendWithStencil(1);

mMirror.Draw(md3dImmediateContext);
    
// Close Reflection Drawing
mDrawingState.isReflection = 0;
mBasicManager.UpdateConstantBuffer(mDrawingState);

Step 4: Draw opaque normal objects and shadows

// ************************
// 4. Drawing opaque normal objects
//
mBasicManager.SetRenderDefault();

for (auto& wall : mWalls)
    wall.Draw(md3dImmediateContext);
mFloor.Draw(md3dImmediateContext);
mWoodCrate.Draw(md3dImmediateContext);

// Shadow rendering
mWoodCrate.SetMaterial(mShadowMat);
mDrawingState.isShadow = 1;         // Reflections close, shadows open
mBasicManager.UpdateConstantBuffer(mDrawingState);
mBasicManager.SetRenderNoDoubleBlend(0);

mWoodCrate.Draw(md3dImmediateContext);

mDrawingState.isShadow = 0;         // Reflex closure
mBasicManager.UpdateConstantBuffer(mDrawingState);
mWoodCrate.SetMaterial(mWoodCrateMat);

The rendering effect is as follows:

Note that this example only generates shadows from the light to the floor. You can use a variety of camera modes for testing.

Posted by yasir_memon on Fri, 14 Dec 2018 14:12:03 -0800