Home » Community » Forums » Graphics Programming and Theory » Water reflections
  Intel sponsors gamedev.net search:   
[Control Panel] [Register] [Bookmarks] [Who's Online] [Active Topics] [Stats] [FAQ] [Search]

Add Forum to Favorites |  Send Topic To a Friend | View Forum FAQ | Track this topic

Page:   1 2 3 »»

 Last Thread Next Thread 
 Water reflections
Post New Topic  Post Reply 
Hey all,

Well, I'm really stuck with something that I'm certain should be rather simple. I'm trying to render my terrain to texture so that I can project it as a reflection on my water surface. Now I've got no problem with the render to texture part, but setting up the view/world/projection matrices has got me stumped. I've read the VCP article here on Gamedev, but to be honest it just confuses me, in particular, the part about moving mirror vertices. Am I just being thick, but why would I want to move my mirror vertices when all I want to do is change the position and orientation of the camera to render the view from the mirrored camera position, I don't understand why vertices need to be touched at all?

So basically I guess my question is how do I reflect the camera position/orientation so that it is in the correct place to render the reflection? I'm using DirectX 8.1 in case that helps.

My guess would be that

Camera.xPos stays the same
Camera.yPos = -(y distance from mirror plane)
Camera.zPos stays the same

Camera orientation, clueless on this one... mm anyways I'll continue reading up and perhaps find the answer on my own, but if anyone can provide a simpler explanation than the article on how to do this, I'd really appreciate the help.

Cheers,

Steve AKA Mephs


 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

I'm not sure exactly what you're trying to do, but probably the easiest way to do this would be to invert the terrain's transformation matrix (so that it becomes upside down under the water) and render it again. If you render the water to the stencil buffer, then you can clip the terrain against the stencil test. You'd apply some kind of blending mode to the water, of course, so that it would be water, rather than a clean mirror

 User Rating: 1895   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Does inverting the geometry have the same effect as mirroring the camera position/orientation though? I'm somewhat confused by it all, but would this not produce anomalies due to the viewing angle not being taken into account? Maybe it wouldn't but like I say.... I'm confused!

Basically I'd like the camera to be mirrored in position relative to the mirror plane, with its direction vector being the same as that of a reflected light vector from the original view, dos this achieve the same effect or is it just an approximation?

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Assuming your water plane is at y = 0, and y is the up axis, then the easiest method is to simply multiply the view matrix with a mirror matrix like this one:
1  0  0  0
0 -1  0  0
0  0  1  0
0  0  0  1

That will flip the geometry (or the camera, it's the same, just seen from a different coordinate frame) around the Y axis.

If your plane is not exactly at y=0, then you need to adjust the translation part accordingly.


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Okay, I've tried that, but didn't get the expected results. Perhaps I overlooked something. What I still don't get though, is that if it is as simple as creating a reflection matrix (which can be done with D3DXMatrixReflection), then why go into the huge number of steps taken in the Virtual Camera Position article, it just seems to be a hugely long way around the problem, or am I missing something?

Thanks for the response, and hopefully sooner or later I'll have it up and running :P

Steve

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

I haven't read the article you mentioned, but there are various ways to achieve what you want. They are all conceptually equivalent, but operate in different coordinate systems. The one I mentioned (or in more general form - a simple plane reflection matrix) works in world space. So it should be applied before doing any camera dependent transforms. That's the usualy chain of transforms:

Object -> World -> *Reflect* -> Camera transform -> Projection

So yes, it's as easy as inserting a single reflection matrix in this chain. Theoretically, you can insert it anywhere you want, but it has to match the geometry's coordinate frame at that position. Inserting it after the local object to world transform, and before the camera transform, is most intuitive for water IMO. It lets you specify the reflection matrix in world space coordinates.

Can you post the matrices you use for transformation, and the order you multiply them ?


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

// Grab the view transformation
m_Graphics->GetDeviceCOM()->GetTransform(D3DTS_VIEW, &view_matrix);

// Set up a reflection plane
reflect_plane.a = 0;
reflect_plane.b = 1;
reflect_plane.c = 0;
reflect_plane.d = 0;

// Create a reflection matrix and multiply it with the view matrix
D3DXMatrixReflect(&reflect_matrix, &reflect_plane);
D3DXMatrixMultiply(&view_matrix, &view_matrix, &reflect_matrix);

// Set mirrored view matrix for render to texture
m_Graphics->GetDeviceCOM()->SetTransform(D3DTS_VIEW, &view_matrix);

// Render terrain to texture here

// Reflect the view matrix back into its original form ready to render to backbuffer
D3DXMatrixReflect(&reflect_matrix, &reflect_plane);
D3DXMatrixMultiply(&view_matrix, &view_matrix, &reflect_matrix);

// Render terrain to backbuffer here


That's pretty much what I'm currently doing, the D3DX function builds the reflection matrix for me, and creates the matrix you showed earlier (checked when debugging).


I now seem to be getting anomalies with my fog that I hadn't previously noticed, and I'm not sure if they were present before, or if it's due to the view matrix tampering, but that could be a clue?

I saw something in another thread about reversing the cullmode to get correctly rendered geometry in the render to texture, is this necessary or should I be ok with the CCW culling I'm doing at present?

Thanks!

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

You left out the interesting part: how do you create the view matrix in the first place ? In that code snippet, you simply multiply the current view matrix with the reflection matrix, but the relationship between your view matrix and the camera transform is not clear. What kind of effects do you get with that code ? Does the geometry reflect ? Or what else does it do ?

About the culling, yes you need to reverse it, since you're applying a negative scale which will invert the vertex order.


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Hmm, not sure what you mean there. My view matrix is created by the cCamera class, which creates the view matrix using the D3DXMatrixLookatLH function. I basically feed in a position and rotation each frame and it calculates my view matrix for me.

Aside from that, I've found what is causing the anomaly with the fog. It appears that some of the terrain I'm rendering to texture is being rendered in the backbuffer or the zbuffer (or rather perhaps it is still affecting the old buffer after making the switch) which is causing the terrain rendered to texture to occlude some of the actual terrain with the fog colour. I'm guessing that it could be that when the render to texture part does it's job, it leaves it's zwrites on the zbuffer or something like that, even though I switch back to the previous zbuffer... not quite sure why that's happening.



Above is an image of the current terrain, along with it's reflection (I've now reversed culling on render to texture). I still get the anomaly I mentioned though but I'll sort. Does it look about right?

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:
Original post by Mephs
It appears that some of the terrain I'm rendering to texture is being rendered in the backbuffer or the zbuffer (or rather perhaps it is still affecting the old buffer after making the switch) which is causing the terrain rendered to texture to occlude some of the actual terrain with the fog colour. I'm guessing that it could be that when the render to texture part does it's job, it leaves it's zwrites on the zbuffer or something like that, even though I switch back to the previous zbuffer... not quite sure why that's happening.

Well normally you'd ask for a render texture with it's own zbuffer. There should be no interference with the framebuffer once the switch to the RTT was made. Are you clearing the zbuffer of the RTT before use ?

Quote:
Original post by Mephs
Above is an image of the current terrain, along with it's reflection (I've now reversed culling on render to texture). I still get the anomaly I mentioned though but I'll sort. Does it look about right?

