Search code examples
androidglslshaderopengl-es-3.0

Creating a waving surface in a shader


I created a waving surface in the vertex shader.

Vertex shader:

#version 300 es
precision lowp float;

uniform mat4 u_mvpMatrix;
uniform sampler2D s_texture;

in vec4 a_position;
out vec2 v_textureCoordinates;

const float pi = 3.14285714286;

void main() {
    vec4 vertexCoord = a_position;
    float distance = length(vertexCoord);

    vertexCoord.y += sin(3.0 * pi * distance * 0.3 + u_lastTime) * 0.5;
    v_textureCoordinates = a_textureCoordinates;
    
    gl_Position = u_mvpMatrix * vertexCoord;
}

Fragment shader:

#version 300 es
precision lowp float;

in vec2 v_textureCoordinates;
out vec4 outColor;
uniform sampler2D s_texture;

void main() {
    outColor = texture(s_texture, v_textureCoordinates);
}

Result:

radial wave

But on the surface there is only one radial wave coming from the center. Is it possible to create a surface with multiple waves using a shader? That is, to make the surface look like bubbling water.

Solution

Thanks @vtastek! According to his answer, I changed the vertex shader:

#version 300 es
precision lowp float;

uniform mat4 u_mvpMatrix;
uniform sampler2D s_texture;

in vec4 a_position;
out vec2 v_textureCoordinates;

const float pi = 3.14285714286;

void main() {
    vec4 vertexCoord = a_position;
    
    vec2 origin1 = vertexCoord.xz + vec2(300.0, 300.0);
    vec2 origin2 = vertexCoord.xz + vec2(-300.0, 300.0);
    vec2 origin3 = vertexCoord.xz + vec2(300.0, -300.0);
    vec2 origin4 = vertexCoord.xz + vec2(-300.0, -300.0); 

    float distance1 = length(origin1);
    float distance2 = length(origin2);
    float distance3 = length(origin3);
    float distance4 = length(origin4);

    float wave = sin(3.3 * pi * distance1 * 0.13 + u_lastTime) * 0.125 +
    sin(3.2 * pi * distance2 * 0.12 + u_lastTime) * 0.125 +
    sin(3.1 * pi * distance3 * 0.24 + u_lastTime) * 0.125 +
    sin(3.5 * pi * distance4 * 0.32 + u_lastTime) * 0.125;
    
    vertexCoord.y += wave;

    v_textureCoordinates = a_textureCoordinates;
    
    gl_Position = u_mvpMatrix * vertexCoord;
}

Result:

final wave


Solution

  • In most examples it is multiple single directional waves. I like your circular waves idea though. I would just offset and duplicate.

    vec2 origin1 = vertexCoord.xz + vec2(300.0, 300.0);
    vec2 origin2 = vertexCoord.xz + vec2(-300.0, 300.0);
    vec2 origin3 = vertexCoord.xz + vec2(300.0, -300.0);
    vec2 origin4 = vertexCoord.xz + vec2(-300.0, -300.0); // random or 4 corners
        
    float distance1 = length(origin1);
    float distance2 = length(origin2);
    float distance3 = length(origin3);
    float distance4 = length(origin4);
        
    float wave = sin(3.3 * PI * distance1 * 0.13 + time) * 0.125 +
                 sin(3.2 * PI * distance2 * 0.12 + time) * 0.125 +
                 sin(3.1 * PI * distance3 * 0.24 + time) * 0.125 +
                 sin(3.5 * PI * distance4 * 0.32 + time) * 0.125;
                    
    vertexCoord.y += wave;
    

    Alternatively you can add single directional waves with different directions(multiplied with random vec2s). You can look up Gerstner waves for more realistic waves. The core idea is you add sine waves together.