mozilla

Revision 54577 of Using shaders to apply color in WebGL

  • Revision slug: WebGL/Using_shaders_to_apply_color_in_WebGL
  • Revision title: Using shaders to apply color in WebGL
  • Revision id: 54577
  • Created:
  • Creator: Brettz9
  • Is current revision? No
  • Comment coloring; one or more formatting changes

Revision Content

{{ gecko_minversion_header("2") }}

{{ PreviousNext("WebGL/Adding 2D content to a WebGL context", "WebGL/Animating objects with WebGL") }}

 

Having created a square in the previous demonstration, the next obvious step is to add a splash of color to it. We can do this by revising the shaders.

Applying color to the vertices

In GL, objects are built using sets of vertices, each of which has a position and a color; by default, all other pixels' colors (and all its other attributes, including position) are computed using linear interpolation, automatically creating smooth gradients. Previously, our vertex shader didn't apply any specific colors to the vertices; between this and the fragment shader assigning the fixed color of white to each pixel, the entire square was rendered as solid white.

Let's say we want to render a gradient in which each corner of the square is a different color: red, blue, green, and white. The first thing to do is to establish these colors for the four vertices. To do this, we first need to create an array of vertex colors, then store it into a WebGL buffer; we'll do that by adding the following code to our initBuffers() function:

  var colors = [
    1.0,  1.0,  1.0,  1.0,    // white
    1.0,  0.0,  0.0,  1.0,    // red
    0.0,  1.0,  0.0,  1.0,    // green
    0.0,  0.0,  1.0,  1.0     // blue
  ];
  
  squareVerticesColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(colors), gl.STATIC_DRAW);
}

This code starts by creating a JavaScript array containing four 4-value vectors, one for each vertex's color. Then a new WebGL buffer is allocated to store these colors, and the array is converted into WebGL floats and stored into the buffer.

To make these colors actually get used, the vertex shader needs to be updated to pull the appropriate color from the color buffer:

    <script id="shader-vs" type="x-shader/x-vertex">
      attribute vec3 aVertexPosition;
      attribute vec4 aVertexColor;
    
      uniform mat4 uMVMatrix;
      uniform mat4 uPMatrix;
      
      varying lowp vec4 vColor;
    
      void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        vColor = aVertexColor;
      }
    </script>

The key difference here is that for each vertex, we set its color to the corresponding value from the color array.

Coloring the fragments

As a refresher, here's what our fragment shader looked like previously:

    <script id="shader-fs" type="x-shader/x-fragment">
      void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    </script>

In order to pick up the interpolated color for each pixel, we simply need to change this to fetch the value from the vColor variable:

    <script id="shader-fs" type="x-shader/x-fragment">
    	varying lowp vec4 vColor;
    	
      void main(void) {
        gl_FragColor = vColor;
      }
    </script>

This is a simple change; each fragment simply receives the interpolated color based on its position relative to the vertices, instead of a fixed value.

Drawing using the colors

Next, it's necessary to add the code to the initShaders() routine to initialize the color attribute for the shader program:

  vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
  gl.enableVertexAttribArray(vertexColorAttribute);

Then, drawScene() can be revised to actually use these colors when drawing the square:

  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);

At this point, if you view the sample in a WebGL compatible browser, you should see a display similar to the following (although centered in a larger field of black):

screenshot.png

{{ PreviousNext("WebGL/Adding 2D content to a WebGL context", "WebGL/Animating objects with WebGL") }}

{{ languages({"de":"de/WebGL/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen"}) }}

Revision Source