Hmm. Where is your water plane in that screenshot ? It looks like the geometry is reflected around the up camera axis, unless you have the camera at water level - in that case it would be OK. Otherwise, you're doing the reflection in camera space, not in world space - a quick fix would be to reverse the matrix multiplication order from view*reflect to reflect*view.

 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

The water plane is at y=0 for simplicities sake at the moment. I found the zbuffer artifact was due to not realizing that the GetDepthStencilBuffer() function of a D3DDevice returns the current depth buffer, not the standard depthbuffer. I made this mistake as GetBackBuffer() returns the backbuffer as opposed to the rendertarget buffer. I assumed GetDepthStencilBuffer worked in the same way.

Anyhoo that's all fixed now, and my reflection now look correct!! weee! Sorry for so many questions, but at least it works now.

Now I just have to figure out how to generate projected exture co-ordinates and I'll be able to see the reflection in real-time instead of hacking my way to it :P

Thanks a lot :)


 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Yep, that looks correct. Did you reverse the matrix multiplication order or did you keep it as is ?

The projective texturing is not very hard either, now that you got this thing working. It's basically the exact same matrix you used for rendering the RTT, only multiplied with your current projection matrix and an additional remapping matrix. Load that into the texture matrix of the texture unit you want to project from (is it also called texture matrix in D3D ? I think so), and feed the unit the same 3D vertex positions you used for rendering, only as texcoords this time.

