Search code examples
androidopengl-esglsl

The shader on OpenGL ES 2.0 does not work the same way as the shader on WebGL


I created a wave equation shader on shadertoy.com: You can see how it works by the link https://www.shadertoy.com/view/dtsyD2

Then I copied the code into my Android project working with GLES 2.0, changed texture() to texture2D() and also replaced fragColor and fragCoord with global gl_FragColor and gl_FragCoord. But the result doesn't look the same as on shadertoy (which works with WebGL).

Here's how it looks on Android:

How does Android shader program look like

What did I do wrong? Maybe there's something I didn't take into account.

Here is my code for vertex shaders (identical for both the main and buffer programs):

attribute vec2 pos;

void main()
{
    gl_Position = vec4(pos.xy, 0.0, 1.0);
}

Here is my code for main fragment shader:

precision lowp float;

uniform vec2 iResolution;
uniform sampler2D iChannel0;

void main()
{
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    gl_FragColor = texture2D(iChannel0, uv);
}

Here is my code for buffer fragment shader:

precision lowp float;

uniform vec3 iMouse;
uniform vec2 iResolution;
uniform float iTime;
uniform sampler2D iChannel0;

const float delta = 1.0;

void main()
{
    float pressure = texture2D(iChannel0, gl_FragCoord.xy / iResolution.xy).x;
    float pVel = texture2D(iChannel0, gl_FragCoord.xy / iResolution.xy).y;

    float pright = texture2D(iChannel0, (gl_FragCoord.xy + vec2(1., 0.)) / iResolution.xy).x;
    float pleft = texture2D(iChannel0, (gl_FragCoord.xy + vec2(-1., 0.)) / iResolution.xy).x;
    float pup = texture2D(iChannel0, (gl_FragCoord.xy + vec2(0., 1.)) / iResolution.xy).x;
    float pdown = texture2D(iChannel0, (gl_FragCoord.xy + vec2(0., -1.)) / iResolution.xy).x;

    // Apply horizontal wave function
    pVel += delta * (-2.0 * pressure + pright + pleft) / 4.0;
    // Apply vertical wave function (these could just as easily have been one line)
    pVel += delta * (-2.0 * pressure + pup + pdown) / 4.0;

    // Change pressure by pressure velocity
    pressure += delta * pVel;

    // "Spring" motion. This makes the waves look more like water waves and less like sound waves.
    pVel -= 0.005 * delta * pressure;

    // Velocity damping so things eventually calm down
    pVel *= 1.0 - 0.002 * delta;

    // Pressure damping to prevent it from building up forever.
    pressure *= 0.999;

    //x = pressure. y = pressure velocity. Z and W = X and Y gradient
    gl_FragColor.xyzw = vec4(pressure, pVel, (pright - pleft) / 2.0, (pup - pdown) / 2.0);

    if (iMouse.z > 1.0) {
        float dist = distance(gl_FragCoord.xy, iMouse.xy);
        if (dist <= 20.0) {
            gl_FragColor.x += 1.0 - dist / 20.0;
        }
    }
}

Solution

  • Answer: OpenGL's GL_RGBA format clamps values to the range [0,1]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexImage2D.xhtml

    So i did some adjustments to my shaders.

    Mapping my values from [-1; 1] to [0; 1] in buffer shader before writing to gl_FragColor and changing

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null)
    

    to

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, null)
    

    helped me. Also i had to switch my version from 2.0 to 3.0

    Also there is some difference in shaders because values are not in range [-1; 1]. But on 99% it works correctly. Thank you Ruud Helderman for your assumption.

    Update. I added blur into my fragment shader:

    precision lowp float;
    
    uniform vec2 iResolution;
    uniform sampler2D iChannel0;
    
    void main()
    {
        vec2 uv = gl_FragCoord.xy / iResolution.xy;
        gl_FragColor = blur(iChannel0, gl_FragCoord.xy);
    }
    

    So now it works perfectly. I used blur function from this shader: https://www.shadertoy.com/view/XssSDs