gltut / Documents / Positioning / 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
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
<?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">
    <?dbhtml filename="Tutorial 03.html" ?>
    <title>OpenGL's Moving Triangle</title>
    <para>This tutorial is about how to move objects around. It will introduce new shader
        techniques.</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 previous tutorials, we learned
            that the vertex data is stored in a buffer object. So the task is to modify the vertex
            data in the buffer object. This is what <filename>cpuPositionOffset.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 language="cpp">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 is not important to know exactly how these functions
            work, but they effectively compute a circle of diameter 2. By multiplying by 0.5f, it
            shrinks the circle down to a circle with a diameter 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 language="cpp">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 just <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 language="cpp">glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);</programlisting>
        <para>with this:</para>
        <programlisting language="cpp">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 do not 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 when making frequent changes.
            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 language="cpp">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(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(0);
    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>
        <?dbhtml filename="Tut03 A Better Way.html" ?>
        <title>A Better Way</title>
        <para>This is fine for a 3-vertex example. But imagine a scene involving millions of
            vertices (and no, that's not an exaggeration for high-end applications). 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 not 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 clip 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 have not 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, since it operates on each vertex. This is what
                <filename>vertPositionOffset.cpp</filename> does.</para>
        <para>The vertex shader is found in <filename>data\positionOffset.vert</filename>. The
            vertex shader used here is as follows:</para>
        <example>
            <title>Offsetting Vertex Shader</title>
            <programlisting language="glsl">#version 330

layout(location = 0) 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 to a new value. </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, there is an index that refers to a specific uniform
            value. Unlike attributes, you cannot set this location yourself; you must query it. In
            this tutorial, this is done in the <function>InitializeProgram</function> function, with
            this line:</para>
        <programlisting language="cpp">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 have a location if the uniform is actually used in the program, as we
            see in the vertex shader; <function>glGetUniformLocation</function> will return -1 if
            the uniform has no location.</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 language="cpp">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(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(0);
    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>
        <formalpara>
            <title>Vector Math</title>
            <para>You may be curious about how these lines work:</para>
        </formalpara>
        <programlisting language="cpp">vec4 totalOffset = vec4(offset.x, offset.y, 0.0, 0.0);
gl_Position = position + totalOffset;</programlisting>
        <para>The <type>vec4</type> that looks like a function here is a constructor; it creates a
                <type>vec4</type> from 4 floats. This is done to make the addition easier.</para>
        <para>The addition of <varname>position</varname> to <varname>totalOffset</varname> is a
            component-wise addition. It is a shorthand way of doing this:</para>
        <programlisting language="cpp">gl_Position.x = position.x + totalOffset.x;
gl_Position.y = position.y + totalOffset.y;
gl_Position.z = position.z + totalOffset.z;
gl_Position.w = position.w + totalOffset.w;</programlisting>
        <para>GLSL has a lot of vector math built in. The math operations are all component-wise
            when applied to vectors. However, it is illegal to add vectors of different dimensions
            to each other. So you cannot have a <type>vec2</type> + <type>vec4</type>. That is why
            we had to convert <varname>offset</varname> to a vec4 before performing the
            addition.</para>
    </section>
    <section>
        <?dbhtml filename="Tut03 More Power To The Shaders.html" ?>
        <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> cannot be moved
            there, since GLSL code cannot directly call C/C++ functions. But everything else can be
            moved. This is what <filename>vertCalcOffset.cpp</filename> does.</para>
        <para>The vertex program is found in <filename>data\calcOffset.vert</filename>.</para>
        <example>
            <title>Offset Computing Vertex Shader</title>
            <programlisting language="glsl">#version 330

layout(location = 0) 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 standard GLSL functions, like
                <function>mod</function>, <function>cos</function>, and <function>sin</function>. We
            saw <function>mix</function> in the last tutorial. And these are just the tip of the
            iceberg; 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 language="cpp">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(0);
    glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
    
    glDrawArrays(GL_TRIANGLES, 0, 3);
    
    glDisableVertexAttribArray(0);
    glUseProgram(0);
    
    glutSwapBuffers();
    glutPostRedisplay();
}</programlisting>
        </example>
        <para>This time, we do not 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>Loop Duration Setting</title>
            <programlisting language="cpp">void InitializeProgram()
{
    std::vector&lt;GLuint> shaderList;
    
    shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, "calcOffset.vert"));
    shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, "standard.frag"));
    
    theProgram = Framework::CreateProgram(shaderList);
    
    elapsedTimeUniform = glGetUniformLocation(theProgram, "time");
    
    GLuint loopDurationUnf = glGetUniformLocation(theProgram, "loopDuration");
    glUseProgram(theProgram);
    glUniform1f(loopDurationUnf, 5.0f);
    glUseProgram(0);
}</programlisting>
        </example>
        <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>
        <?dbhtml filename="Tut03 Multiple Shaders.html" ?>
        <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>fragChangeColor.cpp</filename> does.</para>
        <para>The fragment shader in this tutorial is also loaded from the file
                <filename>data\calcColor.frag</filename>:</para>
        <example>
            <title>Time-based Fragment Shader</title>
            <programlisting language="glsl">#version 330

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 language="cpp">void InitializeProgram()
{
    std::vector&lt;GLuint> shaderList;
    
    shaderList.push_back(Framework::LoadShader(GL_VERTEX_SHADER, "calcOffset.vert"));
    shaderList.push_back(Framework::LoadShader(GL_FRAGMENT_SHADER, "calcColor.frag"));
    
    theProgram = Framework::CreateProgram(shaderList);

    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 does not 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. However, unlike <literal>in</literal>, <literal>out</literal>, and
                    <literal>uniforms</literal>, non-const and <literal>const</literal> variables
                are <emphasis>not</emphasis> shared between stages.</para>
        </formalpara>
    </section>
    <section>
        <?dbhtml filename="Tut03 On Vertex Shader Performance.html" ?>
        <title>On Vertex Shader Performance</title>
        <para>These tutorials are simple and should run fast enough, 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 will not 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 does not 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 math. Sine and cosine values are not particularly fast to compute. They
            require quite a few computations to calculate. And since the offset itself does not
            change for each vertex in a single rendering call, performance-wise 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 values passed to the 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>
        <para>This also has an advantage that we have seen. By passing high-level information to the
            shader and letting it do complex math, you can affect much more than just a simple
            offset. The color animation in the fragment shader would not have been possible with
            just an offset. High-level parameterization gives shaders a great deal of
            freedom.</para>
    </section>
    <section>
        <?dbhtml filename="Tut03 In Review.html" ?>
        <title>In Review</title>
        <para>In this tutorial, you have learned about the following:</para>
        <itemizedlist>
            <listitem>
                <para>Buffer object contents can be updated partially with the
                        <function>glBufferSubData</function> function. This function performs the
                    equivalent of a <function>memcpy</function> operation.</para>
            </listitem>
            <listitem>
                <para>Uniform variables in shaders are variables that are set by code outside of
                    GLSL. They only change between rendering calls, so they are uniform over the
                    surface of any particular triangle.</para>
            </listitem>
            <listitem>
                <para>Uniform variable values are stored with the program object. This state is
                    preserved until it is explicitly changed.</para>
            </listitem>
            <listitem>
                <para>Uniform variables defined in two GLSL stages that have the same name and type
                    are considered the same uniform. Setting this uniform in the program object will
                    change its value for both stages.</para>
            </listitem>
        </itemizedlist>
        <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>vertCalcOffset.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>fragChangeColor.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>
                <listitem>
                    <para>Using our knowledge of uniforms, go back to <link linkend="FragPosition"
                            >Tutorial 2's FragPosition</link> tutorial. Modify the code so that it
                        takes a uniform that describes the window's height, rather than using a
                        hard-coded value. Change the <function>reshape</function> function to bind
                        the program and modify the uniform with the new height.</para>
                </listitem>
            </itemizedlist>
        </section>
        <section>
            <title>OpenGL 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 was not
                            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>
            <title>GLSL Functions of Note</title>
            <funcsynopsis>
                <funcprototype>
                    <funcdef>vec <function>mod</function></funcdef>
                    <paramdef>vec <parameter>numerator</parameter></paramdef>
                    <paramdef>float <parameter>denominator</parameter></paramdef>
                </funcprototype>
                <funcprototype>
                    <funcdef>vec <function>mod</function></funcdef>
                    <paramdef>vec <parameter>numerator</parameter></paramdef>
                    <paramdef>vec <parameter>denominator</parameter></paramdef>
                </funcprototype>
            </funcsynopsis>
            <para>The <function>mod</function> function takes the modulus of the
                    <parameter>numerator</parameter> by the <parameter>denominator</parameter>. The
                modulus can be thought of as a way of causing a loop; the return value will be on
                the range [0, <parameter>denominator</parameter>) in a looping fashion.
                Mathematically, it is defined as <parameter>numerator</parameter> -
                    (<parameter>denominator</parameter> *
                    <function>FLOOR</function>(<parameter>numerator</parameter> /
                    <parameter>denominator</parameter>)), where <function>FLOOR</function> rounds a
                floating-point value down towards the smallest whole number.</para>
            <para>The type <type>vec</type> can be either <type>float</type> or any vector type. It
                must be the same type for all parameters. If a vector denominator is used, then the
                modulus is taken for each corresponding component. The function returns a vector of
                the same size as its <parameter>numerator</parameter> type.</para>
            <funcsynopsis>
                <funcprototype>
                    <funcdef>vec <function>cos</function></funcdef>
                    <paramdef>vec <parameter>angle</parameter></paramdef>
                </funcprototype>
                <funcprototype>
                    <funcdef>vec <function>sin</function></funcdef>
                    <paramdef>vec <parameter>angle</parameter></paramdef>
                </funcprototype>
            </funcsynopsis>
            <para>Returns the trigonometric <link
                    xlink:href="http://en.wikipedia.org/wiki/Sine#Sine.2C_cosine_and_tangent">cosine
                    or sine</link>, respectively, of the given <parameter>angle.</parameter> The
                    <parameter>angle</parameter> is given in units of radians. If the
                    <parameter>angle</parameter> is a vector, then the returned vector will be of
                the same size, and will be the cosine or sine of each component of the
                    <parameter>angle</parameter> vector.</para>
        </section>
    </section>
    <section xml:id="Tut03_Glossary">
        <?dbhtml filename="Tut03 Glossary.html" ?>
        <title>Glossary</title>
        <glosslist>
            <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
                        rendering operation. Their values are set from outside of the shader, and
                        they cannot be changed from within a shader.</para>
                </glossdef>
            </glossentry>
        </glosslist>
    </section>
</chapter>
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.