Quote:

Sorry for so many questions, but at least it works now.

Keep asking if you still have problems, that's what this forum is for after all.


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Thanks again for the reply. I did indeed reverse the multiplication of matrices.

I'm having some difficulty understanding the projective texture coordinate mapping though. DX does indeed use texture matrices, but as I am not using pre-transformed vertices at the moment, I'm unable to use texture matrices. I am however using a vertex shader program, which I could use to the same effect (finally learnt vertex shaders!).

I assume you mean that I take the RTT matrix (reflected view matrix) and multiply with the projection matrix (in that order?). I'm lost on the remapping matrix? What is that exactly, what kind of matrix am I looking to make to do this remapping?

I'm fine with loading in the resulting matrix though, I can just load it as a VS constant and dp4 each of the components with the respective row of the result matrix, into a temp register (correct?).

Now, feeding the vertex data into the texture coordinates, i imagine that you mean that I would lock the vertex buffer, set the texture coordinates so that u = vertex x position and v = vertex y position? Or should I somehow be looking to make use of the x y AND z components? I assume though that I'm just using the x and z components and simply mul'ing with the temp registers holding the earlier results.

Am I going along the right lines or am I lost again... !?

Thanks as always!

Steve


Oh I should also mention DX has a setting D3DTTFF_PROJECTED which does something to do with dividing texture coordinates by the previous two co-ordinates or something, is this useful or unecessary with your method?

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:
Original post by Mephs
Thanks again for the reply. I did indeed reverse the multiplication of matrices.

OK, that's what I thought.

Quote:

I'm having some difficulty understanding the projective texture coordinate mapping though. DX does indeed use texture matrices, but as I am not using pre-transformed vertices at the moment, I'm unable to use texture matrices. I am however using a vertex shader program, which I could use to the same effect (finally learnt vertex shaders!).

Hmm, what do you mean by pre-transformed coordinates ? I know that D3D lets you specify pretransformed vertices, but that should be independent of the texture matrix (since it will only be applied to texture coordinates anyway) ?

Quote:

I assume you mean that I take the RTT matrix (reflected view matrix) and multiply with the projection matrix (in that order?).

OK, let's see - in OpenGL, it's Remap * Projection * View, so in D3D it should be the inverse (due to the inverted order of matrices): View * Projection * Remap should do it.

Quote:

I'm lost on the remapping matrix? What is that exactly, what kind of matrix am I looking to make to do this remapping?

