Color a Triangle Using a Shader Uniform

Notes:

We add a few lines of code to add an SDL2 time function and change our triangle's color using a shader uniform.

Source:

// Uniform.cpp
//
// We can use "uniforms" to send data from the application to the shader.

#include <stdio.h>
#include <GL/glew.h>    // glew likes to be before other GL stuff
#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include <GL/glu.h>

//Screen dimension constants
const int SCREEN_WIDTH = 480;
const int SCREEN_HEIGHT = 480;

//Starts up SDL, creates window, and initializes OpenGL
bool init();

//Initializes rendering program and clear color
bool initGL();

//Input handler
int handleKeys(unsigned char key, int x, int y);

//Per frame update
void update();

//Renders quad to the screen
void render(float green);

//Frees media and shuts down SDL
void close();

//Shader loading utility programs
void printProgramLog(GLuint program);
void printShaderLog(GLuint shader);

//The window we'll be rendering to
SDL_Window* gWindow = NULL;

//OpenGL context
SDL_GLContext gContext;

//Graphics program
GLuint gProgramID = 0;
GLint gVertexPos2DLocation = -1;
GLint gVertexColorLocation = 0;
GLuint gVBO = 0;
GLuint gVAO = 0;

bool init()
{
	//Initialization flag
	bool success = true;

	//Initialize SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
		success = false;
	}
	else
	{
		//Use OpenGL 3.3 core
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

		//Create window
		gWindow = SDL_CreateWindow("SDL2 OpenGL", 
                                    SDL_WINDOWPOS_UNDEFINED, 
                                    SDL_WINDOWPOS_UNDEFINED, 
                                    SCREEN_WIDTH, 
                                    SCREEN_HEIGHT, 
                                    SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
		if(gWindow == NULL)
		{
			printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			//Create context
			gContext = SDL_GL_CreateContext(gWindow);
			if(gContext == NULL)
			{
				printf("OpenGL context could not be created! SDL Error: %s\n", SDL_GetError());
				success = false;
			}
			else
			{
				//Initialize GLEW
				glewExperimental = GL_TRUE; 
				GLenum glewError = glewInit();
				if(glewError != GLEW_OK)
				{
					printf("Error initializing GLEW! %s\n", glewGetErrorString(glewError));
				}

				//Use Vsync
				if(SDL_GL_SetSwapInterval(1) < 0)
				{
					printf("Warning: Unable to set VSync! SDL Error: %s\n", SDL_GetError());
				}

				//Initialize OpenGL
				if(!initGL())
				{
					printf("Unable to initialize OpenGL!\n");
					success = false;
				}
			}
		}
	}

	return success;
}

bool initGL()
{
	//Success flag
	bool success = true;

	//Generate program
	gProgramID = glCreateProgram();

	//Create vertex shader
	GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);

	//Get vertex source
	const GLchar* vertexShaderSource[] =
	{
		"#version 330 core\n"
        "layout (location = 0) in vec3 aPos;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos, 1.0);\n"
        "}\n\0"
	};

	//Set vertex source
	glShaderSource(vertexShader, 1, vertexShaderSource, NULL);

	//Compile vertex source
	glCompileShader(vertexShader);

	//Check vertex shader for errors
	GLint vShaderCompiled = GL_FALSE;
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vShaderCompiled);
	if(vShaderCompiled != GL_TRUE)
	{
		printf("Unable to compile vertex shader %d!\n", vertexShader);
		printShaderLog(vertexShader);
        success = false;
	}
	else
	{
		//Attach vertex shader to program
		glAttachShader(gProgramID, vertexShader);


		//Create fragment shader
		GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

		//Get fragment source
		const GLchar* fragmentShaderSource[] =
		{
            "#version 330 core\n"
            "out vec4 FragColor;\n"
            "uniform vec4 ourColor; // we set this veriable in the OpenGL code.\n"
            "void main()\n"
            "{\n"
            "   FragColor = ourColor;\n"
            "}\n\0"
		};

		//Set fragment source
		glShaderSource(fragmentShader, 1, fragmentShaderSource, NULL);

		//Compile fragment source
		glCompileShader(fragmentShader);

		//Check fragment shader for errors
		GLint fShaderCompiled = GL_FALSE;
		glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fShaderCompiled);
		if(fShaderCompiled != GL_TRUE)
		{
			printf("Unable to compile fragment shader %d!\n", fragmentShader);
			printShaderLog(fragmentShader);
			success = false;
		}
		else
		{
			//Attach fragment shader to program
			glAttachShader(gProgramID, fragmentShader);


			//Link program
			glLinkProgram(gProgramID);

			//Check for errors
			GLint programSuccess = GL_TRUE;
			glGetProgramiv(gProgramID, GL_LINK_STATUS, &programSuccess);
			if(programSuccess != GL_TRUE)
			{
				printf("Error linking program %d!\n", gProgramID);
				printProgramLog(gProgramID);
				success = false;
			}
			else
			{
                //Initialize clear color
                glClearColor(0.7f, 0.7f, 0.7f, 1.0f);

                //VBO data
                GLfloat vertexData[] =
                {
                    -0.5f, -0.5f, 0.0f,
                     0.5f, -0.5f, 0.0f, 
                     0.0f,  0.5f, 0.0f
                };

                //Create VBO
                glGenVertexArrays(1, &gVAO);
                glGenBuffers(1, &gVBO);

                glBindVertexArray(gVAO);
                glBindBuffer(GL_ARRAY_BUFFER, gVBO);
                glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);

                glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
                glEnableVertexAttribArray(0);
			}
		}
	}
	
	return success;
}

