Source

gltut / Documents / Tutorial 02 / Tutorial 03.xml

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
<?xml version="1.0" encoding="UTF-8"?>
<?oxygen RNGSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng" type="xml"?>
<?oxygen SCHSchema="http://docbook.org/xml/5.0/rng/docbookxi.rng"?>
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude"
    xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0">
    <title>OpenGL's Moving Triangle</title>
    <para>This tutorial will be building off of the previous tutorial. In that tutorial, we had a
        single, static triangle. Here, we will move it around.</para>
    <section>
        <title>Moving the Vertices</title>
        <para>The simplest way one might think to move a triangle or other object around is to
            simply modify the vertex position data directly. From the previous tutorial, we learned
            that the vertex data is stored in a buffer object. This is what
                <filename>tut2a.cpp</filename> does.</para>
        <para>The modifications are done in two steps. The first step is to generate the X, Y offset
            that will be applied to each position. The second is to apply that offset to each vertex
            position. The generation of the offset is done with the
                <function>ComputePositionOffset</function> function:</para>
        <example>
            <title>Computation of Position Offsets</title>
            <programlisting>void ComputePositionOffsets(float &amp;fXOffset, float &amp;fYOffset)
{
    const float fLoopDuration = 5.0f;
    const float fScale = 3.14159f * 2.0f / fLoopDuration;
    
    float fElapsedTime = glutGet(GLUT_ELAPSED_TIME) / 1000.0f;
    
    float fCurrTimeThroughLoop = fmodf(fElapsedTime, fLoopDuration);
    
    fXOffset = cosf(fCurrTimeThroughLoop * fScale) * 0.5f;
    fYOffset = sinf(fCurrTimeThroughLoop * fScale) * 0.5f;
}</programlisting>
        </example>
        <para>This function computes offsets in a loop. The offsets produce circular motion, and the
            offsets will reach the beginning of the circle every 5 seconds (controlled by
                <varname>fLoopDuration</varname>). The function
                <function>glutGet(GLUT_ELAPSED_TIME)</function> retrieves the integer time in
            milliseconds since the application started. The <function>fmodf</function> function
            computes the floating-point modulus of the time. In lay terms, it takes the first
            parameter and returns the remainder of the division between that and the second
            parameter. Thus, it returns a value on the range [0, <varname>fLoopDuration</varname>),
            which is what we need to create a periodically repeating pattern.</para>
        <para>The <function>cosf</function> and <function>sinf</function> functions compute the
            cosine and sine respectively. It isn't important to know exactly how these functions
            work, but they effectively compute a circle of radius 2. By multiplying by 0.5f, it
            shrinks the circle down to a radius of 1.</para>
        <para>Once the offsets are computed, the offsets have to be added to the vertex data. This
            is done with the <function>AdjustVertexData</function> function:</para>
        <example>
            <title>Adjusting the Vertex Data</title>
            <programlisting>void AdjustVertexData(float fXOffset, float fYOffset)
{
    std::vector&lt;float> fNewData(ARRAY_COUNT(vertexPositions));
    memcpy(&amp;fNewData[0], vertexPositions, sizeof(vertexPositions));
    
    for(int iVertex = 0; iVertex &lt; ARRAY_COUNT(vertexPositions); iVertex += 4)
    {
        fNewData[iVertex] += fXOffset;
        fNewData[iVertex + 1] += fYOffset;
    }
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexPositions), &amp;fNewData[0]);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}</programlisting>
        </example>
        <para>This function works by copying the vertex data into a std::vector, then applying the
            offset to the X and Y coordinates of each vertex. The last three lines are the
            OpenGL-relevant parts.</para>
        <para>First, the buffer objects containing the positions is bound to the context. Then the
            new function <function>glBufferSubData</function> is called to transfer this data to the
            buffer object.</para>
        <para>The difference between <function>glBufferData</function> and
                <function>glBufferSubData</function> is that the SubData function does not
                <emphasis>allocate</emphasis> memory. <function>glBufferData</function> specifically
            allocates memory of a certain size; <function>glBufferSubData</function> only transfers
            data to the already existing memory. Calling <function>glBufferData</function> on a
            buffer object that has already been allocated tells OpenGL to
                <emphasis>reallocate</emphasis> this memory, throwing away the previous data and
            allocating a fresh block of memory. Whereas calling <function>glBufferSubData</function>
            on a buffer object that has not yet had memory allocated by
                <function>glBufferData</function> is an error.</para>
        <para>Think of <function>glBufferData</function> as a combination of
                <function>malloc</function> and <function>memcpy</function>, while glBufferSubData
            is purely <function>memcpy</function>.</para>
        <para>The <function>glBufferSubData</function> function can update only a portion of the
            buffer object's memory. The second parameter to the function is the byte offset into the
            buffer object to begin copying to, and the third parameter is the number of bytes to
            copy. The fourth parameter is our array of bytes to be copied into that location of the
            buffer object.</para>
        <para>The last line of the function is simply unbinding the buffer object. It is not
            strictly necessary, but it is good form to clean up binds after making them.</para>
        <formalpara>
            <title>Buffer Object Usage Hints</title>
            <para>Every time we draw something, we are changing the buffer object's data. OpenGL has
                a way to tell it that you will be doing something like this, and it is the purpose
                of the last parameter of <function>glBufferData</function>. This tutorial changed
                the allocation of the buffer object slightly, replacing:</para>
        </formalpara>
        <programlisting>glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);</programlisting>
        <para>with this:</para>
        <programlisting>glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STREAM_DRAW);</programlisting>
        <para>GL_STATIC_DRAW tells OpenGL that you intend to only set the data in this buffer object
            once. GL_STREAM_DRAW tells OpenGL that you intend to set this data constantly, generally
            once per frame. These parameters don't mean <emphasis>anything</emphasis> with regard to
            the API; they are simply hints to the OpenGL implementation. Proper use of these hints
            can be crucial for getting good buffer object performance. We will see more of these
            hints later.</para>
        <para>The rendering function now has become this:</para>
        <example>
            <title>Updating and Drawing the Vertex Data</title>
            <programlisting>void display()
{
    float fXOffset = 0.0f, fYOffset = 0.0f;
    ComputePositionOffsets(fXOffset, fYOffset);
    AdjustVertexData(fXOffset, fYOffset);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(theProgram);
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glEnableVertexAttribArray(positionAttrib);
    glVertexAttribPointer(positionAttrib, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(positionAttrib);
    glUseProgram(0);
    
    glutSwapBuffers();
    glutPostRedisplay();
}</programlisting>
        </example>
        <para>The first three lines get the offset and set the vertex data. Everything but the last
            line is unchanged from the first tutorial. The last line of the function is there to
            tell FreeGLUT to constantly call <function>display</function>. Ordinarily,
                <function>display</function> would only be called when the window's size changes or
            when the window is uncovered. <function>glutPostRedisplay</function> causes FreeGLUT to
            call <function>display</function> again. Not immediately, but reasonably fast.</para>
        <para>If you run the tutorial, you will see a smaller triangle (the size was reduced in this
            tutorial) that slides around in a circle.</para>
    </section>
    <section>
        <title>A Better Way</title>
        <para>This is fine for a 3-vertex example. But imagine a scene involving millions of
            vertices. Moving objects this way means having to copy millions of vertices from the
            original vertex data, add an offset to each of them, and then upload that data to an
            OpenGL buffer object. And all of that is <emphasis>before</emphasis> rendering. Clearly
            there must be a better way; games can't possibly do this every frame and still hold
            decent framerates.</para>
        <para>Actually for quite some time, they did. In the pre-GeForce 256 days, that was how all
            games worked. Graphics hardware just took a list of vertices in normalized device
            coordinate space and rasterized them into fragments and pixels. Granted, in those days,
            we were talking about maybe 10,000 triangles per frame. And while CPUs have come a long
            way since then, they haven't scaled with the complexity of graphics scenes.</para>
        <para>The GeForce 256 (note: not a GT 2xx card, but the very first GeForce card) was the
            first graphics card that actually did some from of vertex processing. It could store
            vertices in GPU memory, read them, do some kind of transformation on them, and then send
            them through the rest of the pipeline. The kinds of transformations that the old GeForce
            256 could do were quite useful, but fairly simple.</para>
        <para>Having the benefit of modern hardware and OpenGL 3.x, we have something far more
            flexible: vertex shaders.</para>
        <para>Remember what it is that we are doing. We compute an offset. Then we apply that offset
            to each vertex position. Vertex shaders are given each vertex position. So it makes
            sense to simply give the vertex shader the offset and let it compute the final vertex
            position. This is what <filename>tut2b.cpp</filename> does.</para>
        <para>The vertex shader used here is as follows:</para>
        <example>
            <title>Offsetting Vertex Shader</title>
            <programlisting>#version 150

in vec4 position;
uniform vec2 offset;

void main()
{
    vec4 totalOffset = vec4(offset.x, offset.y, 0.0, 0.0);
    gl_Position = position + totalOffset;
}</programlisting>
        </example>
        <para>After defining the input <varname>position</varname>, the shader defines a
            2-dimensional vector <varname>offset</varname>. But it defines it with the term
                <literal>uniform</literal>, rather than <literal>in</literal> or
                <literal>out</literal>. This has a particular meaning.</para>
        <formalpara>
            <title>Shaders and Granularity</title>
            <para>Recall that with each execution of a shader, the shader gets new values for
                variables defined as <literal>in</literal>. Each time a vertex shader is called, it
                gets a different set of inputs from the vertex attribute arrays and buffers. That is
                useful for vertex position data, but it is not what we want for the offset. We want
                each vertex to use the <emphasis>same</emphasis> offset; a <quote>uniform</quote>
                offset, if you will.</para>
        </formalpara>
        <para>Variables defined as <literal>uniform</literal> do not change at the same frequency as
            variables defined as <literal>in</literal>. Input variables change with every execution
            of the shader. Uniform variables (called <glossterm>uniforms</glossterm>) change only
            between executions of rendering calls. And even then, they only change when the user
            sets them explicitly. </para>
        <para>Vertex shader inputs come from vertex attribute array definitions and buffer objects.
            By contrast, uniforms are set directly on program objects.</para>
        <para>In order to set a uniform in a program, we need two things. The first is a uniform
            location. Much like with attributes, you must get an index that refers to the uniform
            name. In this tutorial, this is done in the <function>InitializeProgram</function>
            function, with this line:</para>
        <programlisting>offsetLocation = glGetUniformLocation(theProgram, "offset");</programlisting>
        <para>The function <function>glGetUniformLocation</function> retrieves the uniform location
            for the uniform named by the second parameter. Note that, just because a uniform is
            defined in a shader, GLSL does not <emphasis>have</emphasis> to provide a location for
            it. It will only if the uniform is actually used in the program, as we see in the vertex
            shader.</para>
        <para>Once we have the uniform location, we can set the uniform's value. However, unlike
            retrieving the uniform location, setting a uniform's value requires that the program be
            currently in use with <function>glUseProgram</function>. Thus, the rendering code looks
            like this:</para>
        <example>
            <title>Draw with Calculated Offsets</title>
            <programlisting>void display()
{
    float fXOffset = 0.0f, fYOffset = 0.0f;
    ComputePositionOffsets(fXOffset, fYOffset);
    
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(theProgram);
    
    glUniform2f(offsetLocation, fXOffset, fYOffset);
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glEnableVertexAttribArray(positionAttrib);
    glVertexAttribPointer(positionAttrib, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(positionAttrib);
    glUseProgram(0);
    
    glutSwapBuffers();
    glutPostRedisplay();
}</programlisting>
        </example>
        <para>We use <function>ComputePositionOffsets</function> to get the offsets, and then use
                <function>glUniform2f</function> to set the uniform's value. The buffer object's
            data is never changed; the shader simply does the hard work. Which is why those shader
            stages exist in the first place.</para>
    </section>
    <section>
        <title>More Power to the Shaders</title>
        <para>It's all well and good that we are no longer having to transform vertices manually.
            But perhaps we can move more things to the vertex shader. Could it be possible to move
            all of <function>ComputePositionOffsets</function> to the vertex shader?</para>
        <para>Well, no. The call to <function>glutGet(GL_ELAPSED_TIME)</function> can't be moved
            there, since GLSL code cannot directly call C/C++ functions. But everything else can be
            moved. This is what <filename>tut2c.cpp</filename> does.</para>
        <para>This is the first tutorial that loads its shaders from files rather than using
            hard-coded data in the .cpp file. The vertex program is found in
                <filename>data\tut2c.vert</filename>.</para>
        <example>
            <title>Offset Computing Vertex Shader</title>
            <programlisting>#version 150

in vec4 position;
uniform float loopDuration;
uniform float time;

void main()
{
    float timeScale = 3.14159f * 2.0f / loopDuration;
    
    float currTime = mod(time, loopDuration);
    vec4 totalOffset = vec4(
        cos(currTime * timeScale) * 0.5f,
        sin(currTime * timeScale) * 0.5f,
        0.0f,
        0.0f);
    
    gl_Position = position + totalOffset;
}</programlisting>
        </example>
        <para>This shader takes two uniforms: the duration of the loop and the elapsed time.</para>
        <para>In this shader, we use a number of built-in functions. Think of these as standard
            library functions. <function>mod</function>, <function>cos</function>, and
                <function>sin</function> are all standard GLSL functions that you can use as needed.
            There are a <emphasis>lot</emphasis> of standard GLSL functions available.</para>
        <para>The rendering code looks quite similar to the previous rendering code:</para>
        <example>
            <title>Rendering with Time</title>
            <programlisting>void display()
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glUseProgram(theProgram);
    
    glUniform1f(elapsedTimeUniform, glutGet(GLUT_ELAPSED_TIME) / 1000.0f);
    
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glEnableVertexAttribArray(positionAttrib);
    glVertexAttribPointer(positionAttrib, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(positionAttrib);
    glUseProgram(0);
    
    glutSwapBuffers();
    glutPostRedisplay();
}</programlisting>
        </example>
        <para>This time, we don't need any code to use the elapsed time; we simply pass it
            unmodified to the shader.</para>
        <para>You may be wondering exactly how it is that the <varname>loopDuration</varname>
            uniform gets set. This is done in our shader initialization routine, and it is done only
            once:</para>
        <example>
            <title>Loading Shaders from Files</title>
            <programlisting>void InitializeProgram()
{
    std::vector&lt;GLuint> shaderList;
    
    shaderList.push_back(LoadShader(GL_VERTEX_SHADER, "tut2c.vert"));
    shaderList.push_back(CreateShader(GL_FRAGMENT_SHADER, strFragmentShader));
    
    theProgram = CreateProgram(shaderList);
    
    positionAttrib = glGetAttribLocation(theProgram, "position");
    elapsedTimeUniform = glGetUniformLocation(theProgram, "time");
    
    GLuint loopDurationUnf = glGetUniformLocation(theProgram, "loopDuration");
    glUseProgram(theProgram);
    glUniform1f(loopDurationUnf, 5.0f);
    glUseProgram(0);
}</programlisting>
        </example>
        <para>Notice the call to <function>LoadShader</function> for the vertex shader, rather than
                <function>CreateShader</function>. This is a simple routine that loads the file and
            passes it in its entirety to the <function>CreateShader</function> function.</para>
        <para>We get the time uniform as normal with <function>glGetUniformLocation</function>. For
            the loop duration, we get that in a local variable. Then we immediately set the current
            program object, set the uniform to a value, and then unset the current program
            object.</para>
        <para>Program objects, like all objects that contain internal state, will retain their state
            unless you explicitly change it. So the value of <varname>loopDuration</varname> will be
            5.0f in perpetuity; we do not need to set it every frame.</para>
    </section>
    <section>
        <title>Multiple Shaders</title>
        <para>Well, moving the triangle around is nice and all, but it would also be good if we
            could do something time-based in the fragment shader. Fragment shaders cannot affect the
            position of the object, but they can control its color. And this is what
                <filename>tut2d.cpp</filename> does.</para>
        <para>The fragment shader in this tutorial is also loaded from the file
                <filename>data\tut2d.frag</filename>:</para>
        <example>
            <title>Time-based Fragment Shader</title>
            <programlisting>#version 150

out vec4 outputColor;

uniform float fragLoopDuration;
uniform float time;

const vec4 firstColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
const vec4 secondColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);

void main()
{
    float currTime = mod(time, fragLoopDuration);
    float currLerp = currTime / fragLoopDuration;
    
    outputColor = mix(firstColor, secondColor, currLerp);
}</programlisting>
        </example>
        <para>This function is similar to the periodic loop in the vertex shader (which did not
            change from the last time we saw it). Instead of using sin/cos functions to compute the
            coordinates of a circle, interpolates between two colors based on how far it is through
            the loop. When it is at the start of the loop, the triangle will be
                <varname>firstColor</varname>, and when it is at the end of the loop, it will be
                <varname>secondColor</varname>.</para>
        <para>The standard library function <function>mix</function> performs linear interpolation
            between two values. Like many GLSL standard functions, it can take vector parameters; it
            will perform component-wise operations on them. So each of the four components of the
            two parameters will be linearly interpolated by the 3rd parameter. The third parameter,
                <varname>currLerp</varname> in this case, is a value between 0 and 1. When it is 0,
            the return value from <function>mix</function> will be the first parameter; when it is
            1, the return value will be the second parameter.</para>
        <para>Here is the program initialization code:</para>
        <example>
            <title>More Shader Creation</title>
            <programlisting>void InitializeProgram()
{
    std::vector&lt;GLuint> shaderList;
    
    shaderList.push_back(LoadShader(GL_VERTEX_SHADER, "tut2d.vert"));
    shaderList.push_back(LoadShader(GL_FRAGMENT_SHADER, "tut2d.frag"));
    
    theProgram = CreateProgram(shaderList);
    
    positionAttrib = glGetAttribLocation(theProgram, "position");
    elapsedTimeUniform = glGetUniformLocation(theProgram, "time");
    
    GLuint loopDurationUnf = glGetUniformLocation(theProgram, "loopDuration");
    GLuint fragLoopDurUnf = glGetUniformLocation(theProgram, "fragLoopDuration");
    
    
    glUseProgram(theProgram);
    glUniform1f(loopDurationUnf, 5.0f);
    glUniform1f(fragLoopDurUnf, 10.0f);
    glUseProgram(0);
}</programlisting>
        </example>
        <para>As before, we get the uniform locations for <varname>time</varname> and
                <varname>loopDuration</varname>, as well as the new
                <varname>fragLoopDuration</varname>. We then set the two loop durations for the
            program.</para>
        <para>You may be wondering how the <varname>time</varname> uniform for the vertex shader and
            fragment shader get set? One of the advantages of the GLSL compilation model, which
            links vertex and fragment shaders together into a single object, is that uniforms of the
            same name and type are concatenated. So there is only one uniform location for
                <varname>time</varname>, and it refers to the uniform in both shaders.</para>
        <para>The downside of this is that, if you create one uniform in one shader that has the
            same name as a uniform in a different shader, but a different <emphasis>type</emphasis>,
            OpenGL will give you a linker error and fail to generate a program. Also, it is possible
            to accidentally link two uniforms into one. In the tutorial, the fragment shader's loop
            duration had to be given a different name, or else the two shaders would have shared the
            same loop duration.</para>
        <para>In any case, because of this, the rendering code is unchanged. The time uniform is
            updated each frame with FreeGLUT's elapsed time.</para>
        <formalpara>
            <title>Globals in shaders</title>
            <para>Variables at global scope in GLSL can be defined with certain storage qualifiers:
                    <literal>const</literal>, <literal>uniform</literal>, <literal>in</literal>, and
                    <literal>out</literal>. A <literal>const</literal> value works like it does in
                C99 and C++: the value doesn't change, period. It must have an initializer. An
                unqualified variable works like one would expect in C/C++; it is a global value that
                can be changed. GLSL shaders can call functions, and globals can be shared between
                functions.</para>
        </formalpara>
    </section>
    <section>
        <title>Vertex Shader Performance</title>
        <para>These tutorials are simple, but it is still important to look at the performance
            implications of various operations. In this tutorial, we present 3 ways of moving vertex
            data: transform it yourself on the CPU and upload it to buffer objects, generate
            transform parameters on the CPU and have the vertex shader use them to do the transform,
            and put as much as possible in the vertex shader and only have the CPU provide the most
            basic parameters. Which is the best to use?</para>
        <para>This is not an easy question to answer. However, it is almost always the case that CPU
            transformations will be slower than doing it on the GPU. The only time it won't be is if
            you need to do the exact same transformations many times within the same frame. And even
            then, it is better to do the transformations once on the GPU and save the result of that
            in a buffer object that you will pull from later. This is called transform feedback, and
            it will be covered in a later tutorial.</para>
        <para>Between the other two methods, which is better really depends on the specific case.
            Take our example. In one case, we compute the offset on the CPU and pass it to the GPU.
            The GPU applies the offset to each vertex position. In the other case, we simply provide
            a time parameter, and for every vertex, the GPU must compute the <emphasis>exact
                same</emphasis> offset. This means that the vertex shader is doing a lot of work
            that all comes out to the same number.</para>
        <para>Even so, that doesn't mean it's always slower. What matters is the overhead of
            changing data. Changing a uniform takes time; changing a vector uniform typically takes
            no more time than changing a single float, due to the way that many cards handle
            floating-point math. The question is this: what is the cost of doing more complex
            operations in a vertex shader vs. how often those operations need to be done.</para>
        <para>The second vertex shader we use, the one that computes the offset itself, does a lot
            of complex map. Sine and cosine values are <emphasis>not</emphasis> fast to compute.
            They require quite a few computations to calculate. And since the offset itself doesn't
            change for each vertex, it would be best to compute the offset on the CPU and pass the
            offset as a uniform value.</para>
        <para>And typically, that is how rendering is done much of the time. Vertex shaders are
            given transformation values that are pre-computed on the CPU. But this does not mean
            that this is the only or best way to do this. In some cases, it is often useful to
            compute the offsets via parameterized functions in a vertex shader.</para>
        <para>This is best done when vertex shader inputs are abstracted away. That is, rather than
            passing a position, the user passes more general information, and the shader generates
            the position at a particular time or some other parameter. This can be done for particle
            systems based on forces; the vertex shader executes the force functions based on time,
            and is able to thus compute the location of the particle at an arbitrary time.</para>
    </section>
    <section>
        <title>In Review</title>
        <para>In this tutorial, you have learned about uniform variables in shaders. Uniforms are
            shader variables that change, not with every shader envocation, but between rendering
            calls. Uniform values are parameters set by the user to control the behavior of the
            shader. Setting them requires querying a uniform location as well as setting the program
            to be in use. Uniform state is stored within a program object and preserved until
            explicitly changed. A uniform that has the same name and type in two different shader
            stages within the same linked program is the same uniform; setting it will change it for
            both stages.</para>
        <para>You have also learned a little about how to update the contents of a buffer object,
            though we are <emphasis>far</emphasis> from finished with that subject.</para>
        <section>
            <title>Further Study</title>
            <para>There are several things you can test to see what happens with these
                tutorials.</para>
            <itemizedlist>
                <listitem>
                    <para>With <filename>tut2c.cpp</filename>, change it so that it draws two
                        triangles moving in a circle, with one a half
                            <varname>loopDuration</varname> ahead of the other. Simply change the
                        uniforms after the <function>glDrawArrays</function> call and then make the
                            <function>glDrawArrays</function> call again. Add half of the loop
                        duration to the time before setting it the second time.</para>
                </listitem>
                <listitem>
                    <para>In <filename>tut2d.cpp</filename>, change it so that the fragment program
                        bounces between <varname>firstColor</varname> and
                            <varname>secondColor</varname>, rather than popping from
                            <varname>secondColor</varname> back to first at the end of a loop. The
                        first-to-second-to-first transition should all happen within a single
                            <function>fragLoopDuration</function> time interval. In case you are
                        wondering, GLSL supports the <literal>if</literal> statement, as well as the
                        ?: operator. For bonus points however, do it without an explicit conditional
                        statement; feel free to use a sin or cos function to do this.</para>
                </listitem>
            </itemizedlist>
        </section>
        <section>
            <title>Functions of Note</title>
            <glosslist>
                <glossentry>
                    <glossterm>glBufferSubData</glossterm>
                    <glossdef>
                        <para>This function copies memory from the user's memory address into a
                            buffer object. This function takes a byte offset into the buffer object
                            to begin copying, as well as a number of bytes to copy.</para>
                        <para>When this function returns control to the user, you are free to
                            immediately deallocate the memory you owned. So you can allocate and
                            fill a piece of memory, call this function, and immediately free that
                            memory with no hazardous side effects. OpenGL will not store the pointer
                            or make use of it later.</para>
                    </glossdef>
                </glossentry>
                <glossentry>
                    <glossterm>glGetUniformLocation</glossterm>
                    <glossdef>
                        <para>This function retrieves the location of a uniform of the given name
                            from the given program object. If that uniform does not exist or wasn't
                            considered in use by GLSL, then this function returns -1, which is not a
                            valid uniform location.</para>
                    </glossdef>
                </glossentry>
                <glossentry>
                    <glossterm>glUniform*</glossterm>
                    <glossdef>
                        <para>Sets the given uniform in the program currently in use (set by
                                <function>glUseProgram)</function> to the given value. This is not
                            merely one function, but an entire suite of functions that take
                            different types.</para>
                    </glossdef>
                </glossentry>
            </glosslist>
        </section>
    </section>
    <glossary>
        <title>Glossary</title>
        <glossentry>
            <glossterm>Uniforms</glossterm>
            <glossdef>
                <para>These are a class of global variable that can be defined in GLSL shaders. They
                    represent values that are uniform (unchanging) over the course of a single
                    rendering operation.</para>
            </glossdef>
        </glossentry>
    </glossary>
</chapter>