Live coding in GSLS (improved)

edited August 2014 in GLSL / Shaders

Hi there!

Here is an improved version of the live coding sketch first introduced here on the previous forum.

The goal of this sketch is to provide a simple boilerplate to start tinkering with glsl without having to stop and run your sketch every time you make a change to the shader file.

Main improvements:

  • The sketch only reloads the shader if the file has been modified
  • If the shader doesn't compile, an error is displayed in the sketch window

I tried to keep the code short and add comments where needed. There might still be some mistakes or inconsistencies so your feedback is more than welcome!

ShaderLiveCoding.pde

PShader shd_gradient;

boolean loaded = false;
boolean errorDisplayed = false;

String shaderFileName = "gradient.frag";
File shaderFile;

PGraphics scene;
PGraphics error;

// When was the file last modified?
long previousTimeStamp, timeStamp;

int reloadEvery = 60; // in frames

void setup() {
    size(800,600,P2D);

  // Load the shader
  tryLoadingShader(shaderFileName);

  // Get the file we want to monitor for changes
  shaderFile = new File(dataPath(shaderFileName));

  // Initialize timestamps
  previousTimeStamp = shaderFile.lastModified();
  timeStamp = previousTimeStamp;

  // The shader and the error message are 
  // drawn on separate PGraphics
  scene = createGraphics(width,height,P2D);
  error = createGraphics(width,height,P2D);

}

void draw() {

  // Try to reload the shader regularly
  if (frameCount % reloadEvery == 0) {
    frame.setTitle("frame: " + frameCount + " - fps: " + frameRate);  

    // Check that we do have a file at that path
    if(shaderFile.exists()){
      // Only reload if the file has changed
      if(isFileUpdated(shaderFile)){
        println("Shader file has changed. Updating.");
        tryLoadingShader(shaderFileName);
      }
    } 
    else {
      println("File not found:", shaderFile);
    }
  }

  if(loaded) {
    // Yes, the shader compiled

    shd_gradient.set("vec2_mouse", float(mouseX), float(height-mouseY));

    scene.beginDraw();
    scene.shader(shd_gradient);
    scene.rect(0,0,width,height);
    scene.resetShader();
    scene.endDraw();

    image(scene,0,0);
  }
  else if (!errorDisplayed) {
    // No, the shader did not compile
    displayError();
  }

}


void tryLoadingShader(String shaderFilePath) {

  try {  
    shd_gradient = loadShader(shaderFilePath);

    // You have to set at least one uniform here to trigger syntax errors
    // see: https://github.com/processing/processing/issues/2268
    shd_gradient.set("vec2_sketchSize", float(width), float(height));

    loaded = true;
    errorDisplayed = false;
  } 

  catch (RuntimeException e) {    
    if(errorDisplayed == false) { 
      // String time = nf(str(hour()),2) + ":" + nf(str(minute()),2) + ":" + nf(str(second()),2);
      println("\n");
      // println("At", time, "loadShader() returned the following error: \n");
      println("loadShader() returned the following error: \n");
      e.printStackTrace(); 
    }
    loaded = false;
  }
}

void displayError() {

  error.beginDraw();
  error.pushStyle();
  error.pushMatrix();

  error.noStroke();
  error.fill(255, 255, 255, 100);
  error.rect(0,0,width,height);

  error.stroke(255);
  error.fill(255, 0, 0);

  error.textAlign(CENTER);
  error.textSize(18);
  error.text("Error compiling the shader.", width/2 , height/2 - 15);
  error.text("Check the console for details.", width/2 , height/2 + 15);

  error.popMatrix();
  error.popStyle();
  error.endDraw();

  image(error,0,0);

  errorDisplayed = true;
}


private boolean isFileUpdated( File file ) {

  //print("Previous file timestamp:", timeStamp, " ");

  long previousTimeStamp = timeStamp;
  timeStamp = file.lastModified();

  //println("New timestamp:", timeStamp, " File:", file);

  if( timeStamp != previousTimeStamp ) {
    previousTimeStamp = timeStamp;
    //Yes, file is updated
    return true;
  }
  //No, file is not updated
  return false;
}    

gradient.frag

uniform vec2 vec2_sketchSize;
uniform vec2 vec2_mouse;

void main() {

  float r = gl_FragCoord.x / vec2_sketchSize.x;
  float g = gl_FragCoord.y / vec2_sketchSize.y;
  float b = vec2_mouse.x / vec2_sketchSize.x;
  float a = 1.0;

  gl_FragColor = vec4(r,g,b,a);
}    

The latest version of the code will be available on github:

https://github.com/SableRaf/Processing-Experiments/tree/master/2014/ShaderLiveCoding

Comments

Sign In or Register to comment.