| <!DOCTYPE html> |
| <head> |
| <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"> |
| <title>WebGL Triangle</title> |
| <style> |
| canvas { |
| width: 2px; |
| height: 2px; |
| } |
| </style> |
| </head> |
| <script id="vertexShaderSource" type="text/glsl"> |
| attribute vec4 position; |
| void main() { |
| gl_Position = position; |
| } |
| </script> |
| <script id="fragmentShaderSource" type="text/glsl"> |
| #ifdef GL_ES |
| precision mediump float; |
| #endif |
| |
| void main() { |
| gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); |
| } |
| </script> |
| <script> |
| |
| if (window.testRunner) { |
| window.testRunner.waitUntilDone(); |
| window.testRunner.dumpAsText(); |
| } |
| |
| function logResult(message) { |
| var output = document.getElementById("results"); |
| output.innerHTML += message + "<br>"; |
| } |
| |
| function drawTriangle(canvas) { |
| |
| canvas.width = 2; |
| canvas.height = 2; |
| |
| var gl = canvas.getContext("webgl", { antialias: true }); |
| |
| if (!gl) { |
| logResult("ERROR: Couldn't create WebGL context."); |
| return; |
| } |
| |
| if (!gl.getContextAttributes().antialias) { |
| logResult("ERROR: Antialiasing was not enabled at creation time."); |
| return; |
| } |
| |
| var vertexShader = gl.createShader(gl.VERTEX_SHADER); |
| |
| gl.shaderSource(vertexShader, document.getElementById("vertexShaderSource").textContent); |
| |
| gl.compileShader(vertexShader); |
| if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) { |
| logResult("ERROR: Vertex Shader failed to compile."); |
| logResult(gl.getShaderInfoLog(vertexShader)); |
| return; |
| } |
| |
| var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); |
| gl.shaderSource(fragmentShader, document.getElementById("fragmentShaderSource").textContent); |
| gl.compileShader(fragmentShader); |
| if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) { |
| logResult("ERROR: Fragment Shader failed to compile."); |
| logResult(gl.getShaderInfoLog(fragmentShader)); |
| return; |
| } |
| |
| var program = gl.createProgram(); |
| gl.attachShader(program, vertexShader); |
| gl.attachShader(program, fragmentShader); |
| gl.linkProgram(program); |
| |
| if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { |
| logResult("ERROR: Unable to link shaders into program."); |
| return; |
| } |
| |
| gl.useProgram(program); |
| var positionAttribute = gl.getAttribLocation(program, "position"); |
| gl.enableVertexAttribArray(positionAttribute); |
| |
| var vertices = new Float32Array([ |
| -1.0, -1.0, |
| 1.0, -1.0, |
| 1.0, 1.0 |
| ]); |
| var triangleBuffer = gl.createBuffer(); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); |
| |
| gl.clearColor(0, 0, 0, 1); |
| gl.clear(gl.COLOR_BUFFER_BIT); |
| gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer); |
| gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0); |
| |
| gl.drawArrays(gl.TRIANGLES, 0, 3); |
| } |
| |
| function pixelAt(cssX, cssY, buffer) { |
| // Remember that "top" in CSS coordinates is the last row in WebGL. i.e. things are upside down. |
| var col = cssX; |
| var row = 1 - cssY; |
| return { |
| r: buffer[(row * 2 + col) * 4 + 0], |
| g: buffer[(row * 2 + col) * 4 + 1], |
| b: buffer[(row * 2 + col) * 4 + 2], |
| a: buffer[(row * 2 + col) * 4 + 3], |
| }; |
| } |
| |
| function pixelToString(pixel) { |
| return "{r: " + pixel.r + ", g: " + pixel.g + ", b: " + pixel.b + ", a: " + pixel.a + "}"; |
| } |
| |
| function testOutput(canvas) { |
| |
| var gl = canvas.getContext("webgl"); |
| |
| if (!gl) { |
| logResult("FAIL: No WebGL context to examine."); |
| return; |
| } |
| |
| var imageBuffer = new Uint8Array(canvas.width * canvas.height * 4); |
| gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, imageBuffer); |
| |
| // Check that the top left pixel is fully black. |
| var topLeft = pixelAt(0, 0, imageBuffer); |
| if (topLeft.r != 0 || topLeft.g != 0 || topLeft.b != 0 || topLeft.a != 255) { |
| logResult("FAIL: top left corner was not fully black. " + pixelToString(topLeft)); |
| return; |
| } |
| logResult("PASS: top left corner was fully black."); |
| |
| // Check that the top right pixel isn't fully red. If it is, it implies we didn't multisample. |
| var topRight = pixelAt(1, 0, imageBuffer); |
| if (topRight.r == 255) { |
| logResult("FAIL: top right corner had a solid red channel. " + pixelToString(topRight)); |
| return; |
| } |
| logResult("PASS: top right corner did not have a completely solid red channel."); |
| } |
| |
| window.addEventListener("load", function () { |
| var canvas = document.querySelector("canvas"); |
| drawTriangle(canvas); |
| testOutput(canvas); |
| if (window.testRunner) |
| window.testRunner.notifyDone(); |
| }, false); |
| </script> |
| <body> |
| <p>To check anti-aliasing, we draw a triangle over half of a 2x2 quad, then check the pixel colors |
| in the corners. Note that if you're looking at the rendering results on a high-dpi display |
| you will see some artefacts as the canvas is scaled by the page zoom. Don't mistake that for |
| anti-aliasing (the test code doesn't).</p> |
| <canvas></canvas> |
| <p id="results"></p> |
| </body> |