OK, what I'm going to explain here is from OpenGL, I'm not entirely sure if it's exactly the same in D3D. In OGL, texture coordinates go from 0 to 1, but transformed device coordinates go from -1 to 1. I guess it should be the same for D3D, if not you'll have to adjust some numbers. The remapping matrix simply remaps the -1 to 1 range to the 0 to 1 range used for indexing the texture (as you can't properly index the texture with a -1 to 1 value, it would wrap around !). To do that, you have to divide the value by 2 (scale by 0.5), and add 0.5 (translate by 0.5). In matrix form, it looks like this:
0.5 0   0   0
0   0.5 0   0
0   0   0.5 0
0.5 0.5 0.5 1

(I hope I got that right for D3D)

Quote:

I'm fine with loading in the resulting matrix though, I can just load it as a VS constant and dp4 each of the components with the respective row of the result matrix, into a temp register (correct?).

Yes, correct.

Quote:

Now, feeding the vertex data into the texture coordinates, i imagine that you mean that I would lock the vertex buffer, set the texture coordinates so that u = vertex x position and v = vertex y position? Or should I somehow be looking to make use of the x y AND z components? I assume though that I'm just using the x and z components and simply mul'ing with the temp registers holding the earlier results.

No no, you will use all 3 vertex components (x,y,z) and theoretically also w, but this is usually one on standard geometry. Asuuming you're using a vertex shader, no need to lock any additional vertex buffers. Just use the vertex coordinates as vector into the texture matrix we talked about above.

Here is an example, it's for OpenGL, but you should be able to easily port it to a D3D vs.

First, we transform the vertex position by the combined view-projection matrix, as usual (you can also do two separate DP4 sets, one for the view and one for the projection matrix. But combining both is more efficient). As you can see, we just transform the original 4D vertex position (x,y,z,1) by the view-projection matrix, and write the result to the transformed vertex position register:
DP4 temp.x, ViewProjMatrix[0], vertex.position;
DP4 temp.y, ViewProjMatrix[1], vertex.position;
DP4 temp.z, ViewProjMatrix[2], vertex.position;
DP4 temp.w, ViewProjMatrix[3], vertex.position;
MOV result.position, temp;


Now the projective texture coordinates. We use the exact same original 3D vertex positions as above, but transform it by the texture matrix (View * Project * Remap) and write the resulting 4D vector into the texture coordinates of some texture unit:
DP4 temp.x, TextureMatrix[0], vertex.position;
DP4 temp.y, TextureMatrix[1], vertex.position;
DP4 temp.z, TextureMatrix[2], vertex.position;
DP4 temp.w, TextureMatrix[3], vertex.position;
MOV result.texcoord, temp;


That's basically it.

Quote:

Oh I should also mention DX has a setting D3DTTFF_PROJECTED which does something to do with dividing texture coordinates by the previous two co-ordinates or something, is this useful or unecessary with your method?

Yes, it's absolutely necessary, as it will enable the perspective divide on texture coordinates (dividing the first three components by the fourth one: a homogeneous divide). Without this step, you won't get the projection.

 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Hi again!

Well now my reflections finally look just about as expected!

Some things to take note of though. All matrices needed transposing before multiplication (except the remapping matrix which was fine as Yann suggested), I believe this is something to do with the dp4 instruction working in reverse order? I followed the matrix multiplications suggested and the result is below :



However, when close to the water I get the following anomaly as the texture wraps (I can't understand why it is wrapping, but when clamped it looks worse).



Any ideas what is causing that? Aside from that though, I'm very pleased with the progress so far, I had expected this would take me at least a week or two to figure out, and I've done most of the work in a couple of days :)

*EDIT* Okay, fixed the problem there by tinkering with the remapping matrix, but I have one final problem.

I think I need to be clipping geometry away in the render to texture stage, either with clipping planes, an alpha test or a stencil test. Am I right in thinking this, or is there a simpler way of removing the problem pictured below (which I'm assuming is arising from not clipping geometry, but perhas it could be something else?)





[Edited by - Mephs on June 26, 2004 12:11:18 PM]

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:
Original post by Mephs
*EDIT* Okay, fixed the problem there by tinkering with the remapping matrix, but I have one final problem.

What did you change ?

Quote:

I think I need to be clipping geometry away in the render to texture stage, either with clipping planes, an alpha test or a stencil test. Am I right in thinking this, or is there a simpler way of removing the problem pictured below (which I'm assuming is arising from not clipping geometry, but perhas it could be something else?)

It's hard to tell the origin of such artifacts from one image alone, but you're perfectly right about the clipping. If you didn't account for the clipping against the water plane, then you'll get visual artifacts.

You have different options when it comes to clipping. The most simple one is an additional geometric clip plane, but some hardware (especially from nvidia) can have problems with those. Alpha test is a possibility, but will use up your alpha and will prevent the early z rejection optimization to kick in. Another alternative is to use a pixelshader to remove pixels above the clipping plane. This is effective, but requires an additional pixelshader. Unfortunately, you can't use the stencil buffer for arbitrary 3D clipping.

And then there is also oblique frustum clipping, which might very well be the best alternative - although a little tricky to implement. It basically modifies your projection matrix in such a way, that the near frustum plane is remapped onto the clip plane, effectively substituting it. The whole clipping mechanism is then reduced to standard frustum clipping, all you have to do is upload a special projection matrix. It works on every hardware, and doesn't require any additional resources. There are unfortunately not too many resources about this technique available. Try Google. nVidia used to have a demo, but they took it down because it was buggy. If you select this option, let me know, and I'll post some more resources and code.

[Edit] Here is sample code for optimized (and bug free) oblique frustum clipping from Eric Lengyel.


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Hiya,

Well I'm at work right now on a break, so I don't have access to my code, but I'll post some when I get home to show what I have right now. I think I had to do something along the lines of mutliplying my reflect matrix with the remap matrix, or tranpossing it or both, I'll take a look when I get home and update the thread with it.

Thanks for the advice with the clipping, I'll certainly give the oblique clipping method a try, as you say, using up the alpha might not be such a great idea, and I seemed to have problems trying to set up a hardware clipping plane in that it didn't seem to have any effect at all, though that may be due to not setting up the plane correctly (and yes, I'm using a Geforce FX5700). I also noticed that the artifacts got worse if I move the water/reflection plane away from 0. I had to adjust my reflection plane coefficients to a=0, b=1, c=0, d= -5.0 (negative 5.0) when my water plane was at a height of 5.0 units to get anything resembling a correct reflection, I don't know if that helps identify my problem a little better? Most of the time the reflection looks correct, but sometimes I appear to get reflections coming from nowhere, which may be clipping, or it could be something with my projected texture co-ordinates not lining up correctly I guess?

Anyhoo, I'll post code when I get home,

Cheers,

Steve



 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

My water rendering code is below, this does not include the code used to render the reflection, only the code used to map the reflection to the water surface :
D3DXMATRIX projection_matrix, reflect_matrix, view_matrix, world_matrix, 
		viewprojremap_matrix, viewproj_matrix, remap_matrix;
	D3DXPLANE reflect_plane;
	D3DXVECTOR4 FogParams, MirrorParams;
	
	// Set the world transform for the water
	m_WorldPosition.Update(m_Graphics);
	m_Graphics->SetWorldPosition(&m_WorldPosition);

	// Plane used to reflect various matrices
	reflect_plane.a=0;
	reflect_plane.b=1;
	reflect_plane.c=0;
	reflect_plane.d=-5.0;
	
	// .x = fog start
	// .y = fog end
	// .z = 1/range
	// .w = fog max
	
	FogParams.x = 150.0f;
	FogParams.y = 200.0f;
	FogParams.z = ((float)1/50);
	FogParams.w = 1.0f;
	
	// .x = Water plane height
	// .y = Unused
	// .z = Water texture scroll modifier
	// .w = Unused

	MirrorParams.x = 5.0f;
	MirrorParams.y = 5.0f;
	MirrorParams.z += 0.0001f;
	MirrorParams.w = 5.0f;

	// Get transform matrices
	m_Graphics->GetDeviceCOM()->GetTransform(D3DTS_WORLD, &world_matrix);
	m_Graphics->GetDeviceCOM()->GetTransform(D3DTS_PROJECTION, &projection_matrix);
	m_Graphics->GetDeviceCOM()->GetTransform(D3DTS_VIEW, &view_matrix);

	// Transpose matrices ready for vertex shader dp4
	D3DXMatrixTranspose(&view_matrix, &view_matrix);
	D3DXMatrixTranspose(&projection_matrix, &projection_matrix);

	// Set remap matrix
	remap_matrix._11=0.5;
	remap_matrix._12=0;
	remap_matrix._13=0;
	remap_matrix._14=0.5;
	remap_matrix._21=0;
	remap_matrix._22=0.5;
	remap_matrix._23=0;
	remap_matrix._24=0.5;
	remap_matrix._31=0;
	remap_matrix._32=0;
	remap_matrix._33=0.5;
	remap_matrix._34=0.5;
	remap_matrix._41=0;
	remap_matrix._42=0;
	remap_matrix._43=0;
	remap_matrix._44=1.0;
	
	// Create view * projection matrix
	D3DXMatrixMultiply(&viewproj_matrix, &projection_matrix, &view_matrix);
	
	// Create & transpose (for dp4) reflection matrix
	D3DXMatrixReflect(&reflect_matrix, &reflect_plane);
	D3DXMatrixTranspose(&reflect_matrix, &reflect_matrix);

	// Reflect the remap matrix
	D3DXMatrixMultiply(&remap_matrix, &reflect_matrix, &remap_matrix);
	
	// Create a mirrored version of view * projection * remap matrix
	D3DXMatrixMultiply(&viewprojremap_matrix, &projection_matrix, &view_matrix);
	D3DXMatrixMultiply(&viewprojremap_matrix,   &remap_matrix,&viewprojremap_matrix);

	// Set shader constants
	m_Graphics->GetDeviceCOM()->SetVertexShaderConstant(0, viewproj_matrix, 4);
	m_Graphics->GetDeviceCOM()->SetVertexShaderConstant(8, viewprojremap_matrix, 4);;
	m_Graphics->GetDeviceCOM()->SetVertexShaderConstant(23, MirrorParams, 1);
	m_Graphics->GetDeviceCOM()->SetVertexShaderConstant(16, FogParams, 1);

	

	// Working with a flat plane for the time being, water solver unused
	// m_WaterSolver->Simulate();
	
	//if(Random > 90)
	//	m_WaterSolver->ApplyForce(RandomX,RandomY,Random2);
	
	//sVertex *pVB;
	//m_WaterVB->Lock(0,0,(BYTE**)&pVB,0);
	//for(int y=0;y<m_NumVertsY;y++)
	//{
	//	for(int x=0;x<m_NumVertsX;x++)
	//	{
	//		pVB[(y*m_NumVertsX)+x].y = m_HeightField[(y*m_NumVertsX)+x];
	//	}
	//}
	//m_WaterVB->Unlock();

	// Set up for projected textures (count4 divides by previous 3 elements)
	m_Graphics->GetDeviceCOM()->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, 
                                D3DTTFF_COUNT4 | D3DTTFF_PROJECTED );

	// Set the render/texture stage states
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX,0);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ALPHAOP,  D3DTOP_SELECTARG1 );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ALPHAARG1,   D3DTA_DIFFUSE);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ALPHAARG2,   D3DTA_TEXTURE );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_ADDRESSW, D3DTADDRESS_WRAP);
	
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX,1);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT );
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_COLOROP,   D3DTOP_MODULATE2X);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_RESULTARG, D3DTA_CURRENT);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
	m_Graphics->GetDeviceCOM()->SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
	m_Graphics->GetDeviceCOM()->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
	
	m_Graphics->EnableZBuffer(TRUE);
	m_Graphics->EnableAlphaBlending(FALSE);
	
	// Set index/vertex stream
	m_Graphics->GetDeviceCOM()->SetIndices(m_WaterIB,0);
	m_Graphics->GetDeviceCOM()->SetStreamSource(0,m_WaterVB,sizeof(sVertex));
	
	// Set vertex shader
	m_Graphics->GetDeviceCOM()->SetVertexShader(m_shader_handle);
	
	// Set textures
	m_Graphics->GetDeviceCOM()->SetTexture(0, Reflection->GetTextureCOM());
	m_Graphics->GetDeviceCOM()->SetTexture(1, m_WaterTexture.GetTextureCOM());

	// Render water
	m_Graphics->GetDeviceCOM()->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,(m_NumVertsX*m_NumVertsY),0, ((m_NumVertsX-1)*(m_NumVertsY-1))*3);

	// Disable projective texturing
	m_Graphics->GetDeviceCOM()->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );


