Nothing is rendering in a canvas - webgl

I am not able to render anything in canvas and the console is not showing any error. I think my eye is missing something. I am doing nothing just trying to render the cube coordinate in canvas but nothing is being displayed.
( this sentence inside the bracket is just unwanted text for making this question pass the error of post is mostly code by StackOverflow)
"use strict";
var vs = `#version 300 es
in vec4 a_position;
uniform mat4 MVPmatrix;
void main(){
gl_Position = MVPmatrix*a_position;
}`;
var fs = `#version 300 es
precision mediump float;
out vec4 FragColor;
void main(){
FragColor = vec4(1, 0.5, 0.5, 1);
}
`;
function main() {
var canvas = document.querySelector("#canvas");
console.log(canvas);
let gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
var positionLocation = gl.getAttribLocation(program, "a_position");
var matrixLocation = gl.getUniformLocation(program, "MVPmatrix");
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
setGeometry(gl);
gl.enableVertexAttribArray(positionLocation);
let size = 3;
let type = gl.FLOAT;
let normalize = false;
let stride = 0;
let offset = 0;
gl.vertexAttribPointer(
positionLocation,
size,
type,
normalize,
stride,
offset
);
function degreeToRadian(degree) {
return (degree * Math.PI) / 180;
}
let fieldOfViewRadian = degreeToRadian(60);
drawScene();
function drawScene() {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0.5, 0.7, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.useProgram(program);
gl.bindVertexArray(vao);
let aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
let zNear = 0.001;
let zFar = 2000;
let projectionMatrix = m4.perspective(
fieldOfViewRadian,
aspect,
zNear,
zFar
);
let modelMatrix = m4.identity();
modelMatrix = m4.translate(modelMatrix, 0, 0, -4);
modelMatrix = m4.xRotate(modelMatrix, 0.5);
modelMatrix = m4.yRotate(modelMatrix, 0.5);
let cameraMatrix = m4.yRotation(0);
cameraMatrix = m4.translate(cameraMatrix, 0, 0, 200);
let viewMatrix = m4.inverse(cameraMatrix);
let modelView = m4.multiply(viewMatrix, modelMatrix);
let viewProjectionMatrix = m4.multiply(projectionMatrix, modelView);
gl.uniformMatrix4fv(matrixLocation, false, viewProjectionMatrix);
let primitiveType = gl.TRAINGLES;
let offset = 0;
let count = 6 * 6;
gl.drawArrays(primitiveType, offset, count);
}
}
main();
function setGeometry(gl) {
let positionData = new Float32Array([
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
-10,
-10,
-10,
10,
10,
-10,
10,
10,
10,
10,
-10,
10,
10,
-10,
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
10,
-10,
10,
10,
10,
10,
-10,
10,
-10,
-10,
-10,
-10,
-10,
10,
10,
-10,
10,
10,
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
10,
10,
10,
10,
10,
-10,
]);
gl.bufferData(gl.ARRAY_BUFFER, positionData, gl.STATIC_DRAW);
}
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title></title>
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="" />
</head>
<style>
#canvas {
width: 100vw;
height: 100vh;
}
</style>
<body>
<canvas id="canvas"></canvas>
<script src="js/script.js" async defer></script>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>
<script src="https://greggman.github.io/webgl-helpers/webgl-gl-error-check.js"></script>
<script src="js/webgl.js"></script>
</body>
</html>

I moved my webgl helper to <script src="https://greggman.github.io/webgl-lint/webgl-lint.js"></script>
Adding it I get this error
error in drawArrays(/*UNKNOWN WebGL ENUM*/ undefined, 0, 36): argument 0 is undefined
with WebGLVertexArrayObject() bound
Checking the code the issue is
let primitiveType = gl.TRAINGLES;
it should be
let primitiveType = gl.TRIANGLES;
And then as LJ points out there are only 24 vertices but the call to drawArrays has 36 as count
error in drawArrays(TRIANGLES, 0, 36): INVALID_OPERATION
WebGLBuffer("*unnamed*") assigned to attribute 0 used as attribute 'a_position' in current program is too small for draw parameters.
index of highest vertex accessed: 35
attribute size: 3, type: FLOAT, stride: 0, offset: 0, divisor: 0
needs 432 bytes for draw but buffer is only 336 bytes
with WebGLProgram("*unnamed*") as current program
with WebGLVertexArrayObject() bound
"use strict";
var vs = `#version 300 es
in vec4 a_position;
uniform mat4 MVPmatrix;
void main(){
gl_Position = MVPmatrix*a_position;
}`;
var fs = `#version 300 es
precision mediump float;
out vec4 FragColor;
void main(){
FragColor = vec4(1, 0.5, 0.5, 1);
}
`;
function main() {
var canvas = document.querySelector("#canvas");
console.log(canvas);
let gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
var positionLocation = gl.getAttribLocation(program, "a_position");
var matrixLocation = gl.getUniformLocation(program, "MVPmatrix");
let vao = gl.createVertexArray();
gl.bindVertexArray(vao);
let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
setGeometry(gl);
gl.enableVertexAttribArray(positionLocation);
let size = 3;
let type = gl.FLOAT;
let normalize = false;
let stride = 0;
let offset = 0;
gl.vertexAttribPointer(
positionLocation,
size,
type,
normalize,
stride,
offset
);
function degreeToRadian(degree) {
return (degree * Math.PI) / 180;
}
let fieldOfViewRadian = degreeToRadian(60);
drawScene();
function drawScene() {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0.5, 0.7, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.useProgram(program);
gl.bindVertexArray(vao);
let aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
let zNear = 0.001;
let zFar = 2000;
let projectionMatrix = m4.perspective(
fieldOfViewRadian,
aspect,
zNear,
zFar
);
let modelMatrix = m4.identity();
modelMatrix = m4.translate(modelMatrix, 0, 0, -4);
modelMatrix = m4.xRotate(modelMatrix, 0.5);
modelMatrix = m4.yRotate(modelMatrix, 0.5);
let cameraMatrix = m4.yRotation(0);
cameraMatrix = m4.translate(cameraMatrix, 0, 0, 200);
let viewMatrix = m4.inverse(cameraMatrix);
let modelView = m4.multiply(viewMatrix, modelMatrix);
let viewProjectionMatrix = m4.multiply(projectionMatrix, modelView);
gl.uniformMatrix4fv(matrixLocation, false, viewProjectionMatrix);
let primitiveType = gl.TRIANGLES;
let offset = 0;
let count = 24;//6 * 6;
gl.drawArrays(primitiveType, offset, count);
}
}
main();
function setGeometry(gl) {
let positionData = new Float32Array([
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
-10,
-10,
-10,
10,
10,
-10,
10,
10,
10,
10,
-10,
10,
10,
-10,
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
10,
-10,
-10,
10,
10,
-10,
10,
10,
10,
10,
-10,
10,
-10,
-10,
-10,
-10,
-10,
10,
10,
-10,
10,
10,
-10,
-10,
-10,
10,
-10,
-10,
10,
10,
10,
10,
10,
10,
10,
-10,
]);
gl.bufferData(gl.ARRAY_BUFFER, positionData, gl.STATIC_DRAW);
}
#canvas {
width: 100vw;
height: 100vh;
}
<canvas id="canvas"></canvas>
<script src="https://webglfundamentals.org/webgl/resources/m4.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/webgl-lessons-ui.js"></script>
<script src="https://greggman.github.io/webgl-lint/webgl-lint.js"></script>
<script src="js/webgl.js"></script>

