Related
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'm start learning WebGL and find some tutorials in Internet how to create first project. The tutorial is so easy for me to compile because i draw code to compile .
Have this errors on compile project in Edge:
WEBGL11163: getAttribLocation: Program not linked.
index.html (61,1)
WEBGL11163: enableVertexAttribArray: Index exceeds MAX_VERTEX_ATTRIBS.
index.html (62,1)
WEBGL11059: INVALID_VALUE: vertexAttribPointer: vertex attribute size must be 1, 2, 3 or 4
index.html (63.1)
WEBGL11042: INVALID_OPERATION: useProgram: program is not connected
index.html (65.1)
WEBGL11163: drawArrays: A program must be bound.
index.html (66,1)
From this code:
const canvas = document.getElementById('object');
const gl = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
const vertexData = [
0, 1, 0,
1, -1, 0,
-1, -1, 0
];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, `
attribute vec3 position;
void main() {
gl_Position = vec4(position, 1);
}
`);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, `
void main() {
gl.fragColor = vec4(1, 0, 0, 1);
}
`);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
const positionLocation = gl.getAttribLocation(program, `position`);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, true, 0, 0);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
What you can say about this subject because in tutorial it's working correctly.
First off let me recommend some different tutorials
Second off, your shaders are bad or rather your fragment shader is bad.
When compiling and linking shader programs you should check for errors by calling gl.getShaderParameter(someShader, gl.COMPILE_STATUS) and gl.getProgramParameter(someProgram, gl.LINK_STATUS). If either return false then your shaders had an error. You can get the compile error with gl.getShaderInfoLog(someShader) and the link error with gl.getProgramInfoLog(someProgram). The fact that these are not in your example suggest the tutorial you're using has so some issues.
As for your shaders you typed gl.fragColor instead of gl_FragColor
const canvas = document.getElementById('object');
const gl = canvas.getContext('webgl');
if (!gl) {
throw new Error('WebGL not supported');
}
const vertexData = [
0, 1, 0,
1, -1, 0,
-1, -1, 0
];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexData), gl.STATIC_DRAW);
function createShader(gl, type, src) {
const shader = gl.createShader(type);
gl.shaderSource(shader, src);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(shader));
throw new Error('could not compile shader');
}
return shader;
}
const vertexShader = createShader(gl, gl.VERTEX_SHADER, `
attribute vec3 position;
void main() {
gl_Position = vec4(position, 1);
}
`);
const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, `
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}
`);
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.log(gl.getProgramInfoLog(program));
throw new Error('could not link shaders');
}
const positionLocation = gl.getAttribLocation(program, `position`);
gl.enableVertexAttribArray(positionLocation);
gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, true, 0, 0);
gl.useProgram(program);
gl.drawArrays(gl.TRIANGLES, 0, 3);
canvas { border: 1px solid black; }
<canvas id="object"></canvas>
I created a simple webGL script, it apply pixel color depending on (x,y) pixel position
What I get:
here's what I did:
#ifdef GL_ES
precision mediump float;
#endif
uniform float width;
uniform float height;
uniform float time;
void main() {
vec2 u_resolution = vec2(width, height);
vec2 st = gl_FragCoord.xy / u_resolution;
gl_FragColor = vec4(st.x, st.y, 0.5, 1.0);
}
Codepen: Hello WebGL
I'm trying to convert it to webGL2 but I don't know how to get current pixel position.
here's what I tried:
#version 300 es
#ifdef GL_ES
precision mediump float;
#endif
uniform float width;
uniform float height;
uniform float time;
out vec4 color;
void main() {
vec2 u_resolution = vec2(width, height);
vec2 st = color.xy / u_resolution;
color = vec4(st.x, st.y, 0.5, 1.0);
}
Codepen: Hello WebGL2
How to get current pixel position in webgl2?
gl_FragCoord is still the correct way in WebGL2
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var gl = canvas.getContext("webgl2");
//************** Shader sources **************
var vertexSource = `
#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
var fragmentSource = `
#version 300 es
#ifdef GL_ES
precision mediump float;
#endif
uniform float width;
uniform float height;
uniform float time;
out vec4 color;
void main() {
vec2 u_resolution = vec2(width, height);
vec2 st = gl_FragCoord.xy / u_resolution;
color = vec4(st.x, st.y, 0.5, 1.0);
}`;
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.uniform1f(widthHandle, window.innerWidth);
gl.uniform1f(heightHandle, window.innerHeight);
}
//Compile shader and combine with source
function compileShader(shaderSource, shaderType) {
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw "Shader compile failed with: " + gl.getShaderInfoLog(shader);
}
return shader;
}
//From https://codepen.io/jlfwong/pen/GqmroZ
//Utility to complain loudly if we fail to find the attribute/uniform
function getAttribLocation(program, name) {
var attributeLocation = gl.getAttribLocation(program, name);
if (attributeLocation === -1) {
throw "Cannot find attribute " + name + ".";
}
return attributeLocation;
}
function getUniformLocation(program, name) {
var attributeLocation = gl.getUniformLocation(program, name);
if (attributeLocation === -1) {
throw "Cannot find uniform " + name + ".";
}
return attributeLocation;
}
//************** Create shaders **************
//Create vertex and fragment shaders
var vertexShader = compileShader(vertexSource.trim(), gl.VERTEX_SHADER);
var fragmentShader = compileShader(fragmentSource.trim(), gl.FRAGMENT_SHADER);
//Create shader programs
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
//Set up rectangle covering entire canvas
var vertexData = new Float32Array([
-1.0,
1.0, // top left
-1.0,
-1.0, // bottom left
1.0,
1.0, // top right
1.0,
-1.0 // bottom right
]);
//Create vertex buffer
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
// Layout of our data in the vertex buffer
var positionHandle = getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionHandle);
gl.vertexAttribPointer(
positionHandle,
2, // position is a vec2 (2 values per component)
gl.FLOAT, // each component is a float
false, // don't normalize values
2 * 4, // two 4 byte float components per vertex (32 bit float is 4 bytes)
0 // how many bytes inside the buffer to start from
);
//Set uniform handle
var timeHandle = getUniformLocation(program, "time");
var widthHandle = getUniformLocation(program, "width");
var heightHandle = getUniformLocation(program, "height");
gl.uniform1f(widthHandle, window.innerWidth);
gl.uniform1f(heightHandle, window.innerHeight);
function draw() {
//Send uniforms to program
gl.uniform1f(timeHandle, performance.now());
//Draw a triangle strip connecting vertices 0-4
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(draw);
}
draw();
html {
overflow: hidden;
}
canvas {
display: block;
}
Some other random tips.
These ifdefs are irrelevant
#ifdef GL_ES
precision mediump float;
#endif
Just
precision mediump float;
is fine.
I'm guessing this obvious but why pass in width and height separate?
How about just
uniform vec2 u_resolution;
No reason to call performance.now. The time is passed to your requestAnimationFrame callback
function draw(time) {
//Send uniforms to program
gl.uniform1f(timeHandle, time);
...
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
The code checks for compile errors but not link errors
You should check for link errors
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw "Program link failed with: " + gl.getProgramInfoLog(program);
}
There will be link errors if your varyings don't match and further the spec doesn't require compiling to ever fail even on bad shaders. Rather it only requires if they were bad to fail to link.
window.innerWidth
see: this
gl.getUniformLocation returns null if the uniform does not exist
The code is checking for -1 which is correct for attributes but not for uniforms.
throwing on attributes and uniforms not existing
Of course it's helpful to know they don't exist but it's common to debug shaders by commenting things out or editing. For example lets say nothing appears on the screen. If it was me the first thing I'd do is change the fragment shader to this
const fragmentSource = `
#version 300 es
precision mediump float;
uniform vec2 u_resolution;
uniform float time;
out vec4 color;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
color = vec4(st.x, st.y, 0.5, 1.0);
color = vec4(1, 0, 0, 1); // <----------------------
}`;
Just output a solid color to check if the issue is in the fragment shader or the vertex shader. The moment I do that most WebGL implentations will optimize out u_resolution and the code that throws when looking up locations effectively makes the program undebuggable.
In fact the code only runs currently because of the previous bug checking for -1 instead of null. With that bug fixed the code crashes beacuse time is optimized out.
var canvas = document.body.appendChild(document.createElement("canvas"));
var gl = canvas.getContext("webgl2");
//************** Shader sources **************
var vertexSource = `
#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
var fragmentSource = `
#version 300 es
precision mediump float;
uniform vec2 u_resolution;
uniform float time;
out vec4 color;
void main() {
vec2 st = gl_FragCoord.xy / u_resolution;
color = vec4(st.x, st.y, 0.5, 1.0);
}`;
function resize() {
if (canvas.width !== canvas.clientWidth || canvas.height !== canvas.clientHeight) {
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.uniform2f(resHandle, canvas.width, canvas.height);
}
}
//Compile shader and combine with source
function compileShader(shaderSource, shaderType) {
var shader = gl.createShader(shaderType);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw "Shader compile failed with: " + gl.getShaderInfoLog(shader);
}
return shader;
}
//From https://codepen.io/jlfwong/pen/GqmroZ
//Utility to complain loudly if we fail to find the attribute/uniform
function getAttribLocation(program, name) {
var attributeLocation = gl.getAttribLocation(program, name);
if (attributeLocation === -1) {
console.warn("Cannot find attribute", name);
}
return attributeLocation;
}
function getUniformLocation(program, name) {
var uniformLocation = gl.getUniformLocation(program, name);
if (uniformLocation === null) {
console.warn("Cannot find uniform", name);
}
return uniformLocation;
}
//************** Create shaders **************
//Create vertex and fragment shaders
var vertexShader = compileShader(vertexSource.trim(), gl.VERTEX_SHADER);
var fragmentShader = compileShader(fragmentSource.trim(), gl.FRAGMENT_SHADER);
//Create shader programs
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw "Program link failed with: " + gl.getProgramInfoLog(program);
}
gl.useProgram(program);
//Set up rectangle covering entire canvas
var vertexData = new Float32Array([
-1.0,
1.0, // top left
-1.0,
-1.0, // bottom left
1.0,
1.0, // top right
1.0,
-1.0 // bottom right
]);
//Create vertex buffer
var vertexDataBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexDataBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
// Layout of our data in the vertex buffer
var positionHandle = getAttribLocation(program, "position");
gl.enableVertexAttribArray(positionHandle);
gl.vertexAttribPointer(
positionHandle,
2, // position is a vec2 (2 values per component)
gl.FLOAT, // each component is a float
false, // don't normalize values
2 * 4, // two 4 byte float components per vertex (32 bit float is 4 bytes)
0 // how many bytes inside the buffer to start from
);
//Set uniform handle
var timeHandle = getUniformLocation(program, "time");
var resHandle = getUniformLocation(program, "u_resolution");
function draw(time) {
resize();
//Send uniforms to program
gl.uniform1f(timeHandle, time);
//Draw a triangle strip connecting vertices 0-4
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
html,body {
height: 100%;
margin: 0;
}
canvas {
width: 100%;
height: 100%;
display: block;
}
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/
In trying to create VSM shadows that work on mobile platforms I'm exploring the possibility of 24 bit depth textures to store the moments (some mobile platforms don't support floating-point textures).
The problem is that I need omni-lights with shadows which means I need cubemaps (ideally). At least firefox does not seem to support this, printing Error: WebGL warning: texImage2D: With format DEPTH_COMPONENT24, this function may only be called with target=TEXTURE_2D, data=null, and level=0. to the console.
I'm calling gl.texImage2D with DEPTH_COMPONENT as format and internal format. For type I've tried gl.UNSIGNED_SHORT, gl.UNSIGNED_INT and ext.UNSIGNED_INT_24_8_WEBGL, all to no avail.
I could map the sides of a cube to a 2d texture and add a margin to each side to avoid interpolation artifacts but that seems overly involved and hard to maintain.
Are there other workarounds to have sampler cubes with DEPTH_COMPONENT format?
This is for WebGL 1
EDIT: I've made a few modifications to the code in gman's answer to better reflect my problem. Here's a jsfiddle. It looks like to does work on chrome (dark red cube on red background) but not on firefox (everything is black).
If you want to use depth textures you need to try to enable the WEBGL_depth_texture extension. note that many mobile devices don't support depth textures. (click the filters in the top left)
Then, according to the spec, you don't pass DEPTH_COMPONENT24 to texImage2D. In pass DEPTH_COMPONENT and a type of gl.UNSIGNED_SHORT or gl.UNSIGNED_INT the implementation chooses the bit depth. You can check what resolution you got by calling gl.getParameter(gl.DEPTH_BITS);
function main() {
const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl");
const ext = gl.getExtension("WEBGL_depth_texture");
if (!ext) {
alert("Need WEBGL_depth_texture");
return;
}
const width = 128;
const height = 128;
const depthTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depthTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0,
gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null);
// calls gl.bindTexture, gl.texParameteri
twgl.setTextureParameters(gl, depthTex, {
minMag: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE,
});
// calls gl.createTexture, gl.bindTexture, gl.texImage2D, gl.texParameteri
const cubeTex = twgl.createTexture(gl, {
target: gl.TEXTURE_CUBE_MAP,
minMag: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE,
width: width,
height: height,
});
const faces = [
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
];
const fbs = faces.map(face => {
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, cubeTex, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) {
console.log("can't use this framebuffer attachment combo");
}
return fb;
});
const vs = `
attribute vec4 position;
attribute vec3 normal;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * position;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
}
`;
const fs = `
precision mediump float;
uniform vec3 u_color;
uniform vec3 u_lightDir;
varying vec3 v_normal;
void main() {
float light = dot(u_lightDir, normalize(v_normal)) * .5 + .5;
gl_FragColor = vec4(u_color * light, 1);
}
`;
const vs2 = `
attribute vec4 position;
uniform mat4 u_matrix;
varying vec3 v_texcoord;
void main() {
gl_Position = u_matrix * position;
v_texcoord = position.xyz;
}
`;
const fs2 = `
precision mediump float;
uniform samplerCube u_cube;
varying vec3 v_texcoord;
void main() {
gl_FragColor = textureCube(u_cube, normalize(v_texcoord));
}
`;
// compile shaders, links program, looks up locations
const colorProgramInfo = twgl.createProgramInfo(gl, [vs, fs]);
// compile shaders, links program, looks up locations
const cubeProgramInfo = twgl.createProgramInfo(gl, [vs2, fs2]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData
const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl);
function render(time) {
time *= 0.001; // seconds
gl.enable(gl.DEPTH_TEST);
gl.useProgram(colorProgramInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, colorProgramInfo, cubeBufferInfo);
// draw a different color on each face
faces.forEach((face, ndx) => {
const c = ndx + 1;
const color = [
(c & 0x1) ? 1 : 0,
(c & 0x2) ? 1 : 0,
(c & 0x4) ? 1 : 0,
];
gl.bindFramebuffer(gl.FRAMEBUFFER, fbs[ndx]);
gl.viewport(0, 0, width, height);
gl.clearColor(1 - color[0], 1 - color[1], 1 - color[2], 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = Math.PI * 0.25;
const aspect = width / height;
const zNear = 0.001;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const world = m4.translation([0, 0, -3]);
m4.rotateY(world, Math.PI * .1 * c * time, world);
m4.rotateX(world, Math.PI * .15 * c * time, world);
// calls gl.uniformXXX
twgl.setUniforms(colorProgramInfo, {
u_color: color,
u_lightDir: v3.normalize([1, 5, 10]),
u_worldViewProjection: m4.multiply(projection, world),
u_worldInverseTranspose: m4.transpose(m4.inverse(world)),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, cubeBufferInfo);
});
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
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(cubeProgramInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, cubeProgramInfo, cubeBufferInfo);
const fov = Math.PI * 0.25;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.001;
const zFar = 10;
const mat = m4.perspective(fov, aspect, zNear, zFar);
m4.translate(mat, [0, 0, -2], mat);
m4.rotateY(mat, Math.PI * .25 * time, mat);
m4.rotateX(mat, Math.PI * .25 * time, mat);
twgl.setUniforms(cubeProgramInfo, {
u_cube: cubeTex,
u_matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, cubeBufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
Otherwise you can use depth renderbuffers. Where's an example who's code is here and the code that creates the framebuffers for the cubemap is here.
Update
As for cubemap depth textures the spec specifically says only TEXTURE_2D is supported.
The error INVALID_OPERATION is generated in the following situations:
texImage2D is called with format and internalformat of DEPTH_COMPONENT
or DEPTH_STENCIL and target is not TEXTURE_2D,
You might have to switch to WebGL2. It works in both firefox and chrome
function main() {
const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl2");
const width = 128;
const height = 128;
const colorTex = twgl.createTexture(gl, {
target: gl.TEXTURE_CUBE_MAP,
minMag: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE,
width: width,
height: height,
});
// calls gl.createTexture, gl.bindTexture, gl.texImage2D, gl.texParameteri
const depthTex = twgl.createTexture(gl, {
target: gl.TEXTURE_CUBE_MAP,
internalFormat: gl.DEPTH_COMPONENT24,
format: gl.DEPTH_COMPONENT,
type: gl.UNSIGNED_INT,
width: width,
height: height,
wrap: gl.CLAMP_TO_EDGE,
minMax: gl.NEAREST,
});
const faces = [
gl.TEXTURE_CUBE_MAP_POSITIVE_X,
gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
];
const fbs = faces.map(face => {
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, face, colorTex, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, face, depthTex, 0);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) {
console.log("can't use this framebuffer attachment combo");
}
return fb;
});
const vs = `
attribute vec4 position;
attribute vec3 normal;
uniform mat4 u_worldViewProjection;
uniform mat4 u_worldInverseTranspose;
varying vec3 v_normal;
void main() {
gl_Position = u_worldViewProjection * position;
gl_Position.z = 0.5;
v_normal = (u_worldInverseTranspose * vec4(normal, 0)).xyz;
}
`;
const fs = `
precision mediump float;
uniform vec3 u_color;
uniform vec3 u_lightDir;
varying vec3 v_normal;
void main() {
float light = dot(u_lightDir, normalize(v_normal)) * .5 + .5;
gl_FragColor = vec4(u_color * light, 1);
}
`;
const vs2 = `
attribute vec4 position;
uniform mat4 u_matrix;
varying vec3 v_texcoord;
void main() {
gl_Position = u_matrix * position;
v_texcoord = position.xyz;
}
`;
const fs2 = `
precision mediump float;
uniform samplerCube u_cube;
varying vec3 v_texcoord;
void main() {
gl_FragColor = textureCube(u_cube, normalize(v_texcoord)) / vec4(2.0, 1.0, 1.0, 1.0);
}
`;
// compile shaders, links program, looks up locations
const colorProgramInfo = twgl.createProgramInfo(gl, [vs, fs]);
// compile shaders, links program, looks up locations
const cubeProgramInfo = twgl.createProgramInfo(gl, [vs2, fs2]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData
const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl);
function render(time) {
time *= 0.001; // seconds
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.useProgram(colorProgramInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, colorProgramInfo, cubeBufferInfo);
// draw a different color on each face
faces.forEach((face, ndx) => {
const c = ndx + 1;
const color = [
(c & 0x1) ? 1 : 0,
(c & 0x2) ? 1 : 0,
(c & 0x4) ? 1 : 0,
];
gl.bindFramebuffer(gl.FRAMEBUFFER, fbs[ndx]);
gl.viewport(0, 0, width, height);
gl.clearColor(1 - color[0], 1 - color[1], 1 - color[2], 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
const fov = Math.PI * 0.25;
const aspect = width / height;
const zNear = 0.001;
const zFar = 100;
const projection = m4.perspective(fov, aspect, zNear, zFar);
const world = m4.translation([0, 0, -3]);
m4.rotateY(world, Math.PI * .1 * c * time, world);
m4.rotateX(world, Math.PI * .15 * c * time, world);
// calls gl.uniformXXX
twgl.setUniforms(colorProgramInfo, {
u_color: color,
u_lightDir: v3.normalize([1, 5, 10]),
u_worldViewProjection: m4.multiply(projection, world),
u_worldInverseTranspose: m4.transpose(m4.inverse(world)),
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, cubeBufferInfo);
});
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
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(cubeProgramInfo.program);
// calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
twgl.setBuffersAndAttributes(gl, cubeProgramInfo, cubeBufferInfo);
const fov = Math.PI * 0.25;
const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
const zNear = 0.001;
const zFar = 10;
const mat = m4.perspective(fov, aspect, zNear, zFar);
m4.translate(mat, [0, 0, -2], mat);
m4.rotateY(mat, Math.PI * .25 * time, mat);
m4.rotateX(mat, Math.PI * .25 * time, mat);
twgl.setUniforms(cubeProgramInfo, {
u_cube: colorTex,
u_matrix: mat,
});
// calls gl.drawArrays or gl.drawElements
twgl.drawBufferInfo(gl, cubeBufferInfo);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>