Next is my vertex shader :
vs.1.1
;c0 = view * proj matrix
;c8 = view * proj * remap matrix
;c16 = fog parameters
dp4 r0.x, v0, c0 ;emit projected position
dp4 r0.y, v0, c1 ;emit projected position
dp4 r0.z, v0, c2 ;emit projected position
dp4 r0.w, v0, c3 ;emit projected position
mov oPos, r0 ; Set vertex positions

dp4 r10.x, c8, v0;
dp4 r10.y, c9, v0;
dp4 r10.z, c10, v0;
dp4 r10.w, c11, v0; multiply view * proj * remap matrix with vertex position
 
mov oT0, r10; output texture coordinates for reflection

add oT1.xy, v7.xy, c23.z; add texture scroll modifier to tex coords for water texture

mov oD0, v5; output diffuse colour

; Scale by fog parameters :
; c16.x = fog start
; c16.y = fog end
; c16.z = 1/range
; c16.w = fog max
dp4 r2, v0, c2 ; r2 = distance to camera
add r3.x, c16.y, -r2.x; (end - distance )
mul oFog.x, c16.z, r3.x ; Normalized distance * (end-distance) 


Finally, for good measure, here is another screenshot showing an error... hope that helps!





 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

I'd say the artifacts you get are clipping errors. Not clipping objects that go through the water plane can create some really weird and unpredictable visual errors.

