Cara saya melakukan ini adalah sebagai berikut.
IDirect3DDevice9 :: GetBackBuffer : Dapatkan akses ke IDirect3DSurface9 yang mewakili buffer belakang, sama seperti yang Anda miliki saat ini. Jangan lupa untuk melepaskan permukaan ini ketika dilakukan karena panggilan ini akan menambah jumlah referensi!
IDirect3DSurface :: GetDesc : Dapatkan deskripsi permukaan buffer belakang, yang akan memberi Anda lebar, tinggi, dan format.
IDirect3DDevice9 :: CreateOffscreenPlainSurface : Buat objek permukaan baru di D3DPOOL_SCRATCH; Anda biasanya ingin menggunakan lebar, tinggi, dan format yang sama (tetapi Anda sebenarnya tidak harus menggunakan metode ini). Sekali lagi, Lepaskan setelah selesai. Jika Anda melakukan operasi ini setiap frame (dalam hal ini Anda lebih baik melihat alternatif seperti pendekatan berbasis shader untuk apa yang Anda coba lakukan), Anda bisa membuat permukaan polos layar sekali pada saat startup dan menggunakan kembali itu, alih-alih membuatnya setiap frame.
D3DXLoadSurfaceFromSurface : Salin dari permukaan buffer belakang ke permukaan polos offsceen. Ini akan melakukan perubahan ukuran dan memformat konversi secara otomatis untuk Anda. Atau, jika Anda tidak ingin atau perlu mengubah ukuran atau mengubah format, Anda bisa menggunakan IDirect3DDevice9 :: GetRenderTargetData , tetapi jika demikian, buat permukaan polos layar di D3DPOOL_SYSTEMMEM sebagai gantinya.
IDirect3DSurface9 :: LockRect : Dapatkan akses ke data di permukaan polos layar dan miliki cara jahat Anda sendiri dengannya; UnlockRect saat selesai.
Ini terlihat seperti kode yang lebih banyak tetapi Anda akan menemukan bahwa itu secepat glReadPixels, dan bahkan dapat lebih cepat jika Anda tidak perlu melakukan konversi format (yang glReadPixels menggunakan GL_RGB hampir pasti melakukannya).
Sunting untuk menambahkan: beberapa fungsi pembantu yang telah saya siapkan yang mungkin berguna untuk menggunakan metode ini untuk tangkapan layar:
// 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);
}
}
}