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¶
/**
* simple_model4.js, By Wayne Brown, Fall 2017
*/
/**
* The MIT License (MIT)
*
* Copyright (c) 2015 C. Wayne Brown
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
"use strict";
//-------------------------------------------------------------------------
/**
* A triangle composed of 3 vertices.
* @param vertices {Array} Three components, (x,y,z) location, RGBA color,
* (s,t) texture coordinate.
* @constructor
// Vertex Shader
// By: Dr. Wayne Brown, Spring 2016
precision mediump int;
precision mediump float;
uniform mat4 u_Transform;
attribute vec3 a_Vertex;
attribute vec4 a_Color;
attribute vec2 a_Texture;
varying vec4 v_vertex_color;
varying vec2 v_texture;
void main() {
// Transform the location of the vertex
gl_Position = u_Transform * vec4(a_Vertex, 1.0);
// Pass on the color and texture coordinates for this vertex to the fragment shader
v_vertex_color = a_Color;
v_texture = a_Texture;
}
// Fragment shader
// By: Dr. Wayne Brown, Spring 2016
precision mediump int;
precision mediump float;
varying vec4 v_vertex_color;
varying vec2 v_texture;
float grid_size = 0.2; // Percentage of whole
//-------------------------------------------------
// modify the color based on the texture coordinates
vec4 modify_color(vec2 tex_coords, vec4 color) {
float s = tex_coords[0];
float t = tex_coords[1];
vec4 new_color;
if ( mod((floor(s/grid_size) + floor(t/grid_size)),2.0) == 1.0) {
// Use the normal face's color.
new_color = color;
} else {
// Make the color darker
new_color = vec4(vec3(color) * 0.8, 1.0);
}
return new_color;
}
//-------------------------------------------------
void main() {
gl_FragColor = modify_color(v_texture, v_vertex_color);
}
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¶
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)
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 ]
];