Transparently supporting both WebGL and WebGPU in Babylon.js

Babylon.js
3 min readJun 6, 2024

--

Since version 5.0, the Babylon team has always been committed to supporting both WebGPU and WebGL.

During the inception of WebGPU, we tried to convince the W3C group to bet on an evolution of GLSL for WebGPU’s shader language.

Unfortunately, we lost that battle, and we now have to deal with rendering technologies using two different languages.

From our users’ standpoint, this is still transparent, though. Under the hood, we are doing quite a lot of work to provide cross-compatibility.

Do you have a GLSL shader running inside a ShaderMaterial, but your engine is a WebGPUEngine? No problem. We will download a 2MB WASM compiler to transpile from GLSL to WGSL. As you can imagine, that transparency costs time and network resources to work.

This is why we also decided to invest in making our Node Material capable of generating both GLSL or WGSL.

The task is currently underway (see here). Out of 140+ nodes we have already converted 110+. And you can already test it :)

If you go to nme.babylonjs.com, you will be able to play with our Node Material Editor, which will, by default, generate GLSL shaders:

Default material

If you click on Export Shaders (in the right toolbox), the code will look like this (this is just an extract):

// Vertex shader
#if defined(WEBGL2) || defines(WEBGPU)
precision highp sampler2DArray;
#endif
precision highp float;

attribute vec3 position;

uniform mat4 u_World;
uniform mat4 u_ViewProjection;
uniform vec4 u_color;

void main(void) {
vec4 output1 = u_World * vec4(position, 1.0);
vec4 output0 = u_ViewProjection * output1;
gl_Position = output0;

}

// Fragment shader
#if defined(PREPASS)
#extension GL_EXT_draw_buffers : require
layout(location = 0) out highp vec4 glFragData[SCENE_MRT_COUNT];
highp vec4 gl_FragColor;
#endif
#if defined(WEBGL2) || defines(WEBGPU)
precision highp sampler2DArray;
#endif
precision highp float;

uniform mat4 u_World;
uniform mat4 u_ViewProjection;
uniform vec4 u_color;


#include<helperFunctions>

But if you go to nme.babylonjs.com?webgpu, then the same material will provides this:

// Vertex shader

attribute vertexInputs.position: vec3f;

uniform u_World: mat4x4f;
uniform u_ViewProjection: mat4x4f;
uniform u_color: vec4f;

@vertex
fn main(input: VertexInputs) -> FragmentInputs{
var output1: vec4f = uniforms.u_World * vec4f(vertexInputs.position, 1.0);
var output0: vec4f = uniforms.u_ViewProjection * output1;
vertexOutputs.position = output0;

}

// Fragment shader

uniform u_World: mat4x4f;
uniform u_ViewProjection: mat4x4f;
uniform u_color: vec4f;

#include<helperFunctions>

So from a code standpoint now, it is super easy to just write your material once with NME and then use it directly with a WebGL backend or a WebGPU backend (without having to transpile and download 2 metric tons of WASM).

For instance, this playground will run with pure WebGPU rendering (besides the light sphere which is still using StandardMaterial):

Babylon.js Playground (babylonjs.com)

And the same code will also run with WebGL with no change whatsoever:

Babylon.js Playground (babylonjs.com)

The code by itself will simply require you to specify if you want your NodeMaterial to target WGSL or GLSL:

var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "textures/heightMap.png", 100, 100, 100, 0, 10, scene, false);
var material = new BABYLON.NodeMaterial("nm", scene, {
shaderLanguage: BABYLON.ShaderLanguage.WGSL
})
BABYLON.NodeMaterial.ParseFromSnippetAsync("N7YGT1#1", scene, undefined, material).then(nodeMaterial => {
ground.material = nodeMaterial;
});

We will be working hard during the next months to make sure NME will be the tool you can rely on while the Web is slowly migrating from WebGL to WebGPU.

Feel free to come chat with us on the forum if you have questions or comments!

Deltakosh (@deltakosh) / X

--

--

Babylon.js

Babylon.js: Powerful, Beautiful, Simple, Open — Web-Based 3D At Its Best. https://www.babylonjs.com/