Last active
January 4, 2021 20:25
-
-
Save Rawze/c5301399727ea2956d6de989686cdf71 to your computer and use it in GitHub Desktop.
Basic SDL2 and OpenGL example using QT creator on linux by Rawze.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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