These ten questions turn on WebGL behaviors that catch experienced developers. Each page loads webgl-helper.js from demos/ch03: save both beside it and run them through the check.mjs script of Test Yourself!, which is how the answers in Appendix H were produced in Chrome 154 1 , on the GPU and on SwiftShader 2,546 . Write down what each line prints, and why, before you look; the warnings Chrome logs are part of the answers.
Questions
<!-- 1. A canvas on the page is cleared to red and read twice: at once and 100 ms later.
What do the two reads return? (Section 3.19.1) -->
<!-- 2. Half-transparent red blended over an opaque blue clear with the usual blendFunc().
What is left in the pixel, alpha included? (Section 3.8.5) -->
<!-- 3. An R8UI texture holding 42, with the default filters, read with texelFetch().
What comes back? (Section 3.19.5) -->
<!-- 4. A uniform declared but never used. What is its location, and does setting it
raise an error? (Section 3.4.5) -->
<!-- 5. An RGBA32F texture on a framebuffer, checked before and after one call. Is the
framebuffer complete? (Section 3.21.4) -->
<script src="webgl-helper.js"></script>
<script type="module">
const context = (parent) => { const c = Object.assign(document.createElement('canvas'),
{ width: 4, height: 4 }); parent?.append(c); return c.getContext('webgl2'); };
const read = (gl) => { const p = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, p); return p.join(','); };
const g1 = context(document.body);
g1.clearColor(1, 0, 0, 1); g1.clear(g1.COLOR_BUFFER_BIT);
const now = read(g1);
setTimeout(() => console.log('1:', now, '|', read(g1)), 100);
const g2 = context();
g2.clearColor(0, 0, 1, 1); g2.clear(g2.COLOR_BUFFER_BIT);
g2.enable(g2.BLEND); g2.blendFunc(g2.SRC_ALPHA, g2.ONE_MINUS_SRC_ALPHA);
GLH.paint(g2.canvas, `#version 300 es
precision mediump float; out vec4 c; void main() { c = vec4(1, 0, 0, 0.5); }`);
console.log('2:', read(g2));
const g3 = context();
g3.bindTexture(g3.TEXTURE_2D, g3.createTexture()); // on unit 0
g3.texImage2D(g3.TEXTURE_2D, 0, g3.R8UI, 1, 1, 0, g3.RED_INTEGER, g3.UNSIGNED_BYTE,
new Uint8Array([42]));
g3.activeTexture(g3.TEXTURE1); // GLH.evaluate's target goes here
console.log('3:', GLH.evaluate(g3, 'result = vec4(texelFetch(t, ivec2(0), 0));',
'uniform highp usampler2D t;')[0]);
const g4 = context();
const p4 = GLH.program(g4, '#version 300 es\nvoid main() { gl_Position = vec4(0); }',
'#version 300 es\nprecision mediump float; uniform float uUnused; out vec4 c;\n'
+ 'void main() { c = vec4(1); }');
g4.useProgram(p4);
const where = g4.getUniformLocation(p4, 'uUnused');
g4.uniform1f(where, 0.5);
console.log('4:', where, g4.getError());
const g5 = context();
g5.bindTexture(g5.TEXTURE_2D, g5.createTexture());
g5.texStorage2D(g5.TEXTURE_2D, 1, g5.RGBA32F, 4, 4);
g5.bindFramebuffer(g5.FRAMEBUFFER, g5.createFramebuffer());
g5.framebufferTexture2D(g5.FRAMEBUFFER, g5.COLOR_ATTACHMENT0, g5.TEXTURE_2D,
g5.getParameter(g5.TEXTURE_BINDING_2D), 0);
const complete = () => g5.checkFramebufferStatus(g5.FRAMEBUFFER) === g5.FRAMEBUFFER_COMPLETE;
const before = complete();
g5.getExtension('EXT_color_buffer_float');
console.log('5:', before, complete());
</script><!-- 6. Red, then RASTERIZER_DISCARD, then a blue clear. What color is the pixel?
(Section 3.19.7) -->
<!-- 7. Seventeen WebGL 2 contexts on one page. Which of them are lost? (Section 3.19.2) -->
<!-- 8. A std140 block of a vec3, a float and a float[2]. What offsets and size does
WebGL report? (Section 3.19.8) -->
<!-- 9. An occlusion query read in the task that ended it. Is the answer available?
(Section 3.19.9) -->
<!-- 10. blitFramebuffer() from a texture framebuffer to the canvas, with the default
attributes and with antialias: false. What does getError() say? (Section 3.21.3) -->
<script src="webgl-helper.js"></script>
<script type="module">
const context = (options) => Object.assign(document.createElement('canvas'),
{ width: 4, height: 4 }).getContext('webgl2', options);
const many = Array.from({ length: 17 }, () => context());
console.log('7: lost', many.flatMap((gl, i) => (gl.isContextLost() ? [i + 1] : [])));
const g6 = context(), px = new Uint8Array(4);
g6.clearColor(1, 0, 0, 1); g6.clear(g6.COLOR_BUFFER_BIT);
g6.enable(g6.RASTERIZER_DISCARD); g6.clearColor(0, 0, 1, 1); g6.clear(g6.COLOR_BUFFER_BIT);
g6.readPixels(0, 0, 1, 1, g6.RGBA, g6.UNSIGNED_BYTE, px);
console.log('6:', px.join(','));
const g8 = context();
const p8 = GLH.program(g8, '#version 300 es\nvoid main() { gl_Position = vec4(0); }',
`#version 300 es
precision mediump float; out vec4 c;
layout(std140) uniform B { vec3 a; float b; float d[2]; };
void main() { c = vec4(a, b + d[1]); }`);
const names = ['a', 'b', 'd'], ix = g8.getUniformIndices(p8, names);
console.log('8:', g8.getActiveUniforms(p8, ix, g8.UNIFORM_OFFSET).join(' '), '| size',
g8.getActiveUniformBlockParameter(p8, 0, g8.UNIFORM_BLOCK_DATA_SIZE));
const g9 = context(), query = g9.createQuery();
g9.beginQuery(g9.ANY_SAMPLES_PASSED, query); g9.endQuery(g9.ANY_SAMPLES_PASSED);
console.log('9:', g9.getQueryParameter(query, g9.QUERY_RESULT_AVAILABLE));
for (const options of [{}, { antialias: false }]) {
const gl = context(options), fbo = GLH.framebuffer(gl, 4, 4);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo.fbo);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.blitFramebuffer(0, 0, 4, 4, 0, 0, 4, 4, gl.COLOR_BUFFER_BIT, gl.NEAREST);
console.log('10:', JSON.stringify(options), gl.getError());
}
</script>