You are on page 1of 5

ertex Data

Unlike old versions of OpenGL or “the ‘2D’ canvas context”, you can’t directly set the color or
location of a vertex
directly into a scene. This is because WebGL does not have fixed functionality but uses
programmable shaders
instead. All data associated with a vertex needs to be streamed (passed along) from the JavaScript
API to the
Graphics Processing Unit (GPU). With WebGL, you have to create vertex buffer objects (VBOs)
that will hold
vertex attributes such as position, color, normal, and texture coordinates.
These vertex buffers are then sent to a shader program that can use and manipulate the passed-in
data in
any way you see fit. Using shaders instead of having fixed functionality is central to WebGL and
will be covered in
depth in the next chapter.
We will now turn our attention to what vertex attributes and uniform values are and show how to
transport
data with VBOs.
Vertex Buffer Objects (VBOs)
Each VBO stores data about a particular attribute of your vertices. This could be position, color, a
normal vector,
texture coordinates, or something else. A buffer can also have multiple attributes interleaved (as we
will discuss
in Chapter 9).
Looking at the WebGL API calls (which can be found at http://www.khronos.org/files/webgl/webgl-
reference-card-1_0.pdf or at http://www.khronos.org/registry/webgl/specs/latest/), to create a buffer,
you call
WebGLBuffer createBuffer()and store the returned object, like so:
var myBuffer = gl.createBuffer();
Next you bind the buffer using void bindBuffer(GLenum target, WebGLBuffer buffer) like this:
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, myBuffer);
The target parameter is either gl.ARRAY_BUFFER or gl.ELEMENT_ARRAY_BUFFER. The
target ELEMENT_ARRAY_
BUFFER is used when the buffer contains vertex indices, and ARRAY_BUFFER is used for vertex
attributes such as
position and color.
Once a buffer is bound and the type is set, we can place data into it with this function:
void bufferData(GLenum target, ArrayBuffer data, GLenum usage)
The usage parameter of the bufferData call can be one of STATIC_DRAW, DYNAMIC_DRAW, or
STREAM_DRAW.
STATIC_DRAW will set the data once and never change throughout the application’s use of it,
which will be many
times. DYNAMIC_DRAW will also use the data many times in the application but will respecify
the contents to be
used each time. STREAM_DRAW is similar to STATIC_DRAW in never changing the data, but it
will be used at most a
few times by the application. Using this function looks like the following:
var data = [
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 1.0
];
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
Altogether the procedure of creating, binding and storing data inside of a buffer looks like:
var data = [
];
6
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 1.0, 1.0CHAPTER 1 N SETTING THE SCENE
var myBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, myBuffer);
gl.bufferData(gl.ARRAY_BUFFER, data, STATIC_DRAW);
Notice that in the gl.bufferData line, we do not explicitly specify the buffer to place the data into.
WebGL
implicitly uses the currently bound buffer.
When you are done with a buffer you can delete it with a call to this:
void deleteBuffer(WebGLBuffer buffer);
As the chapter progresses, we will show how to setup a shader program and pass VBO data into it.
Attributes and Uniforms
As mentioned, vertices have attributes which can be passed to shaders. We can also pass uniform
values to
the shader which will be constant for each vertex. Shader attributes and uniforms can get complex
and will be
covered in more depth in the next chapter but touched upon here. As the shader is a compiled
external program,
we need to be able to reference the location of all variables within the program. Once we obtain the
location of a
variable, we can send data to the shader from our web application. To get the location of an attribute
or uniform
within the WebGL program, we use these API calls:
GLint getAttribLocation(WebGLProgram program, DOMString name)
WebGLUniformLocation getUniformLocation(WebGLProgram program, DOMString name)
The GLint and WebGLUniformLocation return values are references to the location of the attribute
or uniform
within the shader program. The first parameter is our WebGLProgram object and the second
parameter is the
attribute name as found in the vertex or fragment shader source. If we have an attribute in a shader
by the name
of "aVertexPosition", we obtain its position within our JavaScript like this:
var vertexPositionAttribute = gl.getAttribLocation(glProgram, "aVertexPosition");
If we are sending an array of data to an attribute, we have to enable array data with a call to this:
void enableVertexAttribArray(GLuint index)
Here, the index is the attribute location that we previously obtained and stored. The return value is
void
because the function returns no value.
With our previously defined attribute location, this call looks like the following:
gl.enableVertexAttribArray(vertexPositionAttribute);
Now that we have the location of an attribute and have told our shader that we will be using an
array of
values, we assign the currently bound ARRAY_BUFFER target to this vertex attribute as we have
demonstrated in the
previous section:
gl.bindBuffer(gl.ARRAY_BUFFER, myBuffer);
Finally, we let our shader know how to interpret our data. We need to remember that the shader
knows nothing
about the incoming data. Just because we name an array to help us understand what data it contains,
such as
myColorData, the shader just sees data without any context. The API call to explain our data format
is as follows:
void vertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei
stride, GLintptr offset)
size is the number of components per attribute. For example, with RGB colors, it would be 3; and
with an
alpha channel, RGBA, it would be 4. If we have location data with (x,y,z) attributes, it would be 3;
and if we
had a fourth parameter w, (x,y,z,w), it would be 4. Texture parameters (s,t) would be 2. type is the
datatype,
stride and offset can be set to the default of 0 for now and will be reexamined in Chapter 9 when we
discuss
interleaved arrays.
7CHAPTER 1 N SETTING THE SCENE
Altogether, the process of assigning values to a shader attribute looks like the following:
vertexPositionAttribute = gl.getAttribLocation(glProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, myBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
Now that we have gone over some of the relevant theory and methods, we can render our first
example.
Rendering in Two Dimensions
In our first example, we will output two white triangles that look similar to a bowtie (see Figure 1-
4). In order
to get our feet wet and not overwhelm the reader, I have narrowed the focus of this example to have
very
minimalistic shaders and also not perform any transforms or setup of the view. Listing 1-3 builds
upon the code
of Listing 1-2. New code is shown in bold.
Listing 1-3. Partial code for rendering two triangles
<!doctype html>
<html>
<head>
<title>A Triangle</title>
<style>
body{ background-color: grey; }
canvas{ background-color: white; }
</style>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<script>
var
gl = null,
canvas = null,
glProgram = null,
fragmentShader = null,
vertexShader = null;
var
vertexPositionAttribute = null,
trianglesVerticeBuffer = null;
function initWebGL()
{
canvas = document.getElementById("my-canvas");
try{
gl =
8
}catch(e){
}
canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");CHAPTER 1 N SETTING THE SCENE
if(gl)
{
setupWebGL();
initShaders();
setupBuffers();
drawScene();
}else{
alert(
"Error: Your browser does not appear to" +
"support WebGL.");
}
}
function setupWebGL()
{
//set the clear color to a shade of green
gl.clearColor(0.1, 0.5, 0.1, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function initShaders(){}
function setupBuffers(){}
function drawScene(){}
</script>
</head>
<body onload="initWebGL()">
<canvas id="my-canvas" width="400" height="300">
Your browser does not support the HTML5 canvas element.
</canvas>
</body>
</html>
If you run the code at this point, you will still see a green rectangle because we defined shaders but
have not
hooked them into our application yet. The first new parts of Listing 1-3 are our vertex and fragment
shaders. As
mentioned earlier, shaders can get complex and are covered in detail in Chapter 2. Right now, you
simply need to
know that the vertex shader will set the final position of a vertex while the fragment shader (also
known as a pixel
shader) will set the final color of each pixel.
The following vertex shader takes each (x,y,z) vertex point that we will pass in to it and sets the
final
position to the homogeneous coordinate (x,y,z,1.0).
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition, 1.0);
}
</script>
The fragment shader will simply set each fragment that it receives to the color white (1.0, 1.0, 1.0,
1.0). The
fourth component is the alpha value.
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
9CHAPTER 1 N SETTING THE SCENE
Eventually, we will pass in vertex points that correspond to the two triangles that we are rendering,
but right
now nothing is passed in and so we still see only the green clear color. In Listing 1-3 we have also
added new
variables that will store our WebGL shading language progra

You might also like