|
||||||||||||||||||
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 |
|
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 |
||||
|
||||
![]() Promit Moderator - Graphics Programming and Theory Member since: 7/29/2001 From: Baltimore, MD, United States |
||||
|
|
||||
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 ![]() |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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? |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
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. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
| 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 ? |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
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! |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
| 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. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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? |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
Quote: 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: 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. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 :) ![]() |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
| 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: Keep asking if you still have problems, that's what this forum is for after all. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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? |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||||||
|
|
||||||||
Quote: OK, that's what I thought. Quote: 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: 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: 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: Yes, correct. Quote: 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: 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. |
||||||||
|
||||||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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] |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
Quote: What did you change ? Quote: 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. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
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 :
Next is my vertex shader :
Finally, for good measure, here is another screenshot showing an error... hope that helps! ![]() |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
| 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 ? |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
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: 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] |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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 |
||||
|
||||
![]() Anonymous Poster |
||||
|
||||
Quote: What exactly did you change? I am currently having this issue. |
||||
|
||||
![]() Mephs Member since: 5/4/2000 From: Nottingham, United Kingdom |
||||
|
|
||||
| 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. |
||||
|
||||
![]() Yann L Moderator Member since: 2/6/2002 From: Breizh |
||||
|
|
||||
Quote: 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: Quote: 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. |
||||
|
||||
|
Page: 1 2 3 »» All times are ET (US) ![]() |
Last Thread Next Thread ![]() |
|