5.8 - Example 4: Textures¶
This chapter has explained how shader programs, GPU object buffers, and JavaScript rendering code works together to create WebGL renderings. We have keep the shader programs very simple to help make the concepts understandable. However, we have barely scratched the surface on what is possible. The WebGL program below is presented to spark your interest in what is coming in future lessons. We won’t walk through the details of procedural texture mapping in this lesson, but please review the program code to get an idea of what a more complex shader program might do.
The example WebGL program below does the following:
- The pyramid model is defined in
simple_model4.js
and stores “texture coordinates” with each vertex. In this example, the “texture coordinates” are 2 numbers that are used by a procedural texture map, which is implemented in the fragment shader. - Three buffer objects are built: one for the vertex locations,
(x,y,z)
, one for the vertex color values,RGBA
, and one for the texture coordinates, which we refer to as(s,t)
. - At render time, each attribute variable in the vertex shader is linked to the buffer object that contains its data.
- The entire model is rendered with a single call to
gl.drawArrays()
. - The fragment shader uses the texture coordinates to modify the color
of some of the fragments. Note that the texture
coordinates and the vertex colors are
varying
their values based on the values that were set at the vertices.
A Texture Map Example¶
A simple, 3D model where pixel colors are calculated using a procedural texture map.
Animate
Draw the edges of the triangles using a gl.LINE_LOOP
Render the model as a "wireframe"
Render the global axes (x:red, y:green, z:blue)
Summary¶
To render a model, it requires a GPU shader program, one or more GPU object-buffers, and JavaScript rendering code. Let’s summarize the big picture behind rendering:
- A vertex shader program retrieves vertex and other associated data from buffer objects. All buffer objects are 1-dimensional arrays of floats. The data in the buffer objects are organized by vertex. The vertex shader calculates the position a vertex in the scene and prepares any data needed by the fragment shader for calculating colors.
- A fragment shader program receives data from the vertex shader for each vertex and then interpolates the values over the fragments of the primitive (either a point, line or triangle).
- A pre-processing step must create appropriate GPU buffer objects and copy the model data into them.
- Each time a model is rendered, a shader’s
uniform
variables must be assigned a value, a shader’sattribute
variables must be linked to their appropriate buffer objects and a call togl.drawArrays()
executes the graphics pipeline.
Self-Assessments¶
- 0 and 1.
- Correct. The sum of the two floor() values will always be an integer, and the remainder of dividing any integer by 2 has to be 0 or 1, which is equivalent to saying all integers are either even or odd.
- 0, 1, or 2.
- Incorrect. It is not possible to get 2.
- Any positive integer value greater than or equal to 0.
- Incorrect. Mod(n,2) divides by 2 and returns the remainder as an integer.
- Only 0 and 2.
- Incorrect.
Q-345: Code in the example fragment shader in the above WebGL program uses a
floor
function to to convert a floating point number to an integer,
and a mod
function to get the remainder after division by 2.
What are the only possible values this expression can return?
mod((floor(s/grid_size) + floor(t/grid_size)),2.0)
- Yes, the exact structure of the model data is arbitrary.
- Correct.
- No, there is only one way to define model data.
- Incorrect. There are many, many ways.
- Yes, but it would be very confusing.
- Incorrect. There is no confusion if you did it that way for a reason!
- No, model data must be defined in arrays of arrays.
- Incorrect. Using arrays of arrays might make the data more structured, but it is not required.
Q-346: The SimpleModel4
function in the above WebGL program uses a
array of arrays to define the data for each vertex like this:
vertices = [ [ [ 0.0, -0.25, -0.50], [1, 0, 0, 1], [2.0, 0.0] ],
[ [ 0.0, 0.25, 0.00], [0, 1, 0, 1], [0.5, 1.0] ],
[ [ 0.5, -0.25, 0.25], [0, 0, 1, 1], [1.0, 0.0] ],
[ [-0.5, -0.25, 0.25], [1, 0, 1, 1], [0.0, 0.0] ]
];
Could you have defined the vertex data using a single array for each vertex like this:
vertices = [ [ 0.0, -0.25, -0.50, 1, 0, 0, 1, 2.0, 0.0 ],
[ 0.0, 0.25, 0.00, 0, 1, 0, 1, 0.5, 1.0 ],
[ 0.5, -0.25, 0.25, 0, 0, 1, 1, 1.0, 0.0 ],
[ -0.5, -0.25, 0.25, 1, 0, 1, 1, 0.0, 0.0 ]
];