Have you tried some clipping algorithm yet ?


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

Thanks for the reply :)

I have implemented clipping now, however I'm using the stencil buffer rather than the oblique frustum technique. The stencil buffer is working pretty well, with only a couple of tiny errors which I think will be unnoticeable once I add a bumpmap.

I transposed my projection matrix so that I could modify it just like an OpenGL matrix, and rewrote the code on the provided page so that it worked for DirectX, and finally tranposed the resulting matrix back to a RH matrix. I found though that it didn't seem to work at all, instead, the projected texture seemed to move with the view, and nothing was being clipped at all. I'm pretty sure my conversion of the code was fine, but can post it later just to be sure.

My main problem now is improving the framerate, as I've not been writing optimized code, but rather just attempting to get it to work, so my performance hit is quite large. However, I'm going to attempt to only update the reflection every few frames or so, so that I'm not contantly rendering twie the gemoetry I need to.

The other bonus with using the stencil buffer is that it should work, in theory, with the water plane even when I am simulating the movement of the water surface (ie when it is not a flat plane), which clipping planes and oblique frustums would not allow for, so I think this is the method I'm going to stick with.

I'll post additional code and screenshots of the small errors later so anyone with similar problems can see my solution if they need to.

Thanks,

Steve



 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

It won't work with the stencil buffer - it might look more or less OK in your current simple case, but that's a coincidence. It will fail on anything more complex. The reason is that the stencil buffer only allows 2D clipping to a specific set of screen pixels, whereas 3D reflections require direct 3D clipping of the geometry. There's really no way around this.