Related

what is the use and role of texSubImage2D?

After a lot of searching, I managed to get the texSubImage2D function to work. Simply what I haven't found is: what is this function for. In the example below I made a nice effect. In short, I know how to make it work but I am still completely unaware of the role of its parameters. And where to find these explanations?
I'm not looking for the syntax,
the example I give shows that I have (it seems to me) understood it well.
https://registry.khronos.org/webgl/specs/latest/1.0/#5.14.8
What I don't understand at all is the semantics...
Anyway, if someone could answer with examples so that I can understand.
"use strict";
let canvas = document.getElementById("canvas");
let gl = canvas.getContext("webgl");
gl.canvas.width = 30;
gl.canvas.height = 30;
let vertex = `
attribute vec2 a_position;
attribute vec2 a_texCoord;
uniform vec2 u_resolution;
varying vec2 v_texCoord;
void main() {
vec2 zeroToOne = a_position / u_resolution;
vec2 zeroToTwo = zeroToOne * 2.0;
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
v_texCoord = a_texCoord;
}
`;
let fragment = `
precision mediump float;
uniform sampler2D u_image;
varying vec2 v_texCoord;
void main() {
gl_FragColor = texture2D(u_image, v_texCoord);
gl_FragColor.rgb *= gl_FragColor.a;
}
`;
let shader = gl.createProgram();
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
gl.attachShader(shader, vertexShader);
gl.attachShader(shader, fragmentShader);
gl.linkProgram(shader);
let image_RGBA = new Image();
image_RGBA.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeBAMAAADJHrORAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAYUExURdUAAKPTdgCN09Aq0w4A09PS0dOoXwD//56WZMcAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA8SURBVCjPYyAIBJEBNr4SAmDnG8MALr4LBODmh4IAPn5aWhp+fjkBPgH9BOwn4H4C/icQfgTCHx9gYAAArEg8b+0tf+EAAAAASUVORK5CYII=";
image_RGBA.onload = function() {
go(image_RGBA);
};
function go(image) {
let width = image.width;
let height = image.height;
let positionLocation = gl.getAttribLocation(shader, "a_position");
let texcoordLocation = gl.getAttribLocation(shader, "a_texCoord");
let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
width, 0,
0, height,
0, height,
width, 0,
width, height
]), gl.STATIC_DRAW);
let 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);
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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);
const canvas2D = document.getElementById('canvas2D');
canvas2D.width = 30;
canvas2D.height = 30;
const ctx = canvas2D.getContext('2d');
ctx.drawImage(image, 0, 0);
var imgData = ctx.getImageData(0, 0, width, height).data;
var ArrayBufferView = new Uint8Array(imgData.buffer);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
30,
30,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
ArrayBufferView
);
gl.texSubImage2D(
gl.TEXTURE_2D,
0,
0,
0,
29,
29,
gl.RGBA,
gl.UNSIGNED_BYTE,
ArrayBufferView
);
let resolutionLocation = gl.getUniformLocation(shader, "u_resolution");
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(100 / 255, 200 / 255, 150 / 255, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(shader);
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
let size = 2;
let type = gl.FLOAT;
let normalize = false;
let stride = 0;
let offset = 0;
gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
gl.enableVertexAttribArray(texcoordLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
size = 2;
type = gl.FLOAT;
normalize = false;
stride = 0;
offset = 0;
gl.vertexAttribPointer(texcoordLocation, size, type, normalize, stride, offset);
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
#canvas {
width: 150px;
height: 150px;
image-rendering: pixelated;
}
#canvas2D {
width: 150px;
height: 150px;
image-rendering: pixelated;
}
<canvas id="canvas2D"></canvas><canvas id="canvas"></canvas>

WebGL mix instanced sprite rendering

