Skip to content

Instantly share code, notes, and snippets.

@Rawze
Last active January 4, 2021 20:25
Show Gist options
  • Save Rawze/c5301399727ea2956d6de989686cdf71 to your computer and use it in GitHub Desktop.
Save Rawze/c5301399727ea2956d6de989686cdf71 to your computer and use it in GitHub Desktop.
Basic SDL2 and OpenGL example using QT creator on linux by Rawze.
/*
* Linux + QT Creator + SDL2 + OpenGL example program by Rawze. It is my own interpretation after
* following other examples found elsewhere. Some of them seemed to be cryptic and difficult to
* understand, so I came up with my own. This example is probably just as bad, but at least it
* presents a different perspective. I figured out how to get it all running and just wanted to
* share for those who are only trying to learn. Use at own risk.
*/
/*
* I used the following commands to install the required SDL2 and Open GL source libraries into my
* copy of lubuntu 16.04LTS linux. I only show it here for reference in case anyone wants to know.
*
* apt-get install libsdl2* libglew-* libsoil* libglm*
*
*
*/
/*
* I created an "Other", "C++" project and did not use the QTwidgets functionality at all.
*
* For QT creator to use SDL, GLEW, GL, and GLU libraries, the following lines need to be added
* to the project (.pro) file ...
*
* LIBS += -L/usr/lib -lSDL2 -ldl -lpthread
* LIBS += -L/usr/lib/x86_64-linux-gnu -lGLEW -lGL -lGLU
* INCLUDES += /usr/local/include
*
* Here is what my own .pro file looked like when I compiled this example ...
*
* TEMPLATE = app
* CONFIG += console c++11
* CONFIG -= app_bundle
* CONFIG -= qt
* LIBS += -L/usr/lib -lSDL2 -ldl -lpthread
* LIBS += -L/usr/lib/x86_64-linux-gnu -lGLEW -lGL -lGLU
* INCLUDES += /usr/local/include
*
* SOURCES += main.cpp
*
*
*/
//includes --> Adjust the include path(s) as needed...
#include <stdio.h>
#include <SDL2/SDL.h>
#define GLEW_STATIC
#include <GL/glew.h>
//settings...
namespace MY {
char Title[] = "OpenGL Example by Rawze (09-12-2017)";
enum {
//Screen...
ScreenWidth = 640,
ScreenHeight = 480,
//openGl compatibility Version...
OpenGlVersion = 3,
OpenGlSubVersion = 2,
};
}
/* Define a public object collection.
* This is only here to help better understand the vao and other creation processes while reading through the
* code. For only one VAO object it is not likely necessarey to build it up like this, but if we were going
* to define more objects, we could use something like this to manage them convienently.
*
*
*/
namespace GRAPHICS_CARD_OBJECT_COLLECTION{ //Object Collection
enum {
//The individual vertex objects we want to make.
TestTriangle_index = 0,
num_of_vao_objects = 1,
num_of_vbo_objects = num_of_vao_objects
};
//Shared arrays...
GLuint vao[num_of_vao_objects]; //Place to store handles to our graphics objects(the vao).
GLuint vbo[num_of_vbo_objects]; //Place to store handles to objects bnuffers (the vbo).
GLuint vertexShaders[num_of_vao_objects]; //Place to store handles to vertex shaders we make.
GLuint fragmentShaders[num_of_vao_objects]; //Place to store handles to fragment shaders we make.
GLuint shaderPrograms[num_of_vao_objects]; //Place to store handles to shader programs we make.
}
/*
* We need an application(gui) window to display everything. I chose to use SDL2 to create that window. After that,
* aside form checking for the "Application Close Request" in SDL, we are using OpenGL exclusively. I am also using
* OpenGL to refresh the frame too, insted of relying on SDL.
*/
//Shared/public SDL Objects...
SDL_Window* sdlWindow;
SDL_Renderer* sdlRenderer;
int setup_mainwindow() {
//Initialize SDL itself.
if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) != 0 ){
printf( "SDL: Could not initialize! SDL_Error: %s\n", SDL_GetError() );
return 1;
}
//Create the main gui window...
if( (sdlWindow = SDL_CreateWindow(
MY::Title, //Title
SDL_WINDOWPOS_UNDEFINED, //x Window Position at start
SDL_WINDOWPOS_UNDEFINED, //y Window Position at start
MY::ScreenWidth, //initial screen width
MY::ScreenHeight, //Initial screen height
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN //flags (use openGl and show window).
)) == NULL ){
printf( "SDL: Window could not be created! SDL_Error: %s\n", SDL_GetError() );
return 1;
}
printf( "Main Window created: %i x %i\n.", MY::ScreenWidth, MY::ScreenHeight );
//Create a renderer for placing things into the window.
if( (sdlRenderer = SDL_CreateRenderer(
sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE
)) == NULL ){
printf( "SDL: Renderer could not be created! SDL_Error: %s\n", SDL_GetError() );
return 1;
}
return 0;
}
int setup_opengl(){
/*
* Overview: To Setup OpenGl, we need to ...
*
* 1) Specify what version we want to be compatible with.
*
* 2) Use double bufferring and 24-bit coloring so it looks nice.
*
* 3) use manual frame swap intervals.
*
* 4) Tell it to use the latest GLEW features, then initialize the GLEW library.
*
* 5) Establish a shared Vertex Object Array to hold our graphics objects.
*
*/
using namespace GRAPHICS_CARD_OBJECT_COLLECTION;
//specify some default attributes like openGl version, double buffering, manual frame swap...
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, MY::OpenGlVersion);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, MY::OpenGlSubVersion);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetSwapInterval(0);
//init GLEW...
glewExperimental = GL_TRUE; //use the most modern OpenGL methods
glewInit(); //Initialize GLEW.
glGenVertexArrays(num_of_vao_objects, vao); //pre-initialize our vao so that we can use it later.
return 0;
}
int sdlEventHandler(){
/*
* This checks for the Close Window event in SDL so that we can end the application uppin request.
*
*/
SDL_Event event;
while(SDL_PollEvent(&event)){
switch(event.type){
case SDL_QUIT:
return SDL_QUIT; //exit the application.
break;
default:
break;
}
}
return 0;
}
int gl_addShader(GLuint shaderProgram, GLchar *sourcecode, int shaderType){
/*
* My own add-shader routine. I got tired of repeating the same code for each part of the shader
* program. It also has error checking so that if the graphics card complains about our source
* that we send to it, we can then see why it has failed.
*
*/
//Crerate a fragment buffer, fill it with the source, and compile it out on the graphics card...
GLuint _shader = glCreateShader(shaderType);
glShaderSource(_shader, 1, &sourcecode, NULL);
glCompileShader(_shader);
//Verify it compiled correctly...
{
GLint status; glGetShaderiv(_shader, GL_COMPILE_STATUS, &status);
if(status != GL_TRUE){
char buffer[512];
glGetShaderInfoLog(_shader, 512, NULL, buffer);
printf( "OpenGL Compiler message...\n %s", buffer );
printf( "Problem Sourcecode ...\n<<<<>>>>\n" );
printf( "%s", sourcecode );
printf( "\n<<<<>>>>\n" );
return 1;
}
}
glAttachShader(shaderProgram, _shader); //add to our shader program.
return 0;
}
int build_TestTriangle(){
/*
* To build our test triangle, we need to do a few things. They are...
*
* 1) Tell the graphics card that our efforts are to be applied to the vao (vertex array object)
* of our test triangle.
*
* 2) Create a buffer of one sub-items (our triangle) for this vao and store its geometry.
*
* 3) Create a shader/color program for applying both pixels and colors, compile it, link it, and
* tell the graphics card to select it.
*
* 4) Set all attributes for the shader and color routines once the program is active.
*
* -- The object should then be ready to use at our request. Because we first defined a VAO as well,
* all the associated attributes will stick to the object and not have to be re-defined whenever it
* is chosen in the future.
*
*/
using namespace GRAPHICS_CARD_OBJECT_COLLECTION;
int error = 0;
//Tell the graphics card to select our TestTrianlge object.
glBindVertexArray( vao[TestTriangle_index] );
//define the test triangle geometry and colors...
{
float TestTriangle_geometry[] = {
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, // Vertex 1 (X, Y, r, g, b)
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // Vertex 2 (X, Y, r, g, b)
-0.5f, -0.5f, 1.0f, 0.0f, 1.0f // Vertex 3 (X, Y, r, g, b)
};
// Generate a Vertex Buffer Object on the videocard to hold our test triangle and assign its handle
// to our 'vbo' collection ( the 'GRAPHICS_CARD_OBJECT_COLLECTION' ) so that we can refer to it
// when necessary..
glGenBuffers(1, &vbo[TestTriangle_index]);
//Assign our buffer as the active buffer in the graphics card.
glBindBuffer(GL_ARRAY_BUFFER, vbo[TestTriangle_index]);
//Upload the TestTriangle to the active buffer in the graphics card.
glBufferData(
GL_ARRAY_BUFFER, //type of data is a buffer.
sizeof(TestTriangle_geometry), //What size the buffer is.
TestTriangle_geometry, //The actual geometry of the data.
GL_STATIC_DRAW //type of action we will be performing with this data.
);
}//~FINISHED: define test triangle geometry
/*
* Create a shader/fragment program for the graphics card to apply onto our geometry. The shader program recieves
* the geometry. We need it to both apply a fill (a vertex shader) to it, and then a colorizing (fragment) source
* to get the right type of fill and colors applied to our TestTriangle.
*/
{
//Tell the graphics card we want to make a shader program. Save its reference handle to our shader programs collection.
shaderPrograms[TestTriangle_index] = glCreateProgram();
// Define the sourcecode for the Shader(fill) that the graphics should compile and use to fill our geometry.
{
GLchar array[] =
"#version 150 core \n"
"in vec2 position; \n"
"in vec3 color; \n"
"out vec3 Color; \n"
"void main(){ Color = color; gl_Position = vec4(position, 0.0, 1.0); } \n"
;
GLchar *vertexSource = array;
if( (error = gl_addShader(shaderPrograms[TestTriangle_index], vertexSource, GL_VERTEX_SHADER)) != 0) return error;
}
// Define the sourcecode for the Fragment(colors) that the graphics should compile and use to colorize our geometry.
{
GLchar array[] =
"#version 150 core \n"
"in vec3 Color; \n;"
"out vec4 outColor; \n"
"void main(){ outColor = vec4(Color, 1.0); } \n"
;
GLchar *fragmentSource = array;
if( (error = gl_addShader(shaderPrograms[TestTriangle_index], fragmentSource, GL_FRAGMENT_SHADER)) != 0) return error;
}
//Now lets Bind, then Link, and tell the graphics card to Use our new shader program...
glBindFragDataLocation(shaderPrograms[TestTriangle_index], 0, "outColor");
glLinkProgram(shaderPrograms[TestTriangle_index]);
glUseProgram(shaderPrograms[TestTriangle_index]);
//now set the position properties for our program.
GLint position_ref = glGetAttribLocation(shaderPrograms[TestTriangle_index], "position");
glVertexAttribPointer(
position_ref, //Point to our position data.
2, //2 values (x,y).
GL_FLOAT, //coordinate type.
GL_FALSE, //(false)-1.0 to +1.0 range, (true)0.0 to 1.0 range.
5*sizeof(float), //stride (size of a single x,y,r,g,b element).
0 //Initial data Offset
);
glEnableVertexAttribArray(position_ref);
//now set the color properties for our program.
GLint color_ref = glGetAttribLocation(shaderPrograms[TestTriangle_index], "color");
glVertexAttribPointer(
color_ref, //Point to our position data.
3, //3 values (r,g,b).
GL_FLOAT, //coordinate type.
GL_FALSE, //(false)-1.0 to +1.0 range, (true)0.0 to 1.0 range.
5*sizeof(float), //stride (size of a single x,y,r,g,b element).
(void*)(2*sizeof(float)) //Initial Offset (jump over the x,y and point to the r,g,b instead).
);
glEnableVertexAttribArray(color_ref);
}//~Create a shader program
return 0;
}
void build_and_show_frame(){
using namespace GRAPHICS_CARD_OBJECT_COLLECTION;
// Clear the screen to black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//select our TestTriangle from the vao.
glBindVertexArray( vao[TestTriangle_index] );
// Draw a triangle from the 3 vertices
glDrawArrays(
GL_TRIANGLES, //type of shape.
0, //index of starting vertice in the buffer.
3 //num of verticies to step through.
);
//Swap frame buffers so that we can see the result.
SDL_GL_SwapWindow(sdlWindow);
}
int main()
{
using namespace GRAPHICS_CARD_OBJECT_COLLECTION;
int error = 0;
printf( "OpenGL Example...\n\n");
if( (error = setup_mainwindow()) != 0) return error;
if( (error = setup_opengl()) != 0) return error;
if( (error = build_TestTriangle()) != 0) return error;
build_and_show_frame();
//main program loop...
while(sdlEventHandler() != SDL_QUIT){
//...
}
//An attempt at a graceful exit ...
{
//dump everything in the graphics card that we made...
for(unsigned int i=0;i<sizeof(shaderPrograms);++i){ glDeleteProgram(shaderPrograms[i]); }
for(unsigned int i=0;i<sizeof(fragmentShaders);++i){ glDeleteProgram(fragmentShaders[i]); }
for(unsigned int i=0;i<sizeof(vertexShaders);++i){ glDeleteProgram(vertexShaders[i]); }
glDeleteBuffers(num_of_vbo_objects, vbo);
glDeleteVertexArrays(num_of_vao_objects, vao);
//dump everything that we created in SDL...
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(sdlWindow);
SDL_Quit();
}//~FINISHED: Graceful exit.
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment