Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 1041148 of GLSL Shaders

  • Revision slug: Games/Techniques/3D_on_the_web/GLSL_Shaders
  • Revision title: GLSL Shaders
  • Revision id: 1041148
  • Created:
  • Creator: PushpitaPikuDey
  • Is current revision? No
  • Comment

Revision Content

Shaders use GLSL, a special OpenGL Shading Language with syntax similar to C that is executed directly by the graphics pipeline. They can be split into Vertex Shaders and Fragment Shaders (or Pixel Shaders) — the former transforms shape positions to real 3D drawing coordinates while the latter computes rendering colors and other attributes.

Shading Language is different from JavaScript - it's strongly typed, there's a lot of maths calculating vectors and matrices. Writing shaders can be fun though, because it's easy to create a basic one.

Shader types

Shader is a function required to draw something on the screen. Shaders run on your GPU which is optimized for such operations, so that you can offload CPU and focus the processor power on executing your own code.

Vertex Shader

Vertex shaders manipulate the coordinates in a 3D space and they are called once per vertex. The purpose of the vertex shader is to set up gl_Position variable:

void main() {
	gl_Position = makeCalculationsToHaveCoordinates;
}

The calculations result in having a 4D vector containing the information about how the position in the 3D space is projected onto a 2D screen.

Fragment Shader

Frament (or texture) shaders provide colors for the current pixel being processed - single fragment shader is called once per pixel. The purpose of the shader is to set up gl_FragColor variable:

void main() {
	gl_FragColor = makeCalculationsToHaveColor;
}

The calculations are resulting in having a 4D vector containing the information about the RGBA color.

Demo

Let's build a simple demo to explain those shaders in action. Be sure to read Three.js tutorial first to grasp the concept of the scene, its objects and materials.

Note: Remember that you don't have to use Three.js or any other library to write your shaders - pure WebGL is more than enough. This little demo though is focused on showing how to create the shader itself and use it in a working example but using Three.js in the background will make it a lot simpler and clearer to understand, so you can focus on the shaders code. Three.js or other libraries abstract a lot of things for you - if you'd like to create such example you'd have to write a lot of extra code to actually make it work.

Environment setup

To start with the WebGL shaders you don't need much. You should:

  • Make sure you are using a modern browser with good WebGL support, such as the latest Firefox or Chrome.
  • Create a directory to store your experiments in.
  • Save a copy of the latest minimized Three.js library inside your directory.

HTML structure

Here's the HTML structure we will use.

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>MDN Games: Shaders demo</title>
	<style>
		body { margin: 0; padding: 0; font-size: 0; }
		canvas { width: 100%; height: 100%; }
	</style>
	<script src="three.min.js"></script>
</head>
<body>
<script id="vertexShader" type="x-shader/x-vertex">
	// vertex shader's code goes here
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
	// fragment shader's code goes here
</script>
<script>
	// scene setup goes here
</script>
</body>
</html>

It contains some basic information like the document {{htmlelement("title")}}, and some CSS to set the width and height of the {{htmlelement("canvas")}} element that Three.js will insert on the page fully, so that it will fill the entire available viewport space. The {{htmlelement("script")}} element in the {{htmlelement("head")}} includes the Three.js library in the page and we will write our code into three script tags in the {{htmlelement("body")}} tag - the first one is for the vertex shader, second one for the fragment shader and the third one for the actual JavaScript code generating the scene.

Before reading on, copy this code to a new text file and save it in your working directory as index.html.

Cube's source code

Instead of creating everything from scratch we can reuse the Building up a basic demo with Three.js source code of the cube. Most of the components like renderer, camera and lights will stay the same, but instead of the basic material we will set the cube's color and position using shaders.

Check out the cube.html file on GitHub from the Three.js demo and copy all the JavaScript code from between the second {{htmlelement("script")}} tag. See if it works - you should see a blue cube.

Vertex shader's code

We will start with the vertex shader - add the code below to the body's first script tag:

void main() {
	gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x+10.0, position.y, position.z+5.0, 1.0);
}

The resulting gl_Position is calculated by multiplying the model-vew and the projection matrices with each vector to get the final vertex position. Both projectionMatrix and modelViewMatrix are provided by Three.js and the 4D vector is passed as vec4 with the given 3D position and the 1.0 value, which totals in moving the original cube ten units on the x axis and 5 units on the z axis, translated through a shader. The fourth parameter is used for clipping and plays its part while linear algebra transformations are applied to the position - by setting up the value to 1.0 it doesn't affect our transformation.

Texture shader's code

Then there goes the texture shader - add the code below to the body's second script tag:

void main() {
	gl_FragColor = vec4(0.0, 0.58, 0.86, 1.0);
}

This will set the RGBA color to the light blue one - the first three float values (ranging from 0.0 to 1.0) are representing the RGB color and the last, fourth one is the Alpha transparency.

Applying the shaders

To actually apply the newly created shaders, comment out the basicMaterial definition first:

