LabWebGL&ThreeJS 2D Affine Transform
LabWebGL&ThreeJS 2D Affine Transform
MỤC LỤC
1. 2D Geometry Matrix Transform......................................................................1
1.1. File 2DAffineMatrixTransform.html.........................................................................1
1.2. File webgl-lessons-ui.js..............................................................................................6
1.3. File webgl-utils.js.....................................................................................................10
1.4. File webgl-tutorials.css............................................................................................30
1.5. Kết quả thực hiện.....................................................................................................33
1.6. Bài tập......................................................................................................................33
2. Tham khảo........................................................................................................33
>> Yêu cầu chụp hình ảnh là kết quả thực hành của SV. Không sử dụng lại hình ảnh của
bài lab.
void main() {
// Multiply the position by the matrix.
vec2 position = (u_matrix * vec3(a_position, 1)).xy;
void main() {
gl_FragColor = u_color;
}
</script>
<!--
for most samples webgl-utils only provides shader compiling/linking and
canvas resizing because why clutter the examples with code that's the same in every
sample.
See https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="./js/webgl-utils.js"></script>
<script src="./js/webgl-lessons-ui.js"></script>
<script>
"use strict";
function main() {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
var colorLocation = gl.getUniformLocation(program, "u_color");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
drawScene();
// Setup a ui.
webglLessonsUI.setupSlider("#x", {value: translation[0], slide:
updatePosition(0), max: gl.canvas.width });
webglLessonsUI.setupSlider("#y", {value: translation[1], slide:
updatePosition(1), max: gl.canvas.height});
webglLessonsUI.setupSlider("#angle", {slide: updateAngle, max: 360});
webglLessonsUI.setupSlider("#scaleX", {value: scale[0], slide: updateScale(0),
min: -5, max: 5, step: 0.01, precision: 2});
webglLessonsUI.setupSlider("#scaleY", {value: scale[1], slide: updateScale(1),
min: -5, max: 5, step: 0.01, precision: 2});
function updatePosition(index) {
return function(event, ui) {
translation[index] = ui.value;
drawScene();
function updateScale(index) {
return function(event, ui) {
scale[index] = ui.value;
drawScene();
};
}
// Starting Matrix.
var matrix = m3.identity();
var m3 = {
identity: function() {
return [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
},
rotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
];
},
multiply: function(a, b) {
var a00 = a[0 * 3 + 0];
var a01 = a[0 * 3 + 1];
var a02 = a[0 * 3 + 2];
var a10 = a[1 * 3 + 0];
var a11 = a[1 * 3 + 1];
var a12 = a[1 * 3 + 2];
var a20 = a[2 * 3 + 0];
var a21 = a[2 * 3 + 1];
var a22 = a[2 * 3 + 2];
var b00 = b[0 * 3 + 0];
var b01 = b[0 * 3 + 1];
var b02 = b[0 * 3 + 2];
var b10 = b[1 * 3 + 0];
var b11 = b[1 * 3 + 1];
var b12 = b[1 * 3 + 2];
var b20 = b[2 * 3 + 0];
var b21 = b[2 * 3 + 1];
var b22 = b[2 * 3 + 2];
return [
b00 * a00 + b01 * a10 + b02 * a20,
b00 * a01 + b01 * a11 + b02 * a21,
b00 * a02 + b01 * a12 + b02 * a22,
b10 * a00 + b11 * a10 + b12 * a20,
b10 * a01 + b11 * a11 + b12 * a21,
b10 * a02 + b11 * a12 + b12 * a22,
b20 * a00 + b21 * a10 + b22 * a20,
b20 * a01 + b21 * a11 + b22 * a21,
// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl) {
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
// left column
0, 0,
30, 0,
0, 150,
0, 150,
30, 0,
30, 150,
// top rung
30, 0,
100, 0,
30, 30,
30, 30,
100, 0,
100, 30,
// middle rung
30, 60,
67, 60,
30, 90,
30, 90,
67, 60,
67, 90,
]),
gl.STATIC_DRAW);
}
main();
</script>
min /= step;
max /= step;
value /= step;
parent.innerHTML = `
<div class="gman-widget-outer">
<div class="gman-widget-label">${name}</div>
<div class="gman-widget-value"></div>
<input class="gman-widget-slider" type="range" min="${min}" max="${max}"
value="${value}" />
</div>
`;
var valueElem = parent.querySelector(".gman-widget-value");
var sliderElem = parent.querySelector(".gman-widget-slider");
function updateValue(value) {
valueElem.textContent = (value * step * uiMult).toFixed(uiPrecision);
}
updateValue(value);
function handleChange(event) {
var value = parseInt(event.target.value);
updateValue(value);
fn(event, { value: value * step });
}
sliderElem.addEventListener('input', handleChange);
sliderElem.addEventListener('change', handleChange);
return {
elem: parent,
updateValue: (v) => {
v /= step;
sliderElem.value = v;
updateValue(v);
},
};
}
function makeSlider(options) {
const div = document.createElement("div");
return createSlider(div, options);
}
var widgetId = 0;
function getWidgetId() {
return "__widget_" + widgetId++;
}
function makeCheckbox(options) {
const div = document.createElement("div");
div.className = "gman-widget-outer";
const label = document.createElement("label");
const id = getWidgetId();
return {
elem: div,
updateValue: function(v) {
input.checked = !!v;
},
};
}
function makeOption(options) {
const div = document.createElement("div");
div.className = "gman-widget-outer";
const label = document.createElement("label");
const id = getWidgetId();
label.setAttribute('for', id);
label.textContent = gopt["ui-" + options.name] || options.name;
label.className = "gman-widget-label";
const selectElem = document.createElement("select");
options.options.forEach((name, ndx) => {
const opt = document.createElement("option");
opt.textContent = gopt["ui-" + name] || name;
opt.value = ndx;
opt.selected = ndx === options.value;
selectElem.appendChild(opt);
});
selectElem.className = "gman-widget-select";
div.appendChild(label);
div.appendChild(selectElem);
selectElem.addEventListener('change', function(e) {
options.change(e, {
value: selectElem.selectedIndex,
});
});
return {
elem: div,
updateValue: function(v) {
selectElem.selectedIndex = v;
},
};
}
function noop() {
}
const uiFuncs = {
slider: genSlider,
checkbox: genCheckbox,
option: genOption,
};
function getQueryParams() {
var params = {};
if (window.hackedParams) {
Object.keys(window.hackedParams).forEach(function(key) {
params[key] = window.hackedParams[key];
});
}
if (window.location.search) {
window.location.search.substring(1).split("&").forEach(function(pair) {
var keyValue = pair.split("=").map(function(kv) {
return decodeURIComponent(kv);
});
params[keyValue[0]] = keyValue[1];
});
}
return params;
}
return {
setupUI: setupUI,
updateUI: updateUI,
setupSlider: setupSlider,
makeSlider: makeSlider,
makeCheckbox: makeCheckbox,
}));
function isInIFrame(w) {
w = w || topWindow;
return w !== w.top;
}
if (!isInIFrame()) {
console.log("%c%s", 'color:blue;font-weight:bold;', 'for more about webgl-
utils.js see:'); // eslint-disable-line
console.log("%c%s", 'color:blue;font-weight:bold;',
'https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html'); //
eslint-disable-line
}
/**
* Wrapped logging function.
* @param {string} msg The message to log.
*/
function error(msg) {
if (topWindow.console) {
if (topWindow.console.error) {
topWindow.console.error(msg);
} else if (topWindow.console.log) {
topWindow.console.log(msg);
}
}
}
/**
* Error Callback
* @callback ErrorCallback
* @param {string} msg error message.
* @memberOf module:webgl-utils
*/
/**
* Loads a shader.
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
* @param {string} shaderSource The shader source.
* @param {number} shaderType The type of shader.
* @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for
errors.
* @return {WebGLShader} The created shader.
return shader;
}
/**
* Creates a program, attaches shaders, binds attrib locations, links the
* program and calls useProgram.
* @param {WebGLShader[]} shaders The shaders to attach
* @param {string[]} [opt_attribs] An array of attribs names. Locations will be
assigned by index if not passed in
* @param {number[]} [opt_locations] The locations for the. A parallel array to
opt_attribs letting you assign locations.
* @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for
errors. By default it just prints an error to the console
* on error. If you want something else pass an callback. It's passed an
error message.
* @memberOf module:webgl-utils
*/
function createProgram(
gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
const errFn = opt_errorCallback || error;
const program = gl.createProgram();
shaders.forEach(function(shader) {
gl.attachShader(program, shader);
});
if (opt_attribs) {
opt_attribs.forEach(function(attrib, ndx) {
gl.bindAttribLocation(
program,
opt_locations ? opt_locations[ndx] : ndx,
attrib);
});
}
gl.linkProgram(program);
gl.deleteProgram(program);
return null;
}
return program;
}
if (!opt_shaderType) {
if (shaderScript.type === 'x-shader/x-vertex') {
shaderType = gl.VERTEX_SHADER;
} else if (shaderScript.type === 'x-shader/x-fragment') {
shaderType = gl.FRAGMENT_SHADER;
} else if (shaderType !== gl.VERTEX_SHADER && shaderType !==
gl.FRAGMENT_SHADER) {
throw ('*** Error: unknown shader type');
}
}
return loadShader(
gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
opt_errorCallback);
}
const defaultShaderType = [
'VERTEX_SHADER',
'FRAGMENT_SHADER',
];
/**
* Creates a program from 2 script tags.
*
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
* to use.
* @param {string[]} shaderScriptIds Array of ids of the script
* tags for the shaders. The first is assumed to be the
* vertex shader, the second the fragment shader.
* @param {string[]} [opt_attribs] An array of attribs names. Locations will be
assigned by index if not passed in
* @param {number[]} [opt_locations] The locations for the. A parallel array to
opt_attribs letting you assign locations.
* @param {module:webgl-utils.ErrorCallback} opt_errorCallback callback for
errors. By default it just prints an error to the console
* on error. If you want something else pass an callback. It's passed an
error message.
* @return {WebGLProgram} The created program.
* @memberOf module:webgl-utils
*/
function createProgramFromScripts(
gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
const shaders = [];
for (let ii = 0; ii < shaderScriptIds.length; ++ii) {
shaders.push(createShaderFromScript(
gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], opt_errorCallback));
}
return createProgram(gl, shaders, opt_attribs, opt_locations,
opt_errorCallback);
}
/**
* Returns the corresponding bind point for a given sampler type
*/
function getBindPointForSamplerType(gl, type) {
if (type === gl.SAMPLER_2D) return gl.TEXTURE_2D; // eslint-disable-
line
if (type === gl.SAMPLER_CUBE) return gl.TEXTURE_CUBE_MAP; // eslint-disable-
line
return undefined;
}
/**
* @typedef {Object.<string, function>} Setters
*/
/**
* Creates setter functions for all uniforms of a shader
* program.
*
* @see {@link module:webgl-utils.setUniforms}
*
* @param {WebGLProgram} program the program to create setters for.
* @returns {Object.<string, function>} an object with a setter by name for each
uniform
* @memberOf module:webgl-utils
*/
function createUniformSetters(gl, program) {
let textureUnit = 0;
/**
* Creates a setter for a uniform of the given program with it's
* location embedded in the setter.
* @param {WebGLProgram} program
* @param {WebGLUniformInfo} uniformInfo
* @returns {function} the created setter.
*/
function createUniformSetter(program, uniformInfo) {
const location = gl.getUniformLocation(program, uniformInfo.name);
const type = uniformInfo.type;
const uniformSetters = { };
const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
/**
* Set uniforms and binds related textures.
*
* Example:
*
* let programInfo = createProgramInfo(
/**
* Creates setter functions for all attributes of a shader
* program. You can pass this to {@link module:webgl-
utils.setBuffersAndAttributes} to set all your buffers and attributes.
*
* @see {@link module:webgl-utils.setAttributes} for example
* @param {WebGLProgram} program the program to create setters for.
* @return {Object.<string, function>} an object with a setter for each attribute
by name.
* @memberOf module:webgl-utils
*/
function createAttributeSetters(gl, program) {
const attribSetters = {
};
function createAttribSetter(index) {
return function(b) {
if (b.value) {
gl.disableVertexAttribArray(index);
switch (b.value.length) {
case 4:
gl.vertexAttrib4fv(index, b.value);
break;
case 3:
gl.vertexAttrib3fv(index, b.value);
break;
case 2:
gl.vertexAttrib2fv(index, b.value);
break;
case 1:
gl.vertexAttrib1fv(index, b.value);
break;
default:
throw new Error('the length of a float constant value must be
between 1 and 4!');
}
} else {
gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
gl.enableVertexAttribArray(index);
gl.vertexAttribPointer(
index, b.numComponents || b.size, b.type || gl.FLOAT, b.normalize
|| false, b.stride || 0, b.offset || 0);
}
};
}
return attribSetters;
}
/**
* Sets attributes and binds buffers (deprecated... use {@link module:webgl-
utils.setBuffersAndAttributes})
*
* Example:
*
* let program = createProgramFromScripts(
* gl, ["some-vs", "some-fs"]);
*
* let attribSetters = createAttributeSetters(program);
*
* let positionBuffer = gl.createBuffer();
* let texcoordBuffer = gl.createBuffer();
*
* let attribs = {
* a_position: {buffer: positionBuffer, numComponents: 3},
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
* };
*
* gl.useProgram(program);
*
* This will automatically bind the buffers AND set the
* attributes.
*
* setAttributes(attribSetters, attribs);
*
* Properties of attribs. For each attrib you can add
* properties:
*
* * type: the type of data in the buffer. Default = gl.FLOAT
* * normalize: whether or not to normalize the data. Default = false
* * stride: the stride. Default = 0
* * offset: offset into the buffer. Default = 0
*
* For example if you had 3 value float positions, 2 value
* float texcoord and 4 value uint8 colors you'd setup your
* attribs like this
*
* let attribs = {
* a_position: {buffer: positionBuffer, numComponents: 3},
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
* a_color: {
* buffer: colorBuffer,
* numComponents: 4,
* type: gl.UNSIGNED_BYTE,
* normalize: true,
* },
* };
*
* @param {Object.<string, function>|model:webgl-utils.ProgramInfo} setters
Attribute setters as returned from createAttributeSetters or a ProgramInfo as
returned {@link module:webgl-utils.createProgramInfo}
* @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos
mapped by attribute name.
* @memberOf module:webgl-utils
* @deprecated use {@link module:webgl-utils.setBuffersAndAttributes}
*/
function setAttributes(setters, attribs) {
setters = setters.attribSetters || setters;
Object.keys(attribs).forEach(function(name) {
const setter = setters[name];
if (setter) {
setter(attribs[name]);
/**
* Creates a vertex array object and then sets the attributes
* on it
*
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
* to use.
* @param {Object.<string, function>} setters Attribute setters as returned from
createAttributeSetters
* @param {Object.<string, module:webgl-utils.AttribInfo>} attribs AttribInfos
mapped by attribute name.
* @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
*/
function createVAOAndSetAttributes(gl, setters, attribs, indices) {
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
setAttributes(setters, attribs);
if (indices) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);
}
// We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER
// like when creating buffers for other stuff will mess up this VAO's binding
gl.bindVertexArray(null);
return vao;
}
/**
* Creates a vertex array object and then sets the attributes
* on it
*
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
* to use.
* @param {Object.<string, function>| module:webgl-utils.ProgramInfo} programInfo
as returned from createProgramInfo or Attribute setters as returned from
createAttributeSetters
* @param {module:webgl-utils:BufferInfo} bufferInfo BufferInfo as returned from
createBufferInfoFromArrays etc...
* @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
*/
function createVAOFromBufferInfo(gl, programInfo, bufferInfo) {
return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo,
bufferInfo.attribs, bufferInfo.indices);
}
/**
* @typedef {Object} ProgramInfo
* @property {WebGLProgram} program A shader program
* @property {Object<string, function>} uniformSetters: object of setters as
returned from createUniformSetters,
* @property {Object<string, function>} attribSetters: object of setters as
returned from createAttribSetters,
* @memberOf module:webgl-utils
*/
/**
* Creates a ProgramInfo from 2 sources.
*
* A ProgramInfo contains
*
* programInfo = {
* program: WebGLProgram,
* uniformSetters: object of setters as returned from
createUniformSetters,
* attribSetters: object of setters as returned from createAttribSetters,
* }
*
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
/**
* Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if
appropriate
*
* Example:
*
* let programInfo = createProgramInfo(
* gl, ["some-vs", "some-fs"]);
*
* let arrays = {
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10,
10, 0], },
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],
},
* };
*
* let bufferInfo = createBufferInfoFromArrays(gl, arrays);
*
* gl.useProgram(programInfo.program);
*
* This will automatically bind the buffers AND set the
* attributes.
*
* setBuffersAndAttributes(programInfo.attribSetters, bufferInfo);
*
* For the example above it is equivilent to
*
* gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
* gl.enableVertexAttribArray(a_positionLocation);
* gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0);
* gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
* gl.enableVertexAttribArray(a_texcoordLocation);
* gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0);
*
* @param {WebGLRenderingContext} gl A WebGLRenderingContext.
/**
* Given an extension name like WEBGL_compressed_texture_s3tc
* returns the supported version extension, like
* WEBKIT_WEBGL_compressed_teture_s3tc
* @param {string} name Name of extension to look for
* @return {WebGLExtension} The extension or undefined if not
* found.
* @memberOf module:webgl-utils
*/
function getExtensionWithKnownPrefixes(gl, name) {
for (let ii = 0; ii < browserPrefixes.length; ++ii) {
const prefixedName = browserPrefixes[ii] + name;
const ext = gl.getExtension(prefixedName);
if (ext) {
return ext;
}
}
return undefined;
}
/**
* Resize a canvas to match the size its displayed.
* @param {HTMLCanvasElement} canvas The canvas to resize.
* @param {number} [multiplier] amount to multiply by.
* Pass in window.devicePixelRatio for native pixels.
* @return {boolean} true if the canvas was resized.
* @memberOf module:webgl-utils
*/
function resizeCanvasToDisplaySize(canvas, multiplier) {
multiplier = multiplier || 1;
const width = canvas.clientWidth * multiplier | 0;
const height = canvas.clientHeight * multiplier | 0;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
return true;
}
return false;
}
/**
* creates a typed array with a `push` function attached
* so that you can easily *push* values.
*
* `push` can take multiple arguments. If an argument is an array each element
* of the array will be added to the typed array.
*
* Example:
*
* let array = createAugmentedTypedArray(3, 2); // creates a Float32Array
with 6 values
* array.push(1, 2, 3);
* array.push([4, 5, 6]);
* // array now contains [1, 2, 3, 4, 5, 6]
*
* Also has `numComponents` and `numElements` properties.
*
* @param {number} numComponents number of components
* @param {number} numElements number of elements. The total size of the array
will be `numComponents * numElements`.
* @param {constructor} opt_type A constructor for the type. Default =
`Float32Array`.
* @return {ArrayBuffer} A typed array.
* @memberOf module:webgl-utils
*/
function createAugmentedTypedArray(numComponents, numElements, opt_type) {
const Type = opt_type || Float32Array;
return augmentTypedArray(new Type(numComponents * numElements), numComponents);
}
function allButIndices(name) {
return name !== 'indices';
}
function createMapping(obj) {
const mapping = {};
Object.keys(obj).filter(allButIndices).forEach(function(key) {
mapping['a_' + key] = key;
});
return mapping;
function isArrayBuffer(a) {
return a.buffer && a.buffer instanceof ArrayBuffer;
}
return numComponents;
}
if (Array.isArray(array)) {
array = {
data: array,
};
}
if (!array.numComponents) {
array.numComponents = guessNumComponentsFromName(name, array.length);
}
/**
* @typedef {Object} AttribInfo
* @property {number} [numComponents] the number of components for this
attribute.
* @property {number} [size] the number of components for this attribute.
* @property {number} [type] the type of the attribute (eg. `gl.FLOAT`,
`gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT`
* @property {boolean} [normalized] whether or not to normalize the data. Default
= false
* @property {number} [offset] offset into buffer in bytes. Default = 0
* @property {number} [stride] the stride in bytes per element. Default = 0
* @property {WebGLBuffer} buffer the buffer that contains the data for this
attribute
* @memberOf module:webgl-utils
*/
/**
* Creates a set of attribute data and WebGLBuffers from set of arrays
*
* Given
*
* let arrays = {
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10,
10, 0], },
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],
},
* normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1], },
* color: { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0,
255, 0, 0, 255, 255], type: Uint8Array, },
* indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3],
},
* };
*
* returns something like
*
* let attribs = {
* a_position: { numComponents: 3, type: gl.FLOAT, normalize:
false, buffer: WebGLBuffer, },
* a_texcoord: { numComponents: 2, type: gl.FLOAT, normalize:
false, buffer: WebGLBuffer, },
* a_normal: { numComponents: 3, type: gl.FLOAT, normalize:
false, buffer: WebGLBuffer, },
* a_color: { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize:
true, buffer: WebGLBuffer, },
* };
*
* @param {WebGLRenderingContext} gl The webgl rendering context.
* @param {Object.<string, array|typedarray>} arrays The arrays
* @param {Object.<string, string>} [opt_mapping] mapping from attribute name to
array name.
* if not specified defaults to "a_name" -> "name".
* @return {Object.<string, module:webgl-utils.AttribInfo>} the attribs
* @memberOf module:webgl-utils
*/
function createAttribsFromArrays(gl, arrays, opt_mapping) {
const mapping = opt_mapping || createMapping(arrays);
function getArray(array) {
return array.length ? array : array.data;
}
return numComponents;
}
/**
* tries to get the number of elements from a set of arrays.
*/
const positionKeys = ['position', 'positions', 'a_position'];
function getNumElementsFromNonIndexedArrays(arrays) {
let key;
for (const k of positionKeys) {
if (k in arrays) {
key = k;
break;
}
}
key = key || Object.keys(arrays)[0];
const array = arrays[key];
const length = getArray(array).length;
const numComponents = getNumComponents(array, key);
const numElements = length / numComponents;
/**
* @typedef {Object} BufferInfo
* @property {number} numElements The number of elements to pass to
`gl.drawArrays` or `gl.drawElements`.
* @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any
indices exist.
* @property {Object.<string, module:webgl-utils.AttribInfo>} attribs The attribs
approriate to call `setAttributes`
* @memberOf module:webgl-utils
*/
/**
* Creates a BufferInfo from an object of arrays.
*
* This can be passed to {@link module:webgl-utils.setBuffersAndAttributes} and
to
* {@link module:webgl-utils:drawBufferInfo}.
*
* Given an object like
*
* let arrays = {
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10,
10, 0], },
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],
},
* normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,
1], },
* indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3],
},
* };
*
* Creates an BufferInfo like this
*
* bufferInfo = {
* numElements: 4, // or whatever the number of elements is
* indices: WebGLBuffer, // this property will not exist if there are no
indices
* attribs: {
* a_position: { buffer: WebGLBuffer, numComponents: 3, },
* a_normal: { buffer: WebGLBuffer, numComponents: 3, },
* a_texcoord: { buffer: WebGLBuffer, numComponents: 2, },
* },
* };
*
* The properties of arrays can be JavaScript arrays in which case the number of
components
* will be guessed.
*
* let arrays = {
* position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0],
* texcoord: [0, 0, 0, 1, 1, 0, 1, 1],
* normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
* indices: [0, 1, 2, 1, 2, 3],
* };
*
* They can also by TypedArrays
*
* let arrays = {
* position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
* texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
* normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
* indices: new Uint16Array([0, 1, 2, 1, 2, 3]),
return bufferInfo;
}
/**
* Creates buffers from typed arrays
*
* Given something like this
*
* let arrays = {
* positions: [1, 2, 3],
* normals: [0, 0, 1],
* }
*
* returns something like
*
* buffers = {
* positions: WebGLBuffer,
* normals: WebGLBuffer,
* }
*
* If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER.
*
* @param {WebGLRenderingContext} gl A WebGLRenderingContext.
* @param {Object<string, array|typedarray>} arrays
* @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer
per array
* @memberOf module:webgl-utils
*/
function createBuffersFromArrays(gl, arrays) {
const buffers = { };
Object.keys(arrays).forEach(function(key) {
const type = key === 'indices' ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
const array = makeTypedArray(arrays[key], name);
buffers[key] = createBufferFromTypedArray(gl, array, type);
});
// hrm
if (arrays.indices) {
buffers.numElements = arrays.indices.length;
} else if (arrays.position) {
buffers.numElements = arrays.position.length / 3;
}
return buffers;
}
/**
/**
* @typedef {Object} DrawObject
* @property {module:webgl-utils.ProgramInfo} programInfo A ProgramInfo as
returned from createProgramInfo
* @property {module:webgl-utils.BufferInfo} bufferInfo A BufferInfo as returned
from createBufferInfoFromArrays
* @property {Object<string, ?>} uniforms The values for the uniforms
* @memberOf module:webgl-utils
*/
/**
* Draws a list of objects
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
* @param {DrawObject[]} objectsToDraw an array of objects to draw.
* @memberOf module:webgl-utils
*/
function drawObjectList(gl, objectsToDraw) {
let lastUsedProgramInfo = null;
let lastUsedBufferInfo = null;
objectsToDraw.forEach(function(object) {
const programInfo = object.programInfo;
const bufferInfo = object.bufferInfo;
let bindBuffers = false;
// Draw
drawBufferInfo(gl, bufferInfo);
});
function glEnumToString(gl, v) {
const results = [];
for (const key in gl) {
if (gl[key] === v) {
results.push(key);
}
}
return results.length
? results.join(' | ')
: `0x${v.toString(16)}`;
}
return {
createAugmentedTypedArray: createAugmentedTypedArray,
createAttribsFromArrays: createAttribsFromArrays,
createBuffersFromArrays: createBuffersFromArrays,
createBufferInfoFromArrays: createBufferInfoFromArrays,
createAttributeSetters: createAttributeSetters,
createProgram: createProgram,
createProgramFromScripts: createProgramFromScripts,
createProgramFromSources: createProgramFromSources,
createProgramInfo: createProgramInfo,
createUniformSetters: createUniformSetters,
createVAOAndSetAttributes: createVAOAndSetAttributes,
createVAOFromBufferInfo: createVAOFromBufferInfo,
drawBufferInfo: drawBufferInfo,
drawObjectList: drawObjectList,
glEnumToString: glEnumToString,
getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
resizeCanvasToDisplaySize: resizeCanvasToDisplaySize,
setAttributes: setAttributes,
setBuffersAndAttributes: setBuffersAndAttributes,
setUniforms: setUniforms,
};
}));
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
user-select: none;
body {
background-color: #aaa;
font-family: Sans-Serif;
}
canvas {
background-color: #fff;
border: 1px solid black;
/* NOTE: This size is changed if in iframe - see below '.iframe canvas' */
width: 400px;
height: 300px;
display: block;
}
#uiContainer {
position: absolute;
top: 10px;
right: 10px;
z-index: 3;
font-family: monospace;
pointer-events: none;
text-shadow:
-1px -1px 0 #FFF,
1px -1px 0 #FFF,
-1px 1px 0 #FFF,
1px 1px 0 #FFF;
}
#ui {
opacity: 0.8;
}
#ui>div {
pointer-events: none;
}
#ui input,
#ui label,
#ui select,
#ui option,
#ui canvas {
pointer-events: auto;
}
.gman-slider-upper {
height: 1.5em;
}
.gman-slider-outer, .gman-widget-outer {
float: right;
display: flex;
align-items: center;
height: 1.7em;
}
.gman-widget-slider, .gman-widget-checkbox, .gman-widget-select {
opacity: 0.5;
html.iframe {
height: 100vh;
}
body.iframe {
width: 100vw;
height: 100vh;
margin: 0;
}
.iframe>.description {
display: none;
}
.iframe .divcanvas {
width: 100vw;
height: 100vh;
}
.iframe canvas {
width: 100vw;
height: 100vh;
max-width: 100vw;
border: none;
}
.iframe>#example {
width: 100%;
height: 100%;
}
#ui #rotation>canvas {
background-color: rgba(255, 255, 255, 0.6);
}
#ui {
width: 200px;
}
0, 150,
30, 150,
30, 0,
// Right side
30, 0,
60, 150,
60, 0,
30, 0,
60, 150,
90, 0,
// Top bar
15, 75,
45, 75,
15, 90,
15, 90,
45, 75,
45, 90,
]),
Translate:
Symmetric: