Fake flat surface reflections

Started by
16 comments, last by Extrarius 17 years, 8 months ago
Quote:Original post by Rasmadrak
stupid question, but can't you just add a ceiling quad? and then reflect it normally?

I want to do a straight multi-texture render rather than using secondary render target surfaces AND multi-texture renders, or stencil buffers. With the simplified system, I can make any flat surface reflect, rather than just the floor. The reflections would be fake, but they could be used anywhere and everywhere at once, with virtually no performance costs.

I did manage to pull off a hack for the floor.

vector pos = vertex_pos * worldmatrix;
pos += camera_forward_vector * arbitrary_multiplier;
reflect_tex_coords.x = pos.x * arbitrary_divisor;
reflect_tex_coords.y = pos.z * arbitrary_divisor;

It's definitely a hack, and not correct. I simply shoved the texture coordinates away from the camera by a certain amount. But just using the camera's forward vector is incorrect. The amount that the texture needs to be pushed away would depend a lot on the angle. When the camera is looking at the surface from the side (parallel), the coordinates would need to be pushed very far away. But that's where my math falls short. Figuring out how to calculate this distance based on the angle of the vector and surface.

The fact that I used the world space vertex positions (pos.x and pos.z) to generate textures coordinates is also a hack that would only work for surfaces facing up. I'm not sure how to do this with unknown surface directions. All I have to work with in the shader is the vertex normal and vertex coordinates. I would need to get the 2D locations of vertices on the plane of the vertex normal (hence, the 2D location of a vertex on normals facing straight up [x=0,y=1,z=0] would be pos.x and pos.z). But I don't think this is even possible to calculate without puting more data into the stream.

If anyone can help, feel free to kick me back into the right lane.
Advertisement
With todays polygon pushing power, I think you're kind of shooting yourself in the foot by faking it... :/
"Game Maker For Life, probably never professional thou." =)
I'm already using that power for important gameplay effects.
Quote:Original post by Kest
I could get the result I want by throwing a flat quad down under the floor, just as far down from the floor as the ceiling would be up, then making the floor mesh blend by modulating over that quad.

Imagine a glass floor, where another opaque floor is about five feet down under it. The glass floor would not be entirely transparent. If you were drunk, you might think that the glass floor is just a very shiny opaque floor, and that the secondary floor (which is under it) is the ceiling being reflected.

The only input to this calculation is the 5-feet, which represents how far the reflected object is from the surface. I just figured there was a way to accomplish this same effect by generating secondary texture coordinates for the floor in the vertex shader.

I still appreciate the information. Thanks.


This is exactly what I think you should do... Except you need to also reflect the walls (if there's any chance they would be reflected)
That's the other problem with just generating texture coordinates - you probably aren't setting one texture for the entire level's ceiling - it's probably tiled, but there are nooks and crannys (corners) where a plain faked tiled texture won't do justly.

Here's a list of what [Sanders] gave as the steps for stenciled reflections:

1) turn off all pixel shaders, textures, light etcetera
2) render [floorplane] to stencil buffer
3) enable stuff from (1)
4) flip geometry over the [floor] plane
5) enable clipplane over the [floorplane]
6) render geometry with testcil test (only render where stencil buffer is set to 1 in step (2))
7) undo (4) and (5)
8) render [floorplane] (blended)
9) render geometry again

If you wish to get distorted or blurred reflections, you'd be better off doing it similarly to how HL2 did it: http://www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf
See page 92 (Water) - 96 for how they did reflective and refractive water (and use the reflective idea)

Anyway, now you can actually implement one of these ideas if you like!

