Pangram verdict · v3.3
We believe that this document is fully human-written
AI likelihood · overall
HumanArticle text · 1,837 words · 5 segments analyzed
Blogpost Can we use WebGPU to compute real-time global illumination with surface patches called surfels? Does it look good enough? Is it fast enough? And can we finally construct viable compute-heavy rendering pipelines right here on the open web? Join me on this journey and let's find out! Jan 29, 2026 Above is a quick illustration of what we'll be exploring step by step, through a series of interactive visualizations. But before we continue down that path, dear reader, I implore you to make a choice. A brief history of light Greetings, fellow adventurer! Let's begin. Of course at first, there was nothing, not even light. Then very shortly after the big boom, maybe a second after, the first particles of light were created. Unfortunately for them, for a long, long time the universe was so full of energy, so hot and dense, that these photons could not travel where they wanted to go, but instead almost immediately ran into electrons and scattered, kind of like trying to shine a flashlight through the thickest fog you've ever seen. Then after some time things cooled down just a bit, it was still scorching hot, but cold enough that electrons and protons stopped frolicking out in the open, became friends and moved in together into atoms. The poor photons that were constantly crashing into things suddenly had space to move - and the fog lifted. The light that was released then is the oldest light we can observe, the cosmic microwave background. Alas, after this brief spectacle of light, the universe was plunged into darkness yet again. With the photons from the initial boom gone with the wind, there was nothing anywhere that could make new ones. And so the universe waited. Global illumination from year 380,000 to year 400 million was easy peasy, finalColor = vec4(0,0,0,0). Finally, roughly half a billion years after the universe was born, a bunch of hydrogen bits clumped together so hard that they self-ignited, fused into helium atoms, during which super high energy photons were created, and a new type of lightbulb came into existence - a star.
Now, the behaviour of these photons isn't something people would write global illumination theses about - in fact, if Earth was “illuminated” by them there would be no people at all, it would be a sterile frozen wasteland. No, these energetic youngsters need to first calm down and bounce around the star's interior for 100,000 years or so, before all the particle interactions in this cosmic kindergarten whittle them down to visible light energy levels and they reach the surface of the star. Now adults, they are free to roam the universe at the speed of light. It is precisely these photons that we'll be trying to wrangle in this post. The subject of fascination for many scientists (Haytham's Book of Optics, Newton's corpuscles, Young's light waves, Planck's quanta, Einstein's photoelectric effect) and artists (Leonardo's study of light scattering, Monet's series studying the color of stone in different light, Turner's light studies, Vermeer's alleged mechanical aids) alike, these messengers of light behave in many interesting ways (scattering, diffraction, refraction, gravitational lensing, just to name a few), but for the purposes of our exploration, we're particularly interested in diffuse reflection. Or as Leonardo put it in the late 15th century, the effect of light by which: It happens very rarely that reflections are of the same color as the body [from which they come] or of the object where they meet. For example: Let the spherical body d f g e be yellow, and the object reflecting its color upon it be b c, which is blue. I say that the part of the spherical body that is struck by this reflection will be tinged a green color, provided that b c is illuminated by the air or the sun. Leonardo da Vinci, Treatise on Painting: colored reflections. Source Of course, he was right. The phenomenon was first written about by painter Leon Battista Alberti, who in his treatise on painting De pictura remarked: Reflected rays assume the colour they find on the surface from which they are reflected. We see this happen when the faces of people walking about in the meadows appear to have a greenish tinge. And it was Leonardo who turned it into science, isolating the specific variables and predicting the outcome.
Leonardo's thought experiment rendered in Blender What is clear today, but would have perhaps confused them then, is that the experiment only works if a blue with a lot of green is used, like an azure color - since a perfect yellow color perfectly absorbs blue, a perfect blue reflection on a yellow surface would just produce a muddy shadow with zero green (as there was none there to begin with!). Luckily for them, they did not have access to pure blue pigments and so everything worked out just fine. But enough about the history of the study of light, which albeit fascinating, doesn't bring us much closer to a real-time rendering engine capable of simulating these phenomena. Let's jump to the current time and see where we're at. Path tracing and computers Thanks to the extremely speedy computing machinery we have in our pockets and on our desks these days, the logic that Leonardo applied in his thought experiments can be applied millions of times per second, we call this path tracing - following each 'photon' or ray on its journey and seeing what it sees and who it talks to, and using that information to tell the computer what color should be used for a particular pixel. In a rendering engine like Cycles, thousands of rays for each pixel are generally used to render a single image in a span of minutes to hours. This produces images with exceptional quality, but is orders of magnitude too slow for real-time applications, where the entire frame's processing (of which rendering is only a part) has to be completed in 16 milliseconds or less. While we are starting to see games ship with so called 'full ray tracing' modes (Cyberpunk 2077, Alan Wake 2), and the future likely is path traced, they still require powerful hardware not accessible to many. Given that fully path tracing a game in real time is mostly unattainable, many optimizations and approximation techniques evolved over the years, each with its own set of limitations (lightmaps are static, irradiance probes are low resolution, screen space global illumination isn't really global, voxels leak and so on), as there's no free lunch to be had here. However, a few years ago a light snack possibly appeared on the horizon, a technique that uses surfels was presented at SIGGRAPH 21 by EA's SEED research division. "Surf-what-els?" I hear you say.
Let's find out! Surfels But first another time jump! A tiny autobiographical one this time. Wandering the halls of SIGGRAPH 2025 in Vancouver, I came across this poster by Ruipeng Wang, Zhen Ren, and Jinxiang Wang titled "SurfelPlus: A Surfel-Based Global Illumination Solution", in which the researchers state: By developing a surfel-based solution optimized for low-end GPUs, we can deliver rich, dynamic indirect lighting in real time. If you squint a bit, "low-end GPUs" looks almost exactly like "the web", which is where I live, so I got very interested and the next morning started experimenting. That was day one. I did not know then that day two wouldn't happen until many months later, but the itch never stopped itching and surfels kept floating around in my head. Wait, we haven't even explained what surfels are yet, have we? Well! A surfel (surface element, introduced by Pfister and others in a landmark SIGGRAPH 2000 paper) is a flat disc floating in 3D space, represented by a position, a normal (rotation/orientation) and a radius. Of course it's more useful when it's stuck on a surface and its radius dictates how large of an area it influences, but we'll get to that later. Their main benefit is that they decouple lighting from screen resolution. We can shade 50k surfels effectively to get global illumination, even if the screen has 2 million pixels, caching that expensive work over time. Surfels: Oriented Surface Elements. Each surfel approximates a local surface patch with position, normal, and radius. So how does one use surfels? Unfortunately for all involved, surfels don't grow on trees, so in order to benefit from their properties, one must first create them. And here, my friend, is where the fun really starts. 1. Surfelization Surfelization, or the process of converting regular old geometry built with triangles into a collection of discrete surfels that represent small patches of a surface, makes up a large chunk of the rendering pipeline, both conceptually and in terms of needed compute.
A production-ready surfelization pass involves: rendering a G-buffer (the G is for geometry) with normals, depth and albedo, maintaining a surfel 'pool' and the lifecycle of surfels, screen-space analysis for areas where surfels are missing (or where there are too many), and a spatial data structure allowing for fast lookup of relevant surfels. In the final project just the above demands more than 10 compute passes, 7 for spatial structure (grid) maintenance and 4 for managing the surfels' lifecycles, if we include surfelization among them. So that's fun! I'll admit that after embarking on this first conceptual chunk of the pipeline, and seeing the number of moving parts involved, I wasn't sure if WebGPU (or I, for that matter) would be able to handle the entire system - but it did and it does, and it turns out that surfelization is actually the most straightforward step. In a nutshell, you render your scene's normals and depth to textures (that's your G-buffer), and then you sample those textures to figure out what position and orientation would be good for a surfel. You then compare those surfel candidates with your existing surfels, which you get by querying the grid. A little atomic parallel competition takes place and the winners become actual surfels and get to live long happy lives, while the losers are destroyed. Below is an interactive playground, WebGPU required, which implements the crucial bits of the surfelization stage, but notably skips out on the spatial data structure in favour of a simpler brute force surfel neighbour search. Turn the model and zoom in, zoom out, and see how surfels fill in where there are too few. Feel free to play with the parameters to get a feel for how the system behaves: try increasing the number of surfels and watch the system rapidly grind itself to a halt, reduce the base radius and notice how you run out of capacity before you can cover the entire surface of the model. The default visualization shows surfels in 1/3 of their size, allowing you to more easily appreciate their positions and orientations, but you can switch to weighted rendering debug mode, to see those surfels in 1:1 and how their colors would actually blend in a real scenario.