// Copyright 2011 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. WebGLTestUtils = (function() { /** * Converts a WebGL enum to a string * @param {!WebGLContext} gl The WebGLContext to use. * @param {number} value The enum value. * @return {string} The enum as a string. */ var glEnumToString = function(gl, value) { for (var p in gl) { if (gl[p] == value) { return p; } } return '0x' + value.toString(16); }; var lastError = ''; /** * Returns the last compiler/linker error. * @return {string} The last compiler/linker error. */ var getLastError = function() { return lastError; }; // clang-format off /** * A vertex shader for a single texture. * @type {string} */ var simpleTextureVertexShader = [ 'attribute vec4 vPosition;', // 'attribute vec2 texCoord0;', 'varying vec2 texCoord;', 'void main() {', ' gl_Position = vPosition;', ' texCoord = texCoord0;', '}' ].join('\n'); /** * A fragment shader for a single texture. * @type {string} */ var simpleTextureFragmentShader = [ 'precision mediump float;', 'uniform sampler2D tex;', 'varying vec2 texCoord;', 'void main() {', ' gl_FragData[0] = texture2D(tex, texCoord);', '}' ].join('\n'); // clang-format on /** * Creates a simple texture vertex shader. * @param {!WebGLContext} gl The WebGLContext to use. * @return {!WebGLShader} */ var setupSimpleTextureVertexShader = function(gl) { return loadShader(gl, simpleTextureVertexShader, gl.VERTEX_SHADER); }; /** * Creates a simple texture fragment shader. * @param {!WebGLContext} gl The WebGLContext to use. * @return {!WebGLShader} */ var setupSimpleTextureFragmentShader = function(gl) { return loadShader(gl, simpleTextureFragmentShader, gl.FRAGMENT_SHADER); }; /** * Creates a program, attaches shaders, binds attrib locations, links the * program and calls useProgram. * @param {!Array.} shaders The shaders to attach . * @param {!Array.} opt_attribs The attribs names. * @param {!Array.} opt_locations The locations for the attribs. */ var setupProgram = function(gl, shaders, opt_attribs, opt_locations) { var realShaders = []; var program = gl.createProgram(); for (var ii = 0; ii < shaders.length; ++ii) { var shader = shaders[ii]; if (typeof shader == 'string') { var element = document.getElementById(shader); if (element) { shader = loadShaderFromScript(gl, shader); } else { shader = loadShader( gl, shader, ii ? gl.FRAGMENT_SHADER : gl.VERTEX_SHADER); } } gl.attachShader(program, shader); } if (opt_attribs) { for (var ii = 0; ii < opt_attribs.length; ++ii) { gl.bindAttribLocation( program, opt_locations ? opt_locations[ii] : ii, opt_attribs[ii]); } } gl.linkProgram(program); // Check the link status var linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { gl.deleteProgram(program); return null; } gl.useProgram(program); return program; }; /** * Creates a simple texture program. * @param {!WebGLContext} gl The WebGLContext to use. * @param {number} opt_positionLocation The attrib location for position. * @param {number} opt_texcoordLocation The attrib location for texture * coords. * @return {WebGLProgram} */ var setupSimpleTextureProgram = function( gl, opt_positionLocation, opt_texcoordLocation) { opt_positionLocation = opt_positionLocation || 0; opt_texcoordLocation = opt_texcoordLocation || 1; var vs = setupSimpleTextureVertexShader(gl); var fs = setupSimpleTextureFragmentShader(gl); if (!vs || !fs) { return null; } var program = setupProgram( gl, [vs, fs], ['vPosition', 'texCoord0'], [opt_positionLocation, opt_texcoordLocation]); if (!program) { gl.deleteShader(fs); gl.deleteShader(vs); } gl.useProgram(program); return program; }; /** * Creates buffers for a textured unit quad and attaches them to vertex * attribs. * @param {!WebGLContext} gl The WebGLContext to use. * @param {number} opt_positionLocation The attrib location for position. * @param {number} opt_texcoordLocation The attrib location for texture * coords. * @return {!Array.} The buffer objects that were * created. */ var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) { opt_positionLocation = opt_positionLocation || 0; opt_texcoordLocation = opt_texcoordLocation || 1; var objects = []; var vertexObject = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ 1.0, 1.0, 0.0, -1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, 1.0, 0.0, -1.0, -1.0, 0.0, 1.0, -1.0, 0.0 ]), gl.STATIC_DRAW); gl.enableVertexAttribArray(opt_positionLocation); gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0); objects.push(vertexObject); var vertexObject = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( [1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0]), gl.STATIC_DRAW); gl.enableVertexAttribArray(opt_texcoordLocation); gl.vertexAttribPointer(opt_texcoordLocation, 2, gl.FLOAT, false, 0, 0); objects.push(vertexObject); return objects; }; /** * Creates a program and buffers for rendering a textured quad. * @param {!WebGLContext} gl The WebGLContext to use. * @param {number} opt_positionLocation The attrib location for position. * @param {number} opt_texcoordLocation The attrib location for texture * coords. * @return {!WebGLProgram} */ var setupTexturedQuad = function( gl, opt_positionLocation, opt_texcoordLocation) { var program = setupSimpleTextureProgram( gl, opt_positionLocation, opt_texcoordLocation); setupUnitQuad(gl, opt_positionLocation, opt_texcoordLocation); return program; }; /** * Draws a previously setup quad. * @param {!WebGLContext} gl The WebGLContext to use. * @param {!Array.} opt_color The color to fill clear with before * drawing. A 4 element array where each element is in the range 0 to * 255. Default [255, 255, 255, 255] */ var drawQuad = function(gl, opt_color) { opt_color = opt_color || [255, 255, 255, 255]; gl.clearColor( opt_color[0] / 255, opt_color[1] / 255, opt_color[2] / 255, opt_color[3] / 255); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 6); }; /** * Links a WebGL program, throws if there are errors. * @param {!WebGLContext} gl The WebGLContext to use. * @param {!WebGLProgram} program The WebGLProgram to link. * @param {function(string): void) opt_errorCallback callback for errors. */ var linkProgram = function(gl, program, opt_errorCallback) { // Link the program gl.linkProgram(program); // Check the link status var linked = gl.getProgramParameter(program, gl.LINK_STATUS); if (!linked) { // something went wrong with the link gl.deleteProgram(program); return false; } return true; }; /** * Loads a shader. * @param {!WebGLContext} gl The WebGLContext to use. * @param {string} shaderSource The shader source. * @param {number} shaderType The type of shader. * @param {function(string): void) opt_errorCallback callback for errors. * @return {!WebGLShader} The created shader. */ var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) { var errFn = opt_errorCallback || (_ => {}); // Create the shader object var shader = gl.createShader(shaderType); if (shader == null) { errFn('*** Error: unable to create shader \'' + shaderSource + '\''); return null; } // Load the shader source gl.shaderSource(shader, shaderSource); var err = gl.getError(); if (err != gl.NO_ERROR) { errFn( '*** Error loading shader \'' + shader + '\':' + glEnumToString(gl, err)); return null; } // Compile the shader gl.compileShader(shader); // Check the compile status var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!compiled) { // Something went wrong during compilation; get the error lastError = gl.getShaderInfoLog(shader); errFn('*** Error compiling shader \'' + shader + '\':' + lastError); gl.deleteShader(shader); return null; } return shader; } /** * Loads shaders from source, creates a program, attaches the shaders and * links. * @param {!WebGLContext} gl The WebGLContext to use. * @param {string} vertexShader The vertex shader. * @param {string} fragmentShader The fragment shader. * @param {function(string): void) opt_errorCallback callback for errors. * @return {!WebGLProgram} The created program. */ var loadProgram = function( gl, vertexShader, fragmentShader, opt_errorCallback) { var program = gl.createProgram(); gl.attachShader( program, loadShader(gl, vertexShader, gl.VERTEX_SHADER, opt_errorCallback)); gl.attachShader( program, loadShader(gl, fragmentShader, gl.FRAGMENT_SHADER, opt_errorCallback)); return linkProgram(gl, program, opt_errorCallback) ? program : null; }; return { drawQuad: drawQuad, getLastError: getLastError, glEnumToString: glEnumToString, loadProgram: loadProgram, loadShader: loadShader, setupProgram: setupProgram, setupSimpleTextureFragmentShader: setupSimpleTextureFragmentShader, setupSimpleTextureProgram: setupSimpleTextureProgram, setupSimpleTextureVertexShader: setupSimpleTextureVertexShader, setupTexturedQuad: setupTexturedQuad, setupUnitQuad: setupUnitQuad, }; }());