Related
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.
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
I'm drawing two polylines (which are lines in the sample) in webgl with enabled blending.
gl.uniform4f(colorUniformLocation, 0, 0, 0, 0.3);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0.2, -1, 0.2, 1,]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 2);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, -1, 0, 1,0, -1, 0, 1]), gl.STATIC_DRAW);
gl.drawArrays(gl.LINE_STRIP, 0, 4);
Here is the codepen sample.
The left line are crossed with itself and it seems like it blends with itself, so as result it becomes darker.
I would like the blend to work between those polylines, but don't want a polyline to blend with itself. Is there a way to do it?
One way would be to use the stencil test. You'd set webgl so that the stencil stores a certain value when a pixel is drawn and you'd set the stencil test so it fails if it sees that value.
First an example that draws 2 sets of 2 overlapping triangles with blending on. The pairs will get darker where they overlap
function main() {
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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Then the same example with the stencil test on
First we need to ask for a stencil buffer
const gl = someCanvas.getContext('webgl2', {stencil: true});
Then we turn on the stencil test
gl.enable(gl.STENCIL_TEST);
Set up the test so it only draws if the stencil buffer is zero
gl.stencilFunc(
gl.EQUAL, // the test
0, // reference value
0xFF, // mask
);
And set the operation so we increment the stencil when we draw so they will no longer be zero and therefore fail the test
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.INCR, // what to do if both tests pass
);
Between the first draw and the second we clear the stencil buffer
gl.clear(gl.STENCIL_BUFFER_BIT);
Example
function main() {
const m4 = twgl.m4;
const gl = document
.querySelector('canvas')
.getContext('webgl', {stencil: 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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.STENCIL_TEST);
gl.stencilFunc(
gl.EQUAL, // the test
0, // reference value
0xFF, // mask
);
gl.stencilOp(
gl.KEEP, // what to do if the stencil test fails
gl.KEEP, // what to do if the depth test fails
gl.INCR, // what to do if both tests pass
);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
gl.clear(gl.STENCIL_BUFFER_BIT);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Another solution you could also use the depth test if you're drawing 2D stuff. The default depth test only draws if the depth is gl.LESS than the current depth so just turning the depth test on and setting a different depth between draws would also work if the depth of the triangles is the same. You could compute a different depth value for each thing you draw, you'd need to look up the bit resolution of the depth buffer. Or, you could use gl.polygonOffset
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.POLYGON_OFFSET_FILL);
... then ...
for (let i = 0; i < numThingsToDraw; ++i) {
gl.polygonOffset(0, -i); // each thing 1 depth unit less
draw2DThing(things[i]);
}
example
function main() {
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;
}
`;
// compile shader, link program, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
gl.useProgram(programInfo.program);
// create a buffer and put data in it
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
position: {
numComponents: 2,
data: [
-0.5, -0.2,
0.5, -0.2,
0.5, 0.2,
-0.2, -0.5,
-0.2, 0.5,
0.2, 0.5,
],
},
});
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.POLYGON_OFFSET_FILL);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
// calls gl.uniform??
twgl.setUniforms(programInfo, {
color: [0.5, 0, 0, 0.5],
matrix: m4.identity(),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
gl.polygonOffset(0, -1);
twgl.setUniforms(programInfo, {
color: [0, 0, 0.5, 0.5],
matrix: m4.rotateZ(
m4.translation([-0.1, 0.2, 0.0]),
Math.PI * 1.2),
});
twgl.drawBufferInfo(gl, bufferInfo);
}
main();
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
I would like to make it so that these blocks are all drawn to one layer than that entire layer is made transparent. Or if there is a way I can use blend functions or alpha blending to do it that would be fine too. Thanks a lot.
What is your definition of efficient? Under what circumstances? What conditions?
Here's a few solutions. It's hard to tell if they fit without more details.
First let's repo the issue
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(0, .5, 0, .5);
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create buffers and upload vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
render();
function render() {
gl.clearColor(0, .4, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.BLEND);
gl.enable(gl.CULL_FACE);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(programInfo.program);
const halfHeight = 1;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const halfWidth = halfHeight * aspect;
const projection = m4.ortho(
-halfWidth, halfWidth, -halfHeight, halfWidth, 0.1, 20);
const camera = m4.lookAt(
[5, 2, 5], // eye
[0, -.5, 0], // target
[0, 1, 0], // up
);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
for (let x = -1; x <= 1; ++x) {
let mat = m4.translate(viewProjection, [x, 0, 0]);
twgl.setUniforms(programInfo, {
u_matrix: mat,
});
// calls drawArrays or drawElements
twgl.drawBufferInfo(gl, bufferInfo);
}
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Note the example above just clears the background to [0, .4, 0, 1] which is dark green. It then draws 3 cubes using [0, .5, 0, .5] which is full green (as in [0, 1, 0, 1]) except premultiplied by 50% alpha. Using premultiplied colors the blending is set to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) Face culling is on.
As for solutions off the top of my head looking at your picture you could
Draw front to back with z-test on
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(0, .5, 0, .5);
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create buffers and upload vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
render();
function render() {
gl.clearColor(0, .4, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.BLEND);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(programInfo.program);
const halfHeight = 1;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const halfWidth = halfHeight * aspect;
const projection = m4.ortho(
-halfWidth, halfWidth, -halfHeight, halfWidth, 0.1, 20);
const camera = m4.lookAt(
[5, 2, 5], // eye
[0, -.5, 0], // target
[0, 1, 0], // up
);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
for (let x = 1; x >= -1; --x) {
let mat = m4.translate(viewProjection, [x, 0, 0]);
twgl.setUniforms(programInfo, {
u_matrix: mat,
});
// calls drawArrays or drawElements
twgl.drawBufferInfo(gl, bufferInfo);
}
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
Note the only changes to the top version are the addition of
gl.enable(gl.DEPTH_TEST);
And drawing in reverse order
for (let x = 1; x >= -1; --x) {
I have no idea how your data is stored. Assuming it's a grid you'd have to write code to iterate over the grid in the correct order from the view of the camera.
Your example only shows a green background so you could just draw opaque and multiply or mix by a color, the same color as your background.
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
}
`;
const fs = `
precision mediump float;
uniform vec4 u_backgroundColor;
uniform float u_mixAmount;
void main() {
gl_FragColor = mix(vec4(0, 1, 0, 1), u_backgroundColor, u_mixAmount);
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create buffers and upload vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
render();
function render() {
gl.clearColor(0, .4, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.useProgram(programInfo.program);
const halfHeight = 1;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const halfWidth = halfHeight * aspect;
const projection = m4.ortho(
-halfWidth, halfWidth, -halfHeight, halfWidth, 0.1, 20);
const camera = m4.lookAt(
[5, 2, 5], // eye
[0, -.5, 0], // target
[0, 1, 0], // up
);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
for (let x = 1; x >= -1; --x) {
let mat = m4.translate(viewProjection, [x, 0, 0]);
twgl.setUniforms(programInfo, {
u_matrix: mat,
u_backgroundColor: [0, 0.4, 0, 1],
u_mixAmount: 0.5,
});
// calls drawArrays or drawElements
twgl.drawBufferInfo(gl, bufferInfo);
}
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
The solution above changes the fragment shader to
uniform vec4 u_backgroundColor;
uniform float u_mixAmount;
void main() {
gl_FragColor = mix(vec4(0, 1, 0, 1), u_backgroundColor, u_mixAmount);
}
Where vec4(0, 1, 0, 1) is the cube's green color. We then set u_backgroundColor to match the background color of 0, .4, 0, 1 and set u_mixAmount to .5 (50%)
This solution might sound dumb but it's common to want to fade to a background color which is basically how fog works. You don't actually make things more transparent in the distance you just draw with the fog color.
draw all the tiles without transparency into another texture, then draw that texture with transparency
const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl', {alpha: false});
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;
void main() {
gl_Position = u_matrix * position;
}
`;
const fs = `
precision mediump float;
void main() {
gl_FragColor = vec4(0, 1, 0, 1);
}
`;
const mixVs = `
attribute vec4 position;
attribute vec2 texcoord;
uniform mat4 u_matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * position;
v_texcoord = texcoord;
}
`;
const mixFs = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_tex;
uniform float u_alpha;
void main() {
gl_FragColor = texture2D(u_tex, v_texcoord) * u_alpha;
}
`;
// compile shaders, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
const mixProgramInfo = twgl.createProgramInfo(gl, [mixVs, mixFs]);
// create buffers and upload vertex data
const bufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);
const xyQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
// create framebuffer with RGBA/UNSIGNED_BYTE texture
// and depth buffer renderbuffer that matches the size
// of the canvas
const fbi = twgl.createFramebufferInfo(gl);
render();
function render() {
renderTiles();
renderScene();
}
function renderScene() {
// bind canvas and set viewport
twgl.bindFramebufferInfo(gl, null);
gl.clearColor(0, 0.4, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
gl.useProgram(mixProgramInfo.program);
twgl.setBuffersAndAttributes(gl, mixProgramInfo, xyQuadBufferInfo);
twgl.setUniforms(mixProgramInfo, {
u_matrix: m4.identity(),
u_tex: fbi.attachments[0], // the texture
u_alpha: .5,
});
// calls drawArrays or drawElements
twgl.drawBufferInfo(gl, xyQuadBufferInfo);
}
function renderTiles() {
// bind framebuffer and set viewport
twgl.bindFramebufferInfo(gl, fbi);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.disable(gl.BLEND);
gl.enable(gl.CULL_FACE);
gl.enable(gl.DEPTH_TEST);
gl.useProgram(programInfo.program);
const halfHeight = 1;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const halfWidth = halfHeight * aspect;
const projection = m4.ortho(
-halfWidth, halfWidth, -halfHeight, halfWidth, 0.1, 20);
const camera = m4.lookAt(
[5, 2, 5], // eye
[0, -.5, 0], // target
[0, 1, 0], // up
);
const view = m4.inverse(camera);
const viewProjection = m4.multiply(projection, view);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
for (let x = 1; x >= -1; --x) {
let mat = m4.translate(viewProjection, [x, 0, 0]);
twgl.setUniforms(programInfo, {
u_matrix: mat,
u_backgroundColor: [0, 0.4, 0, 1],
u_mixAmount: 0.5,
});
// calls drawArrays or drawElements
twgl.drawBufferInfo(gl, bufferInfo);
}
}
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>
The change above creates an RGBA texture and a depth renderbuffer the same size as the canvas and attaches them to a framebuffer. It then renders the tiles into that texture opaquely. Then it renders the texture over the canvas with 50% alpha. Note that the canvas itself is set to {alpha: false} so that the canvas doesn't blend with the elements behind it.
Generate new geometry that doesn't have the hidden surfaces
The problem is your drawing 3 cubes and the edges between them. A Minecraft like solution would probably generate new geometry that didn't have the inner edges. It would be pretty easy to walk a grid of tiles and decide whether or not to add that edge of the cube based on if there is a neighbor or not.
In Minecraft they only have to generate new geometry when blocks are added or removed and with some creative coding that might involve only modifying a few vertices rather than regenerating the entire mesh. They also probably generate in a gird like very 64x64x64 area.
I am new to WebGL and I'm try to learn create a simple checkerboard game.
I have created the checker boxes based on this tutorial (http://www.martinchristen.ch/webgl/tutorial03).
Now I'm stuck at this part where I need to add in the pawn.
Anybody kind enough to guide me?
HTML
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec2 aVertexPosition;
varying vec2 pos;
void main() {
gl_Position = vec4(aVertexPosition, 0.0, 1.0);
pos = aVertexPosition;
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 uColor;
varying vec2 pos;
void main() {
float x = 0.5*(pos.x+1.0); // range [0,1]
float y = 0.5*(pos.y+1.0); // range [0,1]
// checkerboard shader:
if ((mod(4.0*x, 1.0) < 0.5) ^^ (mod(4.0*y, 1.0) < 0.5))
{
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
else
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
}
</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="checker.js"></script>
<body>
<canvas id="gl-canvas" width="640" height="640">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
Javascript
"use strict";
var canvas;
var gl;
var movement = false;
var numItems;
var vertices = new Float32Array([
-1, 1, 1, 1, 1,-1, // Triangle 1
-1, 1, 1, -1,-1,-1, // Triangle 2
-1, 1, 1, -1,-1,-1
]);
var colors = [
vec4( 0.0, 0.0, 0.0, 1.0 ), // black
vec4( 1.0, 0.0, 0.0, 1.0 ) // red
];
window.onload = function init() {
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
canvas.addEventListener("mousedown", function(event){
movement = true;
//get x y coordinates
var t = vec2(2*event.clientX/canvas.width-1,
2*(canvas.height-event.clientY)/canvas.height-1);
console.log(t);
});
canvas.addEventListener("mouseup", function(event){
movement = false;
});
canvas.addEventListener("mousemove", function(event){
if (movement) {
}
});
// Setup Shaders:
var v = document.getElementById("vertex-shader").firstChild.nodeValue;
var f = document.getElementById("fragment-shader").firstChild.nodeValue;
var vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, v);
gl.compileShader(vs);
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
alert(gl.getShaderInfoLog(vs));
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, f);
gl.compileShader(fs);
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
alert(gl.getShaderInfoLog(fs));
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
alert(gl.getProgramInfoLog(program));
var vbuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var itemSize = 2; // we have 2 triangles!
numItems = vertices.length / itemSize;
// Viewport
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Setup Geometry:
gl.useProgram(program);
program.uColor = gl.getUniformLocation(program, "uColor");
gl.uniform4fv(program.uColor, [0.5, 0.0, 0.0, 1.0]);
program.aVertexPosition = gl.getAttribLocation(program, "aVertexPosition");
gl.enableVertexAttribArray(program.aVertexPosition);
gl.vertexAttribPointer(program.aVertexPosition, itemSize, gl.FLOAT, false, 0, 0);
render();
}
function render() {
gl.drawArrays(gl.TRIANGLES, 0, numItems);
window.requestAnimFrame(render);
}
Thanks a lot!