The WebGL in offscreen canvas is made in oGL, unlike the source code of these demos which is in ThreeJS. A lot of people asked about that last week.
If you want to get started with shaders, today I've officially released a 6-part "Intro to shaders for web developers" email course for you.
Math can be complicated, but shaders are very visual. We are going to see the math's through colors and gradients, instead of symbols and numbers.
Learn the very basics of shaders, build a good foundation of the most used functions in shaders, and make two fun sketches while going through the course. Register here if you are interested!
oGL is my library of choice when I want something very slim. However, for Offscreen Canvas I chose oGL because I wanted to explore. A lot of people ask, so let me explain.
There's a lot of incredibly useful stuff in threeJS. A lot of hidden things that the library does for you mostly to keep you sane and focused on what you want to build. However, I wanted to dig deeper to see what I took for granted and what was essential.
It has been incredibly inconvenient. But by moving away from threeJS, I've learned a lot about why the threeJS's renderer takes care of compiling everything in the renderer instead in the meshes, or why the ThreeJS incredibly confusing shadeChunk system was made. I've also made my own PBR shaders, that was a hell of a ride.
oGL is pretty good and fun, but when you randomly find yourself needing a QuaternionLinearInterpolant or some other random thing that comes with threeJS by default, you'll wish you were coding in threeJS.
While some have taken their own ThreeJs Journey, I've definitely been on that oGL pilgrimage. And it's only made me appreciate the work and love the threeJS folks put into the library. This exploration was very inspired by my favorite quote in the American classic "Walden":
I went to the woods because I wished to live deliberately, to front only the essential facts of life, and see if I could not learn what it had to teach, and not, when I came to die, discover that I had not lived.
Subscribe to get the source code!
If you are subscribed, you already have the download links in this newsletter email!
In circle and spheres
By itself, this demo is uninteresting, a bunch of boxes placed in the shape of a torus. However, it was brought to life with the rounded corners on the boxes and the camera movement.
The camera always looks at the center, however, it is moved in circles by the mouse. This rotation is driven with quartetions' slerp instead of using Euler rotations. Euler rotation caused a camera jump once the mouse angle went over the loop point of the angle PI * 2.
Vertex shader displacement
Making fun distortion to complex shapes is a matter of breaking them down into simpler shapes. Tube Geometries we can think of like a series of circles on top of each other.
With circles we can work more easily, so in the shader we break the tube into its main components:
- The radius → from transformed.xz
- The angle → from transformed.xz
- The height of the point → from transformed.y or uv.y
Then, we can play with it similar to how we distort circles. The height is the most important parameter because you use it to drive the underlying cosine function that bends each section.
Webgl Glass and refraction
Refraction in games, unless raytraced, is mostly an illusion. It's either done with cube map tricks or with screen space reflections, both of which are used in games like Hitman 3. So, you can mix and match, not just choose one.
In my demo, I'm using Screen Space reflections (SSR) on both sides of the icosphere. A double-sided refraction requires three renders. Once, for the background, once for the first back refraction, and the other one for the second front refraction.
Screen Space reflection samples what's currently in view so it's not a real reflection, but it gives the illusion.
WebGL Galleries and sliders
My trick for circular sliders like these is to arrange each slide next to each other using their mesh.position.x
. Then, in the shader, calculate the modelPosition
with the modelMatrix
, to get its actual world position.
With the world position, I can use the X coordinate as an angle to calculate the new X and Z coordinates.