WebGPU is a new graphics and compute API designed by the “GPU for the Web” W3C community group. It aims to provide modern features such as “GPU compute” as well as lower overhead access to GPU hardware and better, more predictable performance. WebGPU should work with existing platform APIs such as Direct3D 12 from Microsoft, Metal from Apple, and Vulkan from the Khronos Group.
WebGPU is designed for the Web, used by JavaScript and WASM applications, and driven by the shared principles of Web APIs. However, it doesn’t have to be only for the Web though. Targeting WebGPU on native enables to write extremely portable and fairly performant graphics applications. The WebGPU API is beginner friendly, meaning that the API automates some of the aspects of low-level graphics APIs which have high complexity but low return on investment. It still has the core pieces of the next-gen APIs, such as command buffers, render passes, pipeline states and layouts. Because the complexity is reduced, users will be able to direct more focus towards writing efficiently application code.
From the very beginning, Google had both native and in-browser use of their implementation, which is now called Dawn. Mozilla has a shared interest in allowing developers to target a shared “WebGPU on native” target instead of a concrete “Dawn” or “wgpu-native”. This is achieved, by a shared header, and C-compatible libraries implementing it. However, this specification is still a moving target.
This repository contains a collection of open source C examples for WebGPU using Dawn the open-source and cross-platform implementation of the work-in-progress WebGPU standard.
- Supported Platforms
- Get the Sources
- Building for native with Dawn
- Running the examples
- Project Layout
- Examples
- Dependencies
- Credits
- References
- Roadmap
- License
This repository contains submodules for external dependencies, so when doing a fresh clone you need to clone recursively:
$ git clone --recursive https://github.com/samdauwe/webgpu-native-examples.git
Existing repositories can be updated manually:
$ git submodule init
$ git submodule update
The examples are built on top of Dawn, an open-source and cross-platform implementation of the work-in-progress WebGPU standard.
A helper bash script was created to fetch the latest version of "depot_tools" and "Dawn". With this approach only the Dawn code base is fetched in order to build the WebGPU implementation using CMake and without using the Chromium build system and dependency management.
The first step into building the WebGPU examples is running this script as follows:
$ cd external/dawn
$ bash download_dawn.sh
Note: running this script takes a while as it needs to pull several Gigabytes of dependencies from the internet.
The second step is building the examples:
$ mkdir build
$ cd build
$ cmake ..
$ make all
To build and run the examples inside a Docker container, follow the steps as described below.
Build the Docker image:
$ bash ./build.sh -docker_build
Run the Docker container:
$ bash ./build.sh -docker_run
Once the docker container is running, update to the latest version of "depot_tools" and "Dawn":
$ bash ./build.sh -update_dawn
Note: The build toolchain from depot_tools will not be used for building Dawn. Therefore, if this steps fails when extracting the build toolchains (i.e. debian_sid_i386_sysroot.tar.xz) and you see an error similar to this:
tar: .: Cannot change ownership to uid 416578, gid 89939: Invalid argument
then just ignore this error, because the required Dawn source code is already fetched at this point.
Finally, build the samples
$ bash ./build.sh -webgpu_native_examples
The build step described in the previous section creates a subfolder "x64" in the build folder. This subfolder contains all libraries and assets needed to run examples. Instead of a separate executable for each different example, a different approach was chosen to create an example launcher. This launcher can be used as follows, "./wgpu_sample_launcher -s <example_name>" where <example_name> is the filename of the example without the extension, like for example:
$ ./wgpu_sample_launcher -s shadertoy
├─ 📂 assets/ # Assets (models, textures, shaders, etc.)
├─ 📂 doc/ # Documentation files
│ └─ 📁 images # WebGPU diagram, logo
├─ 📂 docker/ # Contains the Dockerfile for building Docker image
├─ 📂 external/ # Dependencies dependencies
│ ├─ 📁 cglm # Highly Optimized Graphics Math (glm) for C
│ ├─ 📁 dawn # WebGPU implementation
│ └─ 📁 ... # Other Dependencies (cgltf, cimgui, stb, etc.)
├─ 📂 lib/ # Custom libraries
│ └─ 📁 wgpu_native # Helper functions using the Dawn C++ API exposed as C API
├─ 📂 screenshots/ # Contains screenshots for each functional example
├─ 📂 src/ # Helper functions and examples source code
│ ├─ 📁 core # Base functions (input, camera, logging, etc.)
│ ├─ 📁 examples # Examples source code, each example is located in a single file
│ ├─ 📁 platforms # Platform dependent functionality (input handling, window creation, etc.)
│ ├─ 📁 webgpu # WebGPU related helper functions (buffers & textures creation, etc.)
│ └─ 📄 main.c # Example launcher main source file
├─ 📄 .clang-format # Clang-format file for automatically formatting C code
├─ 📄 .gitmodules # Used Git submodules
├─ 📄 .gitignore # Ignore certain files in git repo
├─ 📄 build.sh # bash script to automate different aspects of the build process
├─ 📄 CMakeLists.txt # CMake build file
├─ 📄 LICENSE # Repository License (Apache-2.0 License)
└─ 📃 README.md # Read Me!
This example shows how to set up a swap chain and clearing the screen. The screen clearing animation shows a fade-in and fade-out effect.
Illustrates the coordinate systems used in WebGPU. WebGPU’s coordinate systems match DirectX and Metal’s coordinate systems in a graphics pipeline. Y-axis is up in normalized device coordinate (NDC): point(-1.0, -1.0) in NDC is located at the bottom-left corner of NDC. This example has several options for changing relevant pipeline state, and displaying meshes with WebGPU or Vulkan style coordinates.
Render | Depth | Texture |
---|---|---|
Minimalistic render pipeline demonstrating how to render a full-screen colored quad.
This example shows how to render a static colored square in WebGPU with only using vertex buffers.
Basic and verbose example for getting a colored triangle rendered to the screen using WebGPU. This is meant as a starting point for learning WebGPU from the ground up.
This example shows some of the alignment requirements involved when updating and binding multiple slices of a uniform buffer.
This example shows how to render points of various sizes using a quad and instancing. You can read more details here.
This example provides example camera implementations
Bind groups are used to pass data to shader binding points. This example sets up bind groups & layouts, creates a single render pipeline based on the bind group layout and renders multiple objects with different bind groups.
Dynamic uniform buffers are used for rendering multiple objects with multiple matrices stored in a single uniform buffer object. Individual matrices are dynamically addressed upon bind group binding time, minimizing the number of required bind groups.
Loads a 2D texture from disk (including all mip levels), uses staging to upload it into video memory and samples from it using combined image samplers.
This example shows how to bind and sample textures.
This example shows how to render and sample from a cubemap texture.
Loads a cube map texture from disk containing six different faces. All faces and mip levels are uploaded into video memory, and the cubemap is displayed on a skybox as a backdrop and on a 3D model as a reflection.
Generates a 3D texture on the cpu (using perlin noise), uploads it to the device and samples it to render an animation. 3D textures store volumetric data and interpolate in all three dimensions.
This example shows how to render volumes with WebGPU using a 3D texture. It demonstrates simple direct volume rendering for photometric content through ray marching in a fragment shader, where a full-screen triangle determines the color from ray start and step size values as set in the vertex shader. This implementation employs data from the BrainWeb Simulated Brain Database, with decompression streams, to save disk space and network traffic.
The original raw data is generated using the BrainWeb Simulated Brain Database before processing in a custom Python script.
This example shows how to render an equirectangular panorama consisting of a single rectangular image. The equirectangular input can be used for a 360 degrees viewing experience to achieve more realistic surroundings and convincing real-time effects.
This example shows how to how to load Basis Universal supercompressed GPU textures in a WebGPU application.
This example demonstrates order independent transparency using a per-pixel linked-list of translucent fragments.
This example shows the use of reversed z technique for better utilization of depth buffer precision. The left column uses regular method, while the right one uses reversed z technique. Both are using depth32float as their depth buffer format. A set of red and green planes are positioned very close to each other. Higher sets are placed further from camera (and are scaled for better visual purpose). To use reversed z to render your scene, you will need depth store value to be 0.0, depth compare function to be greater, and remap depth range by multiplying an additional matrix to your projection matrix.
Visualizes what all the sampler parameters do. Shows a textured plane at various scales (rotated, head-on, in perspective, and in vanishing perspective). The bottom-right view shows the raw contents of the 4 mipmap levels of the test texture (16x16, 8x8, 4x4, and 2x2).
This example shows how to render with conservative rasterization (native extension with limited support).
When enabled, any pixel touched by a triangle primitive is rasterized. This is useful for various advanced techniques, most prominently for implementing realtime voxelization.
The demonstration here is implemented by rendering a triangle to a low-resolution target and then upscaling it with nearest-neighbor filtering. The outlines of the triangle are then rendered in the original solution, using the same vertex shader as the triangle. Pixels only drawn with conservative rasterization enabled are colored red.
Note: Conservative rasterization not supported in Google Dawn.
This example shows how to render a single indexed triangle model as mesh, wireframe, or wireframe with thick lines, without the need to generate additional buffers for line rendering.
Uses vertex pulling to let the vertex shader decide which vertices to load, which allows us to render indexed triangle meshes as wireframes or even thick-wireframes.
- A normal wireframe is obtained by drawing 3 lines (6 vertices) per triangle. The vertex shader then uses the index buffer to load the triangle vertices in the order in which we need them to draw lines.
- A thick wireframe is obtained by rendering each of the 3 lines of a triangle as a quad (comprising 2 triangles). For each triangle of the indexed model, we are drawing a total of 3 lines/quads = 6 triangles = 18 vertices. Each of these 18 vertices belongs to one of three lines, and each vertex shader invocation loads the start and end of the corresponding line. The line is then projected to screen space, and the orthogonal of the screen-space line direction is used to shift the vertices of each quad into the appropriate directions to obtain a thick line.
Basic offscreen rendering in two passes. First pass renders the mirrored scene to a separate framebuffer with color and depth attachments, second pass samples from that color attachment for rendering a mirror surface.
WebGPU doesn't let you set the viewport’s values to be out-of-bounds. Therefore, the viewport’s values need to be clamped to the screen-size, which means the viewport values can’t be defined in a way that makes the viewport go off the screen. This example shows how to render a viewport out-of-bounds.
Uses the stencil buffer and its compare functionality for rendering a 3D model with dynamic outlines.
These samples show how implement different features of the glTF 2.0 3D format 3D transmission file format in detail.
Shows how to load a complete scene from a glTF 2.0 file. The structure of the glTF 2.0 scene is converted into the data structures required to render the scene with WebGPU.
Renders a complete scene loaded from an glTF 2.0 file. The sample uses the glTF model loading functions, and adds data structures, functions and shaders required to render a more complex scene using Crytek's Sponza model with per-material pipelines and normal mapping.
This example shows how to achieve multisample anti-aliasing(MSAA) in WebGPU. The render pipeline is created with a sample count > 1. A new texture with a sample count > 1 is created and set as the color attachment instead of the swapchain. The swapchain is now specified as a resolve_target.
Implements multisample anti-aliasing (MSAA) using a renderpass with multisampled attachments that get resolved into the visible frame buffer.
Implements a high dynamic range rendering pipeline using 16/32 bit floating point precision for all internal formats, textures and calculations, including a bloom pass, manual exposure and tone mapping.
This example shows how to create a basic reflection pipeline.
This example shows how to sample from a depth texture to render shadows from a directional light source.
Generating a complete mip-chain at runtime instead of loading it from a file, by blitting from one mip level, starting with the actual texture image, down to the next smaller size until the lower 1x1 pixel end of the mip chain.
This example shows how to capture an image by rendering a scene to a texture, copying the texture to a buffer, and retrieving the image from the buffer so that it can be stored into a png image. Two render pipelines are used in this example: one for rendering the scene in a window and another pipeline for offscreen rendering. Note that a single offscreen render pipeline would be sufficient for "taking a screenshot," with the added benefit that this method would not require a window to be created.
Uses the instancing feature for rendering (many) instances of the same mesh from a single vertex buffer with variable parameters.
This example demonstrates using Occlusion Queries.
This example shows how to use render bundles. It renders a large number of meshes individually as a proxy for a more complex scene in order to demonstrate the reduction in time spent to issue render commands. (Typically a scene like this would make use of instancing to reduce draw overhead.)
Physical based rendering as a lighting technique that achieves a more realistic and dynamic look by applying approximations of bidirectional reflectance distribution functions based on measured real-world material parameters and environment lighting.
Demonstrates a basic specular BRDF implementation with solid materials and fixed light sources on a grid of objects with varying material parameters, demonstrating how metallic reflectance and surface roughness affect the appearance of pbr lit objects.
Adds image based lighting from an hdr environment cubemap to the PBR equation, using the surrounding environment as the light source. This adds an even more realistic look the scene as the light contribution used by the materials is now controlled by the environment. Also shows how to generate the BRDF 2D-LUT and irradiance and filtered cube maps from the environment map.
Renders a model specially crafted for a metallic-roughness PBR workflow with textures defining material parameters for the PRB equation (albedo, metallic, roughness, baked ambient occlusion, normal maps) in an image based lighting environment.
These examples use a deferred shading setup.
This example shows how to do deferred rendering with webgpu. Render geometry info to multiple targets in the gBuffers in the first pass. In this sample we have 2 gBuffers for normals and albedo, along with a depth texture. And then do the lighting in a second pass with per fragment data read from gBuffers so it's independent of scene complexity. World-space positions are reconstructed from the depth texture and camera matrix. We also update light position in a compute shader, where further operations like tile/cluster culling could happen. The debug view shows the depth buffer on the left (flipped and scaled a bit to make it more visible), the normal G buffer in the middle, and the albedo G-buffer on the right side of the screen.
A WebGPU port of the Animometer MotionMark benchmark.
A GPU compute particle simulation that mimics the flocking behavior of birds. A compute shader updates two ping-pong buffers which store particle data. The data is used to draw instanced particles.
This example shows how to blur an image using a compute shader.
Uses a compute shader to apply different convolution kernels (and effects) on an input image in realtime.
Attraction based 2D GPU particle system using compute shaders. Particle data is stored in a shader storage buffer and only modified on the GPU using compute particle updates with graphics pipeline vertex access.
Particle system using compute shaders. Particle data is stored in a shader storage buffer, particle movement is implemented using easing functions.
A simple N-body simulation based particle system implemented using WebGPU.
Simple GPU ray tracer with shadows and reflections using a compute shader. No scene geometry is rendered in the graphics pass.
A classic Cornell box, using a lightmap generated using software ray-tracing.
Load and render a 2D text overlay created from the bitmap glyph data of a stb font file. This data is uploaded as a texture and used for displaying text on top of a 3D scene in a second pass.
Generates and renders a complex user interface with multiple windows, controls and user interaction on top of a 3D scene. The UI is generated using Dear ImGUI and updated each frame.
Demonstrates the basics of fullscreen shader effects. The scene is rendered into an offscreen framebuffer at lower resolution and rendered as a fullscreen quad atop the scene using a radial blur fragment shader.
Advanced fullscreen effect example adding a bloom effect to a scene. Glowing scene parts are rendered to a low res offscreen framebuffer that is applied atop the scene using a two pass separated gaussian blur.
This example demonstrates multiple different methods that employ fragment shaders to achieve additional perceptual depth on the surface of a cube mesh. Demonstrated methods include normal mapping, parallax mapping, and steep parallax mapping.
Implements multiple texture mapping methods to simulate depth based on texture information: Normal mapping, parallax mapping, steep parallax mapping and parallax occlusion mapping (best quality, worst performance).
This example shows how to use a post-processing effect to blend between two scenes. This example has been ported from this JavaScript implementation to native code.
WebGPU interpretation of glxgears. Procedurally generates and animates multiple gears.
This example shows how to upload video frames to WebGPU. Uses FFmpeg for the video decoding.
This example shows how to display a 360-degree video where the viewer has control of the viewing direction. Uses FFmpeg for the video decoding.
Minimal "shadertoy launcher" using WebGPU, demonstrating how to load an example Shadertoy shader 'Seascape'.
WebGPU implementation of the Gerstner Waves algorithm. This example has been ported from this JavaScript implementation to native code.
This example shows how to render an infinite landscape for the camera to meander around in. The terrain consists of a tiled planar mesh that is displaced with a heightmap. More technical details can be found on this page and this one.
A WebGPU example demonstrating pseudorandom number generation on the GPU. A 32-bit PCG hash is used which is fast enough to be useful for real-time, while also being high-quality enough for almost any graphics use-case.
This example shows how to make Conway's game of life. First, use compute shader to calculate how cells grow or die. Then use render pipeline to draw cells by using instance mesh.
A binary Conway game of life. This example has been ported from this JavaScript implementation to native code.
A conway game of life with paletted blurring over time. This example has been ported from this JavaScript implementation to native code.
WebGPU demo featuring marching cubes and bloom post-processing via compute shaders, physically based shading, deferred rendering, gamma correction and shadow mapping. This example has been ported from this TypeScript implementation to native code. More implementation details can be found in this blog post.
WebGPU demo featuring an implementation of Jos Stam's "Real-Time Fluid Dynamics for Games" paper. This example has been ported from this JavaScript implementation to native code.
This example shows how to map a GPU buffer and use the function wgpuBufferGetMappedRange. This example is based on the vertex_buffer test case.
This example shows how to efficiently draw several procedurally generated meshes. The par_shapes library is used to generate parametric surfaces and other simple shapes.
This example shows how to render tile maps using WebGPU. The map is rendered using two textures. One is the tileset, the other is a texture representing the map itself. Each pixel encodes the x/y coords of the tile from the tileset to draw. The example code has been ported from this JavaScript implementation to native code. More implementation details can be found in this blog post.
This example demonstrates how to render a torus knot mesh with blinn-phong lighting model.
This example demonstrates how to achieve normal mapping in WebGPU. A normal map uses RGB information that corresponds directly with the X, Y and Z axis in 3D space. This RGB information tells the 3D application the exact direction of the surface normals are oriented in for each and every polygon.
A simple WebGPU implementation of the "Pristine Grid" technique described in this wonderful little blog post. The example code has been ported from this JavaScript implementation to native code.
Just like all software, WebGPU Native Examples and Demos are built on the shoulders of incredible people! Here's a list of the used libraries.
- basisu: Single File Basis Universal Transcoder.
- cglm: Highly Optimized Graphics Math (glm) for C.
- cgltf: Single-file glTF 2.0 loader and writer written in C99.
- cimgui: c-api for Dear ImGui
- cJSON: Ultralightweight JSON parser in ANSI C.
- ktx: KTX (Khronos Texture) Library and Tools
- rply: ANSI C Library for PLY file format input and output
- sc: Portable, stand-alone C libraries and data structures. (C99)
- stb: stb single-file public domain libraries for C/C++
A huge thanks to the authors of the following repositories who demonstrated the use of the WebGPU API and how to create a minimal example framework:
- WebGPU - Chrome Platform Status
- Changelog for WebGPU in Chromium / Dawn 94
- Changelog for WebGPU in Chromium / Dawn 96
- Changelog for WebGPU in Chromium / Dawn 98
The list given below summarizes possible examples or functionality that will be added in the future.
- Make wgpu_native lib compatible with latest Dawn version
- Extend command-line options
- Aquarium
- High Dynamic Range Rendering
- glTF vertex skinning
- Physically-Based Rendering using glTF 2.0 models
- Building for Web with Emscripten / WebAssembly
-
Textured PBR with IBL -
Basis Universal supercompressed GPU textures example -
Use helper function for buffer creation to achieve more compact code -
Postprocessing -
Resolve glTF model rendering / loading issues showing up in a couple of example -
WebGPU Fluid Simulation -
Cornell Box -
Procedural mesh -
A-Buffer
Open-source under Apache 2.0 license.