【DirectX12笔记】 第10章 混合

2019-08-20  本文已影响0人  crossous

前言

  这一章讲的是混合,一般是用作半透明物体的渲染,不过一些特殊的写法也能做出其他效果。

原理

混合

  混合方程如P360所述:C=C_{src}\otimes F_{src}\boxplus C_{dst}\otimes F_{dst}  其中C代表颜色,F代表因子,圈乘符号、箱加符号以及因子都可以通过程序设置。
  我们常用的是透明混合(P367),物体颜色乘自身α值,加上目标原颜色乘1-α。

alpha测试

  除了半透明物体外,还存在全透明的物体,例如370页的金属栏箱,这种地方可以用上混合,毕竟alpha值为0,但效率存在问题。
  渲染的各种测试的顺序为:剪裁、alpha测试、模版测试、深度测试,如果是全透明的像素片段,可以在alpha测试阶段直接丢弃,而如果用混合,还需要经过模版测试和深度测试。
  因此如370页最上面代码所示,用clip检测alpha值,如果小于0.1就直接丢弃片段。

  P371页给出了雾的好处以及生成雾的原理。

程序分析

  我们看看程序中有明显区别的地方。
  首先是布局设置函数:

void BlendApp::BuildShadersAndInputLayout()
{
    const D3D_SHADER_MACRO defines[] =
    {
        "FOG", "1",
        NULL, NULL
    };

    const D3D_SHADER_MACRO alphaTestDefines[] =
    {
        "FOG", "1",
        "ALPHA_TEST", "1",
        NULL, NULL
    };

    mShaders["standardVS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", nullptr, "VS", "vs_5_0");
    mShaders["opaquePS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", defines, "PS", "ps_5_0");
    mShaders["alphaTestedPS"] = d3dUtil::CompileShader(L"Shaders\\Default.hlsl", alphaTestDefines, "PS", "ps_5_0");
    
    mInputLayout =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    };
}

  这次我们有两个像素着色器了,区别在于传入的define有区别,去看看pixel shader:

#ifdef ALPHA_TEST
    clip(diffuseAlbedo.a - 0.1f);
#endif
//...

#ifdef FOG
    float fogAmount = saturate((distToEye - gFogStart) / gFogRange);
    litColor = lerp(litColor, gFogColor, fogAmount);
#endif

  操控了是否进行alpha测试。
  然后看看PSO的创建(P364):

void BlendApp::BuildPSOs()
{
//不透明物体的PSO
    D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

    ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
    opaquePsoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
    opaquePsoDesc.pRootSignature = mRootSignature.Get();
    opaquePsoDesc.VS = 
    { 
        reinterpret_cast<BYTE*>(mShaders["standardVS"]->GetBufferPointer()), 
        mShaders["standardVS"]->GetBufferSize()
    };
    opaquePsoDesc.PS = 
    { 
        reinterpret_cast<BYTE*>(mShaders["opaquePS"]->GetBufferPointer()),
        mShaders["opaquePS"]->GetBufferSize()
    };
    opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
    opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
    opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
    opaquePsoDesc.SampleMask = UINT_MAX;
    opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    opaquePsoDesc.NumRenderTargets = 1;
    opaquePsoDesc.RTVFormats[0] = mBackBufferFormat;
    opaquePsoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
    opaquePsoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
    opaquePsoDesc.DSVFormat = mDepthStencilFormat;
    ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPSOs["opaque"])));

//半透明物体的PSO(用于渲染水体)
    D3D12_GRAPHICS_PIPELINE_STATE_DESC transparentPsoDesc = opaquePsoDesc;

    D3D12_RENDER_TARGET_BLEND_DESC transparencyBlendDesc;
    transparencyBlendDesc.BlendEnable = true;//使用老式混合
    transparencyBlendDesc.LogicOpEnable = false;//和上面只能有一个为true
    transparencyBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;//源混凝因子为α
    transparencyBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;//目标混凝因子为1-α
    transparencyBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;//混合操作为+
    transparencyBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;//α值的混合方法
    transparencyBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
    transparencyBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
    transparencyBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;//不用新式方法
    transparencyBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;//向渲染目标渲染前先&mask

    transparentPsoDesc.BlendState.RenderTarget[0] = transparencyBlendDesc;
    ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&transparentPsoDesc, IID_PPV_ARGS(&mPSOs["transparent"])));

//带有完全透明物体的PSO(渲染框箱)
    D3D12_GRAPHICS_PIPELINE_STATE_DESC alphaTestedPsoDesc = opaquePsoDesc;
    alphaTestedPsoDesc.PS = 
    { 
        reinterpret_cast<BYTE*>(mShaders["alphaTestedPS"]->GetBufferPointer()),
        mShaders["alphaTestedPS"]->GetBufferSize()
    };
    alphaTestedPsoDesc.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
    ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&alphaTestedPsoDesc, IID_PPV_ARGS(&mPSOs["alphaTested"])));
}

  接下来看看Draw函数:

void BlendApp::Draw(const GameTimer& gt)
{
//****************************复读部分******************************
    auto cmdListAlloc = mCurrFrameResource->CmdListAlloc;

    ThrowIfFailed(cmdListAlloc->Reset());

    ThrowIfFailed(mCommandList->Reset(cmdListAlloc.Get(), mPSOs["opaque"].Get()));

    mCommandList->RSSetViewports(1, &mScreenViewport);
    mCommandList->RSSetScissorRects(1, &mScissorRect);

    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(CurrentBackBuffer(),
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    // **雾的颜色只会对物体生效,因此要把底色(无限远)设为雾的颜色
    mCommandList->ClearRenderTargetView(CurrentBackBufferView(), (float*)&mMainPassCB.FogColor, 0, nullptr);
    mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

    mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());

    ID3D12DescriptorHeap* descriptorHeaps[] = { mSrvDescriptorHeap.Get() };
    mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

    mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

    auto passCB = mCurrFrameResource->PassCB->Resource();
    mCommandList->SetGraphicsRootConstantBufferView(2, passCB->GetGPUVirtualAddress());
// 绘制不透明物体
    DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Opaque]);
// 绘制alpha测试物体,切换PSO
    mCommandList->SetPipelineState(mPSOs["alphaTested"].Get());
    DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::AlphaTested]);
// 绘制半透明物体,切换PSO
    mCommandList->SetPipelineState(mPSOs["transparent"].Get());
    DrawRenderItems(mCommandList.Get(), mRitemLayer[(int)RenderLayer::Transparent]);

//****************************复读部分******************************
//.........
}

程序结果


  感觉一部分水体颜色不对劲,是因为那部分在山里面,可以发现,为了让线框箱子背面能看到,我们在渲染箱子的PSO中禁用了背面剔除,而山没有,因此背对着我们的山体部分被剔除了。
  同时不要误会,那部分水体颜色浅不是因为没受雾影响,只是因为作为山体的底色不在而已,换个水平的角度会更容易看清。

上一篇下一篇

猜你喜欢

热点阅读