【问题标题】:DirectX - Deferred Shading w/Instanced Stencil VolumesDirectX - 带有实例化模板卷的延迟着色
【发布时间】:2013-07-25 21:06:49
【问题描述】:

我正在使用 DXUT 使用 DirectX9 创建一个图形应用程序。在我的应用程序中,我想实现延迟着色。这种照明方法要求我为场景中的每个灯光渲染一个模板体积(遮罩)。为了避免单独渲染卷,我想使用 DirectX 的实例化功能。使用顶点着色器 3.0 和像素着色器 3.0,我已经成功地绘制了任何给定模型的多个实例,但是,当我尝试绘制实例模板卷时,似乎没有数据写入后缓冲区的深度模板表面。

我的延迟着色的实现在伪代码中如下:

将所有场景几何体(Instanced Geometry Pass)绘制到后台缓冲区:

  • 启用 Z(D3DRS_ZENABLE, TRUE)
  • 启用 ZWrites(D3DRS_ZWRITEENABLE, TRUE)
  • 禁用模板(D3DRS_STENCILENABLE,FALSE)
  • 设置剔除模式(D3DRS_CULLMODE, D3DCULL_CCW)
  • 禁用 ColorWrites(D3DRS_COLORWRITEENABLE, 0)
  • 禁用 Alphablending(D3DRS_ALPHABLENDENABLE, FALSE)
  • 渲染场景

获取一个指向后台缓冲区的指针:

  • IDirect3DDevice9::GetRenderTarget(0, pBackbufferSurface)

设置 3 个渲染目标(COLOR(0)、NORMAL(1)、POSITION(2)):

  • IDirect3DDevice9::SetRenderTarget(0 -> 2, 颜色 -> 位置)

将所有场景几何图形绘制到 3 个渲染目标:

  • 启用 ColorWrites(D3DRS_COLORWRITEENABLE、CW_RED、CW_GREEN、 CW_BLUE, CW_ALPHA)
  • 渲染场景

将后台缓冲区重置为设备 RenderTarget 0:

  • IDirect3DDevice9::SetRenderTarget(0, pBackbufferSurface)

将效果纹理( SrcColor )设置为 COLOR 渲染目标

  • ID3DXEffect::SetTexture("g_TextureSrcColor", pRTColor)

渲染一个全屏四边形并从 COLOR 渲染目标采样(绘制场景)


使用实例化技术对所有场景灯进行模板(这里是实际代码):

// set the instance buffer vertex declaration
    hr = pd3dDevice->SetVertexDeclaration( CContentManager::GetInstanceBufferVertexDeclaration() );
    if( FAILED( hr ) )
    {
        DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetVertexDeclaration( instances )at RenderScene()", __LINE__, hr );
        return hr;
    }

    // render instances of each light( sphere and cone )
    for( UINT i = 0; i < 2; ++i )
    {
        // point to base model
        CXModel *pXBase = ( CXModel* )pCM->GetAppObject( i );
        if( !pXBase )
        {
            DebugStringDX( ClassName, 
                "Failed to retrieve the base light model at index( " + ToString( i ) + " ) at Render()", 
                __LINE__,
                E_POINTER );
            continue;
        }

        // if the model has no instances
        //      there is no reason to try and draw
        UINT ucInstances = pXBase->GetNumInstances();
        if( ucInstances < 1 )
            continue;

        // Set up the geometry data stream
        hr = pd3dDevice->SetStreamSourceFreq( 0,
            ( D3DSTREAMSOURCE_INDEXEDDATA | ucInstances ) );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSourceFreq( source )( index - " + ToString( i ) + " ) at RenderScene()", __LINE__, hr );
            continue;
        }

        // Set up the instance data stream
        IDirect3DVertexBuffer9 *pVBInstances = pXBase->GetInstanceBuffer();
        hr = pd3dDevice->SetStreamSource( 1, pVBInstances, 0, 
            sizeof( CXModel::sInstanceEntry ) );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSource( instances )( index - " + ToString( i ) + " ) at RenderScene()", __LINE__, hr );
            continue;
        }

        hr = pd3dDevice->SetStreamSourceFreq( 1,
            ( D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to IDirect3DDevice9::SetStreamSourceFreq( instances )( index - " + ToString( i ) + " ) at Render()", __LINE__, hr );
            continue;
        }

        // get light model frame root
        pFrame = pXBase->GetFrameRoot();    
        if( !pFrame )
        {
            DebugStringDX( ClassName, "Failed to CXModel::GetFrameRoot( Light Model ) at Render()", __LINE__, hr );
            continue;
        }

        //------------------------------------------------------------------------------------------------------------
        // Stencil Volume Mask Pass
        //------------------------------------------------------------------------------------------------------------
        dev.SetRenderState( D3DRS_ZENABLE, TRUE );
        dev.SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
        dev.SetRenderState( D3DRS_STENCILENABLE, TRUE );
        dev.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
        dev.SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
        dev.SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_INCR );
        dev.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
        dev.SetRenderState( D3DRS_STENCILWRITEMASK, 0xffffffff );
        dev.SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
        dev.SetRenderState( D3DRS_COLORWRITEENABLE, 0 );

        hr = BeginPass( CFXDeferred::STENCIL );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to BeginPass( StencilVolumeMask ) at Render()", __LINE__, hr );
            continue;
        }

        // draw the light model
        hr = DrawStaticModel( pd3dDevice, pFrame, &mWorld, pmView, pmProjection, true, bRenderStrips );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to DrawStaticModel( Light Model ) at Render()", __LINE__, hr );
            continue;
        }

        hr = pE->EndPass();
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to ID3DXEffect::EndPass() at Render()", __LINE__, hr );
            continue;
        }

        //------------------------------------------------------------------------------------------------------------
        // Diffuse Light Pass
        //------------------------------------------------------------------------------------------------------------
        dev.SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
        dev.SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE );
        dev.SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
        dev.SetRenderState( D3DRS_ZENABLE, FALSE );
        dev.SetRenderState( D3DRS_CULLMODE, D3DCULL_CW );
        dev.SetRenderState( D3DRS_COLORWRITEENABLE, 
            D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA );
        dev.SetRenderState( D3DRS_STENCILENABLE, TRUE );
        dev.SetRenderState( D3DRS_STENCILFUNC, D3DCMP_EQUAL );
        dev.SetRenderState( D3DRS_STENCILMASK, 0x1 );
        dev.SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_ZERO );
        dev.SetRenderState( D3DRS_STENCILREF, 0x1 );
        dev.SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
        dev.SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO );

        hr = BeginPass( CFXDeferred::DIFFUSELIGHTSTENCIL );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to BeginPass( DiffuseSpecularLightStencilPass ) at Render()", __LINE__, hr );
            continue;
        }

        // draw light model
        hr = DrawStaticModel( pd3dDevice, pFrame, &mWorld, pmView, pmProjection, true, bRenderStrips );
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to DrawStaticModel( Light Model ) at Render()", __LINE__, hr );
            continue;
        }

        hr = pE->EndPass();
        if( FAILED( hr ) )
        {
            DebugStringDX( ClassName, "Failed to ID3DXEffect::EndPass() at Render()", __LINE__, hr );
            continue;
        }
    }