hth,
-Michael g.
Planar projective texturing is really quite simple. It may sound scary, but it is not. If you know what vectors are, and you can do a little algebra, you can very easily sketch out on paper the scenario. Do it from the side. Draw a line representing your imaginary ceiling. Draw a line that represents your floor. Then draw another line that represents the reflection of the ceiling. Then pick an arbitrary point for your camera. Next, pick an arbitrary point on the ceiling, and cast it straight down to the reflected ceiling. Now, from that reflected point, draw a ray to the camera (the direction the camera is looking does not matter). Now, where that ray intersects the floor is the point you need to find. But you already know the y coordinate of that point, you only need to figure out the x and z. So then using a parameterization of the ray, where the parameter, p, is zero at the reflected point and one at the camera, solve for p when y=0 (or whatever your floor's y position is). Then simply solve for x and z using that value of p. From that you can then transform the (x,z) pair into texture space. Of course, you wouldn't do this exact procedure. You want to do the inverse. But the procedure is the same, except reversed. Draw it out on paper from the side!
.
I managed to calculate the texture offsets correctly for up-facing surfaces. I don't know why I didn't realize it before, but all that needs to be done is a generic ray-intersect test (where most of the values are already known). Here's what I did, without optimization shortcuts:

1. Use the vertex position and normal to represent a plane
[ p.pos = v.pos; p.normal = v.normal ]

2. Push the plane into the opposite direction of the vertex normal (down for the floor), multiplied by the distance to the reflected object (the height to the ceiling from the floor).
[ p.pos -= p.normal * distance ]

3. Intersect this plane from the vertex, with the direction of the ray being a vector in the view * projection matrix which happens to still equal the forward vector of the camera.
[ ray_dir = float3( ViewProj._13, ViewProj._23, ViewProj._33 ) ]
[ push_dist = dot( p.normal, p.pos - v.pos ) / dot( p.normal, ray_dir ) ]

4. This is where I still need help. Push the texture coordinates away from the surface using push_dist.
[ reflected_pos = v.pos + ray_dir * push_dist ]
[ tex_coords.x = reflected_pos.x * modifier ]
[ tex_coords.y = reflected_pos.z * modifier ]

Modifier represents the scale of the reflection texture coordinates, and can be set to any value. But the other parts of step #4 I do not like. As I mentioned above, I'm specifically using reflected_pos.x and reflected_pos.z straight from the vector to generate the texture coordinates, and this will only work on surfaces that point up. IE, it's hard coded specifically for floor surface normals. I need to figure out how to generate these for any surface.

It all comes down to one question. If given a plane normal and a 3D world-space coordinate, is it possible to convert the 3D coordinates to 2D coordinates on the plane? IE, if starting at location 0,0,0, the 2D values would roll across only the X and Y directions of the plane normal until it lands on the 3D coordinate. What would the X and Y coordinates be when it lands? Is it possible to find those values?

That's all that's stopping me from wrapping this up completely. It already looks really cool on the floors, though.

Thanks again for the help.
Look at this sketch

This sketch handles one vertex of a sloped flat surface. Do the same thing for the remaining bounding vertices of the surface.
.
Do you just need to reflect the view frustum?

First intersect each edge (ignoring edges on the far and near clip planes) of the frustum with the floor (call these pairs of position and direction FloorRay) using the formula found Here[1].

Next, use the formula Here[2] to reflect each of the 4 FloorRay direction vectors (call these reflected directions paired with FloorRays' positions ReflectedRays).

Then, intersect each ReflectedRays with the ceiling to find which ceiling position should be mapped to the 4 floor positions.

Finally, you just need to do basic linear interpolation to map those ceiling points to texture coordinates so that the texture is properly aligned.


In case the site isn't available for whatever reason, the formulas are:
[1]The plane has normal PlaneNormal and goes through PlanePoint
The line goes through point LinePoint in the direction LineDir.
Distance = (PlaneNormal dot (PlanePoint - LinePoint)) / (PlaneNormal dot LineDir)
The intersection point is then LinePoint + Distance * LineDir

[2] ReflectedVec = IncomingRayDir - 2 NormalVec * (IncomingRayDir dot NormalVec)

[Edited by - Extrarius on August 11, 2006 2:10:26 PM]
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk

This topic is closed to new replies.

Advertisement