// var basicMaterial = new THREE.MeshBasicMaterial({color: 0x0095DD});

Then, create the shaderMaterial:

var shaderMaterial = new THREE.ShaderMaterial( {
	vertexShader: document.getElementById( 'vertexShader' ).textContent,
	fragmentShader: document.getElementById( 'fragmentShader' ).textContent
});

This shader material takes the code from the scripts and apply it to the object the material is assigned to.

Then, instead of creating a cube with the basicMaterial we also comment out

// var cube = new THREE.Mesh(boxGeometry, basicMaterial);

...we can do it with the newly created shaderMaterial:

var cube = new THREE.Mesh(boxGeometry, shaderMaterial);

Three.js is compiling and running the shaders attached to the mesh to which this material is given. In our case the cube will have both vertex and texture shaders applied. That's it - you've just created the simplest possible shader, congratulations! Here's the cube:

Three.js blue cube demo

It looks exactly the same as the Three.js cube demo but the slightly different position and the same blue color are both achieved using the shader. You can see this in action - here's the final piece of the code:

{{JSFiddleEmbed("https://jsfiddle.net/end3r/LL55bhrz/","","350")}}

You can also see it on GitHub.

Conclusion

I hope you now know what a shader is and how it works. This one is not doing much but there are many more cool things you can do with shaders - check out some really cool ones on ShaderToy for inspiration and to learn from their sources.

Revision Source

<p class="summary"><span class="seosummary">Shaders use GLSL, a special OpenGL Shading Language with syntax similar to C that is executed directly by the graphics pipeline. They can be split into Vertex Shaders and Fragment Shaders (or Pixel Shaders) — the former transforms shape positions to real 3D drawing coordinates while the latter computes rendering colors and other attributes.</span></p>

<p>Shading Language is different from JavaScript - it's strongly typed, there's a lot of maths calculating vectors and matrices. Writing shaders can be fun though, because it's easy to create a basic one.</p>

<h2 id="Shader_types">Shader types</h2>

<p>Shader is a function required to draw something on the screen. Shaders run on your GPU which is optimized for such operations, so that you can offload CPU and focus the processor power on executing your own code.</p>

<h3 id="Vertex_Shader">Vertex Shader</h3>

<p>Vertex shaders manipulate the coordinates in a 3D space and they are called once per vertex. The purpose of the vertex shader is to set up <code>gl_Position</code> variable:</p>

<pre class="brush: glsl">
void main() {
	gl_Position = makeCalculationsToHaveCoordinates;
}
</pre>

<p>The calculations result in having a 4D vector containing the information about how the position in the 3D space is projected onto a 2D screen.</p>

<h3 id="Fragment_Shader">Fragment Shader</h3>

<p>Frament (or texture) shaders provide colors for the current pixel being processed - single fragment shader is called once per pixel. The purpose of the shader is to set up <code>gl_FragColor</code> variable:</p>

<pre class="brush: glsl">
void main() {
	gl_FragColor = makeCalculationsToHaveColor;
}
</pre>

<p>The calculations are resulting in having a 4D vector containing the information about the RGBA color.</p>

<h2 id="Demo">Demo</h2>

<p>Let's build a simple demo to explain those shaders in action. Be sure to read <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js tutorial</a> first to grasp the concept of the scene, its objects and materials.</p>

<div class="note">
<p><strong>Note</strong>: Remember that you don't have to use Three.js or any other library to write your shaders - pure <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API">WebGL</a> is more than enough. This little demo though is focused on showing how to create the shader itself and use it in a working example but using Three.js in the background will make it a lot simpler and clearer to understand, so you can focus on the shaders code. Three.js or other libraries abstract a lot of things for you - if you'd like to create such example you'd have to write a lot of extra code to actually make it work.</p>
</div>

<h3 id="Environment_setup">Environment setup</h3>

<p>To start with the WebGL shaders you don't need much. You should:</p>

<ul>
 <li>Make sure you are using a modern browser with good <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API">WebGL</a> support, such as the latest Firefox or Chrome.</li>
 <li>Create a directory to store your experiments in.</li>
 <li>Save a copy of the <a href="https://threejs.org/build/three.min.js">latest minimized Three.js library</a> inside your directory.</li>
</ul>

<h3 id="HTML_structure">HTML structure</h3>

<p>Here's the HTML structure we will use.</p>

<pre class="brush: html">
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
	&lt;meta charset="utf-8"&gt;
	&lt;title&gt;MDN Games: Shaders demo&lt;/title&gt;
	&lt;style&gt;
		body { margin: 0; padding: 0; font-size: 0; }
		canvas { width: 100%; height: 100%; }
	&lt;/style&gt;
	&lt;script src="three.min.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;script id="vertexShader" type="x-shader/x-vertex"&gt;
	// vertex shader's code goes here
&lt;/script&gt;
&lt;script id="fragmentShader" type="x-shader/x-fragment"&gt;
	// fragment shader's code goes here
&lt;/script&gt;
&lt;script&gt;
	// scene setup goes here
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

