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 trying to draw simple shapes using WebGL LINES mode. These are my vertices:
<canvas width="200" height="200".../>
...
vertices.push(
0.01,0.13, 0.03,0.15,
0.03,0.15, 0.09,0.15,
0.09,0.15, 0.11,0.13,
0.11,0.13, 0.11,0.03,
0.11,0.03, 0.09,0.01,
0.09,0.01, 0.03,0.01,
0.03,0.01, 0.01,0.03,
0.01,0.03, 0.01,0.13);
The picture shows the result which is far from my expectations:
screen
I tried different things without success. How to fix it?
Here is a working example (DPI should be 1):
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2", { antialias: false });
var dpi = window.devicePixelRatio || 1;
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
//vertex shader
var vertexShaderSource = `#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
`;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
//fragment shader
var fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0, 0, 0, 1);
}
`;
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
//program
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
//vertices
var vertices = [];
vertices.push(
0.01,0.13, 0.03,0.15,
0.03,0.15, 0.09,0.15,
0.09,0.15, 0.11,0.13,
0.11,0.13, 0.11,0.03,
0.11,0.03, 0.09,0.01,
0.09,0.01, 0.03,0.01,
0.03,0.01, 0.01,0.03,
0.01,0.03, 0.01,0.13);
//position attribute
var positionLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINES, 0, vertices.length / 2);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="200" height="200" style="width: 200px; height: 200px"></canvas>
</body>
</html>
I found that when anti-aliasing is turned on, the shape is more acurrate but blurry.
I found out that do draw the shape as expected the coordinates should be half pixel:
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl2", { antialias: false });
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
//vertex shader
var vertexShaderSource = `#version 300 es
in vec2 a_position;
void main() {
gl_Position = vec4(a_position, 0, 1);
}
`;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
//fragment shader
var fragmentShaderSource = `#version 300 es
precision highp float;
out vec4 outColor;
void main() {
outColor = vec4(0, 0, 0, 1);
}
`;
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
//program
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
//vertices
var vertices = [];
vertices.push(
0.015,0.135, 0.035,0.155,
0.035,0.155, 0.095,0.155,
0.095,0.155, 0.115,0.135,
0.115,0.135, 0.115,0.035,
0.115,0.035, 0.095,0.015,
0.095,0.015, 0.035,0.015,
0.035,0.015, 0.015,0.035,
0.015,0.035, 0.015,0.135);
//position attribute
var positionLocation = gl.getAttribLocation(program, "a_position");
var positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.LINES, 0, vertices.length / 2);
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<canvas id="canvas" width="200" height="200" style="width: 200px; height: 200px"></canvas>
</body>
</html>
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
My problem is that I'm using the TWGL library to make shaders with textures, it happens that when they load the images always appears a blue box before loading. I could not find anything about that problem or in the documentation, or even in other works.
How can I remove that blue box?
"use strict";
class MathUtils {
constructor() {}
lerp(a, b, n) {
return n * (b - a) + a;
}
}
const main = () => {
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const mathUtils = new MathUtils();
const mouse = { x: 0, y: 0 };
const lastmouse = [0, 0];
if (!gl) {
return;
}
const textures = [
"https://i.ibb.co/9WvFgbZ/fancycrave-165873-unsplash.jpg",
"https://i.ibb.co/NSfVqTq/map2.jpg",
"https://i.ibb.co/cy79kN4/blur.jpg"
];
const textLoaded = [];
for (let tex of textures) {
textLoaded.push(
twgl.createTexture(gl, {
src: tex,
crossOrigin: ""
})
);
}
let originalImage = { width: 1, height: 1 }; // replaced after loading
const text0 = twgl.createTexture(
gl,
{
src: textures[0],
crossOrigin: ""
},
(err, texture, source) => {
originalImage = source;
}
);
let uniforms = {};
let anim = { value: 1 };
// compile shaders, link program, lookup location
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for a quad
const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
const render = time => {
time *= 0.001; // seconds
twgl.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.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const canvasAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const imageAspect = originalImage.width / originalImage.height;
let mat = m3.scaling(imageAspect / canvasAspect, -1);
// this assumes we want to fill vertically
let horizontalDrawAspect = imageAspect / canvasAspect;
let verticalDrawAspect = -1;
// does it fill horizontally?
if (horizontalDrawAspect < 1) {
// no it does not so scale so we fill horizontally and
// adjust vertical to match
verticalDrawAspect /= horizontalDrawAspect;
horizontalDrawAspect = 1;
}
mat = m3.scaling(horizontalDrawAspect, verticalDrawAspect);
uniforms = {
u_text: textLoaded[0],
u_map: textLoaded[1],
u_blur: textLoaded[2],
u_matrix: mat,
u_time: time * 10,
u_res: [gl.canvas.clientWidth, gl.canvas.clientHeight],
u_mouse: lastmouse
};
// calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
twgl.setUniforms(programInfo, uniforms);
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
lastmouse[0] = mathUtils.lerp(lastmouse[0], mouse.x, 0.1);
lastmouse[1] = mathUtils.lerp(lastmouse[1], mouse.y, 0.1);
requestAnimationFrame(render);
};
requestAnimationFrame(render);
canvas.addEventListener("mousemove", ({ clientX, clientY }) => {
mouse.x = -clientX / innerWidth;
mouse.y = -clientY / innerHeight;
});
};
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="canvas"></canvas>
<script id="vs" type="f">
attribute vec2 position;
attribute vec2 texcoord;
uniform mat3 u_matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = vec4(u_matrix * vec3(position, 1), 1);
v_texcoord = texcoord;
}
</script>
<script id="fs" type="f">
precision mediump float;
uniform vec2 u_mouse;
uniform vec2 u_res;
uniform float u_time;
uniform float u_dpi;
uniform sampler2D u_text;
uniform sampler2D u_map;
uniform sampler2D u_blur;
varying vec2 v_texcoord;
void main(){
float distAmount = .003;
vec2 uv = v_texcoord;
vec2 parallax = u_mouse * 0.07;
float freq = 70.0;
float amp = 0.004;
vec4 map = texture2D(u_map, uv);
float dethMap = map.r - .5;
float distMap = map.g;
float x = uv.y * freq + u_time * 3.;
float y = uv.x * freq + u_time * .3;
float distX = cos(x+y) * amp * cos(y);
float distY = sin(x-y) * amp * cos(y);
vec2 distPosition = vec2(uv.x + distX * distMap, uv.y + distY * distMap);
vec2 turbulance = distPosition + (parallax * dethMap);
vec4 ori_img = texture2D(u_text, turbulance);
vec4 blur_img = texture2D(u_blur, turbulance);
vec4 color = mix(ori_img, blur_img, length(distPosition) * distAmount);
gl_FragColor = color;
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/m3.js"></script>
I don't see any blue box
You can set the default color twgl uses to make textures immediately usable by calling
twgl.setDefaults({
textureColor: [0, 0, 0, 0], // make initial color transparent black
});
before loading textures.
You can set any individual texture's temporary initial color via the color option
const texture = twgl.createTexture(gl, {
src: 'https://somedomain.com/path/to/someimage.jpg',
color: [0, 0, 0, 0], // make initial color transparent black
});
Also note you can load all images in one call to twgl.createTextures and that twgl should handle cross origin automatically
// replaced after loading
let srcs = {
text: { width: 1, height: 1 },
};
const textures = twgl.createTextures(gl, {
text: {src: "https://i.ibb.co/9WvFgbZ/fancycrave-165873-unsplash.jpg" },
map: {src: "https://i.ibb.co/NSfVqTq/map2.jpg" },
blur: {src: "https://i.ibb.co/cy79kN4/blur.jpg" },
}, (err, textures, sources) => {
srcs = sources;
});
const imageAspect = srcs.text.width / srcs.text.height;
uniforms = {
u_text: textures.text,
u_map: textures.map,
u_blur: textures.blur,
...
"use strict";
class MathUtils {
constructor() {}
lerp(a, b, n) {
return n * (b - a) + a;
}
}
const main = () => {
const canvas = document.getElementById("canvas");
const gl = canvas.getContext("webgl");
const mathUtils = new MathUtils();
const mouse = { x: 0, y: 0 };
const lastmouse = [0, 0];
if (!gl) {
return;
}
twgl.setDefaults({
textureColor: [0, 0, 0, 0],
});
// replaced after loading
let srcs = {
text: { width: 1, height: 1 },
};
const textures = twgl.createTextures(gl, {
text: {src: "https://i.ibb.co/9WvFgbZ/fancycrave-165873-unsplash.jpg" },
map: {src: "https://i.ibb.co/NSfVqTq/map2.jpg" },
blur: {src: "https://i.ibb.co/cy79kN4/blur.jpg" },
}, (err, textures, sources) => {
srcs = sources;
});
let uniforms = {};
let anim = { value: 1 };
// compile shaders, link program, lookup location
const programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData for a quad
const bufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
const render = time => {
time *= 0.001; // seconds
twgl.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.useProgram(programInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
const canvasAspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const imageAspect = srcs.text.width / srcs.text.height;
let mat = m3.scaling(imageAspect / canvasAspect, -1);
// this assumes we want to fill vertically
let horizontalDrawAspect = imageAspect / canvasAspect;
let verticalDrawAspect = -1;
// does it fill horizontally?
if (horizontalDrawAspect < 1) {
// no it does not so scale so we fill horizontally and
// adjust vertical to match
verticalDrawAspect /= horizontalDrawAspect;
horizontalDrawAspect = 1;
}
mat = m3.scaling(horizontalDrawAspect, verticalDrawAspect);
uniforms = {
u_text: textures.text,
u_map: textures.map,
u_blur: textures.blur,
u_matrix: mat,
u_time: time * 10,
u_res: [gl.canvas.clientWidth, gl.canvas.clientHeight],
u_mouse: lastmouse
};
// calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
twgl.setUniforms(programInfo, uniforms);
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, bufferInfo);
lastmouse[0] = mathUtils.lerp(lastmouse[0], mouse.x, 0.1);
lastmouse[1] = mathUtils.lerp(lastmouse[1], mouse.y, 0.1);
requestAnimationFrame(render);
};
requestAnimationFrame(render);
canvas.addEventListener("mousemove", ({ clientX, clientY }) => {
mouse.x = -clientX / innerWidth;
mouse.y = -clientY / innerHeight;
});
};
main();
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas id="canvas"></canvas>
<script id="vs" type="f">
attribute vec2 position;
attribute vec2 texcoord;
uniform mat3 u_matrix;
varying vec2 v_texcoord;
void main() {
gl_Position = vec4(u_matrix * vec3(position, 1), 1);
v_texcoord = texcoord;
}
</script>
<script id="fs" type="f">
precision mediump float;
uniform vec2 u_mouse;
uniform vec2 u_res;
uniform float u_time;
uniform float u_dpi;
uniform sampler2D u_text;
uniform sampler2D u_map;
uniform sampler2D u_blur;
varying vec2 v_texcoord;
void main(){
float distAmount = .003;
vec2 uv = v_texcoord;
vec2 parallax = u_mouse * 0.07;
float freq = 70.0;
float amp = 0.004;
vec4 map = texture2D(u_map, uv);
float dethMap = map.r - .5;
float distMap = map.g;
float x = uv.y * freq + u_time * 3.;
float y = uv.x * freq + u_time * .3;
float distX = cos(x+y) * amp * cos(y);
float distY = sin(x-y) * amp * cos(y);
vec2 distPosition = vec2(uv.x + distX * distMap, uv.y + distY * distMap);
vec2 turbulance = distPosition + (parallax * dethMap);
vec4 ori_img = texture2D(u_text, turbulance);
vec4 blur_img = texture2D(u_blur, turbulance);
vec4 color = mix(ori_img, blur_img, length(distPosition) * distAmount);
gl_FragColor = color;
}
</script>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://webglfundamentals.org/webgl/resources/m3.js"></script>
I am trying to figure out how to achieve color and alpha blending between primitives using Regl.
I know Regl command's have a blend property and I've tried replicating the following webgl settings that do the trick:
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
using the following blend settings in Regl:
blend: {
enable: true,
func: { src: 'src alpha', dst:'one minus src alpha' }
},
But the blending only seem to work in regard to the background color but not between the points. (See the example below.)
Example: http://jsfiddle.net/8gyf7pek/13/
const canvas1 = document.querySelector('#c1');
const canvas2 = document.querySelector('#c2');
//////////////////////////////////////////////
// PURE WEBGL
//////////////////////////////////////////////
const gl = canvas1.getContext('webgl');
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
const vertexShaderSource = `
attribute vec2 position;
attribute vec4 color;
varying vec4 v_color;
void main() {
gl_PointSize = 50.0;
gl_Position = vec4(position, 0, 1);
v_color = color;
}
`;
const fragmentShaderSource = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
const program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);
const positionAttributeLocation = gl.getAttribLocation(program, 'position');
const colorAttributeLocation = gl.getAttribLocation(program, 'color');
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 2, gl.FLOAT, false, 0, 0);
const colorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
gl.enableVertexAttribArray(colorAttributeLocation);
gl.vertexAttribPointer(colorAttributeLocation, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-0.05, -0.05, -0.05, 0.05, 0.05, 0.05, 0.05, -0.05,
]), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
const red = [1, 0, 0, 0.5];
const blue = [0, 0, 1, 0.5];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
...red, ...red,
...blue, ...blue,
]), gl.STATIC_DRAW);
gl.drawArrays(gl.POINTS, 0, 4);
function createShader(gl, type, shaderSource) {
const shader = gl.createShader(type);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if(!success) {
console.warn(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
return shader;
}
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const success = gl.getProgramParameter(program, gl.LINK_STATUS);
if(!success) {
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
return program;
}
//////////////////////////////////////////////
// REGL
//////////////////////////////////////////////
const regl = createREGL(canvas2);
regl.clear({ color: [0, 0, 0, 0], depth: 1 });
regl({
frag: `
precision mediump float;
varying vec4 fragColor;
void main () {
gl_FragColor = fragColor;
}`,
vert: `
precision mediump float;
attribute vec2 position;
attribute vec4 color;
varying vec4 fragColor;
uniform float pointWidth;
void main () {
fragColor = color;
gl_PointSize = pointWidth;
gl_Position = vec4(position, 0, 1);
}`,
attributes: {
position: [
[-0.05, -0.05],
[-0.05, 0.05],
[0.05, -0.05],
[0.05, 0.05],
],
color: [
[1, 0, 0, 0.5],
[1, 0, 0, 0.5],
[0, 0, 1, 0.5],
[0, 0, 1, 0.5]
],
},
uniforms: {
pointWidth: 50,
},
blend: {
enable: true,
func: { src: 'src alpha', dst:'one minus src alpha' }
},
count: 4,
primitive: 'points',
})();
#bg {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
color: #808080;
background: black;
}
#c1, #c2 {
width: 240px;
height: 240px;
border: 1px solid white;
}
em {
display: block;
}
<div id="bg">
<canvas id="c1"></canvas>
<canvas id="c2"></canvas>
<em>Left is pure WebGL. Right is Regl.</em>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/regl/1.3.7/regl.min.js"></script>
Am I doing something wrong? How could I achieve the same kind of blending that the pure webgl code produces? Thanks!
Thanks to this great answer I figured it out:
In a nutshell, the blend function needs to be adjusted and the depth test needs to be disabled. (But I still don't know why the blend function, that worked in the vanilla WebGL example, didn't work in Regl)
Use the following blend mode
blend: {
enable: true,
func: {
srcRGB: 'src alpha',
srcAlpha: 'src alpha',
dstRGB: 'one minus src alpha',
dstAlpha: 'one minus src alpha',
},
},
Disable depth test
depth: { enable: false },
Here's the fixed example from my question: http://jsfiddle.net/8gyf7pek/22/