   Questions about water
hi there,I've implemented a basic water effect that uses normal maps to animate the effects of waves moving over the surface of the water. I use the typical approach to rendering the water. i.e. create a reflection and refraction map and then use projective texturing to map these onto the water surface, I then offset the texture coordinates by a small x and y displacement vector in the fragment shader based on the normal I get from the normal maps . There are, however a few artifacts that I was wondering how to get rid of...- The x and y offsets are scaled based on the distance each fragment is from the camera, In the image above you can see that when the fragments are closer to the screen there is more visible texture warping. At the bottom and sides however the texture coordinates go outside the 0, 1 range because of this displacement. What is the general approach to solving this?Also...I can't see to find any references about the fresnel effect as it is applied to rendering water so I've just used what I think is the fresnel term:color = (refraction * f) + (reflection * (1-f))where f is the cosine of the angle the camera makes to the water surface.Is there any more to it than that?and...Is there any way to avoid rendering the scene again to the refraction map when most of this data is already in the back buffer? assuming the water gets rendered last.   
Quote:Original post by staticVoid2color = (refraction * f) + (reflection * (1-f))where f is the cosine of the angle the camera makes to the water surface.Is there any more to it than that?I don't think the cosine is the right function to use. The Wikipedia article on Fresnel equations seems to have the information you seek.EDIT: If I am reading it correctly, it should bef =  ((n1*cos(x)-n2*sqrt(1-(n1/n2*sin(x))**2))/(n1*cos(x)+n2*sqrt(1-(n1/n2*sin(x))**2)))**2with n1=1 and n2=1.333 for the air-water transition.Also, just checking: Are you using the correct color space for these computations (i.e., HDR)?;  Regarding the perspective ripple distortion issue, I have solved this in the past by scaling the ripple frequency rather than ripple magnitude with z. It sounds very strange but it worked surprisingly well.Essentially I used the length of the screen space distortion vector to look up into a 1D texture which was periodic (with wrapping obviously) resembling a sine wave but somewhat chaotic to appear organic. This lookup yielded the new distortion magnitude in range 0..1 which I multiplied by the normalized screen space distortion vector (times some constant for the overall ripple magnitude).I found this gave a nice sense of perspective over the water surface while keeping a reasonable limit on the absolute distortion amount. I tried other things as well such limiting the amount of absolute distortion in a more straightforward way but found that the water would look too smooth or too noisy at certain distances.;  Are you doing your projection in the vertex shader or pixel shader? If your water mesh is of low tessellation, projecting in the vertex shader can also cause the distortions.   ;Quote:Are you doing your projection in the vertex shader or pixel shader? If your water mesh is of low tessellation, projecting in the vertex shader can also cause the distortions.I'm translating the projected texture coordinates in the fragment shader, the water surface is just one large quad.Quote:Also, just checking: Are you using the correct color space for these computations (i.e., HDR)?no.After reading more about snell's law Ive realized that there is no way to accurately render local reflections and refractions using a rasterization approach. The only other way would be to use ray tracing. if the water is deep enough the refracted rays can intersect with objects that are outside the view frustum and thus, never initially get rendered to the refraction map. I think this will be a similar problem for reflections.I could use an environment map for global reflection and refraction and accurately calculate the refracted light vector per pixel but this doesn't account for local objects.is there any other way to solve this?   ;  Cubemaps won't work good for larger surfaces. Because a cubemaps basically only give correct reflections/refractions at the point where the cubemap was sampled.It has been a while since I did water, so I might miss a few tricks they use nowadays. But mirrored "snapshots" always did a good job for me. Halflife2 used them, Farcry did, and I think Crysis is still doing the same mostly. What you'll do:1.- Make a rendertarget / FBO. This is a texture where you can render to (instead of rendering to the screenshot). You can use a smaller resolution than your screen if you want to gain some speed.2.- Render your scene as usual into that texture, but flipped (glScale( 1, -1, 1) or something), turn everything upside down. In other words, mirror the scene on your water plane.  * Be aware that the vertex winding is inverted, this will give you wrong lighting. Dunno about DirectX, but in OpenGL you can chose between clockwise or counter clockwise winding. * Maybe you shouldn't render EVERYTHING at this stage. You could skip small crap or use a lower level of detail for example. Also detailed stuff such as normalMaps could get skipped/simplify maybe. You won't really see it in your waving water. * Clip(don't render) everthing that peeks above the water3.- Now that you have a mirrored scene rendered into a texture, you can use that texture later on on your water surface. But first, you also may want a similiar texture for refractions. Again, create a buffer to store.4.- Render the scene again, this time everything below the water surface. 5.- Just a tip, you can apply "height fog" here. The deeper the vertex, the thicker the fog. This will make the water "murky". Use a deep blue color for oceans, or black/brownish for rivers.6.- The two textures will be your input for the final rendering stage. Render your water plane. Eventually as a grid so you can simulate waves in your vertex shader.7.- In the fragment shader, fetch the pixels from the reflection and refraction textures. You'll need to use projective texture coordinates for this.8.- Depending on your viewing angle, you need to mix between the refraction and reflection. This can be done with simplified Fresnel techniques. Eventually get your normal from the normalMaps, and do something like this:float3E = normalize( cameraPos.xyz - iPixelPos.xyz );float3R = reflect( E, normal );// calculate the reflection vector// Cheap Fresnel floatfresnelTerm = ( fresnelBias.x + pow( 1 - saturate( dot( E.xyz , normal.xyz) ),    fresnelContrast.x ) ) * fresnelScale.x;If you look right down into the water, the fresnelTerm will be near to 0 (depending on your 3 fresnel parameters). If the angle is ~90 degrees on the other hand, the fresnelTerm will be ~1.0 (high reflective). Use this to lerp between the refletion and refraction from the two textures:result = lerp( refractionTex, reflectionTex, fresnelTerm );9.- To create (fake) refractions/distortions, you can distort the projective texture coordinates you'll use to get the reflection and refraction pixel. You could use a (second) high-frequency normalMap that repeats oftenfloat3 projectiveCoords = ( out.vertPos.xyz + out.vertPos.www)*0.5;// Distortfloat3 reflCoords = projectiveCoords.xyz + normal.xyz * reflDistortFactor;float3 refrCoords = projectiveCoords.xyz + highFreqNormal.xyz * refrDistortFactor;// Get pixelsfloat3 reflectionColor  = tex2Dproj( reflectionTex, reflCoords.xyz );float3 refractionColor  = tex2Dproj( refractionTex, refrCoords.xyz );It's not based on anything physically correct, but it can produce good results nevertheless after some tweaking.Plenty of other tricks to do with water, but this should be a good basis.Good luck!Rick   ;Quote:Original post by spek// Cheap Fresnel floatfresnelTerm = ( fresnelBias.x + pow( 1 - saturate( dot( E.xyz , normal.xyz) ),    fresnelContrast.x ) ) * fresnelScale.x;I was trying to find a decent approximation to the formula I posted earlier in this thread, and I ended up with a similar functional form. For air-water, these are the approximate parameters I came up with:fresnelBias.x = 0.26fresnelContrast.x = 4fresnelScale.c = 1 - fresnelBias.xIn gnuplot you can see how good the match is by running these commands:n1=1n2=1.333plot [0:pi/2] ((n1*cos(x)-n2*sqrt(1-(n1/n2*sin(x))**2))/(n1*cos(x)+n2*sqrt(1-(n1/n2*sin(x))**2)))**2 ,.026+(1-.026)*(1-cos(x))**4;  Thanks for the replies.I think the only way to solve my original problem would be to somehow adjust the field of view when rendering the refraction map to capture the fragments that lie off screen. And then when I use the projected texture coordinates I would need to scale then to a range less than [0, 1] for e.g [0.1, 0.9] depending on how much i increased the field of view. I could then cap the texture coordinate displacement at a certain size so that it doesn't index fragments outside this field of view.I'll give it a go and post back.   ;  Hi,I've thought of doing a similar thing for refraction maps before, although I never got round to trying it. Instead of increasing the field of view, I intended to move the camera position back whilst pushing the near plane out (essentially leaving it where it was in the scene). That way, you increase the size of the frustum, but keep the frustum planes parallel. This means the extra space would be uniform around the sides of the frustum making it easier to tweak.Hopefully that makes sense without a diagram :).T;  ok, I've found a pretty decent solution to the problem I was having, if the texture coordinates go outside the range 0, 1 then just use the original texture coordinate rather than the offset one.I found this article that also describes a really good way to avoid sampling texels that aren't actually behind the refractive surface by rendering the refractive mesh to the alpha channel and then performing an test to see if alpha == 1 ifso then use the offset coord otherwise use the original coord.   