Home > database >  A Simple Gradient Effect
A Simple Gradient Effect

Time:06-16

I need to code the fragment shader so that the triangle has a simple gradient effect. That is, so that its transparency decreases from left to right. I tried this but it fails:

#version 120
uniform float startX = gl_FragCoord.x;
void main(void) {
    gl_FragColor[0] = 0.0;
    gl_FragColor[1] = 0.0;
    gl_FragColor[2] = 1.0;
    gl_FragColor[3] = startX / gl_FragCoord.x;
}

The full code:

#include <cstdlib>
#include <iostream>
using namespace std;

#include <GL/glew.h>
#include <SDL.h>

GLuint program;
GLint attribute_coord2d;

bool init_resources(void)
{
    GLint compile_ok, link_ok = GL_FALSE;

    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    const char* vs_source = R"(
        #version 120
        attribute vec2 coord2d;
        void main(void) {
            gl_Position = vec4(coord2d, 0.0, 1.0);
        }
    )";
    glShaderSource(vs, 1, &vs_source, NULL);
    glCompileShader(vs);
    glGetShaderiv(vs, GL_COMPILE_STATUS, &compile_ok);
    if (!compile_ok) {
        cerr << "Error in vertex shader" << endl;
        return false;
    }

    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
    const char* fs_source = R"(
        #version 120
        uniform float startX = gl_FragCoord.x;
        void main(void) {
            gl_FragColor[0] = 0.0;
            gl_FragColor[1] = 0.0;
            gl_FragColor[2] = 1.0;
            gl_FragColor[3] = startX / gl_FragCoord.x;
        }
    )";
    glShaderSource(fs, 1, &fs_source, NULL);
    glCompileShader(fs);
    glGetShaderiv(fs, GL_COMPILE_STATUS, &compile_ok);
    if (!compile_ok) {
        cerr << "Error in fragment shader" << endl;
        return false;
    }

    program = glCreateProgram();
    glAttachShader(program, vs);
    glAttachShader(program, fs);
    glLinkProgram(program);
    glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
    if (!link_ok) {
        cerr << "Error in glLinkProgram" << endl;
        return false;
    }

    const char* attribute_name = "coord2d";
    attribute_coord2d = glGetAttribLocation(program, attribute_name);
    if (attribute_coord2d == -1) {
        cerr << "Could not bind attribute " << attribute_name << endl;
        return false;
    }

    return true;
}

void render(SDL_Window* window)
{
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    glUseProgram(program);

    glEnableVertexAttribArray(attribute_coord2d);
    GLfloat triangle_vertices[] = {
        0.0,  0.8,
       -0.8, -0.8,
        0.8, -0.8,
    };

    glVertexAttribPointer(attribute_coord2d, 2, GL_FLOAT, GL_FALSE, 0, triangle_vertices);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(attribute_coord2d);

    SDL_GL_SwapWindow(window);
}

void free_resources()
{
    glDeleteProgram(program);
}

void mainLoop(SDL_Window* window)
{
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    while (true)
    {
        SDL_Event ev;
        while (SDL_PollEvent(&ev))
        {
            if (ev.type == SDL_QUIT)
                return;
        }
        render(window);
    }
}

int main(int argc, char* argv[])
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("My First Triangle", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
    SDL_GL_CreateContext(window);

    GLenum glew_status = glewInit();
    if (glew_status != GLEW_OK) {
        cerr << "Error: glewInit: " << glewGetErrorString(glew_status) << endl;
        return EXIT_FAILURE;
    }

    if (!init_resources())
        return EXIT_FAILURE;

    mainLoop(window);

    free_resources();
    return EXIT_SUCCESS;
}

How to do it right?

CodePudding user response:

vYou can not initialize a uniform with gl_FragCoord.x. A uniform initialization is determined at link time.

uniform float startX = gl_FragCoord.x;

uniform float startX;

You have to set the unform with glUniform1f.


gl_FragCoord.xy are not the vertex coordinates. gl_FragCoord.xy are the window coordinate in pixels. You have to divide gl_FragCoord.xy by the size of the viewport:

#version 120

void main(void) {
    gl_FragColor = vec4(0.0, 0.0, 0.0, gl_FragCoord.x / 640.0);
}

Or passing coord2d to the fragment shader:

#version 120
        
attribute vec2 coord2d;
varying vec2 coord;
        
void main(void) {
    coord = coord2d;         
    gl_Position = vec4(coord2d, 0.0, 1.0);
}
#version 120

varying vec2 coord;

void main(void) {
    float alpha = 2.0 / (1.0 - coord.y);
    gl_FragColor = vec4(0.0, 0.0, 1.0, alpha);
}

Or use a color attribute:

#version 120

attribute vec2 coord2d;
attribute vec4 attrColor;
varying vec4 color;

void main(void) {
    color = attrColor;
    gl_Position = vec4(coord2d, 0.0, 1.0);
}
#version 120

varying vec4 color;
       
void main(void) {
    gl_FragColor = color;
}
attribute_color = glGetAttribLocation(program, "attrColor");
GLfloat triangle_colors[] = {
    0.0f, 0.0f, 1.0f, 0.5f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 1.0f
};

glEnableVertexAttribArray(attribute_color);
glVertexAttribPointer(attribute_color, 4, GL_FLOAT, GL_FALSE, 0, triangle_colors);
  • Related