Changes between Version 47 and Version 48 of tutorial

Jul 2, 2013, 9:03:42 AM (7 years ago)

Embed introduction


  • tutorial

    v47 v48  
    1 = Visualization with OpenGL hands-on =
     1= Visualization with OpenGL =
    3 Tutorial aims to introduce visualization techniques with modern Open Graphics Language (OpenGL)
    4 approaches standardized with version 3.x+. OpenGL Shading Language (GLSL) is used for that without
    5 tendency to introduce ''photo-realism''as output but rather useful colors for scientific data exploration.
    7 Please visit [wiki:opengl-intro] page for quick introduction to OpenGL.
     3Essential approaches to programming computer graphics with Open Graphics
     4Language (OpenGL) graphics library are described. This document serves as
     5the basis for exercises in PRACE Summer of HPC Visualization training.
     6Rationale for giving introduction to OpenGL is that such knowledge is
     7important when developing codes that require some specific visualization
     8for which OpenGL can be handy. Programming Graphical Processing Unit
     9(GPU) through ''shaders'' is an important technique to accelerate
     10graphics and other embarrassing parallel problems. OpenGL evolved from
     11immediate mode to GPU only processing with the advent of OpenGL Shading
     12Language (GLSL). OpenGL Shading Language (GLSL) is used for that without
     13tendency to introduce ''photo-realism''as output but rather useful colors
     14for scientific data exploration.
     15Introduction to the subject is given by recipes to
     16follow, discussing important techniques for visualization that can also
     17be extended to general GPU programming for parallel computing. Instead of
     18jumping to the latest OpenGL specification we use minimum required OpenGL
     192.1 with the extensions currently available on modest hardware and still
     20be able to use modern OpenGL 3.1+ programming principles.
    922Running this tutorial on Linux desktop one requires at least the OpenGL 2.0 graphics
     35== Introduction ==
     36For the visualization of specific phenomena is usually not possible to use
     37a general purpose visualization tools. Such cases occur especially in the
     38visualization of engineering and physics problems. The modeling results are
     39usually not only simple function plots but complex objects such as graphs,
     40hierarchical structure, animation, motion mechanism, control channels,
     41volume models of specific forms, ...
     43Through the time different standards were effective for computer graphics.
     44This is mainly due to the complexity of implementation and closed code in
     45the past. OpenGL remains the only widely accepted open standard, which was
     46first introduced on Silicon Graphics workstations (SGI). There exist also a
     47Microsoft Direct3D, which is limited to PCs with Windows and is not as easy
     48to use as OpenGL, which is due to its openness and capacity provided on all
     49operating systems and hardware platforms. OpenGL stagnated for some time
     50with upgrades to the original SGI specification. Many extensions previously
     51available from hardware vendors are now standardized with OpenGL 3+ where
     52things dramatically changed. ''Immediate mode'' programming where
     53communication from OS to GPU was regular practice and major obstacle to
     54graphics performance. Programming knowledge of OpenGL 1.x is therefore not
     55recommended for nowadays and can simply be forgotten and treated as legacy.
     57[[Image(OpenGL-pipeline.svg, 320px, right, title=OpenGL pipeline)]]
     58Modern OpenGL changed previously fixed rendering pipeline to fully
     59programmable graphics pipeline as shown in Fig.1
     60Processors that transform input vertex data to the window context at the
     61end are called ''shaders''. The Vertex shader and the Fragment shader
     62are most important in the rendering pipeline. To use rendering pipeline as
     63shown in Fig.1 one has to provide program for them
     64as there is no default because they are essential part of every OpenGL
     65program. Programming shaders is done in GLSL (OpenGL Shading Language) that
     66is similar to C language with some predefined variables and reserved
     67keywords that help describing communication between OS and GPU. Programs
     68(for shaders) written in GLSL are compiled on-the-fly, assembled and
     69transferred to GPU for execution.
     71OpenGL is designed as a hardware-independent interface between the program
     72code and graphics accelerator. Hardware independence of OpenGL means also
     73that in the language specification there is no support for control of
     74window system events that occur with interactive programming. For such
     75interactive control for each operating system were designed interfaces that
     76connect the machine with the OpenGL system. Due to the specifics of
     77different window systems (Windows, XWindow, MacOS, iOS, Android) it is
     78required that for each system tailored techniques are used to call OpenGL
     79commands in hardware. Portability is thus limited by graphical user
     80interface (GUI) that handles OpenGL context (window). In order to still be
     81able to write ''portable programs'' with otherwise limited functionality
     82of the user interface, GLUT library (OpenGL Utility Toolkit) was created.
     83It compensates all the differences between operating systems and introduces
     84a unified methods of manipulating ''events''. With the GLUT library it
     85is possible to write portable programs that are easy to write and have
     86sufficient capacity for simple user interfaces.
    2288== Legacy OpenGL ==
    23 Create the following {{{first.c}}} using your favorite editor.
     89Basics of the OpenGL language are given in the (core) GL library. More
     90complex primitives can be build by the GLU library (GL Utility) which
     91contain the routines that use GL routines only. GLU routines contain
     92multiple GL commands that are generally applicable and have therefore been
     93implemented to ease OpenGL programming.
     95To get quickly introduced into OpenGL it is better to start with legacy
     96(short and simple) program that will be later replaced with modern OpenGL
     97after discussion that caused replacement with OpenGL 3.x. Before we can
     98dive in OpenGL we need to revise windowing systems and how they interact
     99with users.
     101=== Events ===
     102All window interfaces (GUI) are designed to operate on the principle of
     103''events''. Events are signals from the Window system to our program.
     104Our program is fully responsible for the content of the window. Windowing
     105system only assigns area (window). The contents of the window area must
     106then be fully controlled. In addition to the window assignment the
     107windowing system to sends messages (events) to our program. The most common
     108messages are:
     109 display:: The command asks for presentation of window contents. There
     110  are several possible occasions when this happens. For example, when
     111  another window reveals part of our window or when window is moved on the
     112  screen. Another example is when window is re-displayed after icon is
     113  being pressed at the taskbar. Interception of such events is mandatory,
     114  because every program must ensure that the contents of the window is
     115  restored window, when such event occurs.
     116 reshape:: Command to our program that occurs when the size and/or
     117  shape of the window changes. In this case the content of the window must
     118  be provided for a new window size. Event occurs, inter alia, when the
     119  mouse resizes the window. Immediately after reshape, display event is
     120  sent.
     121 keyboard:: Commands coming from the keyboard.
     122 mouse:: Describes the mouse buttons at their change when user pressed
     123  or released one of the buttons.
     124 motion:: This command defines the motion tracking of the moving mouse
     125  with pressed button.
     126 timer:: Program requests message after a certain time in order to
     127  change the contents of the window. The function is suitable for timed
     128  simulation (animation).
     129In addition to these events there exist some other too. In general it is
     130not necessary that all events to a window are implemented in our program.
     131It is our responsibility to decide which
     132events will be used in the application. Usually program must notify
     133windowing system which events will took over and for that window will
     134receive events.
     136=== GLUT ===
     137For an abstraction of events (commands from the windowing system) we will
     138use GLUT library (OpenGL Utility Toolkit). Many other GUI libraries are
     139available (native and portable). GLUT falls into the category of simple
     140operating/windowing system independent GUIs for OpenGL. An example of a
     141minimal program that draws a single line is shown in Listing 1 (first.c).
     142\lstinputlisting[caption=Drawing a line with OpenGL and GLUT.,
     143label=first.c ]{first.c} Program in C language consists of two parts: the
     144subroutine display and the main program. Program runs from the start in
     145{{{main()}}} and at the end falls into endless loop
     146{{{glutMainLoop}}} that calls registered subroutines when event
     147occurs. Before falling into {{{glutMainLoop}}} we need to prepare drawing
     150Listing 1: `first.c`
    45172  glutMainLoop();
    46173  return 0;
    47 }
    48 }}}
    50 Create {{{Makefile}}} to build your program. [[Image(first.png,right)]]
    52 {{{
    53 #!sh
    54 CFLAGS=-Wall -g
    55 LDFLAGS=-lGL -lGLU -lglut -lGLEW
    57 ALL=first
    58 default: $(ALL)
    60 first : first.o
    62 clean:
    63       rm -rf  *.o *~ [!m]*.obj core* $(ALL)
    64 }}}
    65 Beware that Makefile is TAB aware. So the last line should contain TAB indentation and not spacing.
    67 Make and run the program with
    68 {{{
    69 #!sh
    70 make
    71 ./first
    72 }}}
    74 Try the same program in Python
    75 {{{
    76 #!python
     178Listing 2: ``
    77181from OpenGL.GLUT import *
    78182from OpenGL.GL import *
    95199    glutMainLoop()
    97 and run it with
     204Structure of the program is usually very similar for
     205all languages. Confer Listing 2 ( rewritten in Python. All GLUT
     206programs include commands in the following order:
     208 * Include definitions of constants and functions for OpenGL and GLUT
     209  with the include statement.
     210 * Initialize GLUT and setup other variables that are not directly
     211  related to OpenGL but rather to the object that is being visualized.
     212 * Set window parameters such as initial position, size, type, bit plane
     213  memory.
     214 * Create the window and name it.
     215 * Setup the features of the OpenGL machine. These are usually commands
     216  {{{glEnable}}} for setup of lighting, materials, lists, and non-default
     217  behavior of OpenGL machine.
     218 * Register call-back routines which will be called at events. Mandatory
     219  registration is just for {{{glutDisplayFunc(display)}}}. The rest are
     220  optional.
     221 * The last command in {{{main}}} is a call to {{{glutMainLoop}}}, from which
     222  the program returns when the window is closed. At the same time the
     223  {{{main}}} program ends.
     226The command {{{glutInit}}} initializes GLUT library routines. It is followed by
     227a request for window creation of a certain type. The constant {{{GLUT_DOUBLE}}}
     228and the default {{{GLUT_RGB}}} suggests that we want a double-buffered window
     229with a RGB space. Variable {{{window}}} keeps reference of window returned by
     230{{{glutCreateWindow}}} and at the same time instructs the OS to set the window
     231title. We have to tell to the window system which events the program will
     232intercept. For example given, this is only {{{display}}} of the contents of the
     233window. Call of the subroutine {{{glutDisplayFunc}}} instructs the
     234{{{glutMainLoop}}} that whenever requests from OS for window redisplay occurs
     235subroutine {{{display}}} should be called. Routines for handling events are
     236usually called ``call-back'' routines as it reside in program as standalone
     237code snippets that are called auto-magically at certain events from the
     238windowing system. When some event occurs is up to the windowing system that
     239follows user interaction. The main point to emphasize here is that
     240registered call-back routines do get additional information on the kind of
     241event. For example of keyboard event we can get also mouse (x,y)
     242coordinates besides the key pressed.
     245We have seen that the subroutine {{{display}}} includes commands responsible
     246for drawing in the window. All routines or functions there are OpenGL and
     247have prefix {{{gl}}} to the name. Prefix is necessary to distinguish them and
     248prevent name clash with other libraries. To understand the language one can
     249interpret function names without prefixes and suffixes as the OpenGL is
     250designed so, that the types of the arguments for all programming languages
     251are similar. Subroutine {{{display}}} is therefore responsible for drawing the
     252contents of the window. The {{{glClear}}} command clears the entire area of the
     253window. When clearing we need to define precisely what we want to clear by
     254argument given. In our case, this is {{{GL_COLOR_BUFFER_BIT}}}, which means
     255clearing of all pixels in the color buffer.
     257The {{{glColor}}} command to sets the current color of graphic elements that
     258will be drawn in subsequent commands. As an argument RGB color components
     259are passed. Usually commands with multiple arguments are provide for
     260different data types (integer, float, double) and some command can have
     261different number of arguments for the same command. To distinguish them
     262suffix is added. For the {{{glColor3f}}} suffix {{{3f}}} therefore means that the
     263subroutine has three arguments of type float. Choice of the arguments type
     264depends on application requirements. Programmer can freely choose data type
     265that suits most without the need of data type conversion. In our example we
     266have two variants for vertex command with different number of arguments of
     267the same type. {{{glVertex2f}}} means that we are specifying just two
     268coordinates while the third is by default z=0. Types of the arguments
     269specified as the suffix letter are as follows:
     270 f:: float in C language and {{{real*4}}} in Fortran.
     271 d:: double for C and {{{real*8}}} in Fortran.
     272 i:: integer (4 bytes).
     273 s:: short integer in C and {{{integer*2}}} in Fortran.
     274Besides fixed number of arguments there are also functions that take as an
     275argument vector (as a pointer to memory). For these the suffix contains
     276letter {{{v}}} at the end. Below are some interpretations of suffixes:
     277 3f:: Three arguments of {{{real}}}s follow as arguments.
     278 3i:: Three arguments of {{{integer}}}s follow as arguments.
     279 3fv:: One argument as a vector that contains three {{{float}}}s
     280  follows.
     281Variety of different arguments for the same command can be in {{{glVertex}}}
     282command where we can find
     285  glVertex2d,  glVertex2f,  glVertex2i, glVertex2s,  glVertex3d,  glVertex3f,
     286  glVertex3i,  glVertex3s,  glVertex4d, glVertex4f,  glVertex4i,  glVertex4s,
     287  glVertex2dv, glVertex2fv, glVertex2iv,glVertex2sv, glVertex3dv, glVertex3fv,
     288  glVertex3iv, glVertex3sv, glVertex4dv,glVertex4fv, glVertex4iv, glVertex4sv.
     290Large number of routines for the same function is performance and language
     291related in order to waive the default conversion and thus provide a more
     292comprehensive and faster code. For languages with name mangling like C++
     293one can find simpler OpenGL wrapped functions (eg. just {{{glVertex}}}) that
     294don't affects performance. But as many languages does not have name
     295mangling built into compiler such practise is not widespread. Besides
     296specifying single vertex each time one can use {{{glVertexPointer}}} and points
     297to memory where number of vertices of specified type exist. This can save
     298us of some looping, but as this is essentially copying of system memory
     299into OpenGL hardware engine, the performance is not really improved.
     301Drawing of graphic primitives in OpenGL occurs between two commands
     302{{{glBegin(primitive type)}}} and {{{glEnd()}}}. Primitive type given as argument
     303at the beginning specifies how subsequent vertices will be used for
     304primitive generation. Instead of giving primitive type as number several
     305predefined constant are provided within {{{include}}} directive to ease
     306readability and portability of the OpenGL programs. Before providing vertex
     307position one can change OpenGL engine primitive state such as current
     308drawing {{{glColor3f}}} or {{{glNormal}}} that is per vertex property.
     310The last command in the {{{display}}} subroutine is {{{glutSwapBuffers()}}}. For
     311applications in which the contents of the display changes frequently, it is
     312most appropriate to use windows dual graphics buffers, which is setup by
     313using the {{{GLUT_DOUBLE}}} at window initialization. The advantage of such
     314drawing strategy is in the fact that while one buffer is used for current
     315drawing the other is shown. Drawing thus occurs in the background and when
     316buffer is ready for display we simply flip the buffers. In particular it
     317should be noted that such behaviour is system dependent and once upon a
     318time when the {{{GLUT_SINGLE}}} (without double buffers) with the {{{glFlush()}}}
     319at the end was used instead. Nowadays {{{GLUT_DOUBLE}}} is usually used, which
     320is most helpful with high frame-rate applications such as animation.
     321Only simple primitives are used within OpenGL. Reason for that is mainly
     322due to the requirement of performance and possible hardware acceleration.
     323There are three types of simple primitives: points, lines, and triangles.
     324Higher level primitives (like quadrilaterals) can be assembled from simple
     325ones. Curves can be approximated by lines. Large surfaces can be tessellated
     326with triangles. For complex surfaces (like NURBS) GLU library can be used
     327to calculate vertices. The following line primitives are possible:
     328 GL_LINES:: Pairs of vertices in a vertex stream create line
     329  segments.
     330 GL_LINE_STRIP:: Vertex stream builds connected lines
     331  (polyline).
     332 GL_LINE_LOOP:: Same as polyline above except that last
     333  vertex is connected by a line to the first.
     334Every surface can be assembled with triangles.
     335 GL_TRIANGLES:: For each triangle three vertices are required
     336  from vertex stream.
     337 GL_TRIANGLE_STRIP:: Strip of triangles. For first triangle
     338  three vertices are needed. For every additional vertex new triangle is
     339  created by using last two vertices.
     340 GL_TRIANGLE_FAN:: Triangles are added to the first one by
     341  using first and last vertex to create a triangle fan.
     343=== Exercises #1: ===
     3451. Create the following {{{first.c}}} using your favorite editor.  [[Image(first.png,right)]]
     348#include <GL/glut.h>
     350void display()
     352  glClear(GL_COLOR_BUFFER_BIT);
     353  glColor3f(1.0, 0.4, 1.0);
     354  glBegin(GL_LINES);
     355    glVertex2f(0.1, 0.1);
     356    glVertex3f(0.8, 0.8, 1.0);
     357  glEnd();
     358  glutSwapBuffers();
     361int main(int argc, char *argv[])
     363  glutInit(&argc,argv);
     364  glutInitDisplayMode(GLUT_DOUBLE);
     365  glutCreateWindow("first.c GL code");
     366  glutDisplayFunc(display);
     367  glutMainLoop();
     368  return 0;
     372Create the {{{Makefile}}} to build your program.
    100 python
    101 }}}
    104 === Exercises #1: ===
    105  1. Add RGB color to vertices with {{{ glColor3f(0.0, 0.4, 1.0);}}}.
    106  2. Replace single line drawing in {{{display()}}} with the following snippet
     376CFLAGS=-Wall -g
     377LDFLAGS=-lGL -lGLU -lglut -lGLEW
     380default: $(ALL)
     382first : first.o
     385      rm -rf  *.o *~ [!m]*.obj core* $(ALL)
     387Beware that Makefile is TAB aware. So the last line should contain TAB indentation and not spacing.
     389Make and run the program with
     396 2. Add RGB color to vertices with {{{ glColor3f(0.0, 0.4, 1.0);}}}.
     397 3. Replace single line drawing in {{{display()}}} with the following snippet
    107398 {{{
    118409 and try to draw two wireframe triangles in a loop. Change primitive to {{{GL_LINE_LOOP}}}.
    119  3. Draw two primitives with {{{GL_TRIANGLES}}}. [[Image(ex1-5.png, right)]]
    120  4. Add different color to each vertex.
     410 4. Draw two primitives with {{{GL_TRIANGLES}}}. [[Image(ex1-5.png, right)]]
     411 5. Add different color to each vertex.
    125416  {1, 1, 0}, {0, 1, 1}, {1, 0, 1}};
    127  5. Replace loop with the following
     418 6. Replace loop with the following
    135426 How can we add color to vertices? See [ glColorPointer] and [ glEnableClientState].
    136  6. Change background to {{{glClearColor(0.9,1,1,1.0);}}} and suggest initial window in `main()`
     427 7. Change background to {{{glClearColor(0.9,1,1,1.0);}}} and suggest initial window in `main()`
    137428  {{{
    138429  #!c
    141432                         (glutGet(GLUT_SCREEN_HEIGHT)-512)/2);
    142433  }}}
    143  7. Add [ keyboard event]
     434 8. Add [ keyboard event]
    144435  to quit the program when pressing ESCape key with keycode 27 by adding callback function
    145436  {{{
    156447== Modern OpenGL ==
    157 [[Image(OpenGL-pipeline.svg, 320px, right, title=OpenGL pipeline)]]
     448Immediate mode programming with {{{glBegin}}} and {{{glEnd}}} was removed from
     449OpenGL 3.x as such transmission of vertex streams and its attributes
     450(colors, normals, ...) from system memory to GPU is considered as a major
     451performance drawback. Display lists were previously used to save stream of
     452OpenGL calls that also included vertex data and was just replayed at
     453redraw. But this is inherently sequential operation that blocked parallel
     454vertex processing. Requirement to store vertex arrays to GPU directly as an
     455''object'' can solve problem described. Storing vertex arrays into GPU
     456also means that manipulation on them to build the model should be inside
     457the GPU. Legacy OpenGL included many ``modelling'' utilities for
     458transforming world coordinates into viewport. Transformations of coordinate
     459systems in 3D space allowed manipulate model stack easily with
     460{{{glPushMatrix}}} and {{{glPopMatrix}}} commands. But similarly to
     461{{{glBegin}}}/{{{glEnd}}} such manipulations are not used outside GPU anymore.
     462Instead all operations on vertex data is transferred to ''vertex shader''.
     463There operations on data can be performed with standard vector
     464math in homogeneous coordinates.
    158466We extend previous exercise with example that introduces OpenGL 3.x techniques:
    159467 * OpenGL Shading Language (GLSL 1.2) where simple vertex and fragment shader are required.