Jason McKesson avatar Jason McKesson committed 244ede3

Finishing swapping all of Tutorial 2 with Tutorial 3.

Comments (0)

Files changed (2)

Documents/TutTemp/Tutorial 03.xml

-<?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>

Documents/Tutorial 03/Tutorial 03.xml

+<?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>
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.