npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@alexjwayne/ts-gl-shader

v0.1.2

Published

Strongly typed no boilerplate WebGL shaders.

Downloads

22

Readme

Build

ts-gl-shader

Strongly typed no boilerplate WebGL shaders.

This library will parse your GLSL shaders at the type level and provide an easy to use object for WebGL rendering.

Currently Unsupported

PR's welcome.

  • [x] ~~Inspect set values in tests without having to spy on gl.uniform3f() and it's friends.~~
  • [x] ~~Precision overrides like uniform highp vec3 uPos.~~
  • [ ] Texture samplers.
  • [ ] Set GLSL arrays like uniform vec3 uPoints[10].
  • [ ] Set GLSL structs.
  • [ ] VAOs
  • [ ] UBOs

Table of Contents

Features

  • Compiles and creates shader programs from vertex and fragment string literals.
  • Provides type safety for setting uniforms and attrubutes.
  • Caches shader locations on creation for you.
  • FAST! Alomst zero overhead from calling the native APIs and allocates no objects or arrays when seting and rendering.

Installation

Install the library as a production depedency.

npm install @alexjwayne/ts-gl-shader

And then import the createShaderProgram function.

import { createShaderProgram } from '@alexjwayne/ts-gl-shader'

Examples

  • Hello World: [ code | view ] Render a simple fullscreen shader.
  • Spinning Cube: [ code | view ] Render a cube that spins in the viewport

Usage

First create a file for your shader.

// my-shader.ts
import { createShaderProgram } from '@alexjwayne/ts-gl-shader'

const vertSrc = /* glsl */ `
  precision mediump float;

  attribute vec2 aVert2;
  attribute vec3 aVert3;
  attribute vec4 aVert4;
  
  uniform vec2 uVec2;
  uniform vec3 uVec3;
  uniform vec4 uVec4;

  uniform float uFloat;
  uniform bool uBool;

  varying vec2 vUV;

  void main() {
    vUV = aVec2;
    gl_Position = vec4(aVert3, 1.0);
  }
` as const

const fragSrc = /* glsl */ `
  precision mediump float;

  uniform int uInt; 
  uniform uint uUnsignedInt;
  uniform vec2 uVec2; 

  varying vec2 vUV;

  void main() {
    gl_FragColor = vec4(vUV, 0.0, 1.0);
  }
` as const

export function createMyShader(gl: WebGL2RenderingContext) {
  return createShaderProgram(gl, { vertSrc, fragSrc })
}

Then use and render that shader.

// index.ts

import { createMyShader } from './my-shader'

function startGame(gl: WebGL2RenderingContext) {
  const myShader = createMyShader(gl)

  // other setup...

  function render() {
    // Make this shader current, so it's uniforms and attributes can be set.
    myShader.use()

    myShader.attributes.aVert2.set(someBuffer2D)
    myShader.attributes.aVert3.set(someBuffer3D)
    myShader.attributes.aVert4.set(someBuffer4D)

    myShader.uniforms.uFloat.set(1)
    myShader.uniforms.uVec2.set(1, 2)
    myShader.uniforms.uVec3.set(1, 2, 3)
    myShader.uniforms.uVec4.set(1, 2, 3, 4)

    // @ts-expect-error Type safety in attributes
    myShader.attributes.noAttributeHere.set(someBuffer) // type error

    // @ts-expect-error Type safety in uniform properties
    myShader.uniforms.noUniformHere.set(1, 2, 3) // type error

    // @ts-expect-error Type safety in uniform values
    myShader.uniforms.uVec3.set(1) // type error, expected three values for a vec3

    // This library does not make any draw calls for you
    // But it's easy to do yourself.
    gl.drawArrays(gl.TRIANGLES, 0, triangleCount)

    requestAnimationFrame(render)
  }

  requestAnimationFrame(render)
}

API Reference

createShaderProgram()

createShaderProgram(
  gl: WebGL2RenderingContext,
  vertSrc: string,
  fragSrc: string
): ShaderProgram

