A maneira como faço isso é a seguinte.
IDirect3DDevice9 :: GetBackBuffer : obtenha acesso ao IDirect3DSurface9 que representa o buffer traseiro, o mesmo que você possui atualmente. Não se esqueça de Liberar esta superfície quando terminar, pois esta chamada aumentará a contagem de referência!
IDirect3DSurface :: GetDesc : obtenha a descrição da superfície do buffer traseiro, que fornecerá largura, altura e formato.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : Crie um novo objeto de superfície em D3DPOOL_SCRATCH; geralmente você deseja usar a mesma largura, altura e formato (mas na verdade não precisa desse método). Novamente, solte quando terminar. Se você estiver executando esta operação em todos os quadros (nesse caso, é melhor procurar alternativas como uma abordagem baseada em shader para o que você está tentando fazer), basta criar a superfície plana fora da tela uma vez na inicialização e reutilizar em vez de criar todos os quadros.
D3DXLoadSurfaceFromSurface : copie da superfície do buffer traseiro para a superfície plana do offsceen. Isso fará um redimensionamento e conversão de formato automaticamente para você. Como alternativa, se você não quiser ou precisar redimensionar ou alterar o formato, poderá usar IDirect3DDevice9 :: GetRenderTargetData , mas, se for o caso, crie a superfície plana fora da tela em D3DPOOL_SYSTEMMEM.
IDirect3DSurface9 :: LockRect : Tenha acesso aos dados na superfície plana fora da tela e faça o que quiser ; UnlockRect quando terminar.
Isso parece muito mais código, mas você descobrirá que é tão rápido quanto o glReadPixels e pode ser ainda mais rápido se você não precisar fazer uma conversão de formato (o que o glReadPixels usando GL_RGB quase certamente faz).
Edite para adicionar: algumas funções auxiliares (prontas para uso) que também tenho, que podem ser úteis para usar este método para capturas de tela:
// assumes pitch is measured in 32-bit texels, not bytes; use locked_rect.Pitch >> 2
void CollapseRowPitch (unsigned *data, int width, int height, int pitch)
{
if (width != pitch)
{
unsigned *out = data;
// as a minor optimization we can skip the first row
// since out and data point to the same this is OK
out += width;
data += pitch;
for (int h = 1; h < height; h++)
{
for (int w = 0; w < width; w++)
out[w] = data[w];
out += width;
data += pitch;
}
}
}
void Compress32To24 (byte *data, int width, int height)
{
byte *out = data;
for (int h = 0; h < height; h++)
{
for (int w = 0; w < width; w++, data += 4, out += 3)
{
out[0] = data[0];
out[1] = data[1];
out[2] = data[2];
}
}
}
// bpp is bits, not bytes
void WriteDataToTGA (char *name, void *data, int width, int height, int bpp)
{
if ((bpp == 24 || bpp == 8) && name && data && width > 0 && height > 0)
{
FILE *f = fopen (name, "wb");
if (f)
{
byte header[18];
memset (header, 0, 18);
header[2] = 2;
header[12] = width & 255;
header[13] = width >> 8;
header[14] = height & 255;
header[15] = height >> 8;
header[16] = bpp;
header[17] = 0x20;
fwrite (header, 18, 1, f);
fwrite (data, (width * height * bpp) >> 3, 1, f);
fclose (f);
}
}
}