GLUT
freeglut - The Open-Source OpenGL Utility Toolkit
glutInit(&argc, argv);
glutCreateWindow("OGL window");
GLUT uses Legacy Profile as default for all created OpenGL contexts.
freeglut extends glut by glutLeaveMainLoop
and glutMainLoopEvent
.
e.g.:
bool condtion = true;
while (condtion)
{
glutMainLoopEvent(); // handle the main loop once
glutPostRedisplay(); // cause `display` to be called in `glutMainLoopEvent`
condtion = ...;
}
void display( void )
{
glClearColor(0.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// [...]
glFlush();
}
int main()
{
// [...]
glutDisplayFunc(display);
// [...]
for(;;)
{
glutMainLoopEvent(); // handle the main loop once
glutPostRedisplay(); // cause `display` to be called in `glutMainLoopEvent`
}
// [...]
}
It is even possible to set a dummy display function, which does nothing, and do the drawing in the loop:
e.g.
void dummyDisplay( void )
{
// does nothing
}
int main()
{
// [...]
glutDisplayFunc(dummyDisplay);
// [...]
for(;;)
{
glClearColor(0.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Whatever I might have to do goes somewhere in here
glFlush();
glutMainLoopEvent(); // does the event handling once
}
// [...]
}
glfwInit
returns GLFW_TRUE
if succeded:
if ( glfwInit() != GLFW_TRUE )
return;
GLFWwindow *wnd = glfwCreateWindow( width, height, "OGL window", nullptr, nullptr );
if ( wnd == nullptr )
{
glfwTerminate();
return;
}
glfwMakeContextCurrent( wnd );
Is there a way to remove 60 fps cap in GLFW?:
The easiest way is to use single buffering instead of double buffering. Since at single buffering is always use the same buffer there is no buffer swap and no “vsync”.
Use the glfwWindowHint
to disable double buffering:
glfwWindowHint(GLFW_DOUBLEBUFFER, GL_FALSE);
GLFWwindow *wnd = glfwCreateWindow(w, h, "OGL window", nullptr, nullptr);
Note, when you use singel buffering, then you have to explicite force execution of the GL commands by (glFlush
), instead of the buffer swap (glfwSwapBuffers
).
Another possibility is to set the number of screen updates to wait from the time glfwSwapBuffers
was called before swapping the buffers to 0. This can be done by glfwSwapInterval
, after making the OpenGL context current (glfwMakeContextCurrent
):
glfwMakeContextCurrent(wnd);
glfwSwapInterval(0);
But note, whether this solution works or not, may depend on the hardware and the driver.
glfwSetCursorPosCallback to function in another class:
An alternative solution would be to associate a pointer to controls
to the GLFWindow
. See glfwSetWindowUserPointer
.
The pointer can be retrieved at an time form the GLFWWindow
object by glfwGetWindowUserPointer
. Of course the return type is void*
and has to be casted to Controls*
.
Instead of the global function or static method a Lambda expressions can be used. e.g:
glfwSetWindowUserPointer(window, this->controls);
glfwSetCursorPosCallback( window, [](GLFWwindow *window, double x, double y)
{
if (Controls *controls = static_cast<Controls*>(glfwGetWindowUserPointer(window)))
controls->handleMouse(window, x, y);
} );
Switching Between windowed and full screen in OpenGL/GLFW 3.2
Is there a way to remove 60 fps cap in GLFW?
How to prevent GLFW window from showing up right in creating?
Retrieving data from callback function
How can i know which opengl version is supported by my system
PyOpenGL OpenGL Version on MacOs
Init SDL.
SDL_Window *window = SDL_CreateWindow(""OGL window", SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL );
SDL_GLContext glContext = SDL_GL_CreateContext( window );
Check for errors by SDL_GetError
.
Make context current by SDL_GL_MakeCurrent
:
SDL_GL_MakeCurrent( window, glContext );
SDL_Init(SDL_INIT_VIDEO);
const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
width = videoInfo->current_w;
height = videoInfo->current_h;
Using SDL2 and OpenGL to rotate camera and a triangle draw wont display anything?
If you want to use the deprecated way of drawing, then you have to use a compatibility profile context instead of a core profile context (see OpenGL Context):
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
Render image using OpenGL in win32 window
TOOD
Child window with OpenGL not responding to gluOrtho2D()
TODO
Your window (default framebuffer) doesn’t have a stencil buffer at all. You have to setup a OpenGL window with a stencil buffer by using the sf::ContextSettings
class.
See Using OpenGL in a SFML window:
sf::ContextSettings settings;
settings.depthBits = 24;
settings.stencilBits = 8;
settings.antialiasingLevel = 4;
settings.majorVersion = 4;
settings.minorVersion = 6;
sf::Window window(sf::VideoMode(1200, 720), "My window", sf::Style::Default, settings);
GLEW is the OpenGL Extension Wrangler Library, it will be necessary if you want to use “modern” OpenGL.
The GLEW library has to be initialized, by glewInit
, after the OpenGL context has become current by glfwMakeContextCurrent
.
See Initializing GLEW.
To link the GLEW library correctly, proper preprocessor definitions have to be set. See GLEW - Installation:
[…] On Windows, you also need to define the
GLEW_STATIC
preprocessor token when building a static library or executable, and theGLEW_BUILD
preprocessor token when building a dll […]
You have to Initialize GLEW. Call glewInit
immediately after creating the OpenGL context:
if ( glewInit() != GLEW_OK )
return;
Note, that glewInit
will return GLEW_OK
f it was successful. glewInit
initializes the function pointers for the OpenGL functions. If you try to call the function through an uninitialized function pointer, a segmentation fault occurs.
GLEW - why should I define GLEW_STATIC?
The signature of a function which is exported by or imported from a static library is marked by the keyword extern
. A function which is imported from a dynamic library has to be marked by extern __declspec(dllimport)
. GLEW_STATIC
is a preprocessor definition which activates the first case.
The relevant code part in the “glew.h” file (verison 2.1.0) is:
/*
* GLEW_STATIC is defined for static library.
* GLEW_BUILD is defined for building the DLL library.
*/
#ifdef GLEW_STATIC
define GLEWAPI extern
#else
ifdef GLEW_BUILD
define GLEWAPI extern __declspec(dllexport)
else
define GLEWAPI extern __declspec(dllimport)
endif
#endif
Glad Loader-Generator has to be initialized by either gladLoadGL
or gladLoadGLLoader
, right after crating and making current the OpenGL context by SDL_GL_CreateContext
.
See also OpenGL Loading Library - glad
e.g.:
const SDL_GLContext context = SDL_GL_CreateContext(window);
if (context == nullptr) {
std::cout << "SDL could not create context";
return 1;
}
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize OpenGL context" << std::endl;
return -1;
}
With glGetIntegerv(GL_NUM_EXTENSIONS, ...)
, the number of extension supported by the GL implementation can be get.
With glGetStringi(GL_EXTENSIONS, ...)
the name of an extension can be asked.
Read the extensions to a std::set
#include <set>
#include <string>
GLint no_of_extensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &no_of_extensions);
std::set<std::string> ogl_extensions;
for ( int i = 0; i < no_of_extensions; ++i )
ogl_extensions.insert( (const char*)glGetStringi(GL_EXTENSIONS, i) );
Check if an extension is supported:
bool filter_anisotropic =
ogl_extensions.find( "GL_ARB_texture_filter_anisotropic" ) != ogl_extensions.end();
Alternatively, you can use glewIsSupported
to ask for an extension. See GLEW - Checking for Extensions.
Related Stack Overflow questions:
#include <stdexcept>
#include <iostream>
void GLAPIENTRY DebugCallback(
unsigned int source, //!< I -
unsigned int type, //!< I -
unsigned int id, //!< I -
unsigned int severity, //!< I -
int length, //!< I - length of debug message
const char *message, //!< I - debug message
const void *userParam ) //!< I - user parameter
{
std::cout << message << std::endl;
}
void init_opengl_debug() {
glewExperimental = true;
if ( glewInit() != GLEW_OK )
throw std::runtime_error( "error initializing glew" );
if ( glDebugMessageCallback != nullptr && glDebugMessageControl != nullptr )
{
glDebugMessageCallback( &DebugCallback, nullptr );
glDebugMessageControl( GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE );
//glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_FALSE);
//glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, NULL, GL_TRUE);
glEnable( GL_DEBUG_OUTPUT );
glEnable( GL_DEBUG_OUTPUT_SYNCHRONOUS );
}
}
Related Stack Overflow questions:
Pass std::ifstream::failbit
to std::ios::exceptions
, because if std::ifstream::open
fails, the failbit status flag is set.
vShaderFile.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
fShaderFile.exceptions ( std::ifstream::failbit | std::ifstream::badbit );
try
{
vShaderFile.open( vertexPath );
fShaderFile.open( fragmentPath );
.....
}
catch ( std::ifstream::failure e )
{
std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
}
sourceCode = std::string(std::istreambuf_iterator<char>(sourceFile), std::istreambuf_iterator<char>());
Related Stack Overflow questions:
I recommend to check if the shader compilation succeeded and if the program object linked successfully.
If the compiling of a shader succeeded can be checked by glGetShaderiv
and the parameter GL_COMPILE_STATUS
. e.g.:
#include <iostream>
#include <vector>
bool CompileStatus(GLuint shader)
{
GLint status = GL_TRUE;
glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
if (status == GL_FALSE)
{
GLint logLen;
glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetShaderInfoLog( shader, logLen, &written, log.data() );
std::cout << "compile error:" << std::endl << log.data() << std::endl;
}
return status != GL_FALSE;
}
If the linking of a program was successful can be checked by glGetProgramiv
and the parameter GL_LINK_STATUS
. e.g.:
bool LinkStatus(GLuint program)
{
GLint status = GL_TRUE;
glGetProgramiv( program, GL_LINK_STATUS, &status );
if (status == GL_FALSE)
{
GLint logLen;
glGetProgramiv( program, GL_INFO_LOG_LENGTH, &logLen );
std::vector< char >log( logLen );
GLsizei written;
glGetProgramInfoLog( program, logLen, &written, log.data() );
std::cout << "link error:" << std::endl << log.data() << std::endl;
}
return status != GL_FALSE;
}
Shader Storage Buffer Object SSBO
Related Stack Overflow questions:
Use glGetProgramInterface
with the parameter GL_SHADER_STORAGE_BLOCK
to get the number of the
Shader Storage Buffer Objects and the maximum name length.
The maximum name length of the buffer variables can be get from the program interface GL_BUFFER_VARIABLE
:
GLuint prog_obj; // shader program object
GLint no_of, ssbo_max_len, var_max_len;
glGetProgramInterfaceiv(prog_obj, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &no_of);
glGetProgramInterfaceiv(prog_obj, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &ssbo_max_len);
glGetProgramInterfaceiv(prog_obj, GL_BUFFER_VARIABLE, GL_MAX_NAME_LENGTH, &var_max_len);
The name of the SSBO can be get by glGetProgramResourceName
and a resource index by glGetProgramResourceIndex
:
std::vector< GLchar >name( max_len );
for( int i_resource = 0; i_resource < no_of; i_resource++ ) {
// get name of the shader storage block
GLsizei strLength;
glGetProgramResourceName(
prog_obj, GL_SHADER_STORAGE_BLOCK, i_resource, ssbo_max_len, &strLength, name.data());
// get resource index of the shader storage block
GLint resInx = glGetProgramResourceIndex(prog_obj, GL_SHADER_STORAGE_BLOCK, name.data());
// [...]
}
Data of the shader storage block can be retrieved by glGetProgramResource
. See also Program Introspection.
Get the number of of buffer variables and its indices from program interface and GL_SHADER_STORAGE_BLOCK
and the shader storage block resource resInx
:
for( int i_resource = 0; i_resource < no_of; i_resource++ ) {
// [...]
GLint resInx = ...
// get number of the buffer variables in the shader storage block
GLenum prop = GL_NUM_ACTIVE_VARIABLES;
GLint num_var;
glGetProgramResourceiv(
prog_obj, GL_SHADER_STORAGE_BLOCK, resInx, 1, &prop,
1, nullptr, &num_var);
// get resource indices of the buffer variables
std::vector<GLint> vars(num_var);
prop = GL_ACTIVE_VARIABLES;
glGetProgramResourceiv(
prog_obj, GL_SHADER_STORAGE_BLOCK, resInx,
1, &prop, (GLsizei)vars.size(), nullptr, vars.data());
// [...]
}
Get the offsets of the buffer variables, in basic machine units, relative to the base of buffer and its names from the program interface GL_BUFFER_VARIABLE
and the resource indices vars[]
:
for( int i_resource = 0; i_resource < no_of; i_resource++ ) {
// [...]
std::vector<GLint> offsets(num_var);
std::vector<std::string> var_names(num_var);
for (GLint i = 0; i < num_var; i++) {
// get offset of buffer variable relative to SSBO
GLenum prop = GL_OFFSET;
glGetProgramResourceiv(
prog_obj, GL_BUFFER_VARIABLE, vars[i],
1, &prop, (GLsizei)offsets.size(), nullptr, &offsets[i]);
// get name of buffer variable
std::vector<GLchar>var_name(var_max_len);
GLsizei strLength;
glGetProgramResourceName(
prog_obj, GL_BUFFER_VARIABLE, vars[i],
var_max_len, &strLength, var_name.data());
var_names[i] = var_name.data();
}
// [...]
}
See also ARB_shader_storage_buffer_object
Related Stack Overflow questions:
If there are different indexes for vertex coordinates and texture coordinates, then the vertex positions must be “duplicated”.
The vertex coordinate and its attributes (like texture coordinate) form a tuple. Each vertex coordinate must have its own texture coordinates and attributes. You can think of a 3D vertex coordinate and a 2D texture coordinate as a single 5D coordinate.
See Rendering meshes with multiple indices.
The vertex attributes for each vertex position form a set of data. This means you have to create tuples of vertex coordinate, and texture coordiantes.
Let’s assume that you have a .obj file like this:
v -1 -1 -1
v 1 -1 -1
v -1 1 -1
v 1 1 -1
v -1 -1 1
v 1 -1 1
v -1 1 1
v 1 1 1
vt 0 0
vt 0 1
vt 1 0
vt 1 1
vn -1 0 0
vn 0 -1 0
vn 0 0 -1
vn 1 0 0
vn 0 1 0
vn 0 0 1
f 3/1/1 1/2/1 5/4/1 7/3/1
f 1/1/2 2/2/2 3/4/2 6/3/2
f 3/1/3 4/2/3 2/4/3 1/3/3
f 2/1/4 4/2/4 8/4/4 6/3/4
f 4/1/5 3/2/5 7/4/5 8/3/5
f 5/1/6 6/2/6 8/4/6 7/3/6
From this you have to find all the combinations of vertex coordinate, texture texture coordinate and normal vector indices, which are used in the face specification:
0 : 3/1/1
1 : 1/2/1
2 : 5/4/1
3 : 7/3/1
4 : 1/1/2
5 : 2/2/2
6 : 3/4/2
7 : 6/3/2
8 : ...
Then you have to create a vertex coordinate, texture coordinate and normal vector array corresponding to the array of index combinations. The vertex coordinates and its attributes can either be combined in one array to data sets, or to three arrays with equal number of attributes:
index vx vy vz u v nx ny nz
0 : -1 1 -1 0 0 -1 0 0
1 : -1 -1 -1 0 1 -1 0 0
2 : -1 -1 1 1 1 -1 0 0
3 : -1 1 1 1 0 -1 0 0
4 : -1 -1 -1 0 0 0 -1 0
5 : 1 -1 -1 0 1 0 -1 0
6 : -1 1 -1 1 1 0 -1 0
7 : 1 -1 1 1 0 0 -1 0
8 : ...
See the very simple c++ function, which can read an .obj file, like that you linked to. The function reads a file and writes the data to an element vector and an attribute vector.
Note, the function can be optimized and does not care about performance. For a small file (like cube3.obj which you liked to), that doesn’t matter, but for large file, especially the linear search in the index table, will have to be improved.
I just tried to give you an idea how to read an .obj file and how to create an element and attribute vector, which can be directly used to draw an mesh with the use of OpenGL.
#include <vector>
#include <array>
#include <string>
#include <fstream>
#include <strstream>
#include <algorithm>
bool load_obj(
const std::string filename,
std::vector<unsigned int> &elements,
std::vector<float> &attributes )
{
std::ifstream obj_stream( filename, std::ios::in );
if( !obj_stream )
return false;
// parse the file, line by line
static const std::string white_space = " \t\n\r";
std::string token, indices, index;
float value;
std::vector<float> v, vt, vn;
std::vector<std::array<unsigned int, 3>> f;
for( std::string line; std::getline( obj_stream, line ); )
{
// find first non whispce characterr in line
size_t start = line.find_first_not_of( white_space );
if ( start == std::string::npos )
continue;
// read the first token
std::istringstream line_stream( line.substr(start) );
line_stream.exceptions( 0 );
line_stream >> token;
// ignore comment lines
if ( token[0] == '#' )
continue;
// read the line
if ( token == "v" ) // read vertex coordinate
{
while ( line_stream >> value )
v.push_back( value );
}
else if ( token == "vt" ) // read normal_vectors
{
while ( line_stream >> value )
vt.push_back( value );
}
else if ( token == "vn" ) // read normal_vectors
{
while ( line_stream >> value )
vn.push_back( value );
}
else if ( token == "f" )
{
// read faces
while( line_stream >> indices )
{
std::array<unsigned int, 3> f3{ 0, 0, 0 };
// parse indices
for ( int j=0; j<3; ++ j )
{
auto slash = indices.find( "/" );
f3[j] = std::stoi(indices.substr(0, slash), nullptr, 10);
if ( slash == std::string::npos )
break;
indices.erase(0, slash + 1);
}
// add index
auto it = std::find( f.begin(), f.end(), f3 );
elements.push_back( (unsigned int)(it - f.begin()) );
if ( it == f.end() )
f.push_back( f3 );
}
}
}
// create array of attributes from the face indices
for ( auto f3 : f )
{
if ( f3[0] > 0 )
{
auto iv = (f3[0] - 1) * 3;
attributes.insert( attributes.end(), v.begin() + iv, v.begin() + iv + 3 );
}
if ( f3[1] > 0 )
{
auto ivt = (f3[1] - 1) * 2;
attributes.insert( attributes.end(), vt.begin() + ivt, vt.begin() + ivt + 2 );
}
if ( f3[2] > 0 )
{
auto ivn = (f3[2] - 1) * 3;
attributes.insert( attributes.end(), vn.begin() + ivn, vn.begin() + ivn + 3 );
}
}
return true;
}
Related Stack Overflow questions:
void createSpring(
GLfloat rounds, GLfloat height, GLfloat thickness, GLfloat radius,
std::vector<GLfloat> &vertices, std::vector<GLuint> &indices)
{
const int slices = 32;
const int step = 5;
for (int i = -slices; i <= rounds * 360 + step; i += step)
{
for (int j = 0; j < slices; j ++)
{
GLfloat t = (GLfloat)i / 360 + (GLfloat)j / slices * step / 360;
t = std::max(0.0f, std::min(rounds, t));
GLfloat a1 = t * M_PI * 2;
GLfloat a2 = (GLfloat)j / slices * M_PI * 2;
GLfloat d = radius + thickness * cos(a2);
vertices.push_back(d * cos(a1));
vertices.push_back(d * sin(a1));
vertices.push_back(thickness * sin(a2) + height * t / rounds);
}
}
for (GLuint i = 0; i < (GLuint)vertices.size() / 3 - slices; ++i)
{
indices.push_back(i);
indices.push_back(i + slices);
}
}