I want to render some instanced sprites using drawArraysInstancedANGLE. The problem is that when i set vertexAttribDivisorANGLE for the buffer to be instanced, it clears the whole screen, erasing anything i drawn earlier. I based this example on https://stackoverflow.com/a/56066386/1227852
Example with the vertexAttribDivisorANGLE issue (only draws the instanced boxes):
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('need ANGLE_instanced_arrays');
}
// create a simple background shader
const backgroundVs = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const backgroundFs = `
precision mediump float;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy/resolution.xy;
vec3 color = uv.xyx;
gl_FragColor = vec4(color, 1.0);
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
gl_FragColor = vec4(color,1.0);
}
`;
const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');
const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1, 1, // 1 +-----+ 2
-1, 1, // | |
1, -1, // | |
1, -1, // | |
-1, 1, // | |
-1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
//Create an instanced point renderer (based on: )
const vs = `
attribute vec4 position; // center point
attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
uniform vec2 resolution;
uniform mat4 matrix;
varying vec3 pointCoord; // only if you need gl_PointCoord substitute
void main() {
// do the normal thing (can mult by matrix or whatever here
gl_Position = matrix * position;
float pointSize = 20.0 / gl_Position.w;
// -- point emulation
gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) /
resolution * gl_Position.w;
// only if you need gl_PointCoord substitute
pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const programInfo = twgl.createProgram(gl, [vs, fs]);
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');
const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.001, -1.001,
1.001, -1.001,
-1.001, 1.001,
1.001, 1.001,
0, 0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
function render(ms) {
const secs = ms * 0.001;
gl.useProgram(backgroundProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
const mat = m4.perspective(
60 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.1,
100);
m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(positionLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
gl.uniformMatrix4fv(matrixLoc, false, mat);
// 6 verts per point
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
requestAnimationFrame(render);
}
render(0);
<html>
<head>
<style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
</head>
<body>
<canvas></canvas>
</body>
</html>
Now when i disable the ext.vertexAttribDivisorANGLE(positionLoc, 1); call, the background renders like expected and the boxes are drawn over the background, but obviously the mesh is incorrect since the instancing / divisor is not set correctly:
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('need ANGLE_instanced_arrays');
}
// create a simple background shader
const backgroundVs = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const backgroundFs = `
precision mediump float;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy/resolution.xy;
vec3 color = uv.xyx;
gl_FragColor = vec4(color, 1.0);
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
gl_FragColor = vec4(color,1.0);
}
`;
const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');
const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1, 1, // 1 +-----+ 2
-1, 1, // | |
1, -1, // | |
1, -1, // | |
-1, 1, // | |
-1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
//Create an instanced point renderer (based on: )
const vs = `
attribute vec4 position; // center point
attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
uniform vec2 resolution;
uniform mat4 matrix;
varying vec3 pointCoord; // only if you need gl_PointCoord substitute
void main() {
// do the normal thing (can mult by matrix or whatever here
gl_Position = matrix * position;
float pointSize = 20.0 / gl_Position.w;
// -- point emulation
gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) /
resolution * gl_Position.w;
// only if you need gl_PointCoord substitute
pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const programInfo = twgl.createProgram(gl, [vs, fs]);
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');
const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.001, -1.001,
1.001, -1.001,
-1.001, 1.001,
1.001, 1.001,
0, 0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
function render(ms) {
const secs = ms * 0.001;
gl.useProgram(backgroundProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
const mat = m4.perspective(
60 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.1,
100);
m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
//ext.vertexAttribDivisorANGLE(positionLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
gl.uniformMatrix4fv(matrixLoc, false, mat);
// 6 verts per point
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
requestAnimationFrame(render);
}
render(0);
<html>
<head>
<style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
</head>
<body>
<canvas></canvas>
</body>
</html>
How can i render the instanced boxes correctly over the background without the background being erased?
edit: removed some unnecessary commented lines
edit 2: as pointed out by user253751, i needed to turn off the divisor using ext.vertexAttribDivisorANGLE(positionLoc, 0); right after the drawArraysInstancedANGLE call
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
alert('need ANGLE_instanced_arrays');
}
// create a simple background shader
const backgroundVs = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const backgroundFs = `
precision mediump float;
uniform vec2 resolution;
void main() {
vec2 uv = gl_FragCoord.xy/resolution.xy;
vec3 color = uv.xyx;
gl_FragColor = vec4(color, 1.0);
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
gl_FragColor = vec4(color,1.0);
}
`;
const backgroundProgramInfo = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundProgram = twgl.createProgram(gl, [backgroundVs, backgroundFs]);
const backgroundPositionLoc = gl.getAttribLocation(backgroundProgram, 'position');
const backgroundResolutionLoc = gl.getUniformLocation(backgroundProgram, 'resolution');
const backgroundQuad = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
1, 1, // 1 +-----+ 2
-1, 1, // | |
1, -1, // | |
1, -1, // | |
-1, 1, // | |
-1, -1, // 3 +-----+ 0
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
//Create an instanced point renderer (based on: )
const vs = `
attribute vec4 position; // center point
attribute vec2 cornerPosition; // the corners (-0.5 to 0.5)
uniform vec2 resolution;
uniform mat4 matrix;
varying vec3 pointCoord; // only if you need gl_PointCoord substitute
void main() {
// do the normal thing (can mult by matrix or whatever here
gl_Position = matrix * position;
float pointSize = 20.0 / gl_Position.w;
// -- point emulation
gl_Position.xy += cornerPosition * (pointSize * 2.0 - 1.0) /
resolution * gl_Position.w;
// only if you need gl_PointCoord substitute
pointCoord = vec3(cornerPosition * 0.5, gl_Position.z);
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`;
const programInfo = twgl.createProgram(gl, [vs, fs]);
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const cornerPositionLoc = gl.getAttribLocation(program, 'cornerPosition');
const resolutionLoc = gl.getUniformLocation(program, 'resolution');
const matrixLoc = gl.getUniformLocation(program, 'matrix');
const bufSprites = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1.001, -1.001,
1.001, -1.001,
-1.001, 1.001,
1.001, 1.001,
0, 0,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
const bufCorners = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.5, -0.5,
0.5, -0.5,
-0.5, 0.5,
-0.5, 0.5,
0.5, -0.5,
0.5, 0.5,
]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
function render(ms) {
const secs = ms * 0.001;
gl.useProgram(backgroundProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, backgroundQuad);
gl.enableVertexAttribArray(backgroundPositionLoc);
gl.vertexAttribPointer(backgroundPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(backgroundResolutionLoc, gl.canvas.width, gl.canvas.height);
gl.drawArrays(gl.TRIANGLES, 0, 6);
const mat = m4.perspective(
60 * Math.PI / 180,
gl.canvas.clientWidth / gl.canvas.clientHeight,
0.1,
100);
m4.translate(mat, [0, 0, -2.11 + Math.sin(secs)], mat);
gl.useProgram(program);
gl.bindBuffer(gl.ARRAY_BUFFER, bufSprites);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(positionLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, bufCorners);
gl.enableVertexAttribArray(cornerPositionLoc);
gl.vertexAttribPointer(cornerPositionLoc, 2, gl.FLOAT, false, 0, 0);
gl.uniform2f(resolutionLoc, gl.canvas.width, gl.canvas.height);
gl.uniformMatrix4fv(matrixLoc, false, mat);
// 6 verts per point
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 5);
ext.vertexAttribDivisorANGLE(positionLoc, 0);
requestAnimationFrame(render);
}
render(0);
<html>
<head>
<style>canvas { border: 1px solid black; width: 100%; height: 100%;}</style>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
</head>
<body>
<canvas></canvas>
</body>
</html>
You need to turn the divisor off by setting it to 0 before drawing the background. Otherwise, it is used for the background as well, and the background is drawn incorrectly.

