Search code examples
glsllwjglslick2dvertex-shaderblending

Overlaying a transparent color over a Texture with GLSL


I have an image that I am loading using the Slick library, and the image renders fine without my shader active. When I use my shader to overlay a transparent color over the image the entire image is replaced by the transparent color.

without the shader

With the shader

Vertex Shader

varying vec4 vertColor;

void main(){
    vec4 posMat = gl_Vertex;
    gl_Position = gl_ModelViewProjectionMatrix * posMat;
    vertColor = vec4(0.5, 1.0, 1.0, 0.2);
}

Fragment Shader

varying vec4 vertColor;

void main(){
    gl_FragColor = vertColor;
}

Sprite Rendering Code

Color.white.bind();
GL11.glBindTexture(GL11.GL_TEXTURE, image.getTextureID());
GL11.glBegin(GL11.GL_QUADS);
GL11.glTexCoord2f(0, 0);
GL11.glVertex2f(this.x, this.y);
GL11.glTexCoord2f(1, 0);
GL11.glVertex2f(x + w, y);
GL11.glTexCoord2f(1, 1);
GL11.glVertex2f(x + w, y + h);
GL11.glTexCoord2f(0, 1);
GL11.glVertex2f(x, y + h);
GL11.glEnd();
GL11.glBindTexture(GL11.GL_TEXTURE, 0);

}

OpenGL Initialization

GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, Screen.getW(), Screen.getH(), 0, -1, 1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

Solution

  • a) vertColor = vec4(0.5, 1.0, 1.0, 0.2); b) gl_FragColor = vertColor;

    the shader does exactly what you asked of it - it sets the color of all fragments to that color. If you want to blend colors, you should add/multiply them in the shader in some fashion (e.g. have a color attribute and/or texture sampler, and then, after exporting the attribute from vertex shader to fragment shader, use gl_FragColor = vertexColor * textureColor * blendColor; etc).

    also note: you're mixing fixed-function pipeline with immediate mode (glBegin/glEnd) with shaders... that's not a good idea. Also, I don't see where your uniforms are set; using shaders without uniforms == asking for trouble.

    IMO the best solution would be to either use regular OpenGL >= 3.1 with proper, compliant shaders etc. or only use fixed-function pipeline and no shaders with legacy OpenGL.


    As to how to load a texture with GLSL: (see https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/texturing.php for more info if needed)

    a) you feed the data to GPU by creating a texture & binding it to GPU texture unit by calling

    int id = glGenTexture();
    glBindTexture( GL_TEXTURE_2D, id );
    glTexImage2D( ... );
    // see https://www.opengl.org/sdk/docs/man/html/glTexImage2D.xhtml for details
    

    (what I suppose you've already done, since you're using glBindTexture with image param already)

    b) you provide UV texture coordinates for your geometry; you're already doing it by supplying glTexCoord2f, which will probably allow you to use legacy attribute names as in https://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/attributes.php, but the proper way way would be to pass it as a part of packed attribute structure,

    c) you use the bound texture by sampling the texture in the shader, e.g. (legacy GLSL follows)

    // vertex shader
    varying vec2 vTexCoord;
    
    void main() {
       vTexCoord = gl_MultiTexCoord0;
       gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    }
    

    // fragment shader
    uniform sampler2D texture;
    varying vec2 vTexCoord;
    
    void main() {
       vec4 colorMultiplier = vec4(0.5, 1.0, 1.0, 0.2);
       gl_FragColor = texture2D(texture, vTexCoord) * colorMultiplier;
    }
    

    still, if you intend on changing it at runtime, it'd be best to pass the colorMultiplier as a uniform.