Sign in to follow this  
smally

Unsure how to create a background

Recommended Posts

Hi, i'm currently unsure of how to go about doing my background in Direct3D 9. I'm making a small game (simple point and click e.g monkey island) that will have a 2D Texture as the background, with a few 3D models in the foreground. The only way I know on how to do this is to create a geometric quad, however I'm struggling to get the 'perfect' lookat camera angle to work (there are either gaps between the window edge and the quad or part of the quad is cut off from viewing). I'm not very familiar with sprites, as the book that I'm learning from only ever talks about the 3d stuff, plus I think I've read somewhere that its best to use quads anyway. How should I go about do this, and if using a quad is the best idea, how do I get the view right? Thanks in advance

Share this post


Link to post
Share on other sites
From your description you really want to be rendering your background in 2D rather than as a billboard/imposter.

It is perfectly normal to mix rendering like this, so just because you have some 3D elements doesn't mean everything must be 3D (although, strictly speaking its more "2D in 3D" as you still have a depth axis [smile]).

Use of ID3DXSprite is a nice and simple way of doing things, but manually creating a full-screen "transformed and lit quad" (aka 'TL Quad') is easy enough. Look up the D3DFVF_XYZRHW or POSITIONT vertex data formats.

hth
Jack

Share this post


Link to post
Share on other sites
This is how I've been doing this sort of thing.


d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

IDirect3DSurface9* backBuffer = NULL;
d3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&backBuffer);

// IDirect3DSurface9* background;
// Initialized outside the render loop!
d3dDevice->UpdateSurface(background,NULL,backBuffer,NULL);

d3dDevice->BeginScene();
// ... rest of render loop ...



No idea of the performance of this method compared to ID3DXSprite or a textured quad.

Share this post


Link to post
Share on other sites
Whilst not as terrible as messing with the raw backbuffer data, I'd suspect that your code is not as efficient as a full-screen TL quad. Then again, if you're not stressing out the GPU and performance isn't an issue it is a neat and elegant solution to the problem [smile]

Jack

Share this post


Link to post
Share on other sites
I've followed this websites advise http://nexe.gamedev.net/directKnowledge/default.asp?p=2D%20In%20Direct3D , and created the follow (bear in mind i'm using vertex elements in preparation for when I implement shaders)


struct VertPosTexRhw
{
VertPosTexRhw(float x, float y, float z, float RHW
float u, float v)
: p(x,y,z), t0(u,v), rhw(RHW) {}

D3DXVECTOR3 p;
float rhw;
D3DXVECTOR2 t0;

static LPDIRECT3DVERTEXDECLARATION9 Decl;
};

...

D3DVERTEXELEMENT9 VertPosTexRhwElements[] =
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
{0, 20, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 1},
D3DDECL_END()
};
device->CreateVertexDeclaration(VertPosTexRhwElements, &VertPosTexRhw::Decl);




and for creating the quad i've done:


void gfxBackground::BuildVertexBuffer(LPDIRECT3DDEVICE9 device)
{
device->CreateVertexBuffer(4*sizeof(VertPosTexRhw), // Number of vertices * Custom Vertex
D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_vb, 0);

VertPosTexRhw *v = 0;

m_vb->Lock(0, 0, (void**)&v, 0);

v[0] = VertPosTexRhw(-1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f);
v[1] = VertPosTexRhw( 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f);
v[2] = VertPosTexRhw( 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f);
v[3] = VertPosTexRhw(-1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 0.0f);

m_vb->Unlock();
}

...

void gfxBackground::BuildIndexBuffer(LPDIRECT3DDEVICE9 device)
{
device->CreateIndexBuffer(2*3*sizeof(WORD), // Number of triangles * 3 * sizeof
D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_ib, 0);

WORD *i = 0;

m_ib->Lock(0, 0, (void**)&i, 0);

i[0] = 0; i[1] = 1; i[2] = 2;
i[3] = 0; i[4] = 2; i[5] = 3;

m_ib->Unlock();
}

...

void gfxBackground::Display(LPDIRECT3DDEVICE9 device, const D3DMATERIAL9* mtrl, LPDIRECT3DTEXTURE9 tex)
{
device->SetTexture(0, tex);
device->SetMaterial(mtrl);

device->SetStreamSource(0, m_vb, 0, sizeof(VertPosTexRhw));
device->SetIndices(m_ib);
device->SetVertexDeclaration(VertPosTexRhw::Decl);

// Draw the background
device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 4, 0, 2);
// TRIANGLELIST, 0, 0, How many vertices, 0, How many triangles
}




But the quad doesn't show now? Am I miss using the rhw thing