Quote:

I transposed my projection matrix so that I could modify it just like an OpenGL matrix, and rewrote the code on the provided page so that it worked for DirectX, and finally tranposed the resulting matrix back to a RH matrix. I found though that it didn't seem to work at all, instead, the projected texture seemed to move with the view, and nothing was being clipped at all. I'm pretty sure my conversion of the code was fine, but can post it later just to be sure.

You're talking about oblique clipping here ? There actually is a slight little detail Eric didn't mention in this source, but he did in the original thread where he designed the new technique: the clipping plane supplied to the code must be in camera space rather than in world space. Also caught me by surprise - all you need to do is transform the plane normal into camera space (by multiplying it with the rotation part of the view matrix, and renormalizing), and recompute D. After that, the source from Eric works like a charm.

[Edit]

Thinking of it, you might actually get some problems while converting the oblique matrix to DirectX, because D3D uses a different mapping range for the z component, afaik (0 to w, instead of -w to w). You'll have to adjust the matrix to compensate for that.


[Edited by - Yann L on June 30, 2004 9:22:49 AM]

 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link

OK, I'll abandon the stencil buffer as you advise. However, the oblique clipping method is still posing problems despite the advice :/

The code given does account for transforming to camera space, to quote the code:

" Calculate the clip-space corner point opposite the clipping plane as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and transform it into camera space by multiplying it by the inverse of the projection matrix"

I've also tried removing that section of the code and simply transform the plane using D3DXTranformPlane and supplying the view matrix as the transformation. I have even tried extracting the direction elements of the view matrix and multiplying the plane coefficients by the respective matrix component (._13 ._23 ._33 {._row,column}).

One thing that also confuses me is what you mean by referring to 'the z component'. Are you referring to the z component of the plane before it is loaded into matrix row 3, or something else?

Hmm, well I'll get there one day :P!

Thanks



 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link


Quote:

