ShaderExpo

WebGL Shader Playground.

Features

  • Rich CodeEditor
  • Simple CodeEditor
  • AutoCompletion
  • Live Editing
  • Basic Debugging

I made shader expo because I wanted to learn WebGL Shaders quickly and to understand how WebGL handles shaders at the runtime. The most critical part of Webgl is setting up the boilerplate for all the code.

Shader Class

I created this modular shader class to handle, update, compile the Fragment, and Vertex shaders. It holds the WebGL Program, fragmentShader and VertexShader source String and shaders itself, and Errors of each shader.

class Shader {
  constructor(gl, vShader, fShader) {
    this.gl = gl
    this.vShader = vShader
    this.fShader = fShader

    this.vertexShader = null
    this.fragmentShader = null

    this.uniforms = {}
    this.attribs = {}

    this.program = null

    this.isVertexShaderError = false
    this.isFragmentShaderError = false
    this.fragmentShaderErrors = []
    this.vertexShaderErrors = []
  }
}

Init Shaders

init code on github

Then I added a init method which initializes the shaders from its source and creates the program with this.createProgram method. If we encounter any errors, then we will not create the program and terminate the function. We also have this.createShader() method, which creates and initialize the shader.

init() {
  this.vertexShader = this.createShader(this.gl.VERTEX_SHADER, this.vShader);
  this.fragmentShader = this.createShader(this.gl.FRAGMENT_SHADER, this.fShader);

  if (this.isFragmentShaderError) return;
  if (this.isVertexShaderError) return;
  this.createProgram();
}

createShader

createShader code on github

createShader method creates the WebGL shader from the source, compiles it, and checks for errors. and if we encounter any errors while compiling we set the isVertexShaderError and isFragmentShaderError to true and also sets the vertexShaderErrors, fragmentShaderErrors with the error message.

createShader(type, source) {
  const shader = this.gl.createShader(type);
  this.gl.shaderSource(shader, source);
  this.gl.compileShader(shader);
  // Check For Shader Errors
  let error = this.getShaderError(shader, source);
  let name = (type == this.gl.VERTEX_SHADER) ? 'VERTEX_SHADER' : 'FRAGMENT_SHADER';

  if (error) {
    error.type = name;
    if (error.type === 'VERTEX_SHADER') {
      this.vertexShaderErrors = error;
      this.isVertexShaderError = true;
    } else {
      this.fragmentShaderErrors = error;
      this.isFragmentShaderError = true;
    }
    return null;
  }

  return shader;
}

createProgram

createProgram code on github

The createProgram function creates the program, attaches the shaders, links the program, and also throws errors if we encounter any Errors. And finally returns the program.

createProgram() {
  this.program = this.gl.createProgram();
  this.gl.attachShader(this.program, this.vertexShader);
  this.gl.attachShader(this.program, this.fragmentShader);
  this.gl.linkProgram(this.program);

  if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) {
    console.warn('Unable to initialize program ' + this.gl.getProgramInfoLog(this.program));
    return null;
  }
  return this.program;
}

getShaderError

getShaderError code on github We also have getShaderError method which is a helper method to get specific error message for specific shader, it also shows where the error happend in the source code with line number.

getShaderError(shader, shaderString) {
  if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
    let lines = [];
    let reg = /ERROR:\s\d+:(\d+):/img;
    let errorMsg = this.gl.getShaderInfoLog(shader);

    errorMsg.replace(reg, function (match, g1, g2) {
      lines.push((+g1));
    });
    let message = this.gl.getShaderInfoLog(shader);
    message = message.split('\n');
    message.pop();

    return {
      msg: message,
      line: lines
    };
  }
}

Other Classes

Shader class was the most important for this project. We also have Camera, RawModel, Mesh classes, which handle different parts of the WebGL context.

see all the classes on Github

Setup

In the end, I also used Ace library to get my code editor up and running. (which was easy)

see index.js setup

I also used some different shaders from GLSL Sandbox to check things out and see the capabilities of ShaderExpo.