Matrices and Skybox

Started by
8 comments, last by brekehan 16 years, 8 months ago
Its been awhile since I've touched my graphics engine hobby project. I am currenty working on a skybox class. I am wondering how I can calculate the postion of each face, such that it fills the screen entirely, when the camera is looking dead center at that face? Given a Projection Matrix of:

D3DXMatrixPerspectiveFovLH(&mat,D3DX_PI/4, 1.0f, 1.0f, 100.0f);
and both the view and world matrices set to Identity. My geometry is currently set like so:

// Set the texture coordinates
m_textures[FACE_FRONT]->GetLevelDesc(0, &desc);
f = 0.5f / (float)(desc.Width);

vert[0].tu = 0.0f + f; vert[0].tv = 0.0f + f;
vert[1].tu = 0.0f + f; vert[1].tv = 1.0f - f;
vert[2].tu = 1.0f - f; vert[2].tv = 0.0f + f;
vert[3].tu = 1.0f - f; vert[3].tv = 1.0f - f;

// Set the vertex diffuse
vert[0].color = vert[1].color = vert[2].color = vert[3].color = 0xFFFFFFFF;

// Set the vertex position
f = m_size * 0.5f;
vert[0].position = D3DXVECTOR3( -25, -25,  100);
vert[1].position = D3DXVECTOR3( -25,  25,  100);
vert[2].position = D3DXVECTOR3(  25, -25,  100);
vert[3].position = D3DXVECTOR3(  25,  25,  100);
	
// Render the face
m_device->SetFVF( D3DFVF_XYZ_DIFFUSE_TEX1 );
m_device->SetTexture(0, m_textures[FACE_FRONT]);
m_device->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
m_device->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_device->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );

m_device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, (LPVOID) vert, sizeof(VERTEX_XYZ_DIFFUSE_TEX1));
The 25s are just a guess, I want a good calculation to take thier place.
Advertisement
It doesn't matter, since it's all relative. As long as you're inside of it, you'll always be surrounded on all 6 directions by skybox.

Changing the size of the skybox won't change anything either. Because as the vertices move away, the face becomes larger. And because the face moves away, it shrinks. The net result is that it stays exactly the same size if you're sitting at the centre of the skybox (which you always should be).

Besides, you're already achieving your goal. You have a 90 degree FOV and a 1:1 aspect ratio set, meaning that if you look down the positive Z axis (for example), the area of exactly 1 face will fill your screen.
NextWar: The Quest for Earth available now for Windows Phone 7.
I have to argue that it seems to matter very much! I base this on a test, as I change the position of the vertices back and forth, which is turn is scaling the face, as the program is running I can observe the following:

A) The face needs imho to be set at the far clipping plane in order to allow for other objects in the scene to be visible and not occluded by the skybox. If I have some 3d object at a Z of 99 untis and the front face of the sky box is at 50, then I will not see the 3d object.

B) With the face set at my far clipping plane, the image is stretched, losing quality in direct relation to its scale. I can smoosh the entire texure together making a single point of light, or stretch it out, causing the pixels to look blocky. I proposed to use a texture that was almost screen size for the best detail. Of course screen size may change, in which case I could use a differant sized texture.

So, while you are correct that as long as I am inside the box it will give the illusion of being in an environment, I still want to use certain calculations to achieve the best looking quality of that illusion.

While I am still would classify myself as a noob, I am fairly confident in the above two points, strictly on observation as I am tinkering with the differant values and watching the results first hand. If I am incorrect, and I might be, then please explain my observations.

On a side note, as I was napping I thought perhaps I should just not use a projection matrix at all when drawing the skybox? Set it to identity and then back when I am done. That would make for a parallel projection? Which leads me to another question: in order to visualize the layout of my scene properly in my head I must understand the criteria for the occlusion calculations that DirectX is going to perfom on its own. How does it determine when one object is in front of another? Is it based upon model, view, or world coordinates or all three?




EDIT: After smoking a cigarette, I must argue with myself. If I am only looking at one face and have not yet drawn the box, then my box can only be a perfect square when the width and height of the face are equal to 2X the distance from the origin, because the distance from the camera is going to be 1/2 the height of the top face. So in theory, it should fill the screen at a distance of 100, when it has a width and height of 200?

