Search code examples
javascriptglslwebgltexturesshader

Apply alpha to texture at certain clipspace coordinates


This is not exactly the problem I have, but a simplified version of it. Say, I have a single image display at full screen size. I want to modify the alpha of this image, so that at the left-half (horizontally) of the screen, the alpha is 0.5 and at the right-half, alpha is 1. Just alpha 0.5 or 1, nothing else in between.

Here are my (failed) codes so far

This is my javascript codes to setup webgl

this.gl = canvas.getContext('webgl', {
    alpha: false,
});
this.gl.enable(this.gl.BLEND);
this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA);
this.gl.clearColor(1, 0, 0, 0); // red to highlight alpha problem

this is my vertex shader code

mediump float;
attribute vec2 coordinates;
attribute vec2 a_texcoord;

varying vec2 v_texcoord;
varying float alpha;

void main() {
    gl_Position = vec4(coordinates.x, coordinates.y, 1.0, 1.0);
    v_texcoord = a_texcoord;
    if (coordinates.x <= 0) {
        alpha = 0.5;
    } else {
        alpha = 1.0;
    }
}    

and my fragment shader is standard simple

precision mediump float;
varying vec2 v_texcoord;
varying float alpha;
uniform sampler2D u_texture;
void main() {
    vec4 color = texture2D(u_texture, v_texcoord).rgba;
    gl_FragColor = color;
}

and at draw time

window.canvas.gl.clear(window.canvas.gl.COLOR_BUFFER_BIT);
window.canvas.gl.useProgram(this.drawProgram);
window.canvas.gl.bindBuffer(window.canvas.gl.ARRAY_BUFFER, this.vertexBuffer);
window.canvas.gl.enableVertexAttribArray(this.positionLocation);
window.canvas.gl.vertexAttribPointer(this.positionLocation, 2, window.canvas.gl.FLOAT, false, 0, 0);
window.canvas.gl.bindBuffer(window.canvas.gl.ARRAY_BUFFER, null);
window.canvas.gl.bindBuffer(window.canvas.gl.ARRAY_BUFFER, this.texcoordBuffer);
window.canvas.gl.enableVertexAttribArray(this.texcoordLocation);
window.canvas.gl.vertexAttribPointer(this.texcoordLocation, 2, window.canvas.gl.FLOAT, false, 0, 0);
window.canvas.gl.bindBuffer(window.canvas.gl.ARRAY_BUFFER, null);

window.canvas.gl.bindTexture(window.canvas.gl.TEXTURE_2D, this.texture);
window.canvas.gl.uniform1i(this.textureLocation, 0);
window.canvas.gl.drawArrays(window.canvas.gl.TRIANGLES, 0, 6);

With these codes, I couldn't achieve what I want. First, the transparency does not start at the middle of the screen (clipspace x = 0), but at a seemingly random location. Also, there is gradual decline from alpha 1.0 to alpha 0.5, not the just the 2 values 0.5 and 1.0 I hope for. And I have no idea where this gradual transition comes from.

Obviously I am learning webgl so any pointer would be much appreciate. Any hint on how to solve the problem would be of great help to me. Thanks in advance!


Solution

  • the transparency does not start at the middle of the screen [..]

    because alpha is evaluated per vertex and interpolated for the fragments.

    You have to do the evaluation per fragment rather than per vertex.

    Pass coordinates.x or gl_Position.x/gl_Position.w from the vertex shader to the fragment shader:

    mediump float;
    attribute vec2 coordinates;
    attribute vec2 a_texcoord;
    
    varying vec2  v_texcoord;
    varying vec2  pos;
    
    void main() {
        gl_Position = vec4(coordinates.xy, 1.0, 1.0);
        v_texcoord = a_texcoord;
        pos        = coordinates.xy;
    }    
    

    Compute the alpha value in the fragment shader:

    precision mediump float;
    
    varying vec2 v_texcoord;
    varying vec2 pos;
    
    uniform sampler2D u_texture;
    
    void main() {
        vec4 color = texture2D(u_texture, v_texcoord).rgba;
        float alpha = pos.x < 0.5 ? 0.5 : 1.0; 
        gl_FragColor = vec4(color.rgb, color.a * alpha);  
    }
    

    Note, the vertex shader is executed once for each vertex coordinate. The coordinates define the corners of the primitives. The fragment shader is executed for each fragment. The output parameters of the vertex shader are interpolated dependent on the position of the fragment on the primitive. The interpolated value is the input to the fragment shader.
    If alpha is calculated in the vertex shader, then the alpha value for the fragments on the left is 0.5 and on the right it is 1.0. The fragments in between get an smoothly interpolated value int he range [0.5, 1.0].
    The same happens to the position when it is passed from the vertex shader to the fragment shader. But since alpha is calculated in the fragment shader, the alpha value of each fragment is either 0.5 or 1.0, dependent on the interpolated value of pos.x