PDA

View Full Version : Weird Waves



Kip Warner
02-14-2010, 11:49 PM
So I have a 2D grid of vertices, each with a Z value representing the displacement at that point. Collectively, these form waves on the surface.

I initialize this Z-map to all zeros (state of rest), with a disturbance in the middle (-0.5, and -0.3 to surrounding 8 vertices).

A 9x9 grid:


0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 -0.30 -0.30 -0.30 0.00 0.00 0.00
0.00 0.00 0.00 -0.30 -0.50 -0.30 0.00 0.00 0.00
0.00 0.00 0.00 -0.30 -0.30 -0.30 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00


I call a method EvaluateWaves() at regular intervals to update the Z values. It takes into account preset constants, such as desired wave speed, fluid viscosity, and so on.

Since a disturbance in the middle should result in the same thing happening outward radially in all directions, I was surprised to see the result after an EvaluateWaves() call resulting in some non-symmetrical results:



0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
0.00 0.00 0.00 0.00 -0.11 -0.11 -0.11 0.00 0.00
0.00 0.00 0.00 -0.11 -0.08 -0.26 -0.08 -0.11 0.00
0.00 0.00 0.00 -0.11 -0.26 -0.21 -0.26 -0.11 0.00
0.00 0.00 0.00 -0.11 -0.08 -0.26 -0.08 -0.11 0.00
0.00 0.00 0.00 0.00 -0.11 -0.11 -0.11 0.00 0.00
0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00


I am sending the grid to my fragment shader which performs the calculations and writes it out to a texture bound to the framebuffer object. The texture is ping-ponged back and forth at each evaluation.

The EvaluateWaves() call:


// Re-evaluate the waves, but must be called at constant interval...
void FluidSurface::EvaluateWaves()
{
// Variables...
GLint Viewport[4] = { 0, 0, 0, 0 };

// Setup viewport for one to one pixel = texel = geometry mapping...

// Projection should not transform...
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, m_Width, 0.0, m_Height);

// There should be no modelview transformations either...
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

// Lastly, backup viewport size, and resize to the size of grid maps...
glGetIntegerv(GL_VIEWPORT, Viewport);
glViewport(0, 0, m_Width, m_Height);

// Redirect rendering through our framebuffer object...
glBindFramebuffer(GL_FRAMEBUFFER, m_FrameBufferObject);

// Check for OpenGL errors...
PrintOpenGLErrors();

// Prepare shader state and dispatch grid location data to program...

// Install program...
glUseProgram(m_PO_EvaluateWaves);

// Prepare texture units...

// First texture unit always contains first z-map...
glActiveTexture(GL_TEXTURE0 + m_ZMaps_TextureUnit[0]);
glBindTexture(GL_TEXTURE_2D, m_ZMaps_TextureID[0]);

// Second texture unit always contains second z-map...
glActiveTexture(GL_TEXTURE0 + m_ZMaps_TextureUnit[1]);
glBindTexture(GL_TEXTURE_2D, m_ZMaps_TextureID[1]);

// Check for OpenGL errors...
PrintOpenGLErrors();

// Update uniforms...

// Z-maps...

// Previous...
glUniform1i(GetUniformIndex(m_PO_EvaluateWaves, "ZMapPrevious"),
m_ZMaps_TextureUnit[!m_SourceBufferSwitch]);

// Current...
glUniform1i(GetUniformIndex(m_PO_EvaluateWaves, "ZMapCurrent"),
m_ZMaps_TextureUnit[m_SourceBufferSwitch]);

// Bind fragment shader outputs to respective data buffers...

// Create list of attachment points for each fragment output ID...
GLuint const DrawBuffers[] = {
m_ZMaps_AttachmentPoint[!m_SourceBufferSwitch],
m_NormalMap_AttachmentPoint,
m_TangentMap_AttachmentPoint
};

// Load the draw buffers...
glDrawBuffers(sizeof(DrawBuffers) / sizeof(DrawBuffers[0]), DrawBuffers);

// Enable required vertex arrays...
glEnableVertexAttribArray(m_GVA_GridLocations);

// Select the grid indices...
glBindBuffer(GL_ARRAY_BUFFER, m_VBO_GridLocations);

// Commit data to shader...
glDrawArrays(GL_POINTS, 0, m_Width * m_Height);

// Disable required vertex arrays...
glDisableVertexAttribArray(m_GVA_GridLocations);

// Swap the source buffer for next evaluation...
m_SourceBufferSwitch = !m_SourceBufferSwitch;

// Check for OpenGL errors...
PrintOpenGLErrors();

// Restore state...

// Default framebuffer...
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Viewport back to original dimensions...
glViewport(Viewport[0], Viewport[1], Viewport[2], Viewport[3]);

// Check for OpenGL errors...
PrintOpenGLErrors();
}


Vertex shader:


// Input variables...

// Grid location...
in vec2 GridLocation;

// Uniform variables...

// Dimensions...
uniform uint Width;
uniform uint Height;