Share this post


Link to post
Share on other sites
Quote:
Original post by jollyjeffers
Whilst not as terrible as messing with the raw backbuffer data, I'd suspect that your code is not as efficient as a full-screen TL quad. Then again, if you're not stressing out the GPU and performance isn't an issue it is a neat and elegant solution to the problem [smile]

Jack


I built a simple application to test your suspicion and found that using ID3DXSprite in place of an UpdateSurface call like I had above was about 8% faster in my test case (1024x768 windowed, no other geometry). Good to know.

Share this post


Link to post
Share on other sites
I think this type of problem comes up a lot because people don't realize how general-purpose Direct3D is. When I was learning D3D coming from a 2D game background I didn't realize the full flexibility you have to render things both '2D' and '3D' in the same frame. You're free to build up the frame buffer(s) with all kinds of pixels coming from different textures, vertex formats, models, whatever. It's totally up to you how to paint the blank canvas that you're given at the start of each frame. Whatever you render first (like a huge sprite or textured quad) will be in the background; don't worry about making sure it fits into your 'world' with the correct units, alignment, and all that garbage.

Share this post


Link to post
Share on other sites
As my understanding of Direct3D and ID3DXSprite goes, sprites are actually textured, lit quads. They just come wrapped in a handy interface to handle the complex bits for you.

Share this post


Link to post
Share on other sites
Ok, well I tried to get the POSITIONT stuff to work but either nothing shows up on screen or it just acts as a normal quad.

I've now implemented a sprite correctly, but I'm still struggling on how I get it to fit the window properly?

Share this post


Link to post
Share on other sites
Image files, when not in square power of two sizes get stretched by D3D when loaded into textures. If you want a background for a 1024x768 screen, make the image file 1024x1024 with a black (or any color) bar along the bottom. The stretching is default behavior and can be changed if the hardware supports non-square, non-pow2 texture sizes IIRC.

Share this post


Link to post
Share on other sites
Quote:
Original post by Cantos
If you want a background for a 1024x768 screen, make the image file 1024x1024 with a black (or any color) bar along the bottom.

I prefer an alternative method: create your image at some large resolution (at least 1365x1024) and then as your last step in an image editor, resize it (bilinear or better filtering!) to 1024x1024. Then, when you render the image fullscreen, it will be the correct aspect ratio AND contain more detail than the 'black bar' method (if your user chooses to run in a higher resolution) while still using the same amount of memory. Why fill valid space for pixel data with black?

Share this post


Link to post
Share on other sites
I'm also having problems creating a background using D3DXCreateSprite. I'd be very grateful for some ideas without spawning a new topic!

i) How can you ensure all 3d appears in front of the background?

ii) The displayed image is scaled larger than life and I don't know why. I would ideally like to get pixel perfect texture from the file. How can I avoid this?



// Load texture
if (FAILED(D3DXCreateTextureFromFile( m_pD3dDevice, L"..\\Clouds_over_hills.jpg", &g_pTexture)))
{
throw new turbine_exception("Could not find Clouds_over_hills.jpg");
}

// Create a sprite
if (FAILED(D3DXCreateSprite(m_pD3dDevice, &m_pSprite)))
{
throw new turbine_exception("Failed to create sprite");
}

D3DXVECTOR3 pos;
D3DCOLOR col = 0xffffffff;
pos.x = 0;
pos.y = 0;
pos.z = 1;

// render loop

m_pSprite->Begin(0);
m_pSprite->Draw(g_pTexture, NULL, NULL, &pos, col);
m_pSprite->End();


Share this post


Link to post
Share on other sites
What are the dimensions of Clouds_over_hills.jpg?

Quote:

I prefer an alternative method: create your image at some large resolution (at least 1365x1024) and then as your last step in an image editor, resize it (bilinear or better filtering!) to 1024x1024. Then, when you render the image fullscreen, it will be the correct aspect ratio AND contain more detail than the 'black bar' method (if your user chooses to run in a higher resolution) while still using the same amount of memory. Why fill valid space for pixel data with black?

Isn't that just needlessly complicated? You're never going to be seeing those extra 1024x256 pixels anyway.

Edit: Did not read your post correctly. Extra detail for higher resolutions. Got it.

Share this post


Link to post
Share on other sites
Ok, I've (finally) managed to create a good looking background using TL Quads that were mentioned to me. My background is like so:


