How to rotate individual shapes in WebGL - javascript

I have been scratching my head over the past week trying to understand rotating shapes in WebGL. I am drawing 3 shapes that gets called from their own functions. The basic structure of the functions is kinda like this:
function drawShape(vertices) {
var a = vertices.length / 2;
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, a);
}
Now I have render where each shape function gets called. Kinda like this:
function render() {
angleInRadians += 0.1;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
drawShape1();
drawShape2();
matrix = mat.rotation(angleInRadians);
gl.uniformMatrix3fv(matrixLocation, false, matrix);
requestAnimFrame( render );
}
The rotation function being:
rotation: function(angle) {
var a = Math.cos(angle);
var b = Math.sin(angle);
return [
a,-b, 0,
b, a, 0,
0, 0, 1,
];
},
I have been trying to get only 1 shape to rotate out of the 3. I tried using the uniform3fv before the drawArrays in the function which draws the shape that I want to be rotated but all the shapes rotate with it. How do I get only one shape to rotate?

First off, It's generally more common to upload vertex data at init time and use it at render time. The code you posted is uploading vertex data at render time. It would also be more common to look up attrib locations at init time instead of render time.
In other words you have this
function drawShape(vertices) {
var a = vertices.length / 2;
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, a);
}
But it would be more common to do something like this
const attribs = {
vPosition: gl.getAttribLocation(program, "vPosition"),
};
const shape = createShape(vertices);
...
drawShape(attribs, shape);
function createShape(vertices) {
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
return {
bufferId,
numVertices: vertices.length / 2,
};
}
function drawShape(attribs, shape) {
gl.bindBuffer(gl.ARRAY_BUFFER, shape.bufferId);
gl.vertexAttribPointer(attribs.vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(attrib.vPosition);
gl.drawArrays(gl.TRIANGLE_FAN, 0, shape.numVertices);
}
or something along those lines.
You'd likely also look up uniforms at init time and pass in the uniforms and set them inside drawShape, or not. Mostly the point is calls to gl.bufferData, gl.getUniformLocation and gl.getAttribLocation generally happen at init time
Next up, you need to set the uniforms once per shape.
matrix = mat.rotation(angleInRadiansForShape1);
gl.uniformMatrix3fv(matrixLocation, false, matrix);
drawShape1();
matrix = mat.rotation(angleInRadiansForShape2);
gl.uniformMatrix3fv(matrixLocation, false, matrix);
drawShape2();
But you need to add in more than just rotation otherwise all the shapes will appear at the same place.
Rotation matrices rotate around the origin (0,0). If you want to rotate around some other point you to translate your vertices. you can do this by multiplying your rotation matrix with translation matrices.
It's hard to show how to do it because it requires a math library and the way to use a math library is different for each library.
This article describes how to multiply matrices using a functions created in the article.
To move the rotation point you'd do this.
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, whereToDrawX, whereToDrawY);
matrix = m3.rotate(matrix, angleToRotate);
matrix = m3.translate(matrix, offsetForRotationX, offsetForRotationY);
I generally read that bottom to top so it's
m3.translate(matrix, offsetForRotationX, offsetForRotationY) = translate the vertices so their origin is where we want to rotate. For example if we have a box going from 0 to 10 across and 0 to 20 down and we want to rotate at bottom right corner then we need to move the bottom right corner to 0,0 which means we need translate -10, -20 so that bottom right corner is at the origin.
m3.rotate(matrix, angleToRotate) = do the rotation
m3.translate(matrix, whereToDrawX, whereToDrawY) = translate it where we actually want it to be drawn.
m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight) = convert from pixels into clip space
Example:
"use strict";
function main() {
// Get A WebGL context
/** #type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
// lookup uniforms
var colorLocation = gl.getUniformLocation(program, "u_color");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
// Create a buffer to put positions in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Put geometry data into buffer
setGeometry(gl);
const shapes = [
{
translation: [50, 75],
scale: [0.5, 0.5],
rotationOffset: [0, 0], // top left corner of F
angleInRadians: 0,
color: [1, 0, 0, 1], // red
},
{
translation: [100, 75],
scale: [0.5, 0.5],
rotationOffset: [-50, -75], // center of F
angleInRadians: 0,
color: [0, 1, 0, 1], // green
},
{
translation: [150, 75],
scale: [0.5, 0.5],
rotationOffset: [0, -150], // bottom left corner of F
angleInRadians: 0,
color: [0, 0, 1, 1], // blue
},
{
translation: [200, 75],
scale: [0.5, 0.5],
rotationOffset: [-100, 0], // top right corner of F
angleInRadians: 0,
color: [1, 0, 1, 1], // magenta
},
];
requestAnimationFrame(drawScene);
// Draw the scene.
function drawScene(time) {
time *= 0.001; // seconds
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas.
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// draw a single black line to make the pivot clearer
gl.enable(gl.SCISSOR_TEST);
gl.scissor(0, 75, 300, 1);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
for (const shape of shapes) {
shape.angleInRadians = time;
// set the color
gl.uniform4fv(colorLocation, shape.color);
// Compute the matrices
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, shape.translation[0], shape.translation[1]);
matrix = m3.scale(matrix, shape.scale[0], shape.scale[1]);
matrix = m3.rotate(matrix, shape.angleInRadians);
matrix = m3.translate(matrix, shape.rotationOffset[0], shape.rotationOffset[1]);
// Set the matrix.
gl.uniformMatrix3fv(matrixLocation, false, matrix);
// Draw the geometry.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 18; // 6 triangles in the 'F', 3 points per triangle
gl.drawArrays(primitiveType, offset, count);
}
requestAnimationFrame(drawScene);
}
}
var m3 = {
projection: function(width, height) {
// Note: This matrix flips the Y axis so that 0 is at the top.
return [
2 / width, 0, 0,
0, -2 / height, 0,
-1, 1, 1
];
},
identity: function() {
return [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
},
translation: function(tx, ty) {
return [
1, 0, 0,
0, 1, 0,
tx, ty, 1,
];
},
rotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
];
},
scaling: function(sx, sy) {
return [
sx, 0, 0,
0, sy, 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,
b20 * a02 + b21 * a12 + b22 * a22,
];
},
translate: function(m, tx, ty) {
return m3.multiply(m, m3.translation(tx, ty));
},
rotate: function(m, angleInRadians) {
return m3.multiply(m, m3.rotation(angleInRadians));
},
scale: function(m, sx, sy) {
return m3.multiply(m, m3.scaling(sx, sy));
},
};
// 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();
<canvas id="canvas"></canvas>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform mat3 u_matrix;
void main() {
// Multiply the position by the matrix.
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
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 http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and http://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
If you didn't want to move the center of rotation just remove the last step related to rotationOffset
"use strict";
function main() {
// Get A WebGL context
/** #type {HTMLCanvasElement} */
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
// lookup uniforms
var colorLocation = gl.getUniformLocation(program, "u_color");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
// Create a buffer to put positions in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Put geometry data into buffer
setGeometry(gl);
const shapes = [
{
translation: [50, 75],
scale: [0.5, 0.5],
rotationOffset: [0, 0], // top left corner of F
angleInRadians: 0,
color: [1, 0, 0, 1], // red
},
{
translation: [100, 75],
scale: [0.5, 0.5],
rotationOffset: [-50, -75], // center of F
angleInRadians: 0,
color: [0, 1, 0, 1], // green
},
{
translation: [150, 75],
scale: [0.5, 0.5],
rotationOffset: [0, -150], // bottom left corner of F
angleInRadians: 0,
color: [0, 0, 1, 1], // blue
},
{
translation: [200, 75],
scale: [0.5, 0.5],
rotationOffset: [-100, 0], // top right corner of F
angleInRadians: 0,
color: [1, 0, 1, 1], // magenta
},
];
requestAnimationFrame(drawScene);
// Draw the scene.
function drawScene(time) {
time *= 0.001; // seconds
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas.
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
// draw a single black line to make the pivot clearer
gl.enable(gl.SCISSOR_TEST);
gl.scissor(0, 75, 300, 1);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
for (const shape of shapes) {
shape.angleInRadians = time;
// set the color
gl.uniform4fv(colorLocation, shape.color);
// Compute the matrices
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, shape.translation[0], shape.translation[1]);
matrix = m3.scale(matrix, shape.scale[0], shape.scale[1]);
matrix = m3.rotate(matrix, shape.angleInRadians);
//matrix = m3.translate(matrix, shape.rotationOffset[0], shape.rotationOffset[1]);
// Set the matrix.
gl.uniformMatrix3fv(matrixLocation, false, matrix);
// Draw the geometry.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 18; // 6 triangles in the 'F', 3 points per triangle
gl.drawArrays(primitiveType, offset, count);
}
requestAnimationFrame(drawScene);
}
}
var m3 = {
projection: function(width, height) {
// Note: This matrix flips the Y axis so that 0 is at the top.
return [
2 / width, 0, 0,
0, -2 / height, 0,
-1, 1, 1
];
},
identity: function() {
return [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
},
translation: function(tx, ty) {
return [
1, 0, 0,
0, 1, 0,
tx, ty, 1,
];
},
rotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
];
},
scaling: function(sx, sy) {
return [
sx, 0, 0,
0, sy, 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,
b20 * a02 + b21 * a12 + b22 * a22,
];
},
translate: function(m, tx, ty) {
return m3.multiply(m, m3.translation(tx, ty));
},
rotate: function(m, angleInRadians) {
return m3.multiply(m, m3.rotation(angleInRadians));
},
scale: function(m, sx, sy) {
return m3.multiply(m, m3.scaling(sx, sy));
},
};
// 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();
<canvas id="canvas"></canvas>
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform mat3 u_matrix;
void main() {
// Multiply the position by the matrix.
gl_Position = vec4((u_matrix * vec3(a_position, 1)).xy, 0, 1);
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
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 http://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and http://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
you might also find this article useful

You have to set a individual rotation matrix (model matrix) for each shape. Define a separate angle per shape:
e.g
var angleShape1 = 0.0;
var angleShape2 = 0.0;
Calculate the rotation matrix far each shape and set the uniform variable, before drawing a shape:
function render() {
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
matrix1 = mat.rotation(angleShape1);
gl.uniformMatrix3fv(matrixLocation, false, matrix1);
drawShape1();
matrix2 = mat.rotation(angleShape2);
gl.uniformMatrix3fv(matrixLocation, false, matrix2);
drawShape2();
angleShape1 += 0.1;
angleShape2 += 0.2;
requestAnimFrame( render );
}
This causes that the shapes are rendered with different model transformations and individual orientations.

Related

WebGL: Loading same data into two attributes

I was going through this website when I came across the following code in the snippet.
I got lost in the code where positionBuffer was created, and gl.ARRAY_BUFFER was bound to it. It seems that no data was loaded onto the newly created buffer, but instead, the buffer was bound to the attribute a_postion. Later on, a new buffer texCoordBuffer was created and some data was written onto it. In the results, it seems like this same data had been loaded by webGL into the a_position attribute as well. My doubt is, how did this happen? When a new buffer was created and the buffer pointer gl.ARRAY_BUFFER was pointed to the new array, how did the positionBuffer get the same data?
// WebGL2 - 2D image 3x3 convolution
// from https://webgl2fundamentals.org/webgl/webgl-2d-image-3x3-convolution.html
"use strict";
var vertexShaderSource = `#version 300 es
// an attribute is an input (in) to a vertex shader.
// It will receive data from a buffer
in vec2 a_position;
in vec2 a_texCoord;
// Used to pass in the resolution of the canvas
uniform vec2 u_resolution;
// Used to pass the texture coordinates to the fragment shader
out vec2 v_texCoord;
// all shaders have a main function
void main() {
// convert the position from pixels to 0.0 to 1.0
vec2 zeroToOne = a_position / u_resolution;
// convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
// convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
// pass the texCoord to the fragment shader
// The GPU will interpolate this value between points.
v_texCoord = a_texCoord;
}
`;
var fragmentShaderSource = `#version 300 es
// fragment shaders don't have a default precision so we need
// to pick one. highp is a good default. It means "high precision"
precision highp float;
// our texture
uniform sampler2D u_image;
// the convolution kernal data
uniform float u_kernel[9];
uniform float u_kernelWeight;
// the texCoords passed in from the vertex shader.
in vec2 v_texCoord;
// we need to declare an output for the fragment shader
out vec4 outColor;
void main() {
vec2 onePixel = vec2(1) / vec2(textureSize(u_image, 0));
vec4 colorSum =
texture(u_image, v_texCoord + onePixel * vec2(-1, -1)) * u_kernel[0] +
texture(u_image, v_texCoord + onePixel * vec2( 0, -1)) * u_kernel[1] +
texture(u_image, v_texCoord + onePixel * vec2( 1, -1)) * u_kernel[2] +
texture(u_image, v_texCoord + onePixel * vec2(-1, 0)) * u_kernel[3] +
texture(u_image, v_texCoord + onePixel * vec2( 0, 0)) * u_kernel[4] +
texture(u_image, v_texCoord + onePixel * vec2( 1, 0)) * u_kernel[5] +
texture(u_image, v_texCoord + onePixel * vec2(-1, 1)) * u_kernel[6] +
texture(u_image, v_texCoord + onePixel * vec2( 0, 1)) * u_kernel[7] +
texture(u_image, v_texCoord + onePixel * vec2( 1, 1)) * u_kernel[8] ;
outColor = vec4((colorSum / u_kernelWeight).rgb, 1);
}
`;
var image = new Image();
image.src = "https://webgl2fundamentals.org/webgl/resources/leaves.jpg"; // MUST BE SAME DOMAIN!!!
image.onload = function() {
render(image);
};
function render(image) {
// Get A WebGL context
/** #type {HTMLCanvasElement} */
var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromSources(gl,
[vertexShaderSource, fragmentShaderSource]);
// look up where the vertex data needs to go.
var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
var texCoordAttributeLocation = gl.getAttribLocation(program, "a_texCoord");
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
var imageLocation = gl.getUniformLocation(program, "u_image");
var kernelLocation = gl.getUniformLocation(program, "u_kernel[0]");
var kernelWeightLocation = gl.getUniformLocation(program, "u_kernelWeight");
// Create a vertex array object (attribute state)
var vao = gl.createVertexArray();
// and make it the one we're currently working with
gl.bindVertexArray(vao);
// Create a buffer and put a single pixel space rectangle in
// it (2 triangles)
var positionBuffer = gl.createBuffer();
// Turn on the attribute
gl.enableVertexAttribArray(positionAttributeLocation);
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionAttributeLocation, size, type, normalize, stride, offset);
// provide texture coordinates for the rectangle.
var texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0.0, 0.0,
1.0, 0.0,
0.0, 1.0,
0.0, 1.0,
1.0, 0.0,
1.0, 1.0,
]), gl.STATIC_DRAW);
// Turn on the attribute
gl.enableVertexAttribArray(texCoordAttributeLocation);
// Tell the attribute how to get data out of texCoordBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
texCoordAttributeLocation, size, type, normalize, stride, offset);
// Create a texture.
var texture = gl.createTexture();
// make unit 0 the active texture uint
// (ie, the unit all other texture commands will affect
gl.activeTexture(gl.TEXTURE0 + 0);
// Bind it to texture unit 0's 2D bind point
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set the parameters so we don't need mips and so we're not filtering
// and we don't repeat at the edges.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
// Upload the image into the texture.
var mipLevel = 0; // the largest mip
var internalFormat = gl.RGBA; // format we want in the texture
var srcFormat = gl.RGBA; // format of data we are supplying
var srcType = gl.UNSIGNED_BYTE; // type of data we are supplying
gl.texImage2D(gl.TEXTURE_2D,
mipLevel,
internalFormat,
srcFormat,
srcType,
image);
// Bind the position buffer so gl.bufferData that will be called
// in setRectangle puts data in the position buffer
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Set a rectangle the same size as the image.
setRectangle(gl, 0, 0, image.width, image.height);
// Define several convolution kernels
var kernels = {
normal: [
0, 0, 0,
0, 1, 0,
0, 0, 0,
],
gaussianBlur: [
0.045, 0.122, 0.045,
0.122, 0.332, 0.122,
0.045, 0.122, 0.045,
],
gaussianBlur2: [
1, 2, 1,
2, 4, 2,
1, 2, 1,
],
gaussianBlur3: [
0, 1, 0,
1, 1, 1,
0, 1, 0,
],
unsharpen: [
-1, -1, -1,
-1, 9, -1,
-1, -1, -1,
],
sharpness: [
0, -1, 0,
-1, 5, -1,
0, -1, 0,
],
sharpen: [
-1, -1, -1,
-1, 16, -1,
-1, -1, -1,
],
edgeDetect: [
-0.125, -0.125, -0.125,
-0.125, 1, -0.125,
-0.125, -0.125, -0.125,
],
edgeDetect2: [
-1, -1, -1,
-1, 8, -1,
-1, -1, -1,
],
edgeDetect3: [
-5, 0, 0,
0, 0, 0,
0, 0, 5,
],
edgeDetect4: [
-1, -1, -1,
0, 0, 0,
1, 1, 1,
],
edgeDetect5: [
-1, -1, -1,
2, 2, 2,
-1, -1, -1,
],
edgeDetect6: [
-5, -5, -5,
-5, 39, -5,
-5, -5, -5,
],
sobelHorizontal: [
1, 2, 1,
0, 0, 0,
-1, -2, -1,
],
sobelVertical: [
1, 0, -1,
2, 0, -2,
1, 0, -1,
],
previtHorizontal: [
1, 1, 1,
0, 0, 0,
-1, -1, -1,
],
previtVertical: [
1, 0, -1,
1, 0, -1,
1, 0, -1,
],
boxBlur: [
0.111, 0.111, 0.111,
0.111, 0.111, 0.111,
0.111, 0.111, 0.111,
],
triangleBlur: [
0.0625, 0.125, 0.0625,
0.125, 0.25, 0.125,
0.0625, 0.125, 0.0625,
],
emboss: [
-2, -1, 0,
-1, 1, 1,
0, 1, 2,
],
};
var initialSelection = 'edgeDetect2';
// Setup UI to pick kernels.
var ui = document.querySelector("#ui");
var select = document.createElement("select");
for (var name in kernels) {
var option = document.createElement("option");
option.value = name;
if (name === initialSelection) {
option.selected = true;
}
option.appendChild(document.createTextNode(name));
select.appendChild(option);
}
select.onchange = function() {
drawWithKernel(this.options[this.selectedIndex].value);
};
ui.appendChild(select);
drawWithKernel(initialSelection);
function computeKernelWeight(kernel) {
var weight = kernel.reduce(function(prev, curr) {
return prev + curr;
});
return weight <= 0 ? 1 : weight;
}
function drawWithKernel(name) {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Bind the attribute/buffer set we want.
gl.bindVertexArray(vao);
// Pass in the canvas resolution so we can convert from
// pixels to clipspace in the shader
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// Tell the shader to get the texture from texture unit 0
gl.uniform1i(imageLocation, 0);
// set the kernel and it's weight
gl.uniform1fv(kernelLocation, kernels[name]);
gl.uniform1f(kernelWeightLocation, computeKernelWeight(kernels[name]));
// Draw the rectangle.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 6;
gl.drawArrays(primitiveType, offset, count);
}
}
function setRectangle(gl, x, y, width, height) {
var x1 = x;
var x2 = x + width;
var y1 = y;
var y2 = y + height;
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
x1, y1,
x2, y1,
x1, y2,
x1, y2,
x2, y1,
x2, y2,
]), gl.STATIC_DRAW);
}
#import url("https://webgl2fundamentals.org/webgl/resources/webgl-tutorials.css");
body {
margin: 0;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<canvas id="canvas"></canvas>
<div id="uiContainer">
<div id="ui"></div>
</div>
<!--
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://webgl2fundamentals.org/webgl/lessons/webgl-boilerplate.html
and https://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
Given I spent more time, I see that a_position is an attribute that takes the coordinates of the screen, but a_texCoord takes clipspace coordinates of the texture.
However, down in the code (#181),
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
the buffer pointer is attached to the positionBuffer and then the setRectangle() function attaches data to the attribute a_position.
To add more to the answer, the line (#115),
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
, is put at the top while defining and binding the vertex array object vao is because the gl.vertexAttribPointer() function needs a bound buffer pointer so that the current ARRAY_BUFFER can be attached to the attribute that it is working on.

Pushing a Sine wave through a piece of geometry while it is translating/scaling/rotating

This is the prelude/simple example to kickstart my bigger idea.
Question: How can we deform the cube's vertices using a sine wave while the cube is scaling, translating or rotating.
Note: Maybe there's some post processing effect I'm not aware of for this and therefore, animating vertices are not best suited for this.
Note 2: My final goal is to push music/audio through geometry/mesh so it has more of an effect like so:
Just to clarify I would like the effect of this image above and I would also like it to be animated and be a piece of 3d geometry not 2d rastered image.
but I fear adding this audio feature is too much for one question.
That being said heres a cube being translated,scaled,rotated. The cube has a light source using normals and color:
var gl,
shaderProgram,
vertices,
matrix = mat4.create(),
vertexCount,
indexCount,
q = quat.create(),
translate =[-3, 0, -10],
scale = [1,1,1],
pivot = [0,0,0];
translate2 = [0, 0, -8],
scale2 = [3,3,3],
pivot2 = [1,1,1]
initGL();
createShaders();
createVertices();
draw();
function initGL() {
var canvas = document.getElementById("canvas");
gl = canvas.getContext("webgl");
gl.enable(gl.DEPTH_TEST);
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(1, 1, 1, 1);
}
function createShaders() {
var vertexShader = getShader(gl, "shader-vs");
var fragmentShader = getShader(gl, "shader-fs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
}
function createVertices() {
vertices = [
[-1, -1, -1, 1, 0, 0, 1], // 0
[ 1, -1, -1, 1, 1, 0, 1], // 1
[-1, 1, -1, 0, 1, 1, 1], // 2
[ 1, 1, -1, 0, 0, 1, 1], // 3
[-1, 1, 1, 1, 0.5, 0, 1], // 4
[1, 1, 1, 0.5, 1, 1, 1], // 5
[-1, -1, 1, 1, 0, 0.5, 1], // 6
[1, -1, 1, 0.5, 0, 1, 1], // 7
];
var normals = [
[0, 0, 1], [0, 1, 0], [0, 0, -1],
[0, -1, 0], [-1, 0, 0], [1, 0, 0] ];
var indices = [
[0, 1, 2, 1, 2, 3],
[2, 3, 4, 3, 4, 5],
[4, 5, 6, 5, 6, 7],
[6, 7, 0, 7, 0, 1],
[0, 2, 6, 2, 6, 4],
[1, 3, 7, 3, 7, 5]
];
var attributes = []
for(let side=0; side < indices.length; ++side) {
for(let vi=0; vi < indices[side].length; ++vi) {
attributes.push(...vertices[indices[side][vi]]);
attributes.push(...normals[side]);
}
}
vertexCount = attributes.length / 10;
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(attributes), gl.STATIC_DRAW);
var coords = gl.getAttribLocation(shaderProgram, "coords");
gl.vertexAttribPointer(coords, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, 0);
gl.enableVertexAttribArray(coords);
var colorsLocation = gl.getAttribLocation(shaderProgram, "colors");
gl.vertexAttribPointer(colorsLocation, 4, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 3);
gl.enableVertexAttribArray(colorsLocation);
var normalLocation = gl.getAttribLocation(shaderProgram, "normal");
gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT * 10, Float32Array.BYTES_PER_ELEMENT * 7);
gl.enableVertexAttribArray(normalLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var lightColor = gl.getUniformLocation(shaderProgram, "lightColor");
gl.uniform3f(lightColor, 1, 1, 1);
var lightDirection = gl.getUniformLocation(shaderProgram, "lightDirection");
gl.uniform3f(lightDirection, 0.5, 0.5, -1);
var perspectiveMatrix = mat4.create();
mat4.perspective(perspectiveMatrix, 1, canvas.width / canvas.height, 0.1, 11);
var perspectiveLoc = gl.getUniformLocation(shaderProgram, "perspectiveMatrix");
gl.uniformMatrix4fv(perspectiveLoc, false, perspectiveMatrix);
}
function draw(timeMs) {
requestAnimationFrame(draw);
let interval = timeMs / 3000
let t = interval - Math.floor(interval);
let trans_t = vec3.lerp([], translate, translate2, t);
let scale_t = vec3.lerp([], scale, scale2, t);
let pivot_t = vec3.lerp([], pivot, pivot2, t);
let quat_t = quat.slerp(quat.create(), q, [1,0,1,1], t /2);
mat4.fromRotationTranslationScaleOrigin(matrix, quat_t, trans_t, scale_t, pivot_t);
var transformMatrix = gl.getUniformLocation(shaderProgram, "transformMatrix");
gl.uniformMatrix4fv(transformMatrix, false, matrix);
gl.clear(gl.COLOR_BUFFER_BIT);
//gl.drawElements(gl.TRIANGLES, indexCount, gl.UNSIGNED_BYTE, 0);
gl.drawArrays(gl.TRIANGLES, 0, vertexCount);
}
/*
* https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Adding_2D_content_to_a_WebGL_context
*/
function getShader(gl, id) {
var shaderScript, theSource, currentChild, shader;
shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
theSource = "";
currentChild = shaderScript.firstChild;
while (currentChild) {
if (currentChild.nodeType == currentChild.TEXT_NODE) {
theSource += currentChild.textContent;
}
currentChild = currentChild.nextSibling;
}
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
// Unknown shader type
return null;
}
gl.shaderSource(shader, theSource);
// Compile the shader program
gl.compileShader(shader);
// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
<canvas id="canvas" width="600" height="600"></canvas>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec4 coords;
uniform mat4 transformMatrix;
attribute vec3 normal;
attribute vec4 colors;
uniform vec3 lightColor;
uniform vec3 lightDirection;
varying vec4 varyingColors;
uniform mat4 perspectiveMatrix;
void main(void) {
vec3 norm = normalize(normal);
vec3 ld = normalize(lightDirection);
float dotProduct = max(dot(norm, ld), 0.0);
vec3 vertexColor = lightColor * colors.rgb * dotProduct;
varyingColors = vec4(vertexColor, 1);
gl_Position = perspectiveMatrix * transformMatrix * coords;
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 color;
varying vec4 varyingColors;
void main(void) {
gl_FragColor = varyingColors;
}
</script>
I think reasonable way to do it is vertex displacement.
That is adding an offset to vertex position.
To make it work you need to tesselate your cube or take some other mesh with high polygon count. Then you can tie your sine phase to position.
Sort of:
float amplitude = 0.1;
vec3 offset = vec3(sin(globalTime + coords.y * 10.0), 0.0, 0.0) * amplitude;
gl_Position = perspectiveMatrix * transformMatrix * (coords + offset);
For using audio as input you can get a spectrum from WebAudio api and use some bar value as amplitude. Low frequency value initially works well since that's there kicks sound at. Consider asking api for low detail frequency data (few wide bars).
Also at that point some spectrum filtering might be required to smooth visual effect. For example interpolating spectrum data across few last frames.
Using multiple freq bars as input can result in nice equalizer effect. To make it work you can bake bar index as geometry attribute and displace based on that bar value.

How to prevent webgl to clean canvas on every frame draw

I'm trying to display multiple frames from different streams in a single canvas, using viewports and calling draw function on every frame I have to render. What I'm trying to replicate is a camera videowall that uses only one canvas and one webgl context. The problem is that, every time I render a frame into a specific viewport, then the other frames I rendered before in a different viewport, it disappear.
I even tried to initiate the webgl context setting "preserveDrawingBuffer" attribute to true, but it does not solved.
Following the code I'm using:
this.drawNextOuptutPictureGL = function (par) {
var gl = this.contextGL;
var texturePosBuffer = this.texturePosBuffer;
var uTexturePosBuffer = this.uTexturePosBuffer;
var vTexturePosBuffer = this.vTexturePosBuffer;
var yTextureRef = this.yTextureRef;
var uTextureRef = this.uTextureRef;
var vTextureRef = this.vTextureRef;
var width = this.width;
var height = this.height;
var yData = par.yData;
var uData = par.uData;
var vData = par.vData;
var yDataPerRow = par.yDataPerRow || width;
var yRowCnt = par.yRowCnt || height;
var uDataPerRow = par.uDataPerRow || (width / 2);
var uRowCnt = par.uRowCnt || (height / 2);
var vDataPerRow = par.vDataPerRow || uDataPerRow;
var vRowCnt = par.vRowCnt || uRowCnt;
var viewportRow = par.viewportRow;
var viewportColumn = par.viewportColumn;
// Calculate coordinates basing on a square matrix
var square = Math.sqrt(this.viewports.length);
var x = (this.canvasElement.width / square) * (viewportColumn - 1);
var y = (this.canvasElement.height / square) * (square - viewportRow);
gl.viewport(x, y, width, height);
var tTop = 0;
var tLeft = 0;
var tBottom = height / yRowCnt;
var tRight = width / yDataPerRow;
var texturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
gl.bindBuffer(gl.ARRAY_BUFFER, texturePosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, texturePosValues, gl.DYNAMIC_DRAW);
if (this.customYUV444) {
tBottom = height / uRowCnt;
tRight = width / uDataPerRow;
} else {
tBottom = (height / 2) / uRowCnt;
tRight = (width / 2) / uDataPerRow;
};
var uTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
gl.bindBuffer(gl.ARRAY_BUFFER, uTexturePosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uTexturePosValues, gl.DYNAMIC_DRAW);
if (this.customYUV444) {
tBottom = height / vRowCnt;
tRight = width / vDataPerRow;
} else {
tBottom = (height / 2) / vRowCnt;
tRight = (width / 2) / vDataPerRow;
};
var vTexturePosValues = new Float32Array([tRight, tTop, tLeft, tTop, tRight, tBottom, tLeft, tBottom]);
gl.bindBuffer(gl.ARRAY_BUFFER, vTexturePosBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vTexturePosValues, gl.DYNAMIC_DRAW);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, yTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, yDataPerRow, yRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, yData);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, uTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, uDataPerRow, uRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, uData);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, vTextureRef);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, vDataPerRow, vRowCnt, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, vData);
// draw image
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
Is there a way to just update a specific viewport without having the entire context to be cleaned?
Thanks
You need to turn on the scissor test and set the scissor rectangle
gl.enable(gl.SCISSOR_TEST);
gl.scissor(x, y, width, height);
Example:
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const fs = `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
// compiles shaders, links program, looks up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData
const bufferInfo = twgl.primitives.createCylinderBufferInfo(gl, 1, 1, 24, 1);
const scenes = [
{
viewport: [0, 0, 150, 150],
xRot: 0,
zRot: 0,
bg: [1, 0, 0, 1],
color: [0, 1, 1, 1],
},
{
viewport: [150, 0, 150, 50],
xRot: Math.PI * .5,
zRot: 0,
bg: [0, 1, 0, 1],
color: [1, 0, 1, 1],
},
{
viewport: [150, 50, 150, 100],
xRot: 0,
zRot: Math.PI * 0.25,
bg: [0, 0, 1, 1],
color: [1, 1, 0, 1],
},
];
function render(time) {
time *= 0.001;
gl.enable(gl.SCISSOR_TEST);
scenes.forEach((scene, ndx) => {
gl.viewport(...scene.viewport);
gl.scissor(...scene.viewport);
gl.clearColor(...scene.bg);
gl.clear(gl.COLOR_BUFFER_BIT);
const fov = Math.PI * 0.25;
const aspect = scene.viewport[2] / scene.viewport[3];
const near = 0.1;
const far = 10;
const matrix = m4.perspective(fov, aspect, near, far);
m4.translate(matrix, [Math.sin(time + ndx), 0, -4], matrix);
m4.rotateX(matrix, scene.xRot, matrix);
m4.rotateZ(matrix, scene.zRot, matrix);
m4.rotateZ(matrix, time, matrix);
gl.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniformXXX
twgl.setUniforms(programInfo, {
color: scene.color,
matrix: matrix,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
});
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
and if you are not rendering every viewport every frame then you'll need to pass in preserveDrawingBuffer: true when creating the webgl context.
Example that only updates one viewport per frame
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl', {
preserveDrawingBuffer: true,
});
const vs = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
const fs = `
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const bufferInfo = twgl.primitives.createCylinderBufferInfo(gl, 1, 1, 24, 1);
const scenes = [
{
viewport: [0, 0, 150, 150],
xRot: 0,
zRot: 0,
bg: [1, 0, 0, 1],
color: [0, 1, 1, 1],
},
{
viewport: [150, 0, 150, 50],
xRot: Math.PI * .5,
zRot: 0,
bg: [0, 1, 0, 1],
color: [1, 0, 1, 1],
},
{
viewport: [150, 50, 150, 100],
xRot: 0,
zRot: Math.PI * 0.25,
bg: [0, 0, 1, 1],
color: [1, 1, 0, 1],
},
];
let count = 0;
function render(time) {
++count;
time *= 0.001;
gl.enable(gl.SCISSOR_TEST);
const ndx = count % scenes.length;
const scene = scenes[ndx];
gl.viewport(...scene.viewport);
gl.scissor(...scene.viewport);
gl.clearColor(...scene.bg);
gl.clear(gl.COLOR_BUFFER_BIT);
const fov = Math.PI * 0.25;
const aspect = scene.viewport[2] / scene.viewport[3];
const near = 0.1;
const far = 10;
const matrix = m4.perspective(fov, aspect, near, far);
m4.translate(matrix, [Math.sin(time + ndx), 0, -4], matrix);
m4.rotateX(matrix, scene.xRot, matrix);
m4.rotateZ(matrix, scene.zRot, matrix);
m4.rotateZ(matrix, time, matrix);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, {
color: scene.color,
matrix: matrix,
});
twgl.drawBufferInfo(gl, bufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

How to draw 3 rectangles with WebGL2

I am able to write out by hand the full boilerplate to WebGL2 pretty much, and have this much working.
const canvas = document.createElement('canvas')
document.body.appendChild(canvas)
const gl = canvas.getContext('webgl2', { antialias: true })
const width = 800
const height = 500
canvas.width = width
canvas.height = height
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, `#version 300 es
in vec3 position;
in vec4 color;
out vec4 thecolor;
void
main() {
gl_Position = vec4(position, 1.0);
thecolor = color;
}
`)
gl.shaderSource(fragmentShader, `#version 300 es
precision mediump float;
in vec4 thecolor;
out vec4 color;
void
main() {
color = thecolor;
}
`)
gl.compileShader(vertexShader)
var success = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)
if (!success) throw new Error(gl.getShaderInfoLog(vertexShader))
gl.compileShader(fragmentShader)
var success = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)
if (!success) throw new Error(gl.getShaderInfoLog(fragmentShader))
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
const positionAttribute = gl.getAttribLocation(program, 'position')
const colorAttribute = gl.getAttribLocation(program, 'color')
gl.viewport(0, 0, width, height)
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// I don't know what the purpose of this is.
const positionVAO = gl.createVertexArray()
gl.bindVertexArray(positionVAO)
const vertexBuffer = gl.createBuffer()
const indexBuffer = gl.createBuffer()
const vertexArray = [
// don't know how to structure this on my own.
]
const indexArray = [
// don't know how to structure this either.
]
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexArray), gl.DYNAMIC_DRAW)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), gl.STATIC_DRAW)
gl.enableVertexAttribArray(positionAttribute)
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(colorAttribute)
gl.vertexAttribPointer(colorAttribute, 4, gl.FLOAT, false, 0, 0)
gl.drawElements(gl.TRIANGLES, indexArray.length, gl.UNSIGNED_SHORT, 0)
However, there are 3 comments in there.
I don't know what the purpose of gl.createVertexArray and gl.bindVertexArray are. This explains it.
Don't know how to structure the vertices in vertexArray.
Don't know how to structure the indices in indexArray.
I've gone through many tutorials but they usually gloss over the creation and definition of the the vertices/indices. They don't really explain how they designed them or structured them or why it's like that, so I haven't really been able to reconstruct it on my own yet. I would like to use drawElements with the indices instead of drawArrays.
Wondering if one could show how to draw 3 rectangles each with a different color (which gets passed in through the vertexArray). I was imagining interleaving the positions/colors in the vertexArray, but I don't know how to do that properly, and also don't know how to associate the data with the indexArray. By "properly", I mean I don't understand intuitively yet what goes into the Float32Array for vertices and the Uint32Array for indices. If it is x, y, or x, y, r, g, b, a in this case, or what. I don't understand how the rectangle closes and its "surface" gets colored. Wondering if one could help explain and demonstrate this drawing of 3 rectangles of different colors. That would help solidify how to draw in WebGL!
My attempt at drawing them is this:
const vertexArray = [
1, 1, 1, 1, 1, 1, // x y r g b a
0, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1
]
const indexArray = [
1,
2,
3,
4
]
But it doesn't do anything.
The key to this is the are the last 2 parameters of gl.vertexAttribPointer.
The 5th parameter specifies the byte offset between the sets of consecutive generic vertex attributes. In your case each set of attributes consists of 6 values (x y r g b a) with type float. So the byte offset is 6*4 = 24.
The 6th parameter specifies the byte offset of the first component of the first generic vertex attribute in the array (In case when a named array buffer object is bound).
The offset for the vertex coordinates is 0, since this are the first 2 values.
The offset for the color attribute is 2*4 = 8, since the color attribute starts at the 3rd position.
So the specification of the vertex array has to be:
const vertexArray = [
1, 1, 1, 1, 1, 1, // x y r g b a
0, 1, 1, 1, 1, 1,
1, 0, 1, 1, 1, 1,
0, 0, 1, 1, 1, 1
]
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexArray), gl.DYNAMIC_DRAW)
gl.enableVertexAttribArray(positionAttribute)
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 6*4, 0)
gl.enableVertexAttribArray(colorAttribute)
gl.vertexAttribPointer(colorAttribute, 4, gl.FLOAT, false, 6*4, 2*4)
You want to draw 2 triangles:
2 0
+--------+ 0: (1, 1)
| /| 1: (0, 1)
| / | 2: (1, 0)
| / | 3: (0, 0)
+ -------+
3 1
each triangle consists of 3 indices, so the array of indices has to be:
const indexArray = [ 0, 2, 3, 0, 3, 1 ]
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), gl.STATIC_DRAW)
If you draw this using the primitive type TRIANGLES,
gl.drawElements(gl.TRIANGLES, indexArray.length, gl.UNSIGNED_SHORT, 0)
then this forms the 2 triangles with the coordinates:
1st : (1, 1) -> (1, 0) -> (0, 0)
2nd : (1, 1) -> (0, 0) -> (0, 1)
Of course it is possible to draw a triangles strip (TRIANGLE_STRIP) or triangle fan (TRIANGLE_FAN) instead:
const indexArray = [ 2, 0, 3, 1 ]
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), gl.STATIC_DRAW)
gl.drawElements(gl.TRIANGLE_STRIP, indexArray.length, gl.UNSIGNED_SHORT, 0)
const indexArray = [ 0, 2, 3, 1 ]
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), gl.STATIC_DRAW)
gl.drawElements(gl.TRIANGLE_FAN, indexArray.length, gl.UNSIGNED_SHORT, 0)
var canvas = document.getElementById('my_canvas');
const gl = canvas.getContext('webgl2', { antialias: true })
const width = 800
const height = 500
canvas.width = width
canvas.height = height
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(vertexShader, `#version 300 es
in vec3 position;
in vec4 color;
out vec4 thecolor;
void
main() {
gl_Position = vec4(position, 1.0);
thecolor = color;
}
`)
gl.shaderSource(fragmentShader, `#version 300 es
precision mediump float;
in vec4 thecolor;
out vec4 color;
void
main() {
color = thecolor;
}
`)
gl.compileShader(vertexShader)
var success = gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)
if (!success) throw new Error(gl.getShaderInfoLog(vertexShader))
gl.compileShader(fragmentShader)
var success = gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)
if (!success) throw new Error(gl.getShaderInfoLog(fragmentShader))
const program = gl.createProgram()
gl.attachShader(program, vertexShader)
gl.attachShader(program, fragmentShader)
gl.linkProgram(program)
gl.useProgram(program)
const positionAttribute = gl.getAttribLocation(program, 'position')
const colorAttribute = gl.getAttribLocation(program, 'color')
gl.viewport(0, 0, width, height)
gl.clearColor(0, 0, 0, 0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// I don't know what the purpose of this is.
const positionVAO = gl.createVertexArray()
gl.bindVertexArray(positionVAO)
const vertexBuffer = gl.createBuffer()
const indexBuffer = gl.createBuffer()
const vertexArray = [
1, 1, 1, 1, 0, 1, // x y r g b a
0, 1, 1, 0, 1, 1,
1, 0, 0, 1, 1, 1,
0, 0, 1, 1, 0, 1
]
const indexArray = [0, 2, 3, 0, 3, 1]
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexArray), gl.DYNAMIC_DRAW)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexArray), gl.STATIC_DRAW)
gl.enableVertexAttribArray(positionAttribute)
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 6*4, 0)
gl.enableVertexAttribArray(colorAttribute)
gl.vertexAttribPointer(colorAttribute, 4, gl.FLOAT, false, 6*4, 2*4)
gl.drawElements(gl.TRIANGLES, indexArray.length, gl.UNSIGNED_SHORT, 0)
<canvas id="my_canvas"></canvas>