// Variables for the fragment shader, none of which need be interpolated...

// Normalized location cooresponding to grid location...
out vec2 NormalizedLocation;

// Texture location cooresponding to grid location...
out vec2 TextureLocation;

// Entry point...
void main()
{
// Calculate texture location which has components in [0, 1] range...
TextureLocation = GridLocation / vec2(Width, Height);

// Calculate normalized location which has components in [-1, 1] range...
NormalizedLocation = TextureLocation * vec2(2.0, 2.0) - vec2(1.0, 1.0);

// Generate a fragment...
gl_Position = vec4(NormalizedLocation, 0.0, 1.0);
}


Fragment shader:


// Input variables...

// Normalized location cooresponding to grid location...
in vec2 NormalizedLocation;

// Texture location cooresponding to grid location...
in vec2 TextureLocation;

// Uniform variables...

// Dimensions...
uniform uint Width;
uniform uint Height;

// Grid spacing between vertices...
uniform float DistanceBetweenVertices;

// Pre-computed equation constants...
uniform float CachedConstant1;
uniform float CachedConstant2;
uniform float CachedConstant3;

// Z-displacement maps...
uniform sampler2D ZMapPrevious;
uniform sampler2D ZMapCurrent;

// Output variables...

// Z-displacement output buffer...
out float ZPreviousOut;

// Normal...
out vec3 NormalOut;

// Tangent...
out vec3 TangentOut;

// Take a grid location and transform to a normalized texture coordinate...
vec2 GridToTexture(const uvec2 Location)
{
// Transform to [0..1] and return it...
return (Location / vec2(Width, Height));
}

// Take coordinate normalized in [-1..1] and transform to [0..Width or Height]...
uvec2 NormalizedPointToGrid(const vec2 Normalized)
{
// Transform and return it... p = (d/2)(p'+1)
return uvec2((vec2(Width, Height) * vec2(0.5, 0.5)) * (Normalized + vec2(1.0, 1.0)));
}

// Transform coordinate in [-1..1] to [0..1]
vec2 NormalizedPointToTexture(const vec2 Normalized)
{
// Transform normalized form to texture coordinate... p_t = (p' + <1,1>) / 2
return (Normalized + vec2(1.0, 1.0)) * vec2(0.5, 0.5);
}

// Entry point...
void main()
{
// Get location on grid...
uvec2 GridLocation = NormalizedPointToGrid(NormalizedLocation);

// We assume the outer edge of fluid surface is fixed. This also makes
// querying neighbours easier since we can assume there always will be
// exactly eight all around it after this, though we don't affect the
// diagonal neighbours...

if(GridLocation.x == 0u || GridLocation.x == (Width - 1u) ||
GridLocation.y == 0u || GridLocation.y == (Height - 1u))
discard;

// Lookup displacement of current location...
float CurrentZ = texture(ZMapCurrent, TextureLocation).r;

// Lookup displacements of current location's neighbours...
float AboveCurrentZ = texture(ZMapCurrent, GridToTexture(GridLocation + uvec2( 0, 1))).r;
float BelowCurrentZ = texture(ZMapCurrent, GridToTexture(GridLocation + uvec2( 0, -1))).r;
float RightCurrentZ = texture(ZMapCurrent, GridToTexture(GridLocation + uvec2( 1, 0))).r;
float LeftCurrentZ = texture(ZMapCurrent, GridToTexture(GridLocation + uvec2(-1, 0))).r;

// Lookup displacement of last passes displacement at current location...
float PreviousZ = texture(ZMapPrevious, TextureLocation).r;

// Update previous buffer's displacement here using equation 12.25, p.335,
// of Mathematics for 3D Game Programming and Computer Graphics. */
ZPreviousOut = CachedConstant1 * CurrentZ + /* first term */
CachedConstant2 * PreviousZ + /* second term */
CachedConstant3 * (RightCurrentZ + LeftCurrentZ + /* third term */
AboveCurrentZ + BelowCurrentZ);

// Calculate normal...
NormalOut = vec3(LeftCurrentZ - RightCurrentZ,
BelowCurrentZ - AboveCurrentZ,
2.0 * DistanceBetweenVertices);

// Calculate tangent...
TangentOut = vec3(2.0 * DistanceBetweenVertices,
0.0,
RightCurrentZ - LeftCurrentZ);
}


I know this isn't a trivial algorithm, but any light one can shed on this is appreciated.

Kip

strattonbrazil
02-15-2010, 09:09 AM
That looks pretty symmetrical to me. What were you expecting?

Kip Warner
02-15-2010, 12:35 PM
No because the -0.08 marks where the disturbance happened originally, but you'll note the values it is surrounded by vary non-symmetrically (-0.11 on the top, -0.26 on the bottom).

Kip Warner
02-15-2010, 06:26 PM
Figured it out. Everywhere where I was using Width or Height variable in the vertex and fragment shader, I changed to Width or Height - 1u.

Still getting used to this shader [censored]. It's a brave new world and I am still thinking in the CPU way of things.

Kip