Need a fast technique to render good looking water?

Started by
5 comments, last by Daniel Wilson 11 years, 9 months ago
A quick google search gave me at least a dozen different articles on water rendering. I was wondering if someone can recommend a certain method and save me days of research :)

I should say i'm not fussed about old hardware. Also i would like to render different kinds of water: ocean, lake, ponds. I don't know if the same method can be used to realistically model all scenarios.

Cheers
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Advertisement
Could you narrow down your requirements a bit more? What are you looking for specifically in terms of reflections? Are you looking for rivers or is that unneccessary?

What are your system requirements in more specificity? Are you looking for 3d waves, or is a flate plane good enough?
It's difficult. The approach you want to use varies depending on the kind of water you want to render. Lake water in a clearly defined contained area (where you can fake it with a normal map) is a bit easier than sea water. I've been playing around with sea water for about a week now. Basically what I did was make a grid of triangles, put that in a buffer, pass it to a vertex shader that moves the 'y' coordinate using a sine wave function, calculate the normals, and pass it to the pixel shader where I applied a repeating texture. I abstracted most of it from this article (although I had to convert it from GLSL to HLSL):

http://www.jayconrod...ulation-in-glsl

I simplified it by having only one wave and one direction. I've put some code below that give you a starting point if you decide to do sea-water. It's still under development, I'm new to shader programming, and I do not fully understand the math....so if anyone wants to pick it apart that would be appreciated.


float4x4 World;
float4x4 View;
float4x4 Projection;
float4 ambientColor;
float ambientIntensity;
float3 lightDirection;
float4 diffuseColor;
float diffuseIntensity;
int time;
float amplitude;
float speed;
float wavelength;
float2 direction;
Texture xColoredTexture;
float4 SpecularColor;
float3 EyePosition;
float3 EyeDirection;
sampler ColoredTextureSampler = sampler_state
{
texture = <xColoredTexture>;
magfilter = LINEAR;
minfilter = LINEAR;
mipfilter = LINEAR;
AddressU = WRAP;
AddressV = WRAP;
};
struct VertexShaderInput
{
float4 Position : POSITION0;
// TODO: add input channels such as texture
// coordinates and vertex colors here.
};
struct VertexShaderOutput
{
float4 Position : POSITION0;
float2 TexCoords : TEXCOORD0;
float3 normal : TEXCOORD1;
float3 View : TEXCOORD2;
};
float dWavedx(float x, float z)
{
float pi = 3.14159;
float frequency = 2*pi/wavelength;
float phase = speed * frequency;
float2 pos;
pos[0] = x;
pos[1] = z;
float theta = dot(direction, pos);
float A = amplitude * direction[0] * frequency;
return A * cos(theta * frequency + time * phase);
}
float dWavedz(float x, float z)
{
float pi = 3.14159;
float frequency = 2*pi/wavelength;
float phase = speed * frequency;
float2 pos;
pos[0] = x;
pos[1] = z;
float theta = dot(direction, pos);
float A = amplitude * direction[1] * frequency;
return A * cos(theta * frequency + time * phase);
}
VertexShaderOutput VertexShaderFunction(VertexShaderInput input, float2 inTexCoords : TEXCOORD0)
{
VertexShaderOutput output = (VertexShaderOutput)0;
float pi = 3.14159;
float freq = 2*pi/wavelength;
float phase = speed * freq;
float2 pos;
pos[0] = input.Position.x;
pos[1] = input.Position.z;
float theta = dot(direction, pos);
input.Position.y = amplitude * sin(theta * freq + time * phase);
float4 worldPosition = mul(input.Position, World);
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
float3 inNormal;
inNormal.x = -dWavedx(pos[0], pos[1]);
inNormal.y = 1.0;
inNormal.z = -dWavedz(pos[0], pos[1]);
inNormal = normalize(inNormal);
output.normal = normalize(mul(inNormal, World));
output.View = normalize(float4(EyePosition,1.0) - worldPosition);
output.TexCoords = inTexCoords;
return output;
}
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
float4 norm = float4(input.normal, 1.0);
float4 diffuse = saturate(dot(-lightDirection, norm));
float4 reflect = normalize(2*diffuse*norm-float4(lightDirection,1.0));
float4 specular = pow(saturate(dot(reflect,float4(EyeDirection, 1.0))),256);
float4 baseColor = tex2D(ColoredTextureSampler, input.TexCoords);
float4 finalColor = baseColor * ambientColor * ambientIntensity +
baseColor * diffuseIntensity * diffuseColor * diffuse +
baseColor * SpecularColor*specular;
finalColor.a = 0.2;
return finalColor;
}
technique Technique1
{
pass Pass1
{
// TODO: set renderstates here.
VertexShader = compile vs_2_0 VertexShaderFunction();
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}


I hope that gives you a starting point. Good luck.

different kinds of water: ocean, lake, ponds
I'm afraid this is a tough call. Ocean water is typically more dynamic than others and the amount of fillrate required to render mandates the use of some other methods. Anyway, when it comes to me the ideal solution is one of the following.

  1. Water as used in Portal2 and Left for Dead. Works best for limited visibility in my opinion. Fine tuning by artist is relatively easy, proven to help for gameplay reasons. Probably good enough for you.
  2. Tiled directional flow. Apparently sufficient for ocean rendering. A step up in quality and processing power.

Previously "Krohm"


Could you narrow down your requirements a bit more? What are you looking for specifically in terms of reflections? Are you looking for rivers or is that unneccessary?

What are your system requirements in more specificity? Are you looking for 3d waves, or is a flate plane good enough?


I didn't really think about reflections. Maybe yes for lakes, no for ocean? What do games usually have?

Yes, i would like rivers too.

3D waves. Should be easy to do with shaders, right?

Also, interactive water would be nice. Think ships in ocean, foam on shore.

I'm sorry if i'm not giving a lot of helping info, i really have no idea about water rendering.
"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Alright then.

First thing's first, water usually has a reflection in games if it's relatively flat, almost always a planar reflection is used in these cases. Here's a good primer: http://developer.amd.com/media/gpu_assets/ShaderX_RipplingRefractiveAndReflectiveWater.pdf

3d waves can actually get quit complicated, simply because making them look decent can get hard. I'd start with the above first.
Here's a couple of nice links using the same techniques:
http://www.rastertek.../dx10tut29.html
http://habibs.wordpress.com/lake/
And the Fresnel Ogre SDK sample uses the same idea (I personally found this the most useful)

This topic is closed to new replies.

Advertisement