DirectX:在不使用渲染目标的情况下渲染到屏幕缓冲区

时间:2010-03-22 19:54:42

标签: c++ windows-mobile directx rendering

我正在编写一个开源2D游戏引擎,我希望尽可能多地支持设备和平台。我目前只有Windows Mobile。我使用DirectX Mobile渲染,DirectDraw作为后备路径。但是,我遇到了一些麻烦。

虽然参考驱动程序支持createRenderTarget,但许多许多物理设备却没有。我需要一些方法来渲染到屏幕而不使用渲染目标,因为我使用纹理四边形渲染精灵,但我还需要能够绘制单个像素。

这就是我现在的做法:

// save old values

if (Error::Failed(m_D3DDevice->GetRenderTarget(&m_D3DOldTarget)))
{
    ERROR_EXPLAIN("Could not retrieve backbuffer.");
    return false;
}

// clear render surface

if (Error::Failed(m_D3DDevice->SetRenderTarget(m_D3DRenderSurface, NULL)))
{
    ERROR_EXPLAIN("Could not set render target to render texture.");
    return false;
}

if  (Error::Failed (m_D3DDevice->Clear( 
            0,
            NULL,                        // target rectangle
            D3DMCLEAR_TARGET,
            D3DMCOLOR_XRGB(0, 0, 0),     // clear color
            1.0f, 
            0
        )
    )
)
{
    ERROR_EXPLAIN("Failed to clear render texture.");
    return false;
}

D3DMLOCKED_RECT render_rect;
if (Error::Failed(m_D3DRenderSurface->LockRect(&render_rect, NULL, NULL)))
{
    ERROR_EXPLAIN("Failed to lock render surface pixels.");
}
else
{
    m_D3DBackSurf->SetBuffer((Pixel*)render_rect.pBits);
    m_D3DRenderSurface->UnlockRect();
}

// begin scene

if (Error::Failed(m_D3DDevice->BeginScene()))
{
    ERROR_EXPLAIN("Failed to start rendering.");
    return false;
}

// =====================
// example rendering
// =====================

// some other stuff, but the most important part of rendering a sprite:
device->SetTexture(0, m_Texture));
device->SetStreamSource(0, m_VertexBuffer, sizeof(Vertex));
device->DrawPrimitive(D3DMPT_TRIANGLELIST, 0, 2);

// plotting a pixel
Surface* target = (Surface*)Device::GetRenderMethod()->GetRenderTarget();
buffer = target->GetBuffer();
buffer[somepixel] = MAKECOLOR(255, 0, 0);

// end scene

if (Error::Failed(device->EndScene()))
{
    ERROR_EXPLAIN("Failed to end scene.");
    return false;
}

// clear screen

if (Error::Failed(device->SetRenderTarget(m_D3DOldTarget, NULL)))
{
    ERROR_EXPLAIN("Couldn't set render target to backbuffer.");
    return false;
}

if (Error::Failed(device->GetBackBuffer (   
        0,
        D3DMBACKBUFFER_TYPE_MONO, 
        &m_D3DBack 
        ) 
    ) 
)
{
    ERROR_EXPLAIN("Couldn't retrieve backbuffer.");
    return false;
}

RECT dest = { 
    0, 
    0, 
    Device::GetWidth(), 
    Device::GetHeight()
};

if (Error::Failed( device->StretchRect (        
            m_D3DRenderSurface, 
            NULL,
            m_D3DBack, 
            &dest, 
            D3DMTEXF_NONE 
        )
    )
)
{
    ERROR_EXPLAIN("Failed to stretch render texture to backbuffer.");
    return false;
}

if (Error::Failed(device->Present(NULL, NULL, NULL, NULL)))
{
    ERROR_EXPLAIN("Failed to present device.");
    return false;
}

我正在寻找一种方法来做同样的事情(使用硬件加速渲染精灵并在缓冲区上绘制像素)而不使用渲染目标。

提前致谢。

2 个答案:

答案 0 :(得分:2)

你没有详细说明想要绘制像素的 ,所以我要假设它真的是在“buffer [somepixel] = MAKECOLOR(255, 0,0);“。

我的建议是,不是回读(根据内存架构可能非常昂贵或相当便宜),修改,写入/上传后备缓冲区;相反,将像素绘制为纹理,让GPU处理GPU加速精灵的组成和CPU绘制的像素。

在基本情况下,这将是:

  1. 创建一个与后备缓冲区大小相同的4通道纹理。如果目标系统支持它,请指定LOCKABLE和DYNAMIC用法标志。 (如果没有,或者如果驱动程序更喜欢不可锁定的纹理,则必须在下面的步骤2中使用额外的系统纹理。并使用UpdateTexture / CopyRects上传它)

  2. 每一帧,锁定纹理,绘制像素,解锁纹理。确保将覆盖率信息写入像素的Alpha通道,以便GPU具有一些可以控制合成的数据。例如。如果要绘制纯红色,请为该像素写入(255,0,0,255)。确保清除实际上不想绘制的像素。

  3. 设置GPU以在精灵之上构图您的绘图数据。通过使用绘制的纹理渲染全屏四边形纹理(并使用点采样)。对于最简单的不透明组合案例,启用AlphaTestEnable,将AlphaRef设置为128,将AlphaFunc设置为GREATER,并禁用ZEnable。这将在精灵顶部渲染绘制的像素,同时保持其余的精灵像素不变。

  4. 如果您正在使用深度缓冲区,并且想要获得一些额外的性能,您可以在精灵之前渲染绘制的纹理,同时启用ZEnable和ZWriteEnabled。 (确保在近平面上渲染全屏四边形)这将填充一个深度缓冲掩模,其中不透明像素将在稍后渲染精灵时遮挡这些像素。

    显然,你并没有真正限制只是覆盖实体像素。您可以将Alpha混合与创意混合模式和Alpha值组合使用,以创建其他类型的合成和叠加。

答案 1 :(得分:0)

如果您希望与没有硬件3D加速的手持设备(多年来每台台式计算机都支持渲染目标)具有广泛的兼容性,那么您可以更轻松地使用GDI而不是DirectX。

我并不完全遵循您在发布的代码中尝试做的事情,但我会补充说,渲染目标不支持LockRect(也不支持使用渲染目标标志创建的纹理)。 DirectX for Windows mobile虽然与普通的DirectX有点不同,所以也许我误解了这个。如果你真的想在Windows Mobile上使用DirectX,请查看CreateOffscreenPlainSurface,或者其管理的任何东西 - 与DirectX等价。这种类型的表面是可锁定的,并且支持将StretchRect直接用于后台缓冲区。