... I get the following anomaly as the texture wraps... *EDIT* Okay, fixed the problem there by tinkering with the remapping matrix...


What exactly did you change?
I am currently having this issue.


 User Rating: 1015    Report this Post to a Moderator | Link

I believe I tranposed the remap matrix (ie changed it from right handed DirectX to left handed OpenGL) and multiplied it by the reflection matrix created earlier, if you read my source you can see the process quite clearly I think. However, it still only works when wrapping is turned on, try as I might, if I turn texture coordinate clamping on, I only see a clamped section of the texture (stretched single pixels) as if I'm looking at a portion of the texture adjacent to where I actually should be looking.

 User Rating: 1165   |  Rate This User  Send Private MessageView ProfileView Journal Report this Post to a Moderator | Link

Quote:
Original post by Mephs
The code given does account for transforming to camera space, to quote the code:

No, you misunderstood. Let me highlight the relevant part:

"Calculate the clip-space corner point opposite the clipping plane as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and transform it into camera space by multiplying it by the inverse of the projection matrix"

He doesn't transform the clipplane to camera space (which doesn't work with the inverse projection matrix anyway, but with the view matrix), he transforms a corner point of the far plane into camera space. This corner point is originally in clip space, so transforming it into camera space requires a multiply with the inverse projection matrix. Your clip plane is originally in world space, and requires a multiply with the view matrix, so that it will end up in camera space.

So you need to first transform your world space clipplane into camera space using the view matrix (not the projection matrix !), and then run Eric's unmodified code on it.

Here, for better understanding, I'll quote his explanation of the technique from the opengl forums:
Quote:

Okay -- here's the short, short version of the optimal implementation of the oblique frustum.

Let C = camera-space clipping plane. Assume the camera is on the negative side of the plane: C_w < 0.

Let M be the original projection matrix; M must be invertible, but otherwise we don't care what
it is. We'll modify the third row of M so that the near plane coincides with the arbitrary
clipping plane C. We aren't allowed to modify the fourth row of M because doing so would screw
up the perspective-correct vertex attribute interpolation. (The fourth row usually just
moves the z-coordinate of a camera-space point into the w-coordinate of a clip-space point.)

Given a projection matrix M, the near plane is always M_4 + M_3, and the far plane is always
M_4 - M_3. (M_i means the i-th row of M.) To force the near plane to coincide with the
clipping plane C, we must have M_3 = aC - M_4, where a is some positive scale factor that we
can adjust.

Why adjust a? Because now our far plane F has been moved to F = 2*M_4 - aC, which is not
generally parallel to C. F intersects C on the x-y plane, so it's really not in a good
position. The best we can do is minimize the size of the view frustum by choosing the
constant a so that F contains the corner Q of the view frustum opposite the near plane C.
This point Q is given by

Q = M^-1 * (sgn(C_x), sgn(C_y), 1, 1),

where M^-1 is the inverse of the projection matrix. (The points (+/-1, +/-1, 1, 1) are the
four corners of the view frustum on the far plane in clip space.) The scale factor a is now
given by

a = (2*M_4 dot Q) / (C dot Q).

Since M_4 is usually (0, 0, -1, 0), this can be simplified a little.

All we have to do to the original projection matrix M is replace the third row with aC - M_4.

-- Eric Lengyel


Quote:
Original post by Mephs
One thing that also confuses me is what you mean by referring to 'the z component'. Are you referring to the z component of the plane before it is loaded into matrix row 3, or something else?

Actually I meant the range of the clip space cube after transformation by the projection matrix - that's the space the far plane corner point is extracted from. In OpenGL, the visible range of the z coordinate in clip space is from -w to +w. Now, as far as I know, in D3D it's from 0 to +w. That means, that OpenGL and D3D projection matrices are not 100% compatible. But I'm not sure if that is still valid with current D3D9 versions.


 User Rating: 1996   |  Rate This User  Send Private MessageView Profile Report this Post to a Moderator | Link
Page:   1 2 3 »»
All times are ET (US)

Post Reply
 Last Thread Next Thread 
Forum Rules:
You may not post new threads
You may post replies
You may not edit your posts
You may not use HTML in your posts
Jump To:
Administrative Options: