/r/webgl
WebGL (Web Graphics Library): a JavaScript API for rendering interactive 3D graphics in compatible browsers without the use of plug-ins. WebGL programs consist of code written in JavaScript and shader code executed on a computer's Graphics Processing Unit (GPU). WebGL is designed and maintained by the non-profit Khronos Group.
Check if your browser supports WebGL:
Khronos Resources
Learn About WebGL
Be Informed About WebGL
Meet WebGL Developers
Game with WebGL
Code in the Cloud with WebGL
Related Subreddits
/r/webgl
A client who commissioned me for a video artwork (created in c4d) for their homepage has asked if I can now deliver it as webgl. I'm trying to figure out if this is even possible? My best guess is that I should open up Spline (for the first time ) and try and match the vibe of it but my guess is it'll look completely different?
I guess what I'm trying to decide on is if I should take this on or not .. and if not me, who would best be able to do so?
Hi I am looking to use WebGl in my web dev project for university, was just wondering where to start when it comes to applying it in a web development environment. Any help is much appreciated.
Hello everyone,
My name is Yohan and I'm a development student at Gobelins. As part of my thesis on the impact of WebGPU on games and the web, I'm conducting a study to understand your experience as a developer.
If you have a 4 minutes, your participation in my questionnaire would be really really appreciated. Your answers are anonymous and valuable to my research.
https://sphinx-campus.com/tiny/a/mc3o8wrq
Many thanks for your time and help !
Have a nice day. ❤️
I'm a beginner in both Blender and Three.js and recently started learning Three.js to create some cool models. I managed to create a model in Blender and added an animation using geometry nodes. However, I'm having trouble exporting it to Three.js.
Here's what I've tried so far:
It seems like I’m missing something specifically related to exporting or viewing the animation. Does anyone know the right way to export animations from geometry nodes so they’ll work with Three.js? I feel like I might be missing something in the export process or in setting up the animation correctly.
I want to make a simple drawing program, where you manipulate individual pixels by drawing, using my own custom functions to set the pixel values, not any of the canvas drawing functions.
I want it to be as performant as possible, so I'm guessing WebGL is the way to go, but is it truly any faster than canvas for just displaying / manipulating a single 2d texture?
I'm having an issue with a WebGL project that I'm hoping someone can help me wrap up before the Friday afternoon.
I have created a cube with a square and a triangle inside and I want the up down arrow keys to change the near panel distance so I can enter and exit this cube. The way things are currently set, I can only go right up to the cube's wall and then it bounces off.
I need to be able to go right up to the triangle inside the box and enter the cube's walls. My professor's suggestion is to change the near panel distance but even when I alter that I'm only getting up the wall but not entering. This is due tomorrow afternoon so any help ASAP would be great as I am still very much learning WebGL.
Below I'll list my current js code and the html with it.
// Vertex shader program
const VSHADER_SOURCE = `
attribute vec4 a_Position;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;
void main() {
gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
}`;
// Fragment shader program
const FSHADER_SOURCE = `
precision mediump float;
void main() {
gl_FragColor = vec4(0.6, 0.8, 0.9, 1.0);
}`;
// Global variables
let g_near = 0.1; // Start with a smaller near plane
let g_far = 100.0;
let g_eyeX = 3.0;
let g_eyeY = 2.0;
let g_eyeZ = 7.0;
let g_rotationAngle = 0;
let g_moveSpeed = 0.2; // Control movement speed
// Global matrices
let projMatrix;
let viewMatrix;
let modelMatrix;
let gl;
function main() {
const canvas = document.getElementById('webgl');
gl = getWebGLContext(canvas);
if (!gl || !initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.error('Failed to initialize shaders.');
return;
}
const n = initVertexBuffers(gl);
// Get uniform locations
const u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
const u_ViewMatrix = gl.getUniformLocation(gl.program, 'u_ViewMatrix');
const u_ProjMatrix = gl.getUniformLocation(gl.program, 'u_ProjMatrix');
if (!u_ModelMatrix || !u_ViewMatrix || !u_ProjMatrix) {
console.error('Failed to get uniform locations');
return;
}
// Initialize matrices as globals
modelMatrix = new Matrix4();
viewMatrix = new Matrix4();
projMatrix = new Matrix4();
// Set up debug display
const debugDiv = document.getElementById('debug');
debugDiv.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
debugDiv.style.padding = '10px';
function updateScene() {
// Update view matrix
viewMatrix.setLookAt(g_eyeX, g_eyeY, g_eyeZ, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix.elements);
// Update model matrix
modelMatrix.setRotate(g_rotationAngle, 0, 1, 0);
gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
// Update projection matrix with adjusted near plane
projMatrix.setPerspective(45, canvas.width/canvas.height, g_near, g_far);
gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix.elements);
// Update debug info
debugDiv.innerHTML = `
Camera Position: (${g_eyeX.toFixed(2)}, ${g_eyeY.toFixed(2)}, ${g_eyeZ.toFixed(2)})<br>
Near Plane: ${g_near.toFixed(2)}<br>
Rotation: ${g_rotationAngle.toFixed(2)}°
`;
draw(gl, n);
}
// Register keyboard event handler
document.onkeydown = function(ev) {
switch(ev.key) {
case 'ArrowUp':
// Move camera forward
g_eyeZ -= g_moveSpeed;
// Adjust near plane based on camera distance
g_near = Math.max(0.1, g_eyeZ - 2.0);
break;
case 'ArrowDown':
// Move camera backward
g_eyeZ += g_moveSpeed;
// Adjust near plane based on camera distance
g_near = Math.max(0.1, g_eyeZ - 2.0);
break;
case 'ArrowLeft':
g_rotationAngle -= 5.0;
break;
case 'ArrowRight':
g_rotationAngle += 5.0;
break;
case 'w': // Move up
g_eyeY += g_moveSpeed;
break;
case 's': // Move down
g_eyeY -= g_moveSpeed;
break;
case 'a': // Move left
g_eyeX -= g_moveSpeed;
break;
case 'd': // Move right
g_eyeX += g_moveSpeed;
break;
default:
return;
}
updateScene();
console.log('Camera position:', g_eyeX, g_eyeY, g_eyeZ);
};
// Enable depth testing
gl.enable(gl.DEPTH_TEST);
// Initial scene setup
updateScene();
}
function initVertexBuffers(gl) {
const vertices = new Float32Array([
// Front face
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
// Back face
-1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0,
// Left face
-1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0,
// Right face
1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0,
// Top face
-1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0,
// Bottom face
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0,
// Inner square at z=0
-0.5, -0.5, 0.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, -0.5, 0.5, 0.0,
// Inner triangle at z=0
-0.3, 0.3, 0.0, 0.0, -0.3, 0.0, 0.3, 0.3, 0.0
]);
const vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.error('Failed to create buffer');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.error('Failed to get attribute location');
return -1;
}
gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(a_Position);
return vertices.length / 3;
}
function draw(gl, n) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// Draw all shapes
gl.drawArrays(gl.LINE_LOOP, 0, 4); // Front face
gl.drawArrays(gl.LINE_LOOP, 4, 4); // Back face
gl.drawArrays(gl.LINE_LOOP, 8, 4); // Left face
gl.drawArrays(gl.LINE_LOOP, 12, 4); // Right face
gl.drawArrays(gl.LINE_LOOP, 16, 4); // Top face
gl.drawArrays(gl.LINE_LOOP, 20, 4); // Bottom face
gl.drawArrays(gl.LINE_LOOP, 24, 4); // Inner square
gl.drawArrays(gl.TRIANGLES, 28, 3); // Inner triangle
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebGL Hollow Box with Objects</title>
<style>
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
canvas {
border: 1px solid black;
}
#instructions {
position: fixed;
bottom: 10px;
left: 10px;
background-color: rgba(255, 255, 255, 0.8);
padding: 10px;
}
</style>
</head>
<body>
<canvas id="webgl" width="600" height="600"></canvas>
<!-- Debug info -->
<div id="debug" style="position: fixed; top: 10px; left: 10px;"></div>
<!-- Instructions -->
<div id="instructions">
Controls:<br>
↑/↓ - Move forward/backward<br>
←/→ - Rotate view<br>
W/S - Move up/down<br>
A/D - Move left/right
</div>
<!-- Helper functions -->
<script>
// WebGL context helper
function getWebGLContext(canvas) {
return canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
}
// Shader initialization helper
function initShaders(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
return null;
}
gl.useProgram(shaderProgram);
gl.program = shaderProgram;
return true;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// Matrix helper class
class Matrix4 {
constructor() {
this.elements = new Float32Array([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);
}
setRotate(angle, x, y, z) {
const c = Math.cos(angle * Math.PI / 180);
const s = Math.sin(angle * Math.PI / 180);
const elements = this.elements;
if (x === 1 && y === 0 && z === 0) {
elements[5] = c;
elements[6] = s;
elements[9] = -s;
elements[10] = c;
} else if (x === 0 && y === 1 && z === 0) {
elements[0] = c;
elements[2] = -s;
elements[8] = s;
elements[10] = c;
}
return this;
}
setPerspective(fovy, aspect, near, far) {
const f = 1.0 / Math.tan(fovy * Math.PI / 360);
const nf = 1 / (near - far);
this.elements[0] = f / aspect;
this.elements[5] = f;
this.elements[10] = (far + near) * nf;
this.elements[11] = -1;
this.elements[14] = 2 * far * near * nf;
this.elements[15] = 0;
return this;
}
setLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ) {
let z = [eyeX - centerX, eyeY - centerY, eyeZ - centerZ];
let length = Math.sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]);
z = [z[0] / length, z[1] / length, z[2] / length];
let x = [upY * z[2] - upZ * z[1],
upZ * z[0] - upX * z[2],
upX * z[1] - upY * z[0]];
length = Math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]);
x = [x[0] / length, x[1] / length, x[2] / length];
let y = [z[1] * x[2] - z[2] * x[1],
z[2] * x[0] - z[0] * x[2],
z[0] * x[1] - z[1] * x[0]];
this.elements[0] = x[0];
this.elements[1] = y[0];
this.elements[2] = z[0];
this.elements[3] = 0;
this.elements[4] = x[1];
this.elements[5] = y[1];
this.elements[6] = z[1];
this.elements[7] = 0;
this.elements[8] = x[2];
this.elements[9] = y[2];
this.elements[10] = z[2];
this.elements[11] = 0;
this.elements[12] = -(x[0] * eyeX + x[1] * eyeY + x[2] * eyeZ);
this.elements[13] = -(y[0] * eyeX + y[1] * eyeY + y[2] * eyeZ);
this.elements[14] = -(z[0] * eyeX + z[1] * eyeY + z[2] * eyeZ);
this.elements[15] = 1;
return this;
}
}
</script>
<!-- Your main WebGL script -->
<script src="main.js"></script>
<script>
// Add event listener for page load
window.onload = function() {
main();
};
</script>
</body>
</html>
Hi everyone! Does anyone know exactly how expo-gl
works?
I'm familiar with the concept of the bridge between the JavaScript VM and the native side in a React Native app. I'm currently developing a React Native photo editor using expo-gl
for image processing (mostly through fragment shaders).
From what I understand, expo-gl
isn’t a direct WebGL implementation because the JS runtime environment in a React Native app lacks the browser-specific API. Instead, expo-gl
operates on the native side, relying mainly on OpenGL. I've also read that expo-gl
bypasses the bridge and communicates with the native side differently. Is that true? If so, how exactly is that achieved?
I'm primarily interested in the technical side, not in code implementation or usage within my app — I’ve already got that part covered. Any insights would be greatly appreciated!
Would like to do something like the image above, but that one is from a tutorial that just duplicates the image and moves each copy to create the effect. I was wondering if there might be a more efficient way to do it, also I'm interested in being able to render just the outline part separately, as it might come in handy for indicating sprites which are hidden behind other objects.
I'm using WebGL 2, and just rendering stuff using WebGL calls without any 3rd party engine. Anyone got some resources for achieving this effect? it doesn't seem as trivial as I hoped.
I want to make a whiteboarding application. Each board should be as big as 7000×8000. I am currently using Konva with Vue (so no webgl atm) but I noticed that the performance becomes awful when rendering tje graphics on a large canvas. At one point, all elements should visible.
My question is what approach can I take in order to render a lot elements 1k and maybe way more to do that knowing that the elements are interactive? What are the optimizations that I can do? And does any of you have an example?
The next WebGL & WebGPU Meetup is right around the corner. Register for free and come join us to hear about the latest API updates and presentations from Microsoft, Dassault Systemes, and Snapchat!
Learn more and register: https://www.khronos.org/events/webgl-webgpu-meetup-november-2024
Hi everyone.
I am following the tutorial/article on webglfundamentals.org on how to perform computations using the fragment shader. My overall goal is to do an n-body simulation (i.e. simulating bodies with gravity interacting with each other). I still have to figure out many details.
At the moment I'm trying to just write a program that takes a vector and doubles it, exactly like the first part of the tutorial, but using FLOATs instead of UNSIGNED_BYTES.
My code is the following: https://pastebin.com/CAm0JgVc
The output I get at the end is an array of NaN
Am I missing something? Is my goal even feasible?
I'm new to WebGL, and I'm trying to write a simple program that adds a jitter to an image based on the time. My problem seems to be that my uniform, uTime, does not change, and as a result, the image remains static. My relevant files are shown below:
main.js:
function compileShader(vs_source, fs_source) {
const vs = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vs, vs_source)
gl.compileShader(vs)
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(vs))
throw Error("Vertex shader compilation failed")
}
const fs = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fs, fs_source)
gl.compileShader(fs)
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
console.error(gl.getShaderInfoLog(fs))
throw Error("Fragment shader compilation failed")
}
const program = gl.createProgram()
gl.attachShader(program, vs)
gl.attachShader(program, fs)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error(gl.getProgramInfoLog(program))
throw Error("Linking failed")
}
return program
}
function setupGeometry(geom) {
var triangleArray = gl.createVertexArray()
gl.bindVertexArray(triangleArray)
for (let i = 0; i < geom.attributes.length; i++) {
let buf = gl.createBuffer()
gl.bindBuffer(gl.ARRAY_BUFFER, buf)
let f32 = new Float32Array(geom.attributes[i].flat())
gl.bufferData(gl.ARRAY_BUFFER, f32, gl.STATIC_DRAW)
gl.vertexAttribPointer(i, geom.attributes[i][0].length, gl.FLOAT, false, 0, 0)
gl.enableVertexAttribArray(i)
}
var indices = new Uint16Array(geom.triangles.flat())
var indexBuffer = gl.createBuffer()
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)
return {
mode: gl.TRIANGLES,
count: indices.length,
type: gl.UNSIGNED_SHORT,
vao: triangleArray
}
}
function draw(geom, program) {
gl.clear(gl.COLOR_BUFFER_BIT);
// Ensure you use the correct program first
gl.useProgram(program);
// Get the uniform locations after using the program
const matrixLocation = gl.getUniformLocation(program, 'uModelViewMatrix');
timeLocation = gl.getUniformLocation(program, 'uTime');
// Identity matrix (no scaling or rotation)
const identityMatrix = mat4.create();
// Scale the time increment
let scaledTime = (Date.now() / 1000); // Speed up time for testing
// Set the uniform matrix and time in the vertex shader
gl.uniformMatrix4fv(matrixLocation, false, identityMatrix);
gl.uniform1f(timeLocation, scaledTime); // Pass scaled time to the shader
gl.bindVertexArray(geom.vao);
gl.drawElements(geom.mode, geom.count, geom.type, 0);
}
window.addEventListener('load', async (event) => {
window.gl = document.querySelector('canvas').getContext('webgl2')
let vs = await fetch('vertex.glsl').then(res => res.text())
let fs = await fetch('fragment.glsl').then(res => res.text())
window.program = compileShader(vs, fs)
let data = await fetch('geometry.json').then(r => r.json())
window.geom = setupGeometry(data)
function renderLoop() {
draw(window.geom, window.program)
requestAnimationFrame(renderLoop)
}
renderLoop()
})
vertex.glsl:
#version 300 es
layout(location = 0) in vec2 aVertexPosition;
layout(location = 1) in vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform float uTime;
out vec4 vColor;
void main() {
// Apply a simple jitter effect based on the uTime value
float jitterAmount = .05; // Adjust this for more/less jitter
float jitterX = jitterAmount * sin(uTime * float(gl_VertexID) * 0.1); // Create jitter based on uTime and gl_VertexID
float jitterY = jitterAmount * cos(uTime * float(gl_VertexID) * 0.1);
//float jitterX = jitterAmount * uTime;
//float jitterY = jitterAmount * uTime;
// Apply jitter to the vertex position
vec2 jitteredPosition = aVertexPosition + vec2(jitterX, jitterY);
// Apply the model-view matrix
gl_Position = uModelViewMatrix * vec4(jitteredPosition, 0.0, 1.0);
// Pass the vertex color to the fragment shader
vColor = aVertexColor;
//vColor = vec4(jitterX, jitterY, 0.0, 1.0); // Use jitter values to set color
}
fragment.glsl:
#version 300 es
precision mediump float;
// Color passed in from the vertex shader
in vec4 vColor;
out vec4 fragColor;
void main() {
// Set the color of the fragment to the interpolated color from the vertex shader
fragColor = vColor;
}
geometry.json:
{
"triangles": [
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11
],
"attributes": [
[
[-0.5, 0.8],
[ 0.5, 0.8],
[ 0.5, 0.6],
[-0.5, 0.6],
[-0.2, 0.6],
[ 0.2, 0.6],
[ 0.2, -0.6],
[-0.2, -0.6],
[-0.5, -0.6],
[ 0.5, -0.6],
[ 0.5, -0.8],
[-0.5, -0.8]
],
[
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0],
[1.0, 0.373, 0.02, 1.0]
]
]
}
I followed this tutorial on setting up a basic WebGL project, and am now stuck on something that should be simple yet I can't find any examples. So I first use gl.ARRAY_BUFFER
to create a data storage in JS that is unique to every vertice in GLSL. But what do I do when I want to give my vertex shader an array that has a common value for all vertices, so for each vertex / fragment the shader finds the same data rather than the data being unique to each one? In my case I need a list of vec4's to specify positions of items in the world, as well as single floats and integers for global settings the shader should use in all calculations. I could have JS set identical data for all entries in an array buffer at the same length as the vertex buffer, but that's clearly not the right way to do it: What I'm looking for might be gl.ELEMENT_ARRAY_BUFFER
but how to use it isn't well explained at least where I looked.
There's one thing I never fully understood about vertex shaders in OpenGL and WebGL in consequence: Are they only able to deform vertices, or also generate them and create faces? I wanted to play with generating geometry on the GPU using point data provided by the JS code. If it's doable I'd appreciate if anyone can link to the most simple example, if not what is the closest and cleanest solution to get close?
A good example of what I'm trying to do: I want a vertex shader that takes a list of integer vec3 positions and generates a 1x1x1 size cube at the location of each one. The JavaScript code doesn't define any vertices itself, it only gives the shader the origin points from an object of the form [{x: 0, y: 0, z: 0}, {x: -4, y: 0, z: 2}]
, from this the shader alone generates the faces of the cube creating one at every location.
Today I learned about WebGPU while searching for efficient ways to do GPU raytracing. It's still a new thing, so much so that web browsers still don't appear to support it to this day or at least Firefox doesn't. But I wanted to ask just to make sure: Is there any risk that WebGL could ever be deprecated in favor of WebGPU and leave existing applications unsupported?
I'm mainly only asking due to the highly questionable decision (to put it mildly) by Apple to deprecate OpenGL support on MacOS, leaving probably only games made after the 2020's supported. I take it WebGL is a different story and there's no plans to ever drop that in the foreseeable future. But given how thoughtless some entities are about dropping support for essential things like that, it seemed best to ask just in case before deciding to work on a project, given I already do nothing but demos and having to port them in the future is the last thing I wish to have to worry about once I pick a system to work with.
I'm building a WebGL weather radar renderer, and I have a need for precisely mapping an output color gradient to the radar intensity read from a texture as I render my geometry.
A typical naive approach to this would be to place the color map into a small 256x1 (could also fit in 128x1 given the resolution of the data) texture or uniform buffer.
My worry is that doing so would introduce a dependent texture fetch to the shader, decreasing its performance by a significant amount on lower end hardware.
So far I have code like this in my fragment shader:
uniform vec4 colorStops[16];
uniform int colorStopCountMinusOne; // number of color stops (up to 15) minus 1
uniform float colorStopBottom; // lowest palette stop maps to this value
uniform float colorStopTop; // ditto for top. Intermediate values interpolate between adjacent stops computed via count.
...
float t = clamp((value - colorStopBottom) / (colorStopTop - colorStopBottom), 0.0, 1.0);
float scaledValue = t * float(colorStopCountMinusOne);
int index1 = int(floor(scaledValue));
int index2 = min(index1 + 1, colorStopCountMinusOne);
float localT = scaledValue - float(index1);
vec4 color = mix(colorStops[index1], colorStops[index2], localT);
Basically this is a collection of uniforms:
In particular the limitation with this that I need to change now is the assumption that my input colors will be mapped uniformly, e.g. if i have 10 colors and i specify Bottom to be 5 and Top to be 55, then the second color stop must correspond to the value 10.
Now I got a new color gradient palette I need to support, and it crucially does not have evenly spaced input stop points. I have colors defined at a inputs of 18, 22, 30, 35, 40, etc, it is not a pattern, it was carefully hand chosen.
Since the colors themselves are still linearly scaling underneath, I feel that I could still adapt my scheme by adding a slight amount of complexity. In this shader I could do a first pass (assuming uniform distribution for example) and then hop up or down a bit to find the actual pair of color indices to do the color interpolation with.
But it leads me to question the approach. Because at some point this added complexity in the shader will be so much that I would be better off loading in 128 vec4 values and looking up the colors.
Or, I could keep the same scheme but push up to something like 30 color stops, some of them being potentially redundant, but it should give me enough resolution. It would also still decrease performance though because any divergence in shader code will reduce occupancy, and having different color stop indices here across threads would constitute a flow divergence.
Since I don't think I have the luxury of the oodles of extra effort to build out both implementations and source a large number of low end devices to evaluate performance across them, how should I approach reasoning about this tradeoff?
Hi! I am relatively new to graphics programming (experienced in Blender, C4D and some three.js) but this is my first time trying out WebGL for a project.
I found this example by David Li which is a great starting point for me and i already found some basic functionalities I could modify to my needs. Now I am struggling a bit, because of my lack of knowledge in programming.
Is there a way to (simply) add boxes which function as colliders for the fluid simulation? I know there is a lot of stuff going on in the background and adding colliders may have an influence on the performance but I just need someone to point me in the right direction because I can't seem to find the right documentation for these kinds of things.
Thanks in advance!
I have a Miro like application, we can create notes and many other complex elements such as lists, notes, etc..., The application is in Vue. However, I’ve noticed the application’s performance becomes very poor when there is an important number of objects (i.e. 50+) in the whiteboard. The elements can contain text, have multiple fields, different colors and shapes, etc...
I’ve been thinking about switching to using HTML5 Canvas or WebGL. Based on some research I've made on Canvas, it seems to have way better performance than the typical DOM, Although resorting to WebGL will probably lead to an even better one, I'm afraid issues would arise when we will deal with complex tasks (steep learning curve, browser issues, etc...).
Is HTML Canvas capable of rendering a large number of elements (around 500-5K, but preferably more), while keeping a smooth experience for the user (~60FPS)? Or is it more suitable to use WebGL in order to achieve that? Is it possible to mix DOM and WebGL? In other words, rendering costly elements with WebGL, while handling simpler ones with normal HTML?
Thanks in advance.
I've been having problems with some vertex shaders and got the idea to transpile the GLSL into TypeScript, which would allow printing values, drawing all kinds of markers and single step debugging.
The general idea would be to overlay the WebGL canvas with an SVG or 2D canvas and draw triangles there with debugging-oriented styling, such as wireframe, on the CPU. With SVG they could be clickable, so you could re-execute the vertex shader on demand on a specific triangle, single step and look at logs. It could also be possible to run the fragment shader on a clicked pixel. This is meant for debugging an individual frame at a time, it of course runs slowly.
Is there any interest in this idea? I've already figured out a lot of ways to get the GLSL syntax working directly in TypeScript, transpiled the syntax as needed to make the TypeScript compiler happy, and made a one-line patch to the TypeScript compiler to support operator overloading so vector and matrix arithmetic operators work as expected. Most of the WebGL 1 API calls available to shaders are implemented, except texture access. Currently working on getting function parameters with an "out" qualifier working.
Update, it's here:
https://github.com/nelipuu/glslog
https://codepen.io/Juha-J-rvi/pen/YzooeGJ
Hi, i'm a engineer student and a computer graphics passionate, for my final work in college I want to create a voxel engine with webGl, any suggestions?
I'm working on an application involving a Unity viewer using WebGL embedded in my React App, and I'm thinking of the best approach to embed some 3D elements fixed to some spatial locations on the 3D space inside the Unity viewer but display them on an overlay made in React, above the Unity app. This has the benefit of separation of concerns of the UI elements from the Unity WebGL renderer to the UI framework (React) but I don't feel this will be performant since React doesn't always perform state updates immediately and likes to batch and trigger state updates all at once when lots of state changes occur (which will happen as the state of the position of the UI element on the overlay will change every time the camera moves), which will give it a jittery and laggy experience.
I'm aware of how to do this in native three js and html, where we can update the element's position on the overlay/canvas inside the render or loop function which is usually called inside the `requestAnimationsFrame` function to achieve high frame rates, but I don't think it will be feasible or recommended to modify exterior UI elements from Unity as it will again be the quite the opposite of separation of concerns. but even in that approach, if updating the element's position, isn't it a DOM operation and aren't DOM operations very expensive? and doing them at every render? like 60 times a second, is it ideal?
With this, I can also think of using a canvas instead of a usual overlay and add the html elements or draw them on the canvas maybe? I'm still unsure of the exact implementation, I'm just trying to clarify the complete high level design for it. I believe drawing on the canvas would be less expensive than DOM updates and also more real time than React's state updates? But that would also mean there will be two canvases, one for the custom elements' and the other used by Unity. I will have to selectively disable pointer events on the top canvas (for the custom elements) but enable pointer events on the custom elements on it? am I thinking in the right direction?
There is an example implementation using webgpu on the webgpu samples repo.
Webgl is much more mature so I was surprised I couldn't find a msdf text rendering implementation in webgl that I could read and understand.
Do you guys know of any webgl msdf text rendering implementations?