varying in webgl is not working as expected

I am new to WebGL and now learning varying variables. In this, my expected output is the image below.
In the code below, I tried to pass colour from the vertex shader to fragment shader.The primitive type pass in drawArray is a triangle but it is not drawing triangles.I wrote this code to understand varying variables in WebGL and is derived from WebGLfundamental website. I don't know what went wrong since from my knowledge, this is what I am supposed to do.
The output I was expecting is :
Code is :
"use strict";
var vs = `#version 300 es
precision highp float;
in vec2 a_position;
in vec4 a_color;
out vec4 v_color;
void main(){
gl_Position = vec4(a_position, 0, 1);
v_color = a_color;
}
`;
var fs = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main(){
outColor = v_color;
}
`;
function main() {
var canvas = document.querySelector("#c");
var gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
var program = webglUtils.createProgramFromSources(gl, [vs, fs]);
var vertexPosition = gl.getAttribLocation(program, 'a_position');
var vertexColor = gl.getAttribLocation(program, 'a_color');
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var position = [
-150, -100,
150, -100,
-150, 100,
-150, 100,
150, -100,
150, 100
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
gl.enableVertexAttribArray(vertexPosition);
var size = 2;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(vertexPosition, size, type, normalize, stride, offset);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var color = [
0.7, 0.2, 0.9, 1,
0.6, 0.7, 0.2, 1,
0.5, 0.7, 0.9, 1,
0.4, 0.7, 0.2, 1,
0.7, 0.2, 0.9, 1,
0.5, 0.7, 0.9, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
gl.enableVertexAttribArray(vertexColor);
var size =4;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(vertexColor, size, type, normalize, stride, offset);
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.bindVertexArray(vao);
var offset = 0;
var count = 6;
gl.drawArrays(gl.TRIANGLES, offset, count);
}
main();
body{
overflow: hidden;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="c">
</canvas>
The issue is you're providing positions in pixels but WebGL requires positions in clip space. So you're drawing what you think you were drawing except only the center 2x2 units of your 300x200 rectangle are being shown.
For example if we just hack and divide the position by 300,200
gl_Position = vec4(a_position / vec2(300, 200);
Then it works
"use strict";
var vs = `#version 300 es
precision highp float;
in vec2 a_position;
in vec4 a_color;
out vec4 v_color;
void main(){
gl_Position = vec4(a_position / vec2(300, 200), 0, 1);
v_color = a_color;
}
`;
var fs = `#version 300 es
precision highp float;
in vec4 v_color;
out vec4 outColor;
void main(){
outColor = v_color;
}
`;
function main() {
var canvas = document.querySelector("#c");
var gl = canvas.getContext("webgl2");
if (!gl) {
return;
}
var program = webglUtils.createProgramFromSources(gl, [vs, fs]);
var vertexPosition = gl.getAttribLocation(program, 'a_position');
var vertexColor = gl.getAttribLocation(program, 'a_color');
var vao = gl.createVertexArray();
gl.bindVertexArray(vao);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var position = [
-150, -100,
150, -100,
-150, 100,
-150, 100,
150, -100,
150, 100
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
gl.enableVertexAttribArray(vertexPosition);
var size = 2;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(vertexPosition, size, type, normalize, stride, offset);
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
var color = [
0.7, 0.2, 0.9, 1,
0.6, 0.7, 0.2, 1,
0.5, 0.7, 0.9, 1,
0.4, 0.7, 0.2, 1,
0.7, 0.2, 0.9, 1,
0.5, 0.7, 0.9, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(color), gl.STATIC_DRAW);
gl.enableVertexAttribArray(vertexColor);
var size =4;
var type = gl.FLOAT;
var normalize = false;
var stride = 0;
var offset = 0;
gl.vertexAttribPointer(vertexColor, size, type, normalize, stride, offset);
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.useProgram(program);
gl.bindVertexArray(vao);
var offset = 0;
var count = 6;
gl.drawArrays(gl.TRIANGLES, offset, count);
}
main();
body{
overflow: hidden;
}
canvas {
width: 100vw;
height: 100vh;
display: block;
}
<script src="https://webgl2fundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="c">
</canvas>
The normal way to convert positions from pixel space to clip space is to use a matrix

can I use multiple texture with drawArraysInstancedANGLE?

I try to draw multiple icon on screen and I use drawArraysInstancedANGLE method.
I try to use multiple texture like this but some icons draw diffrent geometry, I can not find what draw like that.
I use one big icon map texture and fill icon vertex coord array with this func:
FillIconTextCoordBuffer(data, mapW, mapH, i) {
const ULiconW = data.x / mapW
const ULiconH = data.y / mapH
const LRiconW = (data.x + data.width) / mapW
const LRiconH = (data.y + data.height) / mapH
const { gl } = this.FGlobe
this.IconMapTexCoordArr[i] = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapTexCoordArr[i])
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
ULiconW, ULiconH,
LRiconW, LRiconH,
LRiconW, ULiconH,
LRiconW, LRiconH,
ULiconW, ULiconH,
ULiconW, LRiconH]), gl.STATIC_DRAW)
}
and then my draw func like this:
gl.uniform1f(P2DRotationForLayer, icon.rotDeg)
gl.uniform2fv(P2DScaleLocForLayer, icon.__size)
gl.uniform4fv(P2DOpacityLocForLayer, __iconColor)
ext.vertexAttribDivisorANGLE(P2DoffsetForLayer, 1) // This makes it instanced!
gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapVertCoordArr)
gl.enableVertexAttribArray(P2DvertexPosForLayer)
gl.vertexAttribPointer(P2DvertexPosForLayer, 3, gl.FLOAT, false, 0, 0)
gl.bindBuffer(gl.ARRAY_BUFFER, this.IconCoordBuff)
gl.enableVertexAttribArray(P2DoffsetForLayer)
gl.vertexAttribPointer(P2DoffsetForLayer, 2, gl.FLOAT, false, 0, 0)
gl.bindTexture(gl.TEXTURE_2D, IconMap[icon.mapIndex].texture)
gl.disable(gl.BLEND)
for (var j = this.StartCountArr.length; j--;) {
this.DrawInstances(this.StartCountArr[j].start, this.StartCountArr[j].count, j)
}
ext.vertexAttribDivisorANGLE(P2DoffsetForLayer, 0)
and my DrawInstances func like this:
DrawInstances(start, count, j) {
const {
gl, ext,
P2DtextCoordLocForLayer,
} = this.FGlobe
gl.bindBuffer(gl.ARRAY_BUFFER, this.IconMapTexCoordArr[j])
gl.enableVertexAttribArray(P2DtextCoordLocForLayer)
gl.vertexAttribPointer(P2DtextCoordLocForLayer, 2, gl.FLOAT, false, 0, 0)
gl.bindBuffer(gl.ARRAY_BUFFER, null)
ext.drawArraysInstancedANGLE(gl.TRIANGLES, start, 6, count)
}
Actually some icons drawed right I see 2 different icon but there is one type more look like this:
|\
| \
| \
| /
| /
|/
my icons only two triangle like below, I dont set any shape like above,
______
|\ |
| \ |
| \ |
| \|
------
Here's a sample drawing multiple sprites from a sprite sheet using instanced drawing.
Note if it was me I'd use a matrix for each instance like this example but I thought the code would be simpler using offset and scale here.
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) alert('need ANGLE_instanced_arrays');
const vs = `
attribute vec2 position;
attribute vec2 uv;
attribute vec2 offset; // instanced
attribute vec2 scale; // instanced
attribute vec2 uvOffset; // instanced
attribute vec2 uvScale; // instanced
uniform mat4 matrix;
varying vec2 v_uv;
void main() {
gl_Position = matrix * vec4(position * scale + offset, 0, 1);
v_uv = uv * uvScale + uvOffset;
}
`;
const fs = `
precision highp float;
varying vec2 v_uv;
uniform sampler2D spriteAtlas;
void main() {
gl_FragColor = texture2D(spriteAtlas, v_uv);
}
`;
const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const uvLoc = gl.getAttribLocation(program, 'uv');
const offsetLoc = gl.getAttribLocation(program, 'offset');
const scaleLoc = gl.getAttribLocation(program, 'scale');
const uvOffsetLoc = gl.getAttribLocation(program, 'uvOffset');
const uvScaleLoc = gl.getAttribLocation(program, 'uvScale');
const matrixLoc = gl.getUniformLocation(program, 'matrix');
// setup quad positions and uv
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
const uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
]), gl.STATIC_DRAW);
// create typed array for instanced data
const maxSprites = 1000;
const offsets = new Float32Array(maxSprites * 2);
const scales = new Float32Array(maxSprites * 2);
const uvOffsets = new Float32Array(maxSprites * 2);
const uvScales = new Float32Array(maxSprites * 2);
// create buffers fo instanced data
const offsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, offsets.byteLength, gl.DYNAMIC_DRAW);
const scaleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, scaleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, scales.byteLength, gl.DYNAMIC_DRAW);
const uvOffsetBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvOffsetBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uvOffsets.byteLength, gl.DYNAMIC_DRAW);
const uvScaleBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvScaleBuffer);
gl.bufferData(gl.ARRAY_BUFFER, uvScales.byteLength, gl.DYNAMIC_DRAW);
let spriteNdx = 0;
function addSprite(
spriteAtlasWidth, spriteAtlasHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight) {
const off0 = spriteNdx * 2;
const off1 = off0 + 1;
offsets[off0] = dstX;
offsets[off1] = dstY;
scales[off0] = dstWidth;
scales[off1] = dstHeight;
uvOffsets[off0] = srcX / spriteAtlasWidth;
uvOffsets[off1] = srcY / spriteAtlasHeight;
uvScales[off0] = srcWidth / spriteAtlasWidth;
uvScales[off1] = srcHeight / spriteAtlasHeight;
++spriteNdx;
}
const sprites = [
{msg: 'A', x: 0, y: 0, w: 64, h: 32, bg: 'red', fg: 'yellow'},
{msg: 'B', x: 64, y: 0, w: 64, h: 32, bg: 'blue', fg: 'white' },
{msg: 'C', x: 0, y: 32, w: 40, h: 32, bg: 'green', fg: 'pink' },
{msg: 'D', x: 40, y: 32, w: 48, h: 32, bg: 'purple', fg: 'cyan' },
{msg: 'F', x: 88, y: 32, w: 40, h: 32, bg: 'black', fg: 'red' },
];
// make 5 sprites in an atlas
const spriteAtlasWidth = 128;
const spriteAtlasHeight = 64;
const ctx = document.createElement('canvas').getContext('2d');
ctx.canvas.width = spriteAtlasWidth;
ctx.canvas.height = spriteAtlasHeight;
for (const spr of sprites) {
ctx.fillStyle = spr.bg;
ctx.fillRect(spr.x, spr.y, spr.w, spr.h);
ctx.strokeStyle = spr.fg;
ctx.strokeRect(spr.x + .5, spr.y + .5, spr.w - 1, spr.h - 1);
ctx.fillStyle = spr.fg;
ctx.font = 'bold 26px sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(spr.msg, spr.x + spr.w / 2, spr.y + spr.h / 2);
}
// show the atlas
document.body.appendChild(ctx.canvas);
// copy the atlas to a texture
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, ctx.canvas);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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);
function render(time) {
time *= 0.001; // convert to seconds
spriteNdx = 0;
const numSprites = 10;
for (let i = 0; i < numSprites; ++i) {
const sp = sprites[i % sprites.length];
addSprite(
spriteAtlasWidth, spriteAtlasHeight,
sp.x, sp.y, sp.w, sp.h,
Math.sin(time + i * 15) * gl.canvas.width / 2 + gl.canvas.width / 2,
Math.cos(time + i * 17) * gl.canvas.height / 2 + gl.canvas.height / 2,
sp.w, sp.h,
);
}
// copy the latest sprite instance data
// to their respective buffers and setup
// the attributes.
// NOTE: for the attributes it would be better
// to use a vertex array
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.enableVertexAttribArray(uvLoc);
gl.vertexAttribPointer(uvLoc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, offsets);
gl.enableVertexAttribArray(offsetLoc);
gl.vertexAttribPointer(offsetLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(offsetLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, scaleBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, scales);
gl.enableVertexAttribArray(scaleLoc);
gl.vertexAttribPointer(scaleLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(scaleLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, uvOffsetBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, uvOffsets);
gl.enableVertexAttribArray(uvOffsetLoc);
gl.vertexAttribPointer(uvOffsetLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(uvOffsetLoc, 1);
gl.bindBuffer(gl.ARRAY_BUFFER, uvScaleBuffer);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, uvScales);
gl.enableVertexAttribArray(uvScaleLoc);
gl.vertexAttribPointer(uvScaleLoc, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(uvScaleLoc, 1);
gl.useProgram(program);
// pass in a projection matrix that
// converts to pixel space so the top
// left corner is 0,0 and the bottom right corner
// is canvas.width, canvas.height
//
// if you had a 3d math library this would be something like
// m4.ortho(0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
gl.uniformMatrix4fv(matrixLoc, false, [
2 / gl.canvas.width, 0, 0, 0,
0, -2 / gl.canvas.height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1,
]);
// note as there as only 1 texture and
// uniforms default to 0 we don't need to
// bind the texture to setup a uniform
// as the defaults happen to work.
ext.drawArraysInstancedANGLE(
gl.TRIANGLES,
0,
6, // verts per instance
spriteNdx, // num instances
);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
canvas { border: 1px solid black; margin: 5px; }
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl.min.js"></script>
note I'm not skipping instances but if you want to skip instances then you need to set the offset passed to gl.vertexAttribPointer for each instanced attribute. For example in the code above if you wanted to draw instances 7 to 29 it would be
const numInstancesToSkip = 7;
const numInstancesToDraw = 29 - 7 + 1;
const size = 2; // vec2
const sizeOfFloat = 4;
const offset = numInstancesToSkip * sizeOfFloat * size;
gl.bindBuffer(offsetBuffer);
gl.vertexAttribPointer(offsetLoc, size, gl.FLOAT, false, 0, offset);
gl.bindBuffer(scaleBuffer);
gl.vertexAttribPointer(scaleLoc, size, gl.FLOAT, false, 0, offset);
gl.bindBuffer(uvOffsetBuffer);
gl.vertexAttribPointer(uvOffsetLoc, size, gl.FLOAT, false, 0, offset);
gl.bindBuffer(uvScaleBuffer);
gl.vertexAttribPointer(uvScaleLoc, size, gl.FLOAT, false, 0, offset);
and to draw would be
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, mumInstancesToDraw);
note that offset above is the same for each attribute because all the atrributes are the same size (2) and the same type (gl.FLOAT) and they are all in separate buffers so their base offsets are all 0. If they were different sizes or different types or mixed into the same buffer they'd all require different math.

How to dynamically add objects to scene?

I have to write a program in which i should be able to click a button "add cube" and there will be added as many cubes as I want into the scene. I must be able to select any of them and move around. I managed to create one (hardcoded) cube and apply scaling and translation to it.
I tried to create a class "cube" and invoke intitialization and render as part of an object but didnt work as expected. Here is the working example for one cube and its transformations (note only the scaling and translation work)
HTML:
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Lab 0</title>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec4 vColors;
varying vec4 fragColor;
uniform vec3 theta;
uniform float coeff;
uniform vec3 trCoeff;
uniform mat4 modelView;
uniform mat4 projection;
vec3 angles = radians( theta );
void main() {
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
// Remeber: thse matrices are column-major
mat4 rx = mat4( 1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0 );
mat4 sc = mat4(
coeff, 0, 0, 0,
0, coeff, 0, 0,
0, 0, coeff, 0,
0, 0, 0, 1
);
mat4 tr = mat4(
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
trCoeff.x, trCoeff.y, trCoeff.z, 1
);
fragColor = vColors;
//projection * modelView *
gl_Position = tr * rz * ry * rx * sc * vPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 fragColor;
void main() {
gl_FragColor = vec4(fragColor);
}
</script>
<script type="text/javascript" src="webgl-utils.js"></script>
<script type="text/javascript" src="initShaders.js"></script>
<script type="text/javascript" src="MV.js"></script>
<script type="text/javascript" src="shape0.js"></script>
</head>
<body>
<canvas id="gl-canvas" width="512" height="512">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
<p> </p>
<button id="xButton">Rotate X</button>
<button id="yButton">Rotate Y</button>
<button id="zButton">Rotate Z</button>
<button id="stopButton">Stop Fucking Rotation</button>
<p> </p>
<button id="sUpButton">Scale Up</button>
<button id="sDownButton">Scale Down</button>
<p> </p>
<button id="leftButton">Left</button>
<button id="rightButton">Right</button>
<button id="upButton">Up</button>
<button id="downButton">Down</button>
<button id="closeButton">Closer</button>
<button id="furtherButton">Further</button>
<p> </p>
<button id="Button1">Increase Z</button>
<button id="Button2">Decrease Z</button>
<button id="Button3">Increase R</button>
<button id="Button4">Decrease R</button>
<p> </p>
<button id="Button5">Increase theta</button>
<button id="Button6">Decrease theta</button>
<button id="Button7">Increase phi</button>
<button id="Button8">Decrease phi</button>
<p> </p>
</html>
JS:
"use strict"
var canvas;
var gl;
var thetaRot = [10, 10, 10];
var thetaLoc;
var coeff = 1;
var coeffLoc;
var trCoeff = [0, 0, 0];
var trCoeffLoc;
var flag = true;
var axis = 0;
var xAxis = 0;
var yAxis = 1;
var zAxis = 2;
var near = 0.3;
var far = 3.0;
var radius = 4.0;
var theta = 0.0;
var phi = 0.0;
var dr = 5.0 * Math.PI / 180.0;
var fovy = 45.0; // Field-of-view in Y direction angle (in degrees)
var aspect; // Viewport aspect ratio
var mvMatrix, pMatrix;
var modelView, projection;
var eye;
const at = vec3(0.0, 0.0, 0.0);
const up = vec3(0.0, 1.0, 0.0);
var coord = {
// For cube
'1': vec3(0.1, 0.1, 0.1),
'2': vec3(0.5, 0.1, 0.1),
'3': vec3(0.5, 0.1, 0.5),
'4': vec3(0.1, 0.1, 0.5),
'5': vec3(0.1, 0.5, 0.1),
'6': vec3(0.1, 0.5, 0.5),
'7': vec3(0.5, 0.5, 0.5),
'8': vec3(0.5, 0.5, 0.1),
}
var vertices = [
// For cube
coord[4], coord[3], coord[2],
coord[2], coord[4], coord[1],
coord[1], coord[2], coord[5],
coord[5], coord[8], coord[2],
coord[2], coord[3], coord[8],
coord[8], coord[3], coord[7],
coord[7], coord[3], coord[4],
coord[4], coord[7], coord[6],
coord[6], coord[4], coord[1],
coord[1], coord[5], coord[6],
coord[6], coord[7], coord[8],
coord[8], coord[6], coord[5],
]
var colors = [
// each face of the cube
0, 0, 1, 1.0,
0, 0, 1, 1.0,
0, 0, 1, 1.0,
0, 0, 1, 1.0,
0, 0, 1, 1.0,
0, 0, 1, 1.0,
0, 1, 0, 1.0,
0, 1, 0, 1.0,
0, 1, 0, 1.0,
0, 1, 0, 1.0,
0, 1, 0, 1.0,
0, 1, 0, 1.0,
0, 0.7, 1, 1.0,
0, 0.7, 1, 1.0,
0, 0.7, 1, 1.0,
0, 0.7, 1, 1.0,
0, 0.7, 1, 1.0,
0, 0.7, 1, 1.0,
0.5, 0, 1, 1.0,
0.5, 0, 1, 1.0,
0.5, 0, 1, 1.0,
0.5, 0, 1, 1.0,
0.5, 0, 1, 1.0,
0.5, 0, 1, 1.0,
1, 0, 0, 1.0,
1, 0, 0, 1.0,
1, 0, 0, 1.0,
1, 0, 0, 1.0,
1, 0, 0, 1.0,
1, 0, 0, 1.0,
0.2, 0, 1, 1.0,
0.2, 0, 1, 1.0,
0.2, 0, 1, 1.0,
0.2, 0, 1, 1.0,
0.2, 0, 1, 1.0,
0.2, 0, 1, 1.0,
]
// add vertices for cone
window.onload = init;
function init() {
canvas = document.getElementById("gl-canvas");
gl = WebGLUtils.setupWebGL(canvas);
if (!gl) { alert("WebGL isn't available"); }
aspect = canvas.width / canvas.height;
var program = initShaders(gl, "vertex-shader", "fragment-shader");
gl.useProgram(program);
gl.enable(gl.DEPTH_TEST);
var bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, 50000, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, flatten(vertices));
var vPosition = gl.getAttribLocation(program, "vPosition");
gl.vertexAttribPointer(vPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
var bufferColor = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferColor);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
var vColors = gl.getAttribLocation(program, "vColors");
gl.vertexAttribPointer(vColors, 4, gl.FLOAT, false, 4 * Float32Array.BYTES_PER_ELEMENT, 0);
gl.enableVertexAttribArray(vColors);
thetaLoc = gl.getUniformLocation(program, "theta");
document.getElementById("xButton").onclick = function () {
flag = false
axis = xAxis;
};
document.getElementById("yButton").onclick = function () {
flag = false
axis = yAxis;
};
document.getElementById("zButton").onclick = function () {
flag = false
axis = zAxis;
};
document.getElementById("stopButton").onclick = function () {
flag = true;
};
document.getElementById("sUpButton").onclick = function () {
coeff += 0.1;
};
document.getElementById("sDownButton").onclick = function () {
coeff -= 0.1;
};
document.getElementById("leftButton").onclick = function () {
trCoeff[0] -= 0.1;
};
document.getElementById("rightButton").onclick = function () {
trCoeff[0] += 0.1;
};
document.getElementById("upButton").onclick = function () {
trCoeff[1] += 0.1;
};
document.getElementById("downButton").onclick = function () {
trCoeff[1] -= 0.1;
};
document.getElementById("closeButton").onclick = function () {
trCoeff[2] += 0.1;
};
document.getElementById("furtherButton").onclick = function () {
trCoeff[2] -= 0.1;
};
modelView = gl.getUniformLocation(program, "modelView");
projection = gl.getUniformLocation(program, "projection");
document.getElementById("Button1").onclick = function () { near *= 1.1; far *= 1.1; };
document.getElementById("Button2").onclick = function () { near *= 0.9; far *= 0.9; };
document.getElementById("Button3").onclick = function () { radius *= 2.0; };
document.getElementById("Button4").onclick = function () { radius *= 0.5; };
document.getElementById("Button5").onclick = function () { theta += dr; };
document.getElementById("Button6").onclick = function () { theta -= dr; };
document.getElementById("Button7").onclick = function () { phi += dr; };
document.getElementById("Button8").onclick = function () { phi -= dr; };
coeffLoc = gl.getUniformLocation(program, "coeff");
trCoeffLoc = gl.getUniformLocation(program, "trCoeff");
render();
}
function render() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
if (!flag) {
thetaRot[axis] += 2.0;
}
gl.uniform3fv(thetaLoc, thetaRot);
gl.uniform1f(coeffLoc, coeff);
gl.uniform3fv(trCoeffLoc, trCoeff);
eye = vec3(radius * Math.sin(theta) * Math.cos(phi),
radius * Math.sin(theta) * Math.sin(phi), radius * Math.cos(theta));
mvMatrix = lookAt(eye, at, up);
pMatrix = perspective(fovy, aspect, near, far);
gl.uniformMatrix4fv(modelView, false, flatten(mvMatrix));
gl.uniformMatrix4fv(projection, false, flatten(pMatrix));
gl.drawArrays(gl.TRIANGLES, 0, vertices.length);
requestAnimFrame(render);
}
I expect managing to add an infinite amount of object (or until I run out of ram) to the scene and to apply transformations to one at a time.
WebGL just draws pixels. It doesn't have a concept of a "Scene" and "objects". All of that is 100% up to you. How you represent objects and how you represent the scene is completely up to you. One example
const scene = [];
function addNewObjectToScene() {
const obj = {
x: Math.random() * 400,
y: Math.random() * 400,
};
scene.push(obj);
}
Now add a button or a timer or whatever you want to call addNewObjectToScene
In your own code you'd have a render or drawScene function that goes over your "scene" and draws each object
function render() {
for (const object of scene) {
gl.useProgram(whateverShaderProgramCurrentObjectNeeds);
// setBuffersAndAttributes for object
// for each attribute in object
// gl.bindBuffer(gl.ARRAY_BUFFER, bufferForThisAttribute)
// gl.enableVertexAttribArray(locationForThisAttrubute);
// gl.vertexAttribPointer(locationForThisAttibute, other settings for this attribute)
// set any uniforms. This is probably where you'd use x and y from the code above
// example:
// gl.uniform2f(offsetUniformLocation, object.x, object.y);
// now draw
// gl.drawArrays or gl.drawElements with how ever many vertices are in the current object.
}
}
How you organize your "scene" and what's in your objects is entire up to you. Three.js makes a scene from an object called THREE.Object3D each of which contains array of "children". This forms a scene graph.
Other apps do different things.
Being able to select one is also way too big a topic. What does "select" mean. Can you have an HTML <select> form where you pick from a dropdown list which object you want to select? Maybe there is an <input type="text"> element where you type the name of the object you want to select?
If you want to click on objects, well now you've got a way too broad of a topic to cover. You could store collision data for each object. You could compute the position of each triangle your data makes and see if the mouse hit. You could use GPU picking where you draw the scene with each object in a different color using different shaders and then look at the pixel under the mouse to see what color it is effectively telling you want object was chosen. But all of this is dependent on how you created your scene.
Suggest you read some tutorials on WebGL and on drawing multiple things and maybe a scene graph

Resources