However, when I run my app with a width and height of 190 it still more than fills my screen and the outside parts of my texture are not visible. I guess I have to figure out why that is!?

Here is a pic with the folling vertices for the front face:
vert[0].position = D3DXVECTOR3( -35, -35, 100);
vert[1].position = D3DXVECTOR3( -35, 35, 100);
vert[2].position = D3DXVECTOR3( 35, -35, 100);
vert[3].position = D3DXVECTOR3( 35, 35, 100);

ftp://christopherpisz.is-a-geek.com/pub/images/35s.jpg

---------------------------------------------------------------
Here is a pic with the folling vertices for the front face:
vert[0].position = D3DXVECTOR3( -100, -100, 100);
vert[1].position = D3DXVECTOR3( -100, 100, 100);
vert[2].position = D3DXVECTOR3( 100, -100, 100);
vert[3].position = D3DXVECTOR3( 100, 100, 100);

ftp://christopherpisz.is-a-geek.com/pub/images/100s.jpg

//--------------------------------------------------------------

Why am I seeing these results?



[Edited by - brekehan on August 5, 2007 1:00:02 AM]
Quote:Original post by brekehan
I have to argue that it seems to matter very much! I base this on a test, as I change the position of the vertices back and forth, which is turn is scaling the face, as the program is running I can observe the following:

A) The face needs imho to be set at the far clipping plane in order to allow for other objects in the scene to be visible and not occluded by the skybox. If I have some 3d object at a Z of 99 untis and the front face of the sky box is at 50, then I will not see the 3d object.

B) With the face set at my far clipping plane, the image is stretched, losing quality in direct relation to its scale. I can smoosh the entire texure together making a single point of light, or stretch it out, causing the pixels to look blocky. I proposed to use a texture that was almost screen size for the best detail. Of course screen size may change, in which case I could use a differant sized texture.

So, while you are correct that as long as I am inside the box it will give the illusion of being in an environment, I still want to use certain calculations to achieve the best looking quality of that illusion.

While I am still would classify myself as a noob, I am fairly confident in the above two points, strictly on observation as I am tinkering with the differant values and watching the results first hand. If I am incorrect, and I might be, then please explain my observations.

That's happening because you're scaling your face along 2 axes - and ignoring the third. For example, if you were scaling the positive Z face, and you increase the X and Y size, the face would appear larger and stretched. But this is expected since you ignored the Z scaling.

It's akin to taking a cube, and stretching it on the X and Y axes. You end up with a rectangular prisim, with a square base but less depth. What the effect is, is that your square face gets larger but does not move away like it should.

A normal skybox is comprised of 8 vertices - one at each corner of the box. When you scale the entire box up, the vertices move further away from the origin (the camera). This means that the top-left vertex in view will not only move further left and up, but also further in (depth).
NextWar: The Quest for Earth available now for Windows Phone 7.
Looks like you replied while I was editing. Please take a look at the edited part. I agree with you, but can't understand the results in the pics. A perfect cube would have the following for its front face vertices:

vert[0].position = D3DXVECTOR3( -100, -100, 100);
vert[1].position = D3DXVECTOR3( -100, 100, 100);
vert[2].position = D3DXVECTOR3( 100, -100, 100);
vert[3].position = D3DXVECTOR3( 100, 100, 100);

but my results in the pic seem to stretch the texture far past the edges of the screen. I appreciate your help btw, I doubt I can get to the bottom of this myself. The things that seem so simple always end up being the hardest!

Quote:Original post by brekehan
On a side note, as I was napping I thought perhaps I should just not use a projection matrix at all when drawing the skybox? Set it to identity and then back when I am done. That would make for a parallel projection? Which leads me to another question: in order to visualize the layout of my scene properly in my head I must understand the criteria for the occlusion calculations that DirectX is going to perfom on its own. How does it determine when one object is in front of another? Is it based upon model, view, or world coordinates or all three?

