= Visualization with OpenGL hands-on =
[[PageOutline]]
Tutorial aims to introduce visualization techniques with modern Open Graphics Language (OpenGL)
approaches standardized with version 3.x+. OpenGL Shading Language (GLSL) is used for that without
tendency to introduce ''photo-realism''as output but rather useful colors for scientific data exploration.
{{{
#!html
}}}
Running this tutorial on Linux desktop one requires at least the OpenGL 2.0 graphics
with the GLSL 1.1 and supporting libraries GL, GLU, GLUT, GLEW.
This can be verified with the following commands:
{{{
#!sh
$ glxinfo |grep OpenGL.*version
OpenGL version string: 2.1 Mesa 8.0.5
OpenGL shading language version string: 1.20
$ ls /usr/include/GL/{glut.h,glew.h,gl.h,glu.h}
/usr/include/GL/glew.h /usr/include/GL/glu.h
/usr/include/GL/gl.h /usr/include/GL/glut.h
}}}
== Legacy OpenGL ==
Create the following {{{first.c}}} using your favorite editor.
{{{
#!c
#include
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.4, 1.0);
glBegin(GL_LINES);
glVertex2f(0.1, 0.1);
glVertex3f(0.8, 0.8, 1.0);
glEnd();
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutCreateWindow("first.c GL code");
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
}}}
Create {{{Makefile}}} to build your program. [[Image(first.png,right)]]
{{{
#!sh
CFLAGS=-Wall
LDFLAGS=-lGL -lGLU -lglut -lGLEW
ALL=first
default: $(ALL)
first : first.o
clean:
rm -rf *~ *.o $(ALL)
}}}
Beware that Makefile is TAB aware. So the last line should contain TAB indentation and not spacing.
Make and run the program with
{{{
#!sh
make
./first
}}}
Try the same program in Python
{{{
#!python
from OpenGL.GLUT import *
from OpenGL.GL import *
import sys
def display():
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(1.0, 0.4, 1.0)
glBegin(GL_LINES)
glVertex2f(0.1, 0.1)
glVertex3f(0.8, 0.8, 1.0)
glEnd()
glutSwapBuffers()
if __name__ == "__main__":
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE)
glutCreateWindow("first.py GL code")
glutDisplayFunc(display)
glutMainLoop()
}}}
and run it with
{{{
#!sh
python first.py
}}}
== Exercise #1: ==
1. Add RGB color to vertices with {{{ glColor3f(0.0, 0.4, 1.0);}}}.
2. Replace single line drawing in {{{display()}}} with the following snippet
{{{
#!c
GLfloat vertices[][2] = {
{ -0.90, -0.90 }, // Triangle 1
{ 0.85, -0.90 },
{ -0.90, 0.85 },
{ 0.90, -0.85 }, // Triangle 2
{ 0.90, 0.90 },
{ -0.85, 0.90 }
};
}}}
and try to draw two wireframe triangles in a loop. Change primitive to {{{GL_LINE_LOOP}}}.
3. Draw two primitives with {{{GL_TRIANGLES}}}. [[Image(ex1-5.png, right)]]
4. Add different color to each vertex.
{{{
#!c
GLfloat color[][3] = {
{1, 0, 0}, {0, 1, 0}, {0, 0, 1},
{1, 1, 0}, {0, 1, 1}, {1, 0, 1}};
}}}
5. Replace loop with the following
{{{
#!c
glVertexPointer(2, GL_FLOAT, 0, &vertices[0][0]);
glEnableClientState(GL_VERTEX_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableClientState(GL_VERTEX_ARRAY);
}}}
How can we add color to vertices? See [http://www.opengl.org/sdk/docs/man2/xhtml/glColorPointer.xml glColorPointer] and [http://www.opengl.org/sdk/docs/man2/xhtml/glEnableClientState.xml glEnableClientState].
6. Change background to {{{glClearColor(0.9,1,1,1.0);}}}
== Modern OpenGL ==
[[Image(triangle.png,right)]]
We extend previous exercise with example that introduces OpenGL 3.x techniques:
* OpenGL Shading Language where simple vertex and fragment shader are required.
* Vertex Aray Objects (VAOs) stored in GPU
Create {{{triangle.c}}} and update {{{Makefile}}} with new target
{{{
#!sh
#include
#include
#include
#include
static const GLchar * vertex_shader[] =
{"void main()"
"{"
" gl_Position = ftransform();"
"}"
};
static const GLchar * fragment_shader[] =
{"void main()"
"{"
" gl_FragColor = vec4(0.4,0.4,0.8,1.0);"
"}"
};
void setShaders()
{
GLuint v, f, p;
v = glCreateShader(GL_VERTEX_SHADER);
f = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(v, 1, vertex_shader, NULL);
glShaderSource(f, 1, fragment_shader, NULL);
glCompileShader(v);
glCompileShader(f);
p = glCreateProgram();
glAttachShader(p,f);
glAttachShader(p,v);
glLinkProgram(p);
glUseProgram(p);
}
enum VAO_IDs { Triangles, NumVAOs };
enum Buffer_IDs {ArrayBuffer,NumBuffers};
enum Attrib_IDs { vPosition = 0 };
GLuint VAOs[NumVAOs];
GLuint Buffers[NumBuffers];
#define NumVertices 6
void init(void)
{
glGenVertexArrays(NumVAOs, VAOs);
glBindVertexArray(VAOs[Triangles]);
GLfloat vertices[NumVertices][2] = {
{ -0.90, -0.90 }, // Triangle 1
{ 0.85, -0.90 },
{ -0.90, 0.85 },
{ 0.90, -0.85 }, // Triangle 2
{ 0.90, 0.90 },
{ -0.85, 0.90 }
};
glGenBuffers(NumBuffers, Buffers);
glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer]);
glBufferData( GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(vPosition, 2, GL_FLOAT, GL_FALSE, 0, (const void*)0);
glEnableVertexAttribArray(vPosition);
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glBindVertexArray(VAOs[Triangles]);
glDrawArrays(GL_TRIANGLES, 0, NumVertices);
glutSwapBuffers();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutCreateWindow("GLSL Intro");
glutDisplayFunc(display);
glewInit();
if (!glewIsSupported("GL_VERSION_2_0"))
{
printf("GLSL not supported\n");
exit(EXIT_FAILURE);
}
glClearColor(0.9,1.0,1.0,1.0);
init();
setShaders();
glutMainLoop();
return 0;
}
}}}
== Exercises #2 ==
1. To be able to continue and not get lost introduce shader compiler logs in case of compilation errors by adding the following code into {{{setShaders()}}} right at after vertex shader compilation:
{{{
#!c
GLint compiled;
glGetShaderiv(v, GL_COMPILE_STATUS, &compiled );
if ( !compiled ) {
GLsizei len;
glGetShaderiv( v, GL_INFO_LOG_LENGTH, &len );
GLchar* log = malloc(sizeof(GLchar)*(len+1));
printf("Shader compilation failed: %s\n", log);
free(log);
}
}}}
Do not forget to repeat the same thing for fragment shader.
Add linker debugging
{{{
#!c
GLint linked;
glGetProgramiv(p, GL_LINK_STATUS, &linked );
if ( !linked ) {
GLsizei len;
glGetProgramiv( p, GL_INFO_LOG_LENGTH, &len );
GLchar* log = malloc(sizeof(GLchar)*(len+1));
glGetProgramInfoLog( p, len, &len, log );
printf("Shader linking failed: %s\n", log);
free(log);
}
}}}
Create some error to verify if it works.
For general (core) OpenGL errors we can use the following utility at suspicious places.
{{{
#!c
GLenum errCode;
if ((errCode = glGetError()) != GL_NO_ERROR) {
const GLubyte *errString = gluErrorString(errCode);
fprintf (stderr, "OpenGL Error: %s\n", errString);
}
}}}