DirectX:渲染到屏幕缓冲区而不使用渲染目标

问题描述:

我在写一个开源的2D游戏引擎,并且我希望支持尽可能多的设备和平台。我目前只有Windows Mobile。我使用DirectX Mobile进行渲染,并将DirectDraw作为后备路径。但是,我遇到了一些麻烦。DirectX:渲染到屏幕缓冲区而不使用渲染目标

似乎虽然参考驱动程序支持createRenderTarget,但很多很多物理设备都不支持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; 
} 

我正在寻找一种方式做同样的事情(使用一个缓冲硬件加速和情节像素渲染精灵),而不使用渲染目标。

在此先感谢。

如果您想要与没有硬件3d加速的手持设备(多年来每台台式机都支持渲染目标)进行广泛兼容,那么使用GDI代替DirectX可能会更容易一些。

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

你没有详细说明如何你想绘制的像素,所以我会开始假设它是真正的复杂性顺序“缓冲区[somepixel] = MAKECOLOR(255,0,0) ;”。

我的建议是,不要回读(这可能是非常昂贵或取决于内存架构相当便宜),修改和写入/上传backbuffer;相反,将您的像素绘制为纹理,并让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。这将在精灵顶部绘制像素,同时保留精灵像素的其余部分不变。

如果您使用的是深度缓存,并且你想获得幻想约获得一些额外的性能,你可以你的精灵之前渲染绘制的纹理,既ZEnable和ZWriteEnabled启用。 (确保你在接*面渲染全屏四边形)然后在你的不透明像素所在的位置填充一个深度缓冲区遮罩,当你以后渲染这些图像时会遮住这些像素。

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

+0

这是一个很好的回复!但是,您的方法仍然存在问题。在一个完美的世界中,我会渲染一个精灵,一个覆盖该精灵的行,然后是另一个精灵。根据画家的算法,该线将在两个精灵之间绘制。获得这个结果的唯一方法(据我所知)是将每一行渲染到它自己的纹理上。 这种方法的一种方法是直接渲染到后缓冲区,但是我的目标设备不支持这一点。 :\ – knight666 2010-03-27 17:17:43

+0

什么阻止你使用DX渲染你的线,例如作为弥漫色四边形?或者,如果您需要它们获取详细的像素信息,也许您可​​以在应用程序启动时创建纹理,并且以后只渲染纹理?如果你的精灵不透明(或掩盖),确保正确排序的最简单方法就是使用深度缓冲区。如果它们是混合/半透明的,那么是的,你需要将它们排回前面,并将它们与不透明线交错(或者让线条前面写入深度,并且精灵测试深度) – tbone 2010-03-28 03:52:28