| | 35 | == Introduction == |
| | 36 | For the visualization of specific phenomena is usually not possible to use |
| | 37 | a general purpose visualization tools. Such cases occur especially in the |
| | 38 | visualization of engineering and physics problems. The modeling results are |
| | 39 | usually not only simple function plots but complex objects such as graphs, |
| | 40 | hierarchical structure, animation, motion mechanism, control channels, |
| | 41 | volume models of specific forms, ... |
| | 42 | |
| | 43 | Through the time different standards were effective for computer graphics. |
| | 44 | This is mainly due to the complexity of implementation and closed code in |
| | 45 | the past. OpenGL remains the only widely accepted open standard, which was |
| | 46 | first introduced on Silicon Graphics workstations (SGI). There exist also a |
| | 47 | Microsoft Direct3D, which is limited to PCs with Windows and is not as easy |
| | 48 | to use as OpenGL, which is due to its openness and capacity provided on all |
| | 49 | operating systems and hardware platforms. OpenGL stagnated for some time |
| | 50 | with upgrades to the original SGI specification. Many extensions previously |
| | 51 | available from hardware vendors are now standardized with OpenGL 3+ where |
| | 52 | things dramatically changed. ''Immediate mode'' programming where |
| | 53 | communication from OS to GPU was regular practice and major obstacle to |
| | 54 | graphics performance. Programming knowledge of OpenGL 1.x is therefore not |
| | 55 | recommended for nowadays and can simply be forgotten and treated as legacy. |
| | 56 | |
| | 57 | [[Image(OpenGL-pipeline.svg, 320px, right, title=OpenGL pipeline)]] |
| | 58 | Modern OpenGL changed previously fixed rendering pipeline to fully |
| | 59 | programmable graphics pipeline as shown in Fig.1 |
| | 60 | Processors that transform input vertex data to the window context at the |
| | 61 | end are called ''shaders''. The Vertex shader and the Fragment shader |
| | 62 | are most important in the rendering pipeline. To use rendering pipeline as |
| | 63 | shown in Fig.1 one has to provide program for them |
| | 64 | as there is no default because they are essential part of every OpenGL |
| | 65 | program. Programming shaders is done in GLSL (OpenGL Shading Language) that |
| | 66 | is similar to C language with some predefined variables and reserved |
| | 67 | keywords that help describing communication between OS and GPU. Programs |
| | 68 | (for shaders) written in GLSL are compiled on-the-fly, assembled and |
| | 69 | transferred to GPU for execution. |
| | 70 | |
| | 71 | OpenGL is designed as a hardware-independent interface between the program |
| | 72 | code and graphics accelerator. Hardware independence of OpenGL means also |
| | 73 | that in the language specification there is no support for control of |
| | 74 | window system events that occur with interactive programming. For such |
| | 75 | interactive control for each operating system were designed interfaces that |
| | 76 | connect the machine with the OpenGL system. Due to the specifics of |
| | 77 | different window systems (Windows, XWindow, MacOS, iOS, Android) it is |
| | 78 | required that for each system tailored techniques are used to call OpenGL |
| | 79 | commands in hardware. Portability is thus limited by graphical user |
| | 80 | interface (GUI) that handles OpenGL context (window). In order to still be |
| | 81 | able to write ''portable programs'' with otherwise limited functionality |
| | 82 | of the user interface, GLUT library (OpenGL Utility Toolkit) was created. |
| | 83 | It compensates all the differences between operating systems and introduces |
| | 84 | a unified methods of manipulating ''events''. With the GLUT library it |
| | 85 | is possible to write portable programs that are easy to write and have |
| | 86 | sufficient capacity for simple user interfaces. |
| | 87 | |
| 23 | | Create the following {{{first.c}}} using your favorite editor. |
| | 89 | Basics of the OpenGL language are given in the (core) GL library. More |
| | 90 | complex primitives can be build by the GLU library (GL Utility) which |
| | 91 | contain the routines that use GL routines only. GLU routines contain |
| | 92 | multiple GL commands that are generally applicable and have therefore been |
| | 93 | implemented to ease OpenGL programming. |
| | 94 | |
| | 95 | To 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 |
| | 97 | after discussion that caused replacement with OpenGL 3.x. Before we can |
| | 98 | dive in OpenGL we need to revise windowing systems and how they interact |
| | 99 | with users. |
| | 100 | |
| | 101 | === Events === |
| | 102 | All window interfaces (GUI) are designed to operate on the principle of |
| | 103 | ''events''. Events are signals from the Window system to our program. |
| | 104 | Our program is fully responsible for the content of the window. Windowing |
| | 105 | system only assigns area (window). The contents of the window area must |
| | 106 | then be fully controlled. In addition to the window assignment the |
| | 107 | windowing system to sends messages (events) to our program. The most common |
| | 108 | messages 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). |
| | 129 | In addition to these events there exist some other too. In general it is |
| | 130 | not necessary that all events to a window are implemented in our program. |
| | 131 | It is our responsibility to decide which |
| | 132 | events will be used in the application. Usually program must notify |
| | 133 | windowing system which events will took over and for that window will |
| | 134 | receive events. |
| | 135 | |
| | 136 | === GLUT === |
| | 137 | For an abstraction of events (commands from the windowing system) we will |
| | 138 | use GLUT library (OpenGL Utility Toolkit). Many other GUI libraries are |
| | 139 | available (native and portable). GLUT falls into the category of simple |
| | 140 | operating/windowing system independent GUIs for OpenGL. An example of a |
| | 141 | minimal program that draws a single line is shown in Listing 1 (first.c). |
| | 142 | \lstinputlisting[caption=Drawing a line with OpenGL and GLUT., |
| | 143 | label=first.c ]{first.c} Program in C language consists of two parts: the |
| | 144 | subroutine 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 |
| | 147 | occurs. Before falling into {{{glutMainLoop}}} we need to prepare drawing |
| | 148 | context. |
| | 149 | {{{#!td |
| | 150 | Listing 1: `first.c` |
| 97 | | and run it with |
| | 201 | [[br]][[br]] |
| | 202 | }}} |
| | 203 | |
| | 204 | Structure of the program is usually very similar for |
| | 205 | all languages. Confer Listing 2 (first.py) rewritten in Python. All GLUT |
| | 206 | programs include commands in the following order: |
| | 207 | |
| | 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. |
| | 224 | |
| | 225 | |
| | 226 | The command {{{glutInit}}} initializes GLUT library routines. It is followed by |
| | 227 | a request for window creation of a certain type. The constant {{{GLUT_DOUBLE}}} |
| | 228 | and the default {{{GLUT_RGB}}} suggests that we want a double-buffered window |
| | 229 | with 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 |
| | 231 | title. We have to tell to the window system which events the program will |
| | 232 | intercept. For example given, this is only {{{display}}} of the contents of the |
| | 233 | window. Call of the subroutine {{{glutDisplayFunc}}} instructs the |
| | 234 | {{{glutMainLoop}}} that whenever requests from OS for window redisplay occurs |
| | 235 | subroutine {{{display}}} should be called. Routines for handling events are |
| | 236 | usually called ``call-back'' routines as it reside in program as standalone |
| | 237 | code snippets that are called auto-magically at certain events from the |
| | 238 | windowing system. When some event occurs is up to the windowing system that |
| | 239 | follows user interaction. The main point to emphasize here is that |
| | 240 | registered call-back routines do get additional information on the kind of |
| | 241 | event. For example of keyboard event we can get also mouse (x,y) |
| | 242 | coordinates besides the key pressed. |
| | 243 | |
| | 244 | |
| | 245 | We have seen that the subroutine {{{display}}} includes commands responsible |
| | 246 | for drawing in the window. All routines or functions there are OpenGL and |
| | 247 | have prefix {{{gl}}} to the name. Prefix is necessary to distinguish them and |
| | 248 | prevent name clash with other libraries. To understand the language one can |
| | 249 | interpret function names without prefixes and suffixes as the OpenGL is |
| | 250 | designed so, that the types of the arguments for all programming languages |
| | 251 | are similar. Subroutine {{{display}}} is therefore responsible for drawing the |
| | 252 | contents of the window. The {{{glClear}}} command clears the entire area of the |
| | 253 | window. When clearing we need to define precisely what we want to clear by |
| | 254 | argument given. In our case, this is {{{GL_COLOR_BUFFER_BIT}}}, which means |
| | 255 | clearing of all pixels in the color buffer. |
| | 256 | |
| | 257 | The {{{glColor}}} command to sets the current color of graphic elements that |
| | 258 | will be drawn in subsequent commands. As an argument RGB color components |
| | 259 | are passed. Usually commands with multiple arguments are provide for |
| | 260 | different data types (integer, float, double) and some command can have |
| | 261 | different number of arguments for the same command. To distinguish them |
| | 262 | suffix is added. For the {{{glColor3f}}} suffix {{{3f}}} therefore means that the |
| | 263 | subroutine has three arguments of type float. Choice of the arguments type |
| | 264 | depends on application requirements. Programmer can freely choose data type |
| | 265 | that suits most without the need of data type conversion. In our example we |
| | 266 | have two variants for vertex command with different number of arguments of |
| | 267 | the same type. {{{glVertex2f}}} means that we are specifying just two |
| | 268 | coordinates while the third is by default z=0. Types of the arguments |
| | 269 | specified 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. |
| | 274 | Besides fixed number of arguments there are also functions that take as an |
| | 275 | argument vector (as a pointer to memory). For these the suffix contains |
| | 276 | letter {{{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. |
| | 281 | Variety of different arguments for the same command can be in {{{glVertex}}} |
| | 282 | command where we can find |
| | 283 | {{{ |
| | 284 | #!c |
| | 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. |
| | 289 | }}} |
| | 290 | Large number of routines for the same function is performance and language |
| | 291 | related in order to waive the default conversion and thus provide a more |
| | 292 | comprehensive and faster code. For languages with name mangling like C++ |
| | 293 | one can find simpler OpenGL wrapped functions (eg. just {{{glVertex}}}) that |
| | 294 | don't affects performance. But as many languages does not have name |
| | 295 | mangling built into compiler such practise is not widespread. Besides |
| | 296 | specifying single vertex each time one can use {{{glVertexPointer}}} and points |
| | 297 | to memory where number of vertices of specified type exist. This can save |
| | 298 | us of some looping, but as this is essentially copying of system memory |
| | 299 | into OpenGL hardware engine, the performance is not really improved. |
| | 300 | |
| | 301 | Drawing of graphic primitives in OpenGL occurs between two commands |
| | 302 | {{{glBegin(primitive type)}}} and {{{glEnd()}}}. Primitive type given as argument |
| | 303 | at the beginning specifies how subsequent vertices will be used for |
| | 304 | primitive generation. Instead of giving primitive type as number several |
| | 305 | predefined constant are provided within {{{include}}} directive to ease |
| | 306 | readability and portability of the OpenGL programs. Before providing vertex |
| | 307 | position one can change OpenGL engine primitive state such as current |
| | 308 | drawing {{{glColor3f}}} or {{{glNormal}}} that is per vertex property. |
| | 309 | |
| | 310 | The last command in the {{{display}}} subroutine is {{{glutSwapBuffers()}}}. For |
| | 311 | applications in which the contents of the display changes frequently, it is |
| | 312 | most appropriate to use windows dual graphics buffers, which is setup by |
| | 313 | using the {{{GLUT_DOUBLE}}} at window initialization. The advantage of such |
| | 314 | drawing strategy is in the fact that while one buffer is used for current |
| | 315 | drawing the other is shown. Drawing thus occurs in the background and when |
| | 316 | buffer is ready for display we simply flip the buffers. In particular it |
| | 317 | should be noted that such behaviour is system dependent and once upon a |
| | 318 | time when the {{{GLUT_SINGLE}}} (without double buffers) with the {{{glFlush()}}} |
| | 319 | at the end was used instead. Nowadays {{{GLUT_DOUBLE}}} is usually used, which |
| | 320 | is most helpful with high frame-rate applications such as animation. |
| | 321 | Only simple primitives are used within OpenGL. Reason for that is mainly |
| | 322 | due to the requirement of performance and possible hardware acceleration. |
| | 323 | There are three types of simple primitives: points, lines, and triangles. |
| | 324 | Higher level primitives (like quadrilaterals) can be assembled from simple |
| | 325 | ones. Curves can be approximated by lines. Large surfaces can be tessellated |
| | 326 | with triangles. For complex surfaces (like NURBS) GLU library can be used |
| | 327 | to 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. |
| | 334 | Every 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. |
| | 342 | |
| | 343 | === Exercises #1: === |
| | 344 | |
| | 345 | 1. Create the following {{{first.c}}} using your favorite editor. [[Image(first.png,right)]] |
| | 346 | {{{ |
| | 347 | #!c |
| | 348 | #include <GL/glut.h> |
| | 349 | |
| | 350 | void display() |
| | 351 | { |
| | 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(); |
| | 359 | } |
| | 360 | |
| | 361 | int main(int argc, char *argv[]) |
| | 362 | { |
| | 363 | glutInit(&argc,argv); |
| | 364 | glutInitDisplayMode(GLUT_DOUBLE); |
| | 365 | glutCreateWindow("first.c GL code"); |
| | 366 | glutDisplayFunc(display); |
| | 367 | glutMainLoop(); |
| | 368 | return 0; |
| | 369 | } |
| | 370 | }}} |
| | 371 | |
| | 372 | Create the {{{Makefile}}} to build your program. |
| | 373 | |