itsdouges / lamina

🍰 An extensible, layer based shader material for ThreeJS

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool


lamina

🍰 An extensible, layer based shader material for ThreeJS


Chat on Twitter Chat on Twitter


These demos are real, you can click them! They contain the full code, too. πŸ“¦


lamina let's you create materials with a declarative, system of layers. Layers make it incredibly easy to stack and blend effects. This approach was first made popular by the Spline team.

import { LayerMaterial, BaseLayer, DepthLayer } from 'lamina'

extend({ LayerMaterial, BaseLayer, DepthLayer })

function GradientSphere() {
  return (
    <Sphere>
      <layerMaterial>
        <baseLayer
          color={'#ffffff'} //
          alpha={1}
          mode="NORMAL"
        />
        <depthLayer
          colorA={'#810000'}
          colorB={'#ffd0d0'}
          alpha={1}
          mode="MULTIPLY"
          near={0}
          far={2}
          origin={[1, 1, 1]}
        />
      </layerMaterial>
    </Sphere>
  )
}
Show Vanilla example

Lamina can be used with vanilla Three.js. Each layer is just a class.

import { LayerMaterial, BaseLayer, DepthLayer } from 'lamina'

const geometry = new THREE.SphereGeometry(1, 128, 64)
const material = new LayerMaterial({
  layers: [
    new BaseLayer({
      color: '#d9d9d9', //
      alpha: 1,
      mode: 'NORMAL',
    }),
    new DepthLayer({
      colorA: '#002f4b',
      colorB: '#f2fdff',
      alpha: 1,
      mode: 'MULTIPLY',
      near: 0,
      far: 2,
      origin: [1, 1, 1],
    }),
  ],
})

const mesh = new THREE.Mesh(geometry, material)

Layers

Built-in layers

Here are the layers that laminia currently provides

Name Function
BaseLayer Flat color
DepthLayer Depth based gradient
FresnelLayer Fresnel shading
NoiseLayer White noise

Writing your own layers

You can write your won layers by extending the AbstractLayer class.

class CustomLayer extends AbstractLayer {
  // Name of your layer
  name: string = 'CustomLayer'
  // Give it an ID
  protected uuid: string = AbstractLayer.genID()

  // Define your own uniforms
  uniforms: {
    [key: string]: IUniform<any>
  }
  constructor(props?: CustomLayerProps) {
    super()
    const { customUniform } = props || {}

    // Make your uniforms unique in the layer
    // stack by appending the ID of the layer to it.
    this.uniforms = {
      [`u_${this.uuid}_customUniform`]: {
        value: customUniform ?? defaultValue,
      },

      // We recommend having an alpha and a blend mode defined
      [`u_${this.uuid}_alpha`]: {
        value: 1,
      },
      [`u_${this.uuid}_mode`]: {
        value: SC_BLEND_MODES['NORMAL'],
      },
    }
  }

  // Return a shader chunk that describes your variable
  // in the Fragment shader.
  getFragmentVariables() {
    return /* glsl */ `    
    // Lets assume this is a color
    uniform vec3 u_${this.uuid}_customUniform;

    uniform float u_${this.uuid}_alpha;
    uniform int u_${this.uuid}_mode;
`
  }

  // Return an shader chunk with your layer's implementation.
  // Parameter `e` is the result of the previous layer.
  // `sc_blend` is a blending function.
  //
  // vec4 e = vec4(0.);
  // ...
  // e = sc_blend(previousLayer(e), e, prevBlendMode);
  // e = sc_blend(currentLayer(e), e, currentBlendMode);
  // ...
  // gl_FragColor = e;
  //
  // List of blend modes: https://github.com/pmndrs/lamina/blob/9ebbd6ece31a1e8313c6a2f316a7d591e978437f/src/types.ts#L3
  getFragmentBody(e: string) {
    return /* glsl */ `    
      // Make sure to create unique local variables
      // by appending the UUID to them
      vec3 f_${this.uuid}_color = u_${this.uuid}_customUniform;

      ${e} = sc_blend(vec4(f_${this.uuid}_color, u_${this.uuid}_alpha), ${e}, u_${this.uuid}_mode );
  `
  }

  // Optionals

  // Return a shader chunk that describes your variables
  // in the vertex shader.
  // Mostly used to pass varyings to the Fragment shader
  getVertexVariables(): string {
    return /* glsl */ `
    varying vec2 v_${this.uuid}_uv;
    `
  }

  // Return an shader chunk with your layer's of the vertex shader.
  // Mostly used to assign varyings to values.
  getVertexBody(): string {
    return `
    v_${this.uuid}_uv = uv;
    `
  }

  // Setters and getters for uniforms
  set customUniform(v) {
    this.uniforms[`u_${this.uuid}_customUniform`].value = v
  }

  get customUniform() {
    return this.uniforms[`u_${this.uuid}_customUniform`].value
  }
  // ...
}

About

🍰 An extensible, layer based shader material for ThreeJS

License:MIT License


Languages

Language:TypeScript 92.9%Language:JavaScript 3.3%Language:HTML 2.5%Language:CSS 1.4%