int handleKeys(unsigned char key, int x, int y)
{
	//Toggle triangle
	if(key == 'q')
	{
        return 1;
	}
    
    return 0;
}

void update()
{
	//No per frame update needed
}

void render(float green)
{
	//Clear color buffer
	glClear(GL_COLOR_BUFFER_BIT);
	
    //Bind program
    glUseProgram(gProgramID);

    //Enable vertex position
    glEnableVertexAttribArray(gVertexPos2DLocation);

    // get color location for shader
    gVertexColorLocation = glGetUniformLocation(gProgramID, "ourColor");
    glUniform4f(gVertexColorLocation, 0.0f, green, 0.0f, 1.0f);

    //Set vertex data
    glBindBuffer(GL_ARRAY_BUFFER, gVBO);
    glVertexAttribPointer(gVertexPos2DLocation, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);

    //Render
    glDrawArrays(GL_TRIANGLES, 0, 3);

    //Unbind program
    //glUseProgram(0);
}

void close()
{
	//Deallocate program
	glDeleteProgram(gProgramID);

	//Destroy window	
	SDL_DestroyWindow(gWindow);
	gWindow = NULL;

	//Quit SDL subsystems
	SDL_Quit();
}

void printProgramLog(GLuint program)
{
	//Make sure name is shader
	if(glIsProgram(program))
	{
		//Program log length
		int infoLogLength = 0;
		int maxLength = infoLogLength;
		
		//Get info string length
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
		
		//Allocate string
		char* infoLog = new char[maxLength];
		
		//Get info log
		glGetProgramInfoLog(program, maxLength, &infoLogLength, infoLog);
		if(infoLogLength > 0)
		{
			//Print Log
			printf("%s\n", infoLog);
		}
		
		//Deallocate string
		delete[] infoLog;
	}
	else
	{
		printf("Name %d is not a program\n", program);
	}
}

void printShaderLog(GLuint shader)
{
	//Make sure name is shader
	if(glIsShader(shader))
	{
		//Shader log length
		int infoLogLength = 0;
		int maxLength = infoLogLength;
		
		//Get info string length
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
		
		//Allocate string
		char* infoLog = new char[maxLength];
		
		//Get info log
		glGetShaderInfoLog(shader, maxLength, &infoLogLength, infoLog);
		if(infoLogLength > 0)
		{
			//Print Log
			printf("%s\n", infoLog);
		}

		//Deallocate string
		delete[] infoLog;
	}
	else
	{
		printf("Name %d is not a shader\n", shader);
	}
}

int main(int argc, char* args[])
{
	//Start up SDL and create window
	if(!init())
	{
		printf("Failed to initialize!\n");
	}
	else
	{
		//Main loop flag
		bool quit = false;

        // Time variables
        unsigned int lastTime = 0, currentTime = 0;

		//Event handler
		SDL_Event e;
		
		//Enable text input
		SDL_StartTextInput();

        // our green color
        float green = 0.0;
        // is the color getting brighter or darker
        bool brighter = true;

		//While application is running
		while(!quit)
		{
            // Time handling
            currentTime = SDL_GetTicks();
            // increment time every 20 milliseconds
            if (currentTime > lastTime + 20)
            {
                lastTime = currentTime;
                // is the color getting brighter?
                if (brighter)
                {
                    green += 0.02f;
                    // reverse brightness if green reached 1.0
                    if (green > 1.0)
                    {
                        brighter = false;
                    }
                }
                else
                {
                    green -= 0.02f;
                    // reverse brightness if green reached 0.0
                    if (green < 0.0)
                    {
                        brighter = true;;
                    }
                }

            }

			//Handle events on queue
			while(SDL_PollEvent(&e) != 0)
			{
				//User requests quit
				if(e.type == SDL_QUIT)
				{
					quit = true;
				}
				//Handle keypress with current mouse position
				else if(e.type == SDL_TEXTINPUT)
				{
					int x = 0, y = 0;
					SDL_GetMouseState(&x, &y);
					if (handleKeys(e.text.text[0], x, y))
                    {
                        quit = true;
                    }
				}
			}

			//Render quad
			render(green);
			
			//Update screen
			SDL_GL_SwapWindow(gWindow);
		}
		
		//Disable text input
		SDL_StopTextInput();
	}

	//Free resources and close SDL
	close();

	return 0;
}

Answer:


Back to index