Creates and returns a shader program that can be used for rendering.

NOTE: vertSrc and fragSrc must be string literals that contain the source code of the shader itself. Otherwise Typescript cannot see the settable properties in the shader. This means you must declare the shader in a typescript file as const. This will not work if the shader source code is typed as string.

// example
const shader = createShaderProgram(gl, `vertex shader source`, `fragment shader source`)

shaderProgram.use()

shaderProgram.use(fn?: (shaderProgram: ShaderProgram)): void

Activates the shader program so that its attributes and uniforms may be set, and the shader may be rendered. This must be called before setting any values or rendering.

// example
shader.use()
// set attributes
// set uniforms
// render

The method optionally accepts a callback that provides the shader program use() was called on, which can reduce verbosity and provide a nice indent for the body of code that works with the shader.

// example
deeply.nested.object.shader.use((shader) => {
  // set attributes
  // set uniforms
  // render
})

shaderProgram.attributes[attributeName].set

shaderProgram
  .attributes[attributeNameHere]
    .set(buffer: WebGLBuffer): void

Sets a shader attribute to a WebGLBuffer. The size argument to WebGL that sets the number of values per vertex is set for you based on the data type of the attribute. For instance, a vec3 will set a size of 3.

// example

// shader has:
//
//     attribute vec3 aVert;
//

shader.use()
shader.attributes.aVert.set(myGeometryBuffer)

shaderProgram.uniforms[uniformName].set

shaderProgram
  .uniforms[uniformNameHere]
    .set(...values: UniformSetterArgs): void

Sets a shader uniform to a specific set of numeric values. The type of this setter function is derived from the type of the uniform in the shader. For instance, a vec3 would accept three numbers as arguments.

// example

// shader has:
//
//     uniform vec4 uColor;
//

shader.use()
shader.uniforms.uColor.set(1, 0, 1, 1)

shaderProgram.uniforms[uniformName].setArray

shaderProgram
  .uniforms[uniformNameHere]
    .setArray(values: UniformSetterArrayArg): void

Sets a shader uniform to a specific binary typed array or tuple of numbers. The type of this setter function is derived from the type of the uniform in the shader. For instance, a vec3 would accept [number, number, number] | Float32Array but a mat2 would accept [number, number, number, number] | Float32Array.

If a typed array is passed to setArray() this is not the correct length, a runtime error will be thrown.

// example

// shader has:
//
//     uniform vec4 uColor;
//

shader.use()
shader.uniforms.uColor.setArray([1, 0, 1, 1])
shader.uniforms.uColor.setArray(new Float32Array([1, 0, 1, 1]))

// This will throw a runtime error.
shader.uniforms.uColor.setArray(new Float32Array([1]))

Testing

Use createShaderProgram.enableTestMode() to capture the value that most recent value that was set on the value property of the uniform.

NOTE: Test mode is set in the shader at creation time, and so it must be enabled before the shader program is created.

Given a shader definition which defines custom logic such as this:

// my-shader.ts
const vertSrc = /* glsl */ `
  uniform vec2 uPos;
  uniform vec2 uVel;
  // ...
`

const fragSrc = /* glsl */ `
  //...
`

function createMyShader(gl: WebGL2RenderingContext) {
  const shaderProgram = createShaderProgram(gl, vertSrc, fragSrc)

  return {
    ...shaderProgram,
    setPhysicalProperties(x: number, y: number, vx: number, vy: number) {
      shaderProgram.uniforms.uPos.set(x, y)
      shaderProgram.uniforms.uVel.set(vx, vy)
    },
  }
}

You would test it like this:

// my-shader.spec.ts
beforeEach(() => {
  createShaderProgram.enableTestMode()
})

afterEach(() => {
  createShaderProgram.disableTestMode()
})

it('should set the uniform', () => {
  const shader = createMyShader(gl)
  shader.setPhysicalProperties(1, 2, 3, 4)
  expect(shader.uniforms.uPos.value).toEqual([1, 2])
  expect(shader.uniforms.uVel.value).toEqual([3, 4])
})