| 40 | |
| 41 | [[Image(OpenGL-pipeline.svg, right)]] |
| 42 | Modern OpenGL changed previously fixed rendering pipeline to fully |
| 43 | programmable graphics pipeline as shown in Fig.1 |
| 44 | Processors that transform input vertex data to the window context at the |
| 45 | end are called ''shaders''. The Vertex shader and the Fragment shader |
| 46 | are most important in the rendering pipeline. To use rendering pipeline as |
| 47 | shown in Fig.1 one has to provide program for them |
| 48 | as there is no default because they are essential part of every OpenGL |
| 49 | program. Programming shaders is done in GLSL (OpenGL Shading Language) that |
| 50 | is similar to C language with some predefined variables and reserved |
| 51 | keywords that help describing communication between OS and GPU. Programs |
| 52 | (for shaders) written in GLSL are compiled on-the-fly, assembled and |
| 53 | transferred to GPU for execution. |
| 54 | |
| 55 | OpenGL is designed as a hardware-independent interface between the program |
| 56 | code and graphics accelerator. Hardware independence of OpenGL means also |
| 57 | that in the language specification there is no support for control of |
| 58 | window system events that occur with interactive programming. For such |
| 59 | interactive control for each operating system were designed interfaces that |
| 60 | connect the machine with the OpenGL system. Due to the specifics of |
| 61 | different window systems (Windows, XWindow, MacOS, iOS, Android) it is |
| 62 | required that for each system tailored techniques are used to call OpenGL |
| 63 | commands in hardware. Portability is thus limited by graphical user |
| 64 | interface (GUI) that handles OpenGL context (window). In order to still be |
| 65 | able to write ''portable programs'' with otherwise limited functionality |
| 66 | of the user interface, GLUT library (OpenGL Utility Toolkit) was created. |
| 67 | It compensates all the differences between operating systems and introduces |
| 68 | a unified methods of manipulating ''events''. With the GLUT library it |
| 69 | is possible to write portable programs that are easy to write and have |
| 70 | sufficient capacity for simple user interfaces. |
| 71 | |
| 72 | == Legacy OpenGL coding == |
| 73 | Basics of the OpenGL language are given in the (core) GL library. More |
| 74 | complex primitives can be build by the GLU library (GL Utility) which |
| 75 | contain the routines that use GL routines only. GLU routines contain |
| 76 | multiple GL commands that are generally applicable and have therefore been |
| 77 | implemented to ease OpenGL programming. |
| 78 | |
| 79 | To get quickly introduced into OpenGL it is better to start with legacy |
| 80 | (short and simple) program that will be later replaced with modern OpenGL |
| 81 | after discussion that caused replacement with OpenGL 3.x. Before we can |
| 82 | dive in OpenGL we need to revise windowing systems and how they interact |
| 83 | with users. |
| 84 | |
| 85 | == Events == |
| 86 | All window interfaces (GUI) are designed to operate on the principle of |
| 87 | ''events''. Events are signals from the Window system to our program. |
| 88 | Our program is fully responsible for the content of the window. Windowing |
| 89 | system only assigns area (window). The contents of the window area must |
| 90 | then be fully controlled. In addition to the window assignment the |
| 91 | windowing system to sends messages (events) to our program. The most common |
| 92 | messages are: |
| 93 | display:: The command asks for presentation of window contents. There |
| 94 | are several possible occasions when this happens. For example, when |
| 95 | another window reveals part of our window or when window is moved on the |
| 96 | screen. Another example is when window is re-displayed after icon is |
| 97 | being pressed at the taskbar. Interception of such events is mandatory, |
| 98 | because every program must ensure that the contents of the window is |
| 99 | restored window, when such event occurs. |
| 100 | reshape:: Command to our program that occurs when the size and/or |
| 101 | shape of the window changes. In this case the content of the window must |
| 102 | be provided for a new window size. Event occurs, inter alia, when the |
| 103 | mouse resizes the window. Immediately after reshape, display event is |
| 104 | sent. |
| 105 | keyboard:: Commands coming from the keyboard. |
| 106 | mouse:: Describes the mouse buttons at their change when user pressed |
| 107 | or released one of the buttons. |
| 108 | motion:: This command defines the motion tracking of the moving mouse |
| 109 | with pressed button. |
| 110 | timer:: Program requests message after a certain time in order to |
| 111 | change the contents of the window. The function is suitable for timed |
| 112 | simulation (animation). |
| 113 | In addition to these events there exist some other too. In general it is |
| 114 | not necessary that all events to a window are implemented in our program. |
| 115 | It is our responsibility to decide which |
| 116 | events will be used in the application. Usually program must notify |
| 117 | windowing system which events will took over and for that window will |
| 118 | receive events. |
| 119 | |
| 120 | == GLUT == |
| 121 | For an abstraction of events (commands from the windowing system) we will |
| 122 | use GLUT library (OpenGL Utility Toolkit). Many other GUI libraries are |
| 123 | available (native and portable). GLUT falls into the category of simple |
| 124 | operating/windowing system independent GUIs for OpenGL. An example of a |
| 125 | minimal program that draws a single line is shown in Listing 1 (first.c). |
| 126 | \lstinputlisting[caption=Drawing a line with OpenGL and GLUT., |
| 127 | label=first.c ]{first.c} Program in C language consists of two parts: the |
| 128 | subroutine display and the main program. Program runs from the start in |
| 129 | {{{main()}}} and at the end falls into endless loop |
| 130 | {{{glutMainLoop}}} that calls registered subroutines when event |
| 131 | occurs. Before falling into {{{glutMainLoop}}} we need to prepare drawing |
| 132 | context. |
| 133 | {{{ |
| 134 | #!c |
| 135 | #include <GL/glut.h> |
| 136 | |
| 137 | void display() |
| 138 | { |
| 139 | glClear(GL_COLOR_BUFFER_BIT); |
| 140 | glColor3f(1.0, 0.4, 1.0); |
| 141 | glBegin(GL_LINES); |
| 142 | glVertex2f(0.1, 0.1); |
| 143 | glVertex3f(0.8, 0.8, 1.0); |
| 144 | glEnd(); |
| 145 | glutSwapBuffers(); |
| 146 | } |
| 147 | |
| 148 | int main(int argc, char *argv[]) |
| 149 | { |
| 150 | glutInit(&argc,argv); |
| 151 | glutInitDisplayMode(GLUT_DOUBLE); |
| 152 | glutCreateWindow("first.c GL code"); |
| 153 | glutDisplayFunc(display); |
| 154 | glutMainLoop(); |
| 155 | return 0; |
| 156 | } |
| 157 | }}} |
| 158 | {{{ |
| 159 | #!python |
| 160 | from OpenGL.GLUT import * |
| 161 | from OpenGL.GL import * |
| 162 | import sys |
| 163 | |
| 164 | def display(): |
| 165 | glClear(GL_COLOR_BUFFER_BIT) |
| 166 | glColor3f(1.0, 0.4, 1.0) |
| 167 | glBegin(GL_LINES) |
| 168 | glVertex2f(0.1, 0.1) |
| 169 | glVertex3f(0.8, 0.8, 1.0) |
| 170 | glEnd() |
| 171 | glutSwapBuffers() |
| 172 | |
| 173 | if __name__ == "__main__": |
| 174 | glutInit(sys.argv) |
| 175 | glutInitDisplayMode(GLUT_DOUBLE) |
| 176 | glutCreateWindow("first.py GL code") |
| 177 | glutDisplayFunc(display) |
| 178 | glutMainLoop() |
| 179 | }}} |
| 180 | |
| 181 | Structure of the program is usually very similar for |
| 182 | all languages. Confer Listing 2 (first.py) rewritten in Python. All GLUT |
| 183 | programs include commands in the following order: |
| 184 | |
| 185 | *Include definitions of constants and functions for OpenGL and GLUT |
| 186 | with the include statement. |
| 187 | * Initialize GLUT and setup other variables that are not directly |
| 188 | related to OpenGL but rather to the object that is being visualized. |
| 189 | * Set window parameters such as initial position, size, type, bit plane |
| 190 | memory. |
| 191 | * Create the window and name it. |
| 192 | * Setup the features of the OpenGL machine. These are usually commands |
| 193 | {{{glEnable}}} for setup of lighting, materials, lists, and non-default |
| 194 | behavior of OpenGL machine. |
| 195 | * Register call-back routines which will be called at events. Mandatory |
| 196 | registration is just for {{{glutDisplayFunc(display)}}}. The rest are |
| 197 | optional. |
| 198 | * The last command in {{{main" is a call to "glutMainLoop}}}, from which |
| 199 | the program returns when the window is closed. At the same time the |
| 200 | {{{main}}} program ends. |
| 201 | |
| 202 | |
| 203 | The command {{{glutInit}}} initializes GLUT library routines. It is followed by |
| 204 | a request for window creation of a certain type. The constant {{{GLUT_DOUBLE}}} |
| 205 | and the default {{{GLUT_RGB}}} suggests that we want a double-buffered window |
| 206 | with a RGB space. Variable {{{window}}} keeps reference of window returned by |
| 207 | {{{glutCreateWindow}}} and at the same time instructs the OS to set the window |
| 208 | title. We have to tell to the window system which events the program will |
| 209 | intercept. For example given, this is only {{{display}}} of the contents of the |
| 210 | window. Call of the subroutine {{{glutDisplayFunc}}} instructs the |
| 211 | {{{glutMainLoop}}} that whenever requests from OS for window redisplay occurs |
| 212 | subroutine {{{display}}} should be called. Routines for handling events are |
| 213 | usually called ``call-back'' routines as it reside in program as standalone |
| 214 | code snippets that are called auto-magically at certain events from the |
| 215 | windowing system. When some event occurs is up to the windowing system that |
| 216 | follows user interaction. The main point to emphasize here is that |
| 217 | registered call-back routines do get additional information on the kind of |
| 218 | event. For example of keyboard event we can get also mouse (x,y) |
| 219 | coordinates besides the key pressed. |
| 220 | |
| 221 | |
| 222 | We have seen that the subroutine {{{display}}} includes commands responsible |
| 223 | for drawing in the window. All routines or functions there are OpenGL and |
| 224 | have prefix {{{gl}}} to the name. Prefix is necessary to distinguish them and |
| 225 | prevent name clash with other libraries. To understand the language one can |
| 226 | interpret function names without prefixes and suffixes as the OpenGL is |
| 227 | designed so, that the types of the arguments for all programming languages |
| 228 | are similar. Subroutine {{{display}}} is therefore responsible for drawing the |
| 229 | contents of the window. The {{{glClear}}} command clears the entire area of the |
| 230 | window. When clearing we need to define precisely what we want to clear by |
| 231 | argument given. In our case, this is {{{GL_COLOR_BUFFER_BIT}}}, which means |
| 232 | clearing of all pixels in the color buffer. |
| 233 | |
| 234 | The {{{glColor}}} command to sets the current color of graphic elements that |
| 235 | will be drawn in subsequent commands. As an argument RGB color components |
| 236 | are passed. Usually commands with multiple arguments are provide for |
| 237 | different data types (integer, float, double) and some command can have |
| 238 | different number of arguments for the same command. To distinguish them |
| 239 | suffix is added. For the {{{glColor3f}}} suffix {{{3f}}} therefore means that the |
| 240 | subroutine has three arguments of type float. Choice of the arguments type |
| 241 | depends on application requirements. Programmer can freely choose data type |
| 242 | that suits most without the need of data type conversion. In our example we |
| 243 | have two variants for vertex command with different number of arguments of |
| 244 | the same type. {{{glVertex2f}}} means that we are specifying just two |
| 245 | coordinates while the third is by default z=0. Types of the arguments |
| 246 | specified as the suffix letter are as follows: |
| 247 | f:: float in C language and {{{real*4}}} in Fortran. |
| 248 | d:: double for C and {{{real*8}}} in Fortran. |
| 249 | i:: integer (4 bytes). |
| 250 | s:: short integer in C and {{{integer*2}}} in Fortran. |
| 251 | Besides fixed number of arguments there are also functions that take as an |
| 252 | argument vector (as a pointer to memory). For these the suffix contains |
| 253 | letter {{{v}}} at the end. Below are some interpretations of suffixes: |
| 254 | 3f:: Three arguments of {{{real}}}s follow as arguments. |
| 255 | 3i:: Three arguments of {{{integer}}}s follow as arguments. |
| 256 | 3fv:: One argument as a vector that contains three {{{float}}}s |
| 257 | follows. |
| 258 | Variety of different arguments for the same command can be in {{{glVertex}}} |
| 259 | command where we can find |
| 260 | {{{ |
| 261 | #!c |
| 262 | glVertex2d, glVertex2f, glVertex2i, glVertex2s, glVertex3d, glVertex3f, |
| 263 | glVertex3i, glVertex3s, glVertex4d, glVertex4f, glVertex4i, glVertex4s, |
| 264 | glVertex2dv, glVertex2fv, glVertex2iv,glVertex2sv, glVertex3dv, glVertex3fv, |
| 265 | glVertex3iv, glVertex3sv, glVertex4dv,glVertex4fv, glVertex4iv, glVertex4sv. |
| 266 | }}} |
| 267 | Large number of routines for the same function is performance and language |
| 268 | related in order to waive the default conversion and thus provide a more |
| 269 | comprehensive and faster code. For languages with name mangling like C++ |
| 270 | one can find simpler OpenGL wrapped functions (eg. just {{{glVertex}}}) that |
| 271 | don't affects performance. But as many languages does not have name |
| 272 | mangling built into compiler such practise is not widespread. Besides |
| 273 | specifying single vertex each time one can use {{{glVertexPointer}}} and points |
| 274 | to memory where number of vertices of specified type exist. This can save |
| 275 | us of some looping, but as this is essentially copying of system memory |
| 276 | into OpenGL hardware engine, the performance is not really improved. |
| 277 | |
| 278 | Drawing of graphic primitives in OpenGL occurs between two commands |
| 279 | {{{glBegin(primitive type)}}} and {{{glEnd()}}}. Primitive type given as argument |
| 280 | at the beginning specifies how subsequent vertices will be used for |
| 281 | primitive generation. Instead of giving primitive type as number several |
| 282 | predefined constant are provided within {{{include}}} directive to ease |
| 283 | readability and portability of the OpenGL programs. Before providing vertex |
| 284 | position one can change OpenGL engine primitive state such as current |
| 285 | drawing {{{glColor3f" or "glNormal}}} that is per vertex property. |
| 286 | |
| 287 | The last command in the {{{display}}} subroutine is {{{glutSwapBuffers()}}}. For |
| 288 | applications in which the contents of the display changes frequently, it is |
| 289 | most appropriate to use windows dual graphics buffers, which is setup by |
| 290 | using the {{{GLUT_DOUBLE}}} at window initialization. The advantage of such |
| 291 | drawing strategy is in the fact that while one buffer is used for current |
| 292 | drawing the other is shown. Drawing thus occurs in the background and when |
| 293 | buffer is ready for display we simply flip the buffers. In particular it |
| 294 | should be noted that such behaviour is system dependent and once upon a |
| 295 | time when the {{{GLUT_SINGLE" (without double buffers) with the "glFlush()}}} |
| 296 | at the end was used instead. Nowadays {{{GLUT_DOUBLE}}} is usually used, which |
| 297 | is most helpful with high frame-rate applications such as animation. |
| 298 | Only simple primitives are used within OpenGL. Reason for that is mainly |
| 299 | due to the requirement of performance and possible hardware acceleration. |
| 300 | There are three types of simple primitives: points, lines, and triangles. |
| 301 | Higher level primitives (like quadrilaterals) can be assembled from simple |
| 302 | ones. Curves can be approximated by lines. Large surfaces can be tessellated |
| 303 | with triangles. For complex surfaces (like NURBS) GLU library can be used |
| 304 | to calculate vertices. The following line primitives are possible: |
| 305 | GL_LINES:: Pairs of vertices in a vertex stream create line |
| 306 | segments. |
| 307 | GL_LINE_STRIP:: Vertex stream builds connected lines |
| 308 | (polyline). |
| 309 | GL_LINE_LOOP:: Same as polyline above except that last |
| 310 | vertex is connected by a line to the first. |
| 311 | Every surface can be assembled with triangles. |
| 312 | GL_TRIANGLES:: For each triangle three vertices are required |
| 313 | from vertex stream. |
| 314 | GL_TRIANGLE_STRIP:: Strip of triangles. For first triangle |
| 315 | three vertices are needed. For every additional vertex new triangle is |
| 316 | created by using last two vertices. |
| 317 | GL_TRIANGLE_FAN:: Triangles are added to the first one by |
| 318 | using first and last vertex to create a triangle fan. |
| 319 | == Modern OpenGL == |
| 320 | Immediate mode programming with {{{glBegin" and "glEnd}}} was removed from |
| 321 | OpenGL 3.x as such transmission of vertex streams and its attributes |
| 322 | (colors, normals, ...) from system memory to GPU is considered as a major |
| 323 | performance drawback. Display lists were previously used to save stream of |
| 324 | OpenGL calls that also included vertex data and was just replayed at |
| 325 | redraw. But this is inherently sequential operation that blocked parallel |
| 326 | vertex processing. Requirement to store vertex arrays to GPU directly as an |
| 327 | ''object'' can solve problem described. Storing vertex arrays into GPU |
| 328 | also means that manipulation on them to build the model should be inside |
| 329 | the GPU. Legacy OpenGL included many ``modelling'' utilities for |
| 330 | transforming world coordinates into viewport. Transformations of coordinate |
| 331 | systems in 3D space allowed manipulate model stack easily with |
| 332 | {{{glPushMatrix" and "glPopMatrix}}} commands. But similarly to |
| 333 | {{{glBegin"/"glEnd}}} such manipulations are not used outside GPU anymore. |
| 334 | Instead all operations on vertex data is transferred to ''vertex shader''. |
| 335 | There operations on data can be performed with standard vector |
| 336 | math in homogeneous coordinates. |
| 337 | |
| 338 | |