<p>It contains some basic information like the document {{htmlelement("title")}}, and some CSS to set the <code>width</code> and <code>height</code> of the {{htmlelement("canvas")}} element that Three.js will insert on the page fully, so that it will fill the entire available viewport space. The {{htmlelement("script")}} element in the {{htmlelement("head")}} includes the Three.js library in the page and we will write our code into three script tags in the {{htmlelement("body")}} tag - the first one is for the vertex shader, second one for the fragment shader and the third one for the actual JavaScript code generating the scene.</p>

<p>Before reading on, copy this code to a new text file and save it in your working directory as <code>index.html</code>.</p>

<h3 id="Cube's_source_code">Cube's source code</h3>

<p>Instead of creating everything from scratch we can reuse the <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Building up a basic demo with Three.js</a> source code of the cube. Most of the components like renderer, camera and lights will stay the same, but instead of the basic material we will set the cube's color and position using shaders.</p>

<p>Check out the <a href="https://github.com/end3r/MDN-Games-3D/blob/gh-pages/Three.js/cube.html">cube.html file on GitHub</a> from the Three.js demo and copy all the JavaScript code from between the second {{htmlelement("script")}} tag. See if it works - you should see a blue cube.</p>

<h3 id="Vertex_shader's_code">Vertex shader's code</h3>

<p>We will start with the vertex shader - add the code below to the body's first script tag:</p>

<pre class="brush: glsl">
void main() {
	gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x+10.0, position.y, position.z+5.0, 1.0);
}
</pre>

<p>The resulting <code>gl_Position</code> is calculated by multiplying the model-vew and the projection matrices with each vector to get the final vertex position. Both <code>projectionMatrix</code> and <code>modelViewMatrix</code> are provided by Three.js and the 4D vector is passed as <code>vec4</code> with the given 3D position and the <code>1.0</code> value, which totals in moving the original cube ten units on the <code>x</code> axis and 5 units on the <code>z</code> axis, translated through a shader. The fourth parameter is used for clipping and plays its part while linear algebra transformations are applied to the position - by setting up the value to <code>1.0</code> it doesn't affect our transformation.</p>

<h3 id="Texture_shader's_code">Texture shader's code</h3>

<p>Then there goes the texture shader - add the code below to the body's second script tag:</p>

<pre class="brush: glsl">
void main() {
	gl_FragColor = vec4(0.0, 0.58, 0.86, 1.0);
}
</pre>

<p>This will set the RGBA color to the light blue one - the first three float values (ranging from <code>0.0</code> to <code>1.0</code>) are representing the RGB color and the last, fourth one is the Alpha transparency.</p>

<h3 id="Applying_the_shaders">Applying the shaders</h3>

<p>To actually apply the newly created shaders, comment out the <code>basicMaterial</code> definition first:</p>

<pre class="brush: js">
// var basicMaterial = new THREE.MeshBasicMaterial({color: 0x0095DD});
</pre>

<p>Then, create the <a href="https://threejs.org/docs/#Reference/Materials/ShaderMaterial"><code>shaderMaterial</code></a>:</p>

<pre class="brush: js">
var shaderMaterial = new THREE.ShaderMaterial( {
	vertexShader: document.getElementById( 'vertexShader' ).textContent,
	fragmentShader: document.getElementById( 'fragmentShader' ).textContent
});
</pre>

<p>This shader material takes the code from the scripts and apply it to the object the material is assigned to.</p>

<p>Then, instead of creating a cube with the <code>basicMaterial</code> we also comment out</p>

<pre class="brush: js">
// var cube = new THREE.Mesh(boxGeometry, basicMaterial);
</pre>

<p>...we can do it with the newly created <code>shaderMaterial</code>:</p>

<pre class="brush: js">
var cube = new THREE.Mesh(boxGeometry, shaderMaterial);
</pre>

<p>Three.js is compiling and running the shaders attached to the mesh to which this material is given. In our case the cube will have both vertex and texture shaders applied. That's it - you've just created the simplest possible shader, congratulations! Here's the cube:</p>

<p><img alt="Three.js blue cube demo" src="https://end3r.github.io/MDN-Games-3D/Shaders/img/cube.png" /></p>

<p>It looks exactly the same as the Three.js cube demo but the slightly different position and the same blue color are both achieved using the shader. You can see this in action - here's the final piece of the code:</p>

<p>{{JSFiddleEmbed("https://jsfiddle.net/end3r/LL55bhrz/","","350")}}</p>

<p>You can also <a href="https://github.com/end3r/MDN-Games-3D/blob/gh-pages/Shaders/shaders.html">see it on GitHub</a>.</p>

<h2 id="Conclusion">Conclusion</h2>

<p>I hope you now know what a shader is and how it works. This one is not doing much but there are many more cool things you can do with shaders - check out some really cool ones on <a href="https://shadertoy.com/">ShaderToy</a> for inspiration and to learn from their sources.</p>
Revert to this revision