<p>{{ gecko_minversion_header("2") }}</p>
<p>{{ PreviousNext("WebGL/Adding 2D content to a WebGL context", "WebGL/Animating objects with WebGL") }}</p>
<p> </p>
<p>Having created a square in the <a href="/en/WebGL/Adding_2D_content_to_a_WebGL_context" title="en/WebGL/Adding 2D content to a WebGL context">previous demonstration</a>, the next obvious step is to add a splash of color to it. We can do this by revising the shaders.</p>
<h2>Applying color to the vertices</h2>
<p>In GL, objects are built using sets of vertices, each of which has a position and a color; by default, all other pixels' colors (and all its other attributes, including position) are computed using linear interpolation, automatically creating smooth gradients. Previously, our vertex shader didn't apply any specific colors to the vertices; between this and the fragment shader assigning the fixed color of white to each pixel, the entire square was rendered as solid white.</p>
<p>Let's say we want to render a gradient in which each corner of the square is a different color: red, blue, green, and white. The first thing to do is to establish these colors for the four vertices. To do this, we first need to create an array of vertex colors, then store it into a WebGL buffer; we'll do that by adding the following code to our <code>initBuffers()</code> function:</p>
<pre class="brush: js">  var colors = [
    1.0,  1.0,  1.0,  1.0,    // white
    1.0,  0.0,  0.0,  1.0,    // red
    0.0,  1.0,  0.0,  1.0,    // green
    0.0,  0.0,  1.0,  1.0     // blue
  ];
  
  squareVerticesColorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new WebGLFloatArray(colors), gl.STATIC_DRAW);
}
</pre>
<p>This code starts by creating a JavaScript array containing four 4-value vectors, one for each vertex's color. Then a new WebGL buffer is allocated to store these colors, and the array is converted into WebGL floats and stored into the buffer.</p>
<p>To make these colors actually get used, the vertex shader needs to be updated to pull the appropriate color from the color buffer:</p>
<pre class="brush: html">    &lt;script id="shader-vs" type="x-shader/x-vertex"&gt;
      attribute vec3 aVertexPosition;
      attribute vec4 aVertexColor;
    
      uniform mat4 uMVMatrix;
      uniform mat4 uPMatrix;
      
      varying lowp vec4 vColor;
    
      void main(void) {
        gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        vColor = aVertexColor;
      }
    &lt;/script&gt;
</pre>
<p>The key difference here is that for each vertex, we set its color to the corresponding value from the color array.</p>
<h2>Coloring the fragments</h2>
<p>As a refresher, here's what our fragment shader looked like previously:</p>
<pre class="brush: html">    &lt;script id="shader-fs" type="x-shader/x-fragment"&gt;
      void main(void) {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
      }
    &lt;/script&gt;
</pre>
<p>In order to pick up the interpolated color for each pixel, we simply need to change this to fetch the value from the <code>vColor</code> variable:</p>
<pre class="brush: html">    &lt;script id="shader-fs" type="x-shader/x-fragment"&gt;
    	varying lowp vec4 vColor;
    	
      void main(void) {
        gl_FragColor = vColor;
      }
    &lt;/script&gt;
</pre>
<p>This is a simple change; each fragment simply receives the interpolated color based on its position relative to the vertices, instead of a fixed value.</p><h2>Drawing using the colors</h2>
<p>Next, it's necessary to add the code to the <code>initShaders()</code> routine to initialize the color attribute for the shader program:</p>
<pre class="brush: js">  vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
  gl.enableVertexAttribArray(vertexColorAttribute);
</pre>
<p>Then, drawScene() can be revised to actually use these colors when drawing the square:</p>
<pre class="brush: js">  gl.bindBuffer(gl.ARRAY_BUFFER, squareVerticesColorBuffer);
  gl.vertexAttribPointer(vertexColorAttribute, 4, gl.FLOAT, false, 0, 0);
</pre>
<p>At this point, if you <a href="/samples/webgl/sample3/index.html" title="https://developer.mozilla.org/samples/webgl/sample3/index.html">view the sample in a WebGL compatible browser</a>, you should see a display similar to the following (although centered in a larger field of black):</p>
<p><img alt="screenshot.png" class="internal default" src="/@api/deki/files/4081/=screenshot.png"></p>
<p>{{ PreviousNext("WebGL/Adding 2D content to a WebGL context", "WebGL/Animating objects with WebGL") }}</p>
<p>{{ languages({"de":"de/WebGL/Farben_mittels_Shader_in_einen_WebGL-Kontext_hinzufügen"}) }}</p>
Revert to this revision