美文网首页
【DirectX12笔记】 第10章 混合

【DirectX12笔记】 第10章 混合

作者: crossous | 来源:发表于2019-08-20 19:03 被阅读0次

    前言

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

    原理

    混合

      混合方程如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中禁用了背面剔除,而山没有,因此背对着我们的山体部分被剔除了。
      同时不要误会,那部分水体颜色浅不是因为没受雾影响,只是因为作为山体的底色不在而已,换个水平的角度会更容易看清。

    相关文章

      网友评论

          本文标题:【DirectX12笔记】 第10章 混合

          本文链接:https://www.haomeiwen.com/subject/heepsctx.html