Viewport and Scissor

The Viewport, Scissor Test and Display Regions

viewport(x, y, width, height) scales clip space's -1 to 1 into a rectangle of the drawing buffer (pixels from the bottom-left), distorting the image if the aspects differ. The scissor test cuts instead: with SCISSOR_TEST enabled, scissor() discards fragments outside its box, and it also limits clear():

One triangle squeezed into two viewports, then cut by a scissor boxHTML
<canvas id="c" width="480" height="120" style="width:480px;height:120px"></canvas>
<script src="webgl-helper.js"></script>
<script>
const gl = GLH.context('c');
GLH.load(gl, `#version 300 es
  out vec3 vColor;
  void main() {
    vColor = vec3[3](vec3(0.88, 0.6, 0.06), vec3(0.12, 0.37, 0.55),
                     vec3(0.16, 0.62, 0.56))[gl_VertexID];
    gl_Position = vec4(vec2[3](vec2(0, -0.8), vec2(-0.8, 0.8), vec2(0.8))[gl_VertexID], 0, 1);
  }`, `#version 300 es
  precision mediump float; in vec3 vColor; out vec4 c; void main() { c = vec4(vColor, 1); }`);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(0, 0, 480, 85);                          // keep only the bottom 85 rows
gl.clearColor(0.85, 0.82, 0.76, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);                      // clear() obeys the scissor box too
gl.viewport(0, 0, 120, 120);                        // left: square, undistorted
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.viewport(120, 0, 360, 120);                      // right: stretched 3:1
gl.drawArrays(gl.TRIANGLES, 0, 3);
window.__done = true;
</script>
Browser output of Listing 3.43
Browser output of 43

The viewport is set when the context is created and never updated: after canvas.width = 600 it still read 0, 0, 300, 150 here, so call gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight) after resizing. All enable() capabilities start disabled except DITHER.

One triangle squeezed into two viewports, then cut by a scissor boxHTMLLive
<!doctype html>
<style>
  body { margin: 0; background: #f7f4ee; }
  canvas { display: block; width: 100%; max-width: 480px; }
</style>
<canvas id="c" width="480" height="120"></canvas>
<script>
const gl = document.getElementById('c').getContext('webgl2');
gl.clearColor(0.93, 0.91, 0.87, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, `#version 300 es
  out vec3 vColor;
  void main() {
    vColor = vec3[3](vec3(0.88, 0.6, 0.06), vec3(0.12, 0.37, 0.55),
                     vec3(0.16, 0.62, 0.56))[gl_VertexID];
    gl_Position = vec4(vec2[3](vec2(0, -0.8), vec2(-0.8, 0.8), vec2(0.8))[gl_VertexID], 0, 1);
  }`], [gl.FRAGMENT_SHADER, `#version 300 es
  precision mediump float; in vec3 vColor; out vec4 c; void main() { c = vec4(vColor, 1); }`]]) {
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);
gl.bindVertexArray(gl.createVertexArray());

gl.enable(gl.SCISSOR_TEST);
gl.scissor(0, 0, 480, 85);                          // keep only the bottom 85 rows
gl.clearColor(0.85, 0.82, 0.76, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);                      // clear() obeys the scissor box too
gl.viewport(0, 0, 120, 120);                        // left: square, undistorted
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.viewport(120, 0, 360, 120);                      // right: stretched 3:1
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>