index previous next

Vertex shader

  1. Create vertex shader
    1. write vertex shader code
    2. embed vertex shader code into the page and get that code into string
    3. create vertex shader object
    4. put vertex shader code inside vertex shader object
    5. compile vertex shader

Recap

This is our simplified plan:

  1. Get webgl context from canvas
  2. Create vertex data
  3. Put data into GPU buffer
  4. Create vertex shader
  5. Create fragment shader
  6. Create shading program from vertex and fragment shader
  7. Plug in buffer into shading program
  8. Draw

and as of now we have done points 1-3. We have our canvas on place and triangle vertex data inside GPU memory waiting to be used.

Now it is time to do some GPU programming, that is, to write shaders.

First a bit of remainder.

Shaders are programs written in GLSL (OpenGL Shading Language) that run on a graphics card and there are two types of them.

Vertex shader is responsible for shape and position transformations.

Fragment shader is responsible for computing pixel color.

Vertex and fragment shader create together a shading program that is used to process data passed to the GPU and give pixels' colors as output.

We will not be doing any transformations of vertices nor passing values from vertex shader to fragment shader, so there is nothing more to explain.

write vertex shader code

Writing vertex shader code is quite straightforward. You define what input it gets, write main function and you are set. Let's have a look.


attribute vec3 vertexPos;

void main(void)
{
	gl_Position = vec4(vertexPos, 1.0);
}

As you can see, we have a main() function which is our vertex shader entry point. It does not return, nor accepts any parameters (this is what void says). All value passing into the shader and from shader to the outer space is by reading and writing appropriate variables.

vertexPos is our input variable - it will contain vertex data from the buffer, that is vertex position.

gl_Position is our output variable. It tells what position has a given vertex. It is the only mandatory output variable, that vertex shader must always set before return.

So the flow is like follows:
vertexPos -> main() -> gl_Position

Now let's discuss some details.

attribute means that this is an input variable and it will get successive values from a buffer. The buffer will be plugged in to vertexPos later. You can think of attribute variables as iterators over values of connected buffers. Every time main() is called, attribute variable has a new value.

vec3 is of course a type declaration - is is a 3 component vector type, for our x, y, z coordinates of a vertex of the triangle.

gl_Position is an output variable, but it is of type vec4. This is why we have to create new 4-component vector. It's three first values will be taken from vertexPos. Fourth value is set to be 1.0. Fourth value has to do with perspective transformation*, which we will not be doing. So you can set it to 1.0 and forget that it exists.

embed vertex shader code into the page and get that code into string

Before we can do anything with that vertex shader code, we need to have it in a javascript string. Some people put it directly into string,


var vertexShaderSource = "
attribute vec3 vertexPos;\
\
void main(void)\
{\
	gl_Position = vec4(vertexPos, 1.0);\
}\

"

but I find it ugly and unpleasent to use. There is though a nicer, cleaner and easier to use solution.

Every tag in html has some content, that may me represented as text. That text representation can be accessed by using innerHTML attribute on the element.

A problem with that solution is that we need some container that will not be displayed by a browser. And there is a good one - script.

<script> tag normally contains javascript code, but we can set what type of content it has. If the browser does not know the type, script content is ignored but still exists and we can get to it. This is how you do this.


<script type="unknown" id="vertexShader">
attribute vec3 vertexPos;

void main(void)
{
	gl_Position = vec4(vertexPos, 1.0);
}
</script>

<!-- and later on the web page -->

<script>
var vertexShaderTag = document.getElementById('vertexShader')
var vertexShaderSource = vertexShaderTag.innerHTML
</script>

This may not be the easiest solution but it definetely works and does not require manual carving of the string as above.

This is how whole page code looks like.


<!DOCTYPE html> 

<html>
<head>
<meta charset="UTF-8">
<title>noniwoo webgl tutorial</title>
</head>
<body>
<canvas id="canvas1" width="640" height="480"></canvas>

<script type="unknown" id="vertexShader">
attribute vec3 vertexPos;

void main(void)
{
	gl_Position = vec4(vertexPos, 1.0);
}
</script>


<script>
var canvas = document.getElementById('canvas1')
if( canvas == null )
{
	throw "Could not get canvas element!"
}

var gl = canvas.getContext('webgl')
if( gl == null )
{
	gl = canvas.getContext('experimental-webgl')
	if( gl == null )
	{
		throw "Could not get webgl context"
	}
}

gl.clearColor(1.0, 1.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)

var triangleVertices = new Float32Array([
	 0.0,  1.0,  0.0,  // top
	 1.0, -1.0,  0.0,  // right
	-1.0, -1.0,  0.0   // left
])

var triangleBuffer = gl.createBuffer()
gl.bindBuffer( gl.ARRAY_BUFFER, triangleBuffer )
gl.bufferData( gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW )

var vertexShaderTag = document.getElementById('vertexShader')
var vertexShaderSource = vertexShaderTag.innerHTML

</script>

</body>
</html>

You can also put this vertex shader <script> tag into <head> if you wish. I will just stick to putting everything into body.


* remember that book about graphics math I talked about at the beggining?

create vertex shader object

Another thing that is as simple as you can get.


var vertexShader = gl.createShader(gl.VERTEX_SHADER)

put vertex shader code inside vertex shader object

Now we have created vertex shader and have its source code. Let's put that code into freshly created shader.


gl.shaderSource(vertextShader, vertexShaderSource)

compile vertex shader

After source code is uploaded to shader, we can compile it and check for errors. If everything went fine, our vertex shader is ready to go.


gl.compileShader(vertexShader)

if(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) != true)
{
	throw "Vertex shader compilation failed!\n" + gl.getShaderInfoLog(vertexShader)
}

By using getShaderInfoLog() we can present information that says much more than just "Vertex shader compilation failed. Good luck, have fun.". It usually tells in which line and what is wrong, so everything we need to fix that right away.

Whole code with vertex shader creation code looks now like this.


<!DOCTYPE html> 

<html>
<head>
<meta charset="UTF-8">
<title>noniwoo webgl tutorial</title>
</head>
<body>
<canvas id="canvas1" width="640" height="480"></canvas>

<script type="unknown" id="vertexShader">
attribute vec3 vertexPos;

void main(void)
{
	gl_Position = vec4(vertexPos, 1.0);
}
</script>


<script>
var canvas = document.getElementById('canvas1')
if( canvas == null )
{
	throw "Could not get canvas element!"
}

var gl = canvas.getContext('webgl')
if( gl == null )
{
	gl = canvas.getContext('experimental-webgl')
	if( gl == null )
	{
		throw "Could not get webgl context"
	}
}

gl.clearColor(1.0, 1.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)

var triangleVertices = Float32Array([
	 0.0,  1.0,  0.0,  // top
	 1.0, -1.0,  0.0,  // right
	-1.0, -1.0,  0.0   // left
])

var triangleBuffer = gl.createBuffer()
gl.bindBuffer( gl.ARRAY_BUFFER, triangleBuffer )
gl.bufferData( gl.ARRAY_BUFFER, triangleVertices, gl.STATIC_DRAW )

var vertexShaderTag = document.getElementById('vertexShader')
var vertexShaderSource = vertexShaderTag.innerHTML
var vertexShader = gl.createShader(gl.VERTEX_SHADER)

gl.shaderSource(vertexShader, vertexShaderSource)
gl.compileShader(vertexShader)
if(gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS) != true)
{
	throw "Vertex shader compilation failed!\n" + gl.getShaderInfoLog(vertexShader)
}

</script>

</body>
</html>

index previous next