wiki:tutorial

Version 16 (modified by leon, 6 years ago) (diff)

Prepare for temperature

Visualization with OpenGL hands-on

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-realismas output but rather useful colors for scientific data exploration.

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:

$ 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.

#include <GL/glut.h>

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. Legacy sample

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

make
./first

Try the same program in 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

python first.py

Exercises #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
       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. Exercise #1.5
  4. Add different color to each vertex.
     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
      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 glColorPointer and glEnableClientState.
  6. Change background to glClearColor(0.9,1,1,1.0);
  7. Add keyboard event to quit the program when pressing ESCape key with keycode 27 by adding callback function
     void keyboard(unsigned char key, int x, int y) 
     {
       if (key == 27) 
              exit(0);
     }
    
    and registering event within main() by glutKeyboardFunc(keyboard);

Modern OpenGL

OpenL pipeline 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

#include <stdio.h>
#include <stdlib.h>
#include <GL/glew.h>
#include <GL/glut.h>

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;
}

First GLSL example

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:
     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
     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.
       GLenum errCode;
       if ((errCode = glGetError()) != GL_NO_ERROR) {
          const GLubyte *errString = gluErrorString(errCode);
          fprintf (stderr, "OpenGL Error: %s\n", errString);
       }
    
  2. Introduce vertex temperature with additional array
    GLfloat vertex_temperature[] = {0, 0.5, 1, 0.7, 0.2, 0.9};
    
    and replace shaders with
    static const GLchar * vertex_shader[] = {
    "attribute float VertexTemp;" // receive this custom attribute along with vertex position
    "varying float Temperature;"  // communicate between the vertex and the fragment shader
    "void main() { gl_position  = gl_ModelViewProjectionMatrix * gl_Vertex; }"
    };
    static const GLchar * fragment_shader[] = {
    "vec3 Cool = vec3(0, 0, 1);" // Red
    "vec3 Hot  = vec3(1, 0, 1);" // Blue
    "void main()
    "{"
    "  vec3 color = mix(Cool, Hot, Temperature);" // use the built-in mix() function
    "  gl_FragColor = vec4(color, 1.0);"          // append alpha channel
    "}"
    }; 
    

Attachments (24)