--- title: Writing shaders for WebGPU in WGSL image: description: Explain how to use WGSL in shaders when using the WebGPU engine keywords: babylon.js, shader, WebGPU, WGSL further-reading: video-overview: video-content: --- Currently, most of the main shaders used by **Babylon.js** are written in [WGSL](https://gpuweb.github.io/gpuweb/wgsl/), the only shader language that WebGPU supports. That being said, we actively maintain compatibility with [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language). If you decide to write a shader with [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language), **Babylon.js** will detect it and use internal tools to compile it to [WGSL](https://gpuweb.github.io/gpuweb/wgsl/). This takes some time and downloads a WASM library to do the conversion. It is recommended to write your shaders directly in [WGSL](https://gpuweb.github.io/gpuweb/wgsl/) for faster startup time and smaller download sizes. For some specific materials like `CustomMaterial` or `PBRCustomMaterial`, to protect backward compatibility, you will need to inject your custom shader code in [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language). ## Using ShaderMaterial to write WGSL code You can use the `ShaderMaterial` class to write WGSL code in much the same way you use it to write [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language), but with a few small differences. Note: if you use the "color" attribute in your shader code, don't add it to the **attributes** property passed to the `ShaderMaterial` constructor! This attribute will be automatically added if a vertex buffer named "color" is attached to the mesh. If you add "color" to the **attributes** array, you will get an error like "Attribute shader location (1) is used more than once". ### Setting the right shader language You must set the `shaderLanguage` property to `BABYLON.ShaderLanguage.WGSL` in the `options` parameter you pass to the constructor. For example: ```javascript const mat = new BABYLON.ShaderMaterial("shader", scene, { vertex: "myShader", fragment: "myShader", }, { attributes: ["position", "uv", "normal"], uniformBuffers: ["Scene", "Mesh"], shaderLanguage: BABYLON.ShaderLanguage.WGSL, } ); ``` ### Using the WGSL shader store If using the shader store, you must put your WGSL code in `BABYLON.ShaderStore.ShadersStoreWGSL` instead of `BABYLON.ShaderStore.ShadersStore`. For example: ```javascript BABYLON.ShaderStore.ShadersStoreWGSL["myShaderVertexShader"]=` #include #include ... `; BABYLON.ShaderStore.ShadersStoreWGSL["myShaderFragmentShader"]=` varying vPositionW : vec3; varying vUV : vec2; ... `; ``` ### Declaration of the entry points You must also declare the entry point for the vertex and fragment shader in a special way. Vertex: ```wgsl @vertex fn main(input : VertexInputs) -> FragmentInputs { ... } ``` Fragment: ```wgsl @fragment fn main(input : FragmentInputs) -> FragmentOutputs { ... } ``` Even though the entry point parameter is named `input`, do **not** access shader variables through `input.XXX`. Always use `vertexInputs.XXX` (vertex shader) or `fragmentInputs.XXX` (fragment shader) instead. The engine may apply transformations to certain input variables — for example, attributes stored as integers (such as `position` when using an integer vertex buffer) are automatically converted to their float equivalent, so accessing them via `input.XXX` would give the raw unconverted value and produce incorrect results. ### Using pre-defined uniforms To use the pre-defined uniforms of the scene (`view`, `viewProjection`, `projection`, `vEyePosition`) and mesh (`world`, `visibility`), you must include the appropriate file(s) in the shader code: ```wgsl #include #include ``` and add the uniform buffer name(s) to the `uniformBuffers` option of the `ShaderMaterial` class constructor: ```javascript const mat = new BABYLON.ShaderMaterial("shader", scene, { vertex: "myShader", fragment: "myShader", }, { attributes: ["position", "uv", "normal"], uniformBuffers: ["Scene", "Mesh"], shaderLanguage: BABYLON.ShaderLanguage.WGSL, } ); ``` In the WGSL code, you access a uniform by prefixing its name by `scene.` or `mesh.` for the scene or mesh uniforms, respectively: ```wgsl @vertex fn main(input : VertexInputs) -> FragmentInputs { vertexOutputs.position = scene.viewProjection * mesh.world * vec4(vertexInputs.position, 1.0); } ``` ## Special syntax used in WGSL code Unlike compute shaders that use ordinary WGSL code, the shader code you write for `ShaderMaterial` must use special syntax to work with the existing workflow. To make it easier for developers, the declaration of variables is the same as that used in [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language): * declaring a varying variable: ```wgsl varying varName : varType; ``` * declaring an attribute variable: ```wgsl attribute varName : varType; ``` * declaring a uniform variable: ```wgsl uniform varName : varType; ``` Contrary to [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language), the inputs and outputs of the vertex/fragment shader are not declared internally as separate global variables, but are defined in structures managed by the engine for you. However, this means that you need a special syntax to access these variables. Here is the mapping between the [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) syntax and the WGSL syntax: * In the vertex shader: * an attribute must be referenced by `vertexInputs.attributeName` * `gl_VertexID` => `vertexInputs.vertexIndex` * `gl_InstanceID` => `vertexInputs.instanceIndex` * a varying must be referenced by `vertexOutputs.varName` * `gl_Position` => `vertexOutputs.position` * In the fragment shader: * a varying must be referenced by `fragmentInputs.varName` * `gl_FragCoord` => `fragmentInputs.position` * `gl_FrontFacing` => `fragmentInputs.frontFacing` * `gl_FragColor` => `fragmentOutputs.color` * `gl_FragDepth` => `fragmentOutputs.fragDepth` Notes: * When using the `uniform varName : varType` syntax, you access the variable through `uniforms.varName`, not simply `varName`. Variables declared this way can be set from the JavaScript code by using the regular methods of the `ShaderMaterial` class (`setFloat`, `setInt`, etc.), as with [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language) * `varType` must use WGSL syntax, not [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language)! For example: `varying vUV : vec2;` * You must **NOT** add the `@group(X) @binding(Y)` decoration! The system will add it automatically ## Using new objects available in WGSL You can use the standard WGSL syntax to declare: * custom uniform buffers: ```wgsl struct MyUBO { scale: f32, }; var myUBO: MyUBO; ``` * storage textures: ```wgsl var storageTexture : texture_storage_2d; ``` * storage buffers: ```wgsl struct Buffer { items: array, }; var storageBuffer : Buffer; ``` * external textures: ```wgsl var videoTexture : texture_external; ``` Again, you must **NOT** add the `@group(X) @binding(Y)` decoration! The system will add them automatically. On the JavaScript side, you have the corresponding methods to set a value for these variables: * uniform buffer: `setUniformBuffer(name, buffer)` * storage texture: same method as for regular textures (`setTexture(name, texture)`) * storage buffer: `setStorageBuffer(name, buffer)` * external texture: `setExternalTexture(name, buffer)` ## Important difference from GLSL The [normalized device coordinates](https://gpuweb.github.io/gpuweb/#coordinate-systems) (NDC) in WebGPU are slightly different from those in WebGL. In GLSL, NDC for the z-axis lies in the -1.0 ≤ z ≤ 1.0 range. In WGSL, NDC for the z-axis lies in the 0.0 ≤ z ≤ 1.0 range. If you are using NDC in your shader, please ensure you make the necessary adjustments for the z-axis. There are no differences for the xy axes. ## Examples This playground is a basic example of using WGSL in a `ShaderMaterial`: As when using [GLSL](https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language), `ShaderMaterial` supports morphs, bones and instancing in WGSL. You will need to add the appropriate includes in your code to support these features. See how it is done in this playground (this example also demonstrates how to use a storage texture and a storage buffer): You can also use the baked vertex animation feature introduced in 5.0, as well as clip planes. See: WebGPU uses a special texture object for fast video playback, called an "external texture". Our [VideoTexture](/typedoc/classes/babylon.videotexture) class takes advantage of this, but if you want to do it yourself, this playground shows how to use the `ShaderMaterial` class to implement video playback with `texture_external`: