Software Rendering in a DX8 Environment
by Ryan Geiss
8/16/2002
I really don't have time to document all this, so I'm just slapping the
code up here. Enjoy!
First, to create a texture in system memory:
// create a texture for the fractal, using the D3DPOOL_MANAGED pool so that it can be co-located
// in system memory and we can update it whenever we like. If we used D3DPOOL_DEFAULT, the
// texture would probably not exist in system memory, so when we locked it and started writing pixels,
// it would be super-super-slow.
// note that we use the pixel format of the backbuffer to make sure that the blitting will work!
// also, the actual texture created might be a bit bigger than m_fractal_texsize x m_fractal_texsize,
// (especially if it's not a power of 2), so we'll probably end up using just a corner of it.
D3DFORMAT fmt = m_lpDX->m_current_mode.display_mode.Format;
D3DPOOL pool = D3DPOOL_MANAGED;
if (D3DXCreateTexture(m_lpDX->m_lpDevice, m_fractal_texsize, m_fractal_texsize, 1, 0, fmt, pool, &m_fractal_tex) != S_OK)
{
MessageBox(m_lpDX->GetHwnd(), "D3DX failed to allocate system-memory texture", "ERROR ALLOCATING TEXTURE", MB_OK|MB_SETFOREGROUND|MB_TOPMOST);
return false;
}
Then, to draw to it. This example draws a simple julia fractal.
void CSomeClass::UpdateFractalTexture()
{
// Update the texture with an image of an animated fractal.
// This can (and should) be called before BeginScene()!
// This function is here solely to show you how to render images,
// USING THE CPU, and store them into a texture on the video card,
// in any of the standard color formats (888, 565, 555, 444).
D3DSURFACE_DESC sd;
if (m_fractal_tex->GetLevelDesc(0, &sd) != D3D_OK)
return;
D3DLOCKED_RECT lr;
if (m_fractal_tex->LockRect(0, &lr, NULL, D3DLOCK_NOSYSLOCK) != D3D_OK)
return;
if (lr.pBits == NULL)
return;
// 1. figure out how to chop up the 8-bit R/G/B values
// to package them in the texture's own pixel color format.
// (note that this should be optimally be done just once
// right after the texture is created, instead of each frame!)
int start;
int bpp;
int rshift[3]; // 1. bits to first shift right r,g,b
int mask[3]; // 2. mask for r, g, b
int lshift[3]; // 3. bits to then shift left r,g,b
switch(sd.Format)
{
case D3DFMT_R8G8B8:
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8:
start = 0xFF000000;
bpp = 32;
rshift[0] = 0;
rshift[1] = 0;
rshift[2] = 0;
mask[0] = 0xFF;
mask[1] = 0xFF;
mask[2] = 0xFF;
lshift[0] = 16;
lshift[1] = 8;
lshift[2] = 0;
break;
case D3DFMT_R5G6B5:
start = 0x00000000;
bpp = 16;
rshift[0] = 3;
rshift[1] = 2;
rshift[2] = 3;
mask[0] = 0x1F;
mask[1] = 0x3F;
mask[2] = 0x1F;
lshift[0] = 11;
lshift[1] = 5;
lshift[2] = 0;
break;
case D3DFMT_X1R5G5B5:
case D3DFMT_A1R5G5B5:
start = 0x8000;
bpp = 16;
rshift[0] = 3;
rshift[1] = 3;
rshift[2] = 3;
mask[0] = 0x1F;
mask[1] = 0x1F;
mask[2] = 0x1F;
lshift[0] = 10;
lshift[1] = 5;
lshift[2] = 0;
break;
case D3DFMT_A4R4G4B4:
start = 0xF000;
bpp = 16;
rshift[0] = 4;
rshift[1] = 4;
rshift[2] = 4;
mask[0] = 0x0F;
mask[1] = 0x0F;
mask[2] = 0x0F;
lshift[0] = 8;
lshift[1] = 4;
lshift[2] = 0;
break;
default:
return;
}
float i1 = 0.5f + 0.07f*sinf(5 - m_time*1.2430f) - 0.07f*cosf(7 + m_time*1.3121f);
float i2 = -0.4f + 0.07f*cosf(0 + m_time*1.4398f) - 0.07f*sinf(2 - m_time*1.0700f);
unsigned __int16* p16 = (unsigned __int16*)lr.pBits;
unsigned __int32* p32 = (unsigned __int32*)lr.pBits;
int w = min((int)sd.Width, m_fractal_texsize);
int h = min((int)sd.Height, m_fractal_texsize);
// render a julia fractal.
// the number of iterations we get through ('c') before x0*x0+z0*z0 exceeds
// some threshold determines the color intensity; then we can use an
// interpolation trick to increase the color precision.
int max_its = 10;
float c_mult = 1.0f / (max_its + 1.0f);
int half_w = w/2;
int half_h = h/2;
float one_over_half_w = 1.0f/(float)(w/2);
float one_over_half_h = 1.0f/(float)(h/2);
for (int y=0; y5)
{
// done; interpolate, using endpoint, to increase color precision
partial_c = (5 - prev_breakval) / (breakval - prev_breakval);
break;
}
prev_breakval = breakval;
z0 = 2 * x0 * z0 + i2;
x0 = x0x0 - z0z0 + i1;
}
float intensity = (c + partial_c)*c_mult;
if (intensity>1)
intensity=1;
float temp = (1-intensity);
int r = (int)(temp*255); // 0..255
int g = (int)(temp*temp*255); // 0..255
int b = (int)(0); // 0..255
if (bpp==16)
p16[offset] = start |
(((r >> rshift[0]) & mask[0]) << lshift[0]) |
(((g >> rshift[1]) & mask[1]) << lshift[1]) |
(((b >> rshift[2]) & mask[2]) << lshift[2]);
else
p32[offset] = start |
(((r >> rshift[0]) & mask[0]) << lshift[0]) |
(((g >> rshift[1]) & mask[1]) << lshift[1]) |
(((b >> rshift[2]) & mask[2]) << lshift[2]);
offset++;
}
}
m_fractal_tex->UnlockRect(0);
}
Finally, some code to render it. The first function sets DirectX up
to draw in 2D mode; the second function draws the fractal texture
full-screen.
void PrepareFor2DDrawing(IDirect3DDevice8 *pDevice, int w, int h)
{
// w and h should be the width and height of the CLIENT area of the window.
// (use GetClientRect())
// NOTE: After calling this, be sure to then call:
// 1. SetVertexShader()
// 2. SetTexture(), if you need it
// Also be sure your sprites have a z coordinate of 0!!!
pDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
pDevice->SetRenderState( D3DRS_ZWRITEENABLE, TRUE );
pDevice->SetRenderState( D3DRS_ZFUNC, D3DCMP_LESSEQUAL );
pDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_FLAT );
pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
pDevice->SetRenderState( D3DRS_FOGENABLE, FALSE );
pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
pDevice->SetRenderState( D3DRS_CLIPPING, TRUE );
pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
pDevice->SetRenderState( D3DRS_LOCALVIEWER, FALSE );
pDevice->SetTexture(0, NULL);
pDevice->SetTexture(1, NULL);
pDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
pDevice->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_POINT);//D3DTEXF_LINEAR);
pDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
pDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );
pDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE );
pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
pDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
pDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
// set up for 2D drawing:
{
D3DXMATRIX Ortho2D;
D3DXMATRIX Identity;
D3DXMatrixOrthoLH(&Ortho2D, (float)w, (float)h, 0.0f, 1.0f);
D3DXMatrixIdentity(&Identity);
pDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
pDevice->SetTransform(D3DTS_WORLD, &Identity);
pDevice->SetTransform(D3DTS_VIEW, &Identity);
}
}
void CSomeClass::RenderFractal()
{
// draw the texture fullscreen
m_lpDX->m_lpDevice->SetVertexShader( SPRITEVERTEX_FORMAT );
m_lpDX->m_lpDevice->SetTexture(0, m_fractal_tex);
m_lpDX->m_lpDevice->SetRenderState( D3DRS_SHADEMODE, D3DSHADE_GOURAUD );
// turn on bilinear texture filtering, to smooth out the display of the
// low-resolution fractal:
m_lpDX->m_lpDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
m_lpDX->m_lpDevice->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
D3DSURFACE_DESC sd;
if (m_fractal_tex->GetLevelDesc(0, &sd) != D3D_OK)
return;
// set up a quad
SPRITEVERTEX verts[4];
for (int i=0; i<4; i++)
{
verts[i].x = m_lpDX->m_client_width *0.5f * ((i%2)*2-1);
verts[i].y = m_lpDX->m_client_height*0.5f * ((i/2)*2-1);
verts[i].z = 1.0f;
verts[i].tu = (float)(i%2)*(m_fractal_texsize/(float)sd.Width );
verts[i].tv = (float)(i/2)*(m_fractal_texsize/(float)sd.Height);
DWORD alpha_opacity = 255; // note: only works if alpha blending enabled
DWORD r = 255;
DWORD g = 255;
DWORD b = 255;
verts[i].Diffuse = (alpha_opacity<<24) | (r<<16) | (g<<8) | b; // alpha channel here sets the opacity, if alpha blending turned on (see above + below)
}
// draw it
m_lpDX->m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, verts, sizeof(SPRITEVERTEX));
// turn alpha blending back off (regardless)
m_lpDX->m_lpDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
}
This document copyright (c)2002+ Ryan M. Geiss.
Return to faq page