void gfxBackground::BuildVertexBuffer()
{
g_device->CreateVertexBuffer(4*sizeof(VertPosRhwTex), // Number of vertices * Custom Vertex
D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &m_vb, 0);

VertPosRhwTex *v = 0;

m_vb->Lock(0, 0, (void**)&v, 0);

v[0] = VertPosRhwTex( 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f); // Top Left
v[1] = VertPosRhwTex( 1024.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f); // Top right
v[2] = VertPosRhwTex( 1024.0f, 768.0f, 1.0f, 1.0f, 1.0f, 1.0f); // Bottom right
v[3] = VertPosRhwTex( 0.0f, 768.0f, 1.0f, 1.0f, 0.0f, 1.0f); // Bottom left

m_vb->Unlock();
}



with VertPosRhwTex declared as:


struct VertPosRhwTex
{
VertPosRhwTex(float x, float y, float z, float rhw,
float u, float v)
: p(x,y,z,rhw), t0(u,v) {}

D3DXVECTOR4 p;
D3DXVECTOR2 t0;

static LPDIRECT3DVERTEXDECLARATION9 Decl;
};

...

D3DVERTEXELEMENT9 VertPosRhwTexElements[] =
{
{0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITIONT, 0},
{0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DDECL_END()
};
g_device->CreateVertexDeclaration(VertPosRhwTexElements, &VertPosRhwTex::Decl);



Now, i'm trying to configure it to get the best possible performance.

With a screen resolution of 1024x768 (also said when the vertex buffer is made) I have tried using a 1024x768 texture, 1024x1024 (stretch via photoshop, squash by d3d), and 1024x1024 with the top and bottom strips on (changing the vertex y positions to -128 or 896).

But I see no significant difference in frames per second, I assumed using a 1024x1024 texture would have been considerably faster?


also Zukix, I didn't understand why sprited were rather large, I think using this TL Quad i've just posted code for is very easy though and to make sure it's always in the background, just make the z value 1.0

Share this post


Link to post
Share on other sites
>> What are the dimensions of Clouds_over_hills.jpg?

1024x681
300 dpi
24 bit

I tried removing the extra to make it 1024x680 but no difference.

I will try smally's code! (thanks smally).

Share this post


Link to post
Share on other sites
Quote:

1024x681
300 dpi
24 bit

I tried removing the extra to make it 1024x680 but no difference.


Take your 1024x681 image into your image editor, turn off the "preserve aspect ratio" option, and stretch/resize it to 1024x1024, save it and see if that fixes your problem.

Quote:
But I see no significant difference in frames per second, I assumed using a 1024x1024 texture would have been considerably faster?


Well, the hardware is not doing anything significantly different when it squashes a whole texture, or uses only part of it, the performance difference is likely to be similarly insignificant. Compare your method to an ID3DXSprite implementation.

Share this post


Link to post
Share on other sites
Quote:
Original post by smally
With a screen resolution of 1024x768 (also said when the vertex buffer is made) I have tried using a 1024x768 texture, 1024x1024 (stretch via photoshop, squash by d3d), and 1024x1024 with the top and bottom strips on (changing the vertex y positions to -128 or 896).

As you're discovering, there are a lot of ways to get the same results in D3D so choose whatever methods work best for your application.

I still think NOT using 'strips' and resampling to the correct power-of-2 image size is the best general purpose solution. In addition to faring better at higher resolutions, you get coding advantages as well. If you start having 'strips' or 'black areas' in your image files then you either need more complicated classes or ugly hard-coding on a per-sprite basis so that your engine knows how to deal with them correctly.

In my 2D games I have to use such techniques for many sprites, not just the background, so getting away from 'pixels' internally has a lot of advantages. One of my favorite things about doing 2D stuff in Direct3D is the flexibility that using texture coordinates for quads gives you. If you have a sprite that's using a 256x256 texture, you can open that image in Photoshop, resize it to whatever dimensions you want (even ignoring the proportions if you need to, like making it 1024x64), hit save, and it will work with no changes to your code. It might not be as pretty if you overdo it, but it will work. (More realistically, you can use this power to have high-resolution images for development and resize them as needed as a last step before release.)

As far as performance, note that your vertex buffer creation flags (and locking flags, when applicable) are usually going to influence performance far more than the pixel content of your data. Make sure you get the static/dynamic stuff right, make sure you're not recreating vertex buffers every frame, stuff like that. Not that you are, but these errors can create huge bottlenecks.

Quote:
to make sure it's always in the background, just make the z value 1.0

Your textured quads don't even need to HAVE z values if you don't want; just turn z testing off and your render calls will overwrite whatever is in the backbuffer. If you're drawing a fullscreen quad, this effectively means that you'll erase whatever used to be there -- you don't even NEED to clear the backbuffer after every frame since you'll just be overwriting it anyways.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this