Lamina is a library that allows the definition of shader materials via a declarative, layer-based system. Lamina materials can be declared in JSX for React Three Fiber or a nested object, and is based on THREE-CustomShaderMaterial for the shader composition. A shader material created with lamina with JSX looks a little like this:
|
|
Lamina compiles transpiles JSX declaration, during the build step, into GLSL code suitable for use in Three.js.
The future of Lamina
As of July 2023 the lamina project on Github has been archived and the library is no longer being actively developed. A reason for this was provided by the main contributor:
This project needs maintainers and a good rewrite from scratch. Lamina does a lot of hacky processing to achieve its API goals. As time has gone by I have started to doubt if it’s worth it. These hacks make it unreliable, unpredictable and slow. Not to mentaion, quite convoluted to maintain and debug. There might be better APIs or implimentations for this kind of library but I currently do not have the bandwidth to dedicate to finding them. Perhaps in the future.
This means lamina would become more difficult to use due to missing features, unsolved bugs and won’t be recommended going forward.
Replacing lamina
I couldn’t find any alternatives and decided this would be a good time to learn to compose WebGL shaders in a similar fashion by digging into Lamina’s source code. This article attempts to compose a custom environment material, similar to the example above without the use of Lamina. The end result should look a little like this:
Intended audience This blog post assumes you’re familiar with these concepts: Javascript, React, Three.js, GLSL shaders
This article will explore the container material, LayerMaterial
.
The core layer material
A Lamina shader material is composed by adding layers as children to the base <LayerMaterial />
.
|
|
A LayerMaterial
is an instance of the LayerMaterial
class, which is based on the CustomShaderMaterial
class.
|
|
The
CustomShaderMaterial
is outside the scope of our exploration as we’ll be recreating the environment material’s shader from the ground up.
Structure
The LayerMaterial
is instantiated with a few key parameters:
color
: the base color to be used in generated fragment shader; defaults to ‘white’alpha
: the alpha to be used in the generated fragment shader; defaults to 1lighting
: the lighting mode that determines the base code for both the shaders. For the scope of this article, we’ll focus on aLayerMaterial
created with the'basic'
lighting mode which provides the base code from the THREE.BaseMaterial’s shaders.layers
: an ordered list of the constituent layers.
A few base uniforms, global GLSL variables, are created based on some of the initialization parameters.
|
|
After initialization, and on each update to the material, the LayerMaterial
is transpiled via its genShaders
method, which composes its constituent layers into both the vertex and fragment shaders that are then passed on to the THREE.ShaderMaterial
.
In the order in which the layers are added, each layer’s uniforms, vertex shader and fragment shader are combined into the LayerMaterial
’s uniforms, vertex shader and fragment shader respectively.
|
|
The vertexVariables and fragmentVariables are variables defined and scoped to each layer. We’ll explore those when we look at the layers.
The HelpersChunk, NoiseChunk and BlendModesChunk are pre-defined shader functions that are used in the layers and have to be hoisted to the top of the shaders.
To better understand the output of a transpiled LayerMaterial
, we’ll explore a LayerMaterial
with no layers added and the ‘chunks’ removed.
|
|
The layer material when added to an environment and rendered produces the result:
If we expand the layer material to its constituent parts, we’ll see the following:
uniforms
|
|
vertex shader
|
|
fragment shader
|
|
The recreated shader when added to an environment and rendered produces the result:
While the shader does not change the material much, we’ve been able to achieve a similar result to the lamina-based shader material.
What next?
In the next article, we’ll explore one of the layers that can be added to a LayerMaterial
and how they affect the final shader.