Uniforms

Uniforms are used to pass in information to the shader at runtime.

Creating uniforms

A uniform can be defined with a given key that later can be used to define the default value and to update the uniform on the instance of a material.

import { uniformFloat, float, sin, translateY } from "@hology/core/shader-nodes"

const uniformTime = uniformFloat("time")

const material = new NodeShaderMaterial({
  color: rgba(0x00ff00, sin(uniformTime).multiply(float(0.5)).add(float(0.5)),
  transform: translateY(sin(uniformTime.multiply(float(5)))),
  uniforms: {
    time: { value: 0 }
  }
})

You can update the uniform value just like you would for ShaderMaterial in Three.js.

material.uniforms.time.value = clock.getElapsedTime()

These are some helper methods to create uniforms for common types. Note that you can also pass in a default value to most uniform type when defining the uniform.

uniformBool(name: string, value?: boolean): BoolNode
uniformFloat(name: string, value?: number): FloatNode
uniformVec2(name: string, value?: Vector2): Vec2Node
uniformVec3(name: string, value?: Vector3): Vec3Node
uniformVec4(name: string, value?: Vector4): Vec4Node
uniformMat2(name: string, value?: Matrix2): Mat2Node
uniformMat3(name: string, value?: Matrix3): Mat3Node
uniformMat4(name: string, value?: Matrix4): Mat4Node
uniformSampler2d(name: string): Sampler2dNode

Uniform arrays

Uniform arrays requires in addition to the name of the uniform the type it should contain and an IntNode to represent the size of the array which needs to be known at compile time.

The example below illustrates how to refer to the directional shadow matrix uniform provided by ThreeJS. In this case, the number of elements is provided with a defined constant NUM_DIR_LIGHT_SHADOWS.

import { UniformArrayNode, IntExpressionNode, Mat4Node } from "@hology/core/shader-nodes"

export const uniformDirectionalShadowMatrix = new UniformArrayNode(
  'directionalShadowMatrix',
  Mat4Node,
  new IntExpressionNode('NUM_DIR_LIGHT_SHADOWS')
);

Uniform struct arrays

Structs exist in GLSL but are normally not necessary when using this library as regular JavaScript objects and classes can do their job. However, when passing an array of a struct type to the shader, the struct needs to be defined.

The example below illustrates how this can be done to create a struct for the DirectionalLight type.

import { StructType }  from "@hology/core/shader-nodes"

abstract class DirectionalLight extends StructType {
  static readonly typeName = 'DirectionalLight';
  readonly direction = this.get(Vec3Node, 'direction');
  readonly color = this.get(RgbNode, 'color');
}
  • A struct should be defined as an abstract class.

  • It must have its name defined using a static property named typeName. This does not need to match the class name.

  • All properties must be defined with a call to this.get(type, name)

The class is abstract as you should not try to instantiate it on your own. Internally, a subclass will be defined during the build process of the shader and passed to array methods.

With this struct defined, it can now be used to create a uniform struct array.

const uniformDirectionalLights: ArrayNode<DirectionalLight> = new UniformArrayNode(
    'directionalLights', 
    DirectionalLight,
    new IntExpressionNode('NUM_DIR_LIGHTS')
);

Predefined

Camera and model

Three.js provides uniforms documented here.

import { uniforms }  from "@hology/core/shader-nodes"

uniforms.modelMatrix // Mat4Node 
uniforms.modelViewMatrix // Mat4Node
uniforms.projectionMatrix // Mat4Node
uniforms.viewMatrix // Mat4Node
uniforms.normalMatrix // Mat3Node
uniforms.cameraPosition // Vec3Node

Time

The time uniforms can be useful to animate colors or vertex transforms.

import { timeUniforms } from "@hology/core/shader-nodes"

timeUniforms.elapsed // FloatNode - Time in seconds. 

Particles

import { particleUniforms } from "@hology/core/shader-nodes"

particleUniforms.energy // FloatNode - A value going from 1 to 0 over the lifetime of the particle
particleUniforms.velocity // Vec3Node - The velocity of the particle 

Depth

The depth uniforms can be used to get the depth behind a fragment on a triangle to the geometry behind it. This can be useful create create effects like water where the edges are more translucent.

import { fragmentLinearEyeDepth, linearEyeDepth } from "@hology/core/shader-nodes";

const depth = linearEyeDepth.subtract(fragmentLinearEyeDepth).divide(linearEyeDepth)

Lights and shadows

The following uniforms are provided in case you want to implement your own lighting shaders.

import {
    uniformPointLights,
    uniformHemisphereLights,
    uniformDirectionalLights,
    uniformAmbient,
    uniformReceiveShadow,
    uniformDirectionalLightShadows,
    uniformDirectionalShadowMap,
    uniformDirectionalShadowMatrix 
}  from "@hology/core/shader-nodes"

uniformPointLights // ArrayNode<PointLight>
uniformHemisphereLights // ArrayNode<HemisphereLight>
uniformDirectionalLights // ArrayNode<DirectionalLight>
uniformAmbient // Vec3Node
uniformReceiveShadow // Boolean
uniformDirectionalLightShadows // ArrayNode<DirectionalLightShadow>
uniformDirectionalShadowMap // ArrayNode<Sampler2DNode>
uniformDirectionalShadowMatrix // ArrayNode<Mat4Node>

Last updated