Writing to the depth buffer occurs during rasterization. That is, when it draws pixels to the render target, it draws to the Z-buffer as well. This is why people recommend rendering front-to-back (meaning, render your skybox LAST). So the front-most objects are rendered first, write to the Z-buffer, and then occlude the rest of the scene behind them. If you render back-to-front, it means that you basically draw everything.
NextWar: The Quest for Earth available now for Windows Phone 7.
Quote:Original post by brekehan
Looks like you replied while I was editing. Please take a look at the edited part. I agree with you, but can't understand the results in the pics. A perfect cube would have the following for its front face vertices:

vert[0].position = D3DXVECTOR3( -100, -100, 100);
vert[1].position = D3DXVECTOR3( -100, 100, 100);
vert[2].position = D3DXVECTOR3( 100, -100, 100);
vert[3].position = D3DXVECTOR3( 100, 100, 100);

but my results in the pic seem to stretch the texture far past the edges of the screen. I appreciate your help btw, I doubt I can get to the bottom of this myself. The things that seem so simple always end up being the hardest!

Ah, I made a miscalculation. Earlier I said that your FOV was set to 90 degrees - it is in fact set to 45 degrees (pi / 4 radians). Set it to 90 degrees and you will see the correct results for that example.

My other posts still apply, though. But also keep in mind that few monitors support 1:1 aspect ratios (and if they do, they're used extremely rarely or never).

But really, I must ask: why exactly are you so adamant on having one face of your skybox fit exactly 1 screen?
NextWar: The Quest for Earth available now for Windows Phone 7.
You are a genious! Can I email you a beer? It worked like a champ when I set the perspective to pi/2!

To answer the question, I just want my skybox to be as detailed as I can get it and not suffer any stretching which makes it look blocky. So I figure if I draw it at the same resolution as my screen and render it the same way, then that is as much detail as I can possibly get.

Some of the images I plan on using could take weeks in photoshop and I want them to look as spiffy as possible. Of course that is going to suck up more memory having all those huge textures, but it is a starting point.
Just one thing that I must note:

If you intend this to be an actual skybox (which I assume you do), then all of the stuff in this thread is rather irrelevant. As I said, if you make this an actual box that you're at the center of, it no longer matters how large the box is. The quality of the texture remains exactly the same.

If you don't believe me, then try this:
Set the vertices of your face to represent the front face of a unit cube. That is:
vert[0].position = D3DXVECTOR3(-1, -1, 1);
vert[1].position = D3DXVECTOR3(-1, 1, 1);
vert[2].position = D3DXVECTOR3(1, -1, 1);
vert[3].position = D3DXVECTOR3(1, 1, 1);

Run your program. It will look exactly the same. This is because even though the face was shrunk in size, it was brought closer (thereby negating the effect).

Now change it to:
vert[0].position = D3DXVECTOR3(-42, -42, 42);
vert[1].position = D3DXVECTOR3(-42, 42, 42);
vert[2].position = D3DXVECTOR3(42, -42, 42);
vert[3].position = D3DXVECTOR3(42, 42, 42);

Once again, you'll see that it will look exactly the same. In this way, you can see that it doesn't matter how much you scale it by; as long as you scale all 3 axes by the same amount, it will always look the same.

I'd also like to point out that forcing a 1:1 aspect ratio is a rather unrealistic approach. No monitors run on 1:1... many CRTs run at 4:3 (such as 1024x768) and many LCDs run at 5:4. With widescreen resolutions (such as 16:10), a 1:1 perspective aspect ratio in your program will look rather bad. I suggest that you adjust your aspect ratio depending on the resolution - rather than a fixed amount. This will actually increase the quality on non-1:1 aspect resolutions, since you're not stretching everything out horizontally.

Of course, following the above advice also requires that you abandon the quest to have 1 face fit on exactly 1 screen of space. In any case, it isn't a particularly realistic goal, and the benefits (if any) are far outweighed by the disadvantages.

Hope this helps.
NextWar: The Quest for Earth available now for Windows Phone 7.
Absolutly correct. I chose 100 because I was worried about other object getting occluded. But if that has no effect on occlusion, as you said occlusion occurs at the time of rasterization, then my assumptions were incorrect. I think I will need to start reading up on the z buffer next.

I can't thank you enough for your help! I've learned more in this short time tonight than I would have in weeks sitting in the cs graphics classroom. It is so rare finding people that are willing to help on the net, and your replies were very speedy.

This topic is closed to new replies.

Advertisement