How to translate the sphere along y-axis (it just go up, so not come down on translation) and how to deform

how to move an object in up and down and make it deform when it touch the roof and bottom ?
I have created a sphere which rotates and moves just up infinitely by translating along y-axis but now i have to move it down and i want it to deform when it collide up and bottom, Now my question are:
(1) how can i make my sphere move up and down on y axis ?
(2) How to deform when it touch the top ? and recover same shape back as the contact is finished with top or bottom while moving up and down.
My code for rotating sphere is(I have followed Lesson 11 on Git Hub tutorials on this link https://github.com/gpjt/webgl-lessons/blob/master/lesson11/index.html):
var newRotationMatrix = mat4.create();
mat4.identity(newRotationMatrix);
mat4.rotate(newRotationMatrix, degToRad(2 / 10), [0, 1, 0]);
mat4.multiply(newRotationMatrix, [1,0,0], moonRotationMatrix);
gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
The full function is :
function tick()
{
requestAnimFrame(tick);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
gl.uniform1i(shaderProgram.useLightingUniform, false);
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0, 0, -6]);
mat4.multiply(mvMatrix, moonRotationMatrix);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, moonTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, moonVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexTextureCoordBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, moonVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, moonVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, moonVertexIndexBuffer);
setMatrixUniforms();
/*rotation part is below*/
var newRotationMatrix = mat4.create();
mat4.identity(newRotationMatrix);
mat4.rotate(newRotationMatrix, degToRad(2 / 10), [0, 1, 0]);
mat4.multiply(newRotationMatrix, [1,0,0], moonRotationMatrix);
gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
What to add in this code that such that it will rotate and must touch top and bottom (letssay which are at the distance of 250 and -250 fromn the center along y-axis-as the motion has to be along y-axis)
and how to deform it when the sphere touches 250 while going up and -250 whil going down and recover immediately as the contact between the sphere and top or bottom is finished, and this process should repeat infinitely.
EDIT:
In this question moonVertexIndexBuffer contains the vertex of square in the tick function.
This my initfuffer function
function initBuffers(latitudeBands, longitudeBands)
{
var radius = 1;
var vertexPositionData = [];
var normalData = [];
var textureCoordData = [];
for (var latNumber = 0; latNumber <= latitudeBands; latNumber++)
{
var theta = latNumber * Math.PI / latitudeBands;
var sinTheta = Math.sin(theta);
var cosTheta = Math.cos(theta);
for (var longNumber = 0; longNumber <= longitudeBands; longNumber++)
{
var phi = longNumber * 2 * Math.PI / longitudeBands;
var sinPhi = Math.sin(phi);
var cosPhi = Math.cos(phi);
var x = cosPhi * sinTheta;
var y = cosTheta;
var z = sinPhi * sinTheta;
var u = 1 - (longNumber / longitudeBands);
var v = 1 - (latNumber / latitudeBands);
normalData.push(x);
normalData.push(y);
normalData.push(z);
textureCoordData.push(u);
textureCoordData.push(v);
vertexPositionData.push(radius * x);
vertexPositionData.push(radius * y);
vertexPositionData.push(radius * z);
}
}
alert("vertexPositionData:" + vertexPositionData);
var indexData = [];
for (var latNumber = 0; latNumber < latitudeBands; latNumber++) {
for (var longNumber = 0; longNumber < longitudeBands; longNumber++) {
var first = (latNumber * (longitudeBands + 1)) + longNumber;
var second = first + longitudeBands + 1;
indexData.push(first);
indexData.push(second);
indexData.push(first + 1);
indexData.push(second);
indexData.push(second + 1);
indexData.push(first + 1);
//console.log("four points for iteration" + latNumber, +longNumber + " are " + "1: " + first + "2 :" + (first + 1) + "3: " + (second) + "4 :" + (second + 1));
}
}
moonVertexNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexNormalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
moonVertexNormalBuffer.itemSize = 3;
moonVertexNormalBuffer.numItems = normalData.length / 3;
moonVertexTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexTextureCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordData), gl.STATIC_DRAW);
moonVertexTextureCoordBuffer.itemSize = 2;
moonVertexTextureCoordBuffer.numItems = textureCoordData.length / 2;
///
moonVertexPositionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexPositionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexPositionData), gl.STATIC_DRAW);
moonVertexPositionBuffer.itemSize = 3;
moonVertexPositionBuffer.numItems = vertexPositionData.length / 3;
moonVertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, moonVertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.DYNAMIC_DRAW);
moonVertexIndexBuffer.itemSize = 1;
moonVertexIndexBuffer.numItems = indexData.length;
}
function tick()
{
requestAnimFrame(tick);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(45, gl.viewportWidth / gl.viewportHeight, 0.1, 100.0, pMatrix);
// var lighting = false;
gl.uniform1i(shaderProgram.useLightingUniform, false);
/*I removed some part here */
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, [0, 0, -6]);
mat4.multiply(mvMatrix, moonRotationMatrix);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, moonTexture);
gl.uniform1i(shaderProgram.samplerUniform, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, moonVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexTextureCoordBuffer);
gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, moonVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, moonVertexNormalBuffer);
gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, moonVertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, moonVertexIndexBuffer);
setMatrixUniforms();
var newRotationMatrix = mat4.create();
mat4.identity(newRotationMatrix);
mat4.rotate(newRotationMatrix, degToRad(2 / 10), [0, 1, 0], newRotationMatrix);
mat4.multiply(newRotationMatrix, moonRotationMatrix, moonRotationMatrix);
gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
}
This is my vertex shader:
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;
uniform vec3 uAmbientColor;
uniform vec3 uLightingDirection;
uniform vec3 uDirectionalColor;
uniform bool uUseLighting;
varying vec2 vTextureCoord;
varying vec3 vLightWeighting;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
if (!uUseLighting) {
vLightWeighting = vec3(1.0, 1.0, 1.0);
} else {
vec3 transformedNormal = uNMatrix * aVertexNormal;
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 0.0);
vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
}
}
</script>
And this is initshaders function
function initShaders()
{
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
shaderProgram.textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(shaderProgram.textureCoordAttribute);
shaderProgram.vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(shaderProgram.vertexNormalAttribute);
shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.nMatrixUniform = gl.getUniformLocation(shaderProgram, "uNMatrix");
shaderProgram.samplerUniform = gl.getUniformLocation(shaderProgram, "uSampler");
shaderProgram.useLightingUniform = gl.getUniformLocation(shaderProgram, "uUseLighting");
shaderProgram.ambientColorUniform = gl.getUniformLocation(shaderProgram, "uAmbientColor");
shaderProgram.lightingDirectionUniform = gl.getUniformLocation(shaderProgram, "uLightingDirection");
shaderProgram.directionalColorUniform = gl.getUniformLocation(shaderProgram, "uDirectionalColor");
}
**I am not able to understand how to move it up and down infinitely(translate it along y axis) as we have all the squares in an array, because sphere is made of squares (depending upon latitude and longitude passed in initBuffers() function) **
MY try is this (but sphere just go up infinitely, never come back down, how to bring it down)?
var translation = [0, 0.5,0];
function tick()
{
....//I am elminating the code which was shown previously
var translationMatrix = makeTranslation(translation[0], translation[1], translation[2]);
mat4.multiply(moonRotationMatrix, translationMatrix, moonRotationMatrix);
}
function makeTranslation(tx, ty, tz) {
return [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
tx, ty, tz, 1
];
}
EDIT2: My try for scaling for deformation, after the first answer :
In the vertex shader i kept uniform as suggested like this:
<script id="shader-vs" type="x-shader/x-vertex">
uniform vec4 uScaleY;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
}
</script>
And in initshader() i do this :
shaderProgram.uScaleYAttribute = gl.getUniformLocation(shaderProgram, "uScaleY");
In setuniform i do this :
var yScale =[0,1,0];
function setMatrixUniforms()
{
gl.uniformMatrix4fv(shaderProgram.uScaleYAttribute, false, yScale);
}
And in my tick() i try to do this :
var newRotationMatrix = mat4.create();
mat4.identity(newRotationMatrix);
mat4.rotate(newRotationMatrix, degToRad(2 / 10), [0, 1, 0]);
mat4.multiply(newRotationMatrix, moonRotationMatrix, moonRotationMatrix);
translateMatrix = translation(translate[0], translate[1], translate[2]);
mat4.multiply(moonRotationMatrix, translateMatrix,moonRotationMatrix);
var maxY =1.5;
var moonPosY = moonRotationMatrix[3 * 4 +1];
if (moonPosY > maxY || moonPosY < (-maxY))
{
translate[1] *= -1;
}
var maxDiff = 0.05;
var minDiff = 0.01;
var diff = Math.abs(moonPosY - maxY);
if (diff < maxDiff)//sphere when go up
{
yScale = 1 - (minDiff - diff) * 0.1;
}
/*
else if (moonPosY > (-maxDiff)) // scaling when sphere come down
{
yScale = 1 - (minDiff - diff) * 0.1;
}
*/
gl.drawElements(gl.TRIANGLES, moonVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
And the scaling do not work for deformation along y-axis while up and down touch of sphere at top and bottom, whats wrong in my code ?
Change your tick so that it keeps track of the total translation and reverse the translation vector used when the threshold is met.
It'd be something like:
var maxY = 100;
var moonPosY = moonRotationMatrix[/*row*/ 3 * 4 + /*col*/ 1];
if (moonPosY < maxY || moonPosY < (-maxY)) {
translation = translation.map(a => -a);
// or simply
// translation[1] *= -1;
// if you want to reverse it just on Y axis
}
To deform it, it depends on the deformation you want, but if you simply want to squash it along Y axis, you can add a uniform to your vertex shader for Y-scale and fill it with something like:
var yScale = 1;
var maxDiff = 10;
var diff = Math.abs(moonPosY - maxY);
if (diff < maxDiff) {
yScale = 1 - (minDiff - diff) * 0.1;
}

Categories

Resources