这是实例缓冲区顶点声明:

// create instance vertex declaration
    D3DVERTEXELEMENT9 pElements[] = {
        { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
        { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
        { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
        { 1, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
        { 1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },
        { 1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },
        { 1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 },
        { 1, 64, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 5 },
        D3DDECL_END()
    };

这种方法在没有实例化的情况下也能正常工作...实例化会阻止深度模板写入吗?当我使用 PIX 进行调试时,它表明“像素未通过模板测试”。这是在渲染之前清除后台缓冲区的调用:

pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_ARGB( 0, 0, 0, 0 ), 1.0f, 0 ) );

在创建设备期间,我修改设置以使用 AutoDepthStencilFormat D3DFMT_D24S8,如下所示:

// check for D3DFMT_D24S8 AutoDepthStencilFormat
    // if present set the AutoDepthStencilFormat to D3DFMT_D24S8
    if( DXUTGetD3D9Enumeration()->GetPossibleDepthStencilFormatList()->Contains( D3DFMT_D24S8 ) )
    {
        pDeviceSettings->d3d9.pp.AutoDepthStencilFormat = D3DFMT_D24S8;
    }

如果我将 IDirect3DDevice9::Clear() 的 stencil 参数(最后一个参数)更改为 1。 stencil 卷通过了 stencil 测试...这是因为我将 stencil 操作设置为将 stencil 值增加 1 if体积掩码的一个像素与后缓冲区上的一个像素相交,并且在该事件中,承认光的像素颜色。这表明存在以下两个问题之一:

  1. 在“实例化几何通道”期间绘制场景(对象)时...后缓冲区不存储绘制对象时的深度(因此模板测试的比较值为“0” 或
  2. 绘制模板卷(灯)时,后备缓冲区不会存储绘制时卷的深度...

这是由于我的实例化方法吗?是因为 Shader Versions(vs3, ps3) 还是 DX 的版本?

【问题讨论】:

  • "这种光照方法要求我为场景中的每个灯光渲染一个模板体积(遮罩)。" 一般来说,延迟渲染和模板阴影 不要一起工作。就像,完全。您需要切换到阴影贴图。
  • 谁说过阴影?没有阴影...我正在实施一种使用模板卷(后期处理)的延迟照明技术。对于给定的体积,通过 stecil 测试但未通过深度测试的所有像素都被“点亮”(光点的表示使用球体,点使用锥体)...
  • 那么什么是模板卷的“模板卷”?如果不是阴影,那么你的场景中的模板应该做什么?
  • 每个模板体积要么是球体,要么是圆锥体...所有通过模板测试但未通过深度测试的像素都与模板卷相交。然后我可以为所有符合条件的像素添加来自光的颜色(以模板体积的形式表示)...

标签: c++ directx vertex-shader pixel-shader stencil-buffer


【解决方案1】:

问题是我用 vs_3_0 编译了顶点着色器(用于模板体积掩码)...我将其更改为 vs_2_0,现在它工作正常...我不明白,因为我认为有必要使用顶点带实例化的着色器模型 3.0...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多