jump to navigation

Implementing PShader.set() October 5, 2013

Posted by Andor Saga in JavaScript, Processing, Processing.js, PShader.
add a comment

I was in the process of writing ref tests for my implementation of PShader.set() in Processing.js, when I ran into a nasty problem. PShader.set() can take on a variety of types including single floats and integers to set uniform shader variables. For example, we can have the following:

pShader.set("i", 1);
pShader.set("f", 1.0);

If the second argument is an integer, we must call uniform1i on the WebGL context, otherwise uniform1f needs to be called. But in JavaScript, we can’t distinguish between 1.0 and 1. I briefly considered modifying the the interface for this method, but knew there was a better solution. No, the last thing I wanted was to change the interface. So I just thought about it until I came up with an interesting solution. I figured, why not call both uniform1i and uniform1f right after each other? What would happen? It turns out, it works! It seems one will always fail and the other will succeed, leaving us with the proper uniform set!

Advertisements

Experimenting with Normal Mapping using PShaders May 19, 2013

Posted by Andor Saga in GLSL, Processing, PShader.
4 comments

Normal_Mapping_PShader

Just over half a year ago, I wrote a blog about Experimenting with Normal Mapping. I wrote a simple Processing sketch that demonstrated the technique and I also wrote a hacked up Processing.js sketch to squeeze out some extra few frames/sec on the browser side of things.

This long weekend, I found myself with some extra time to hack on something. I remember that several weeks ago, Processing 2 introduced PShaders, which at the time I found exciting, but I didn’t have a chance to look at them. So this weekend I decided to take a look into this new PShader object. I haven’t touched shaders in a while, so I brushed up on them by reading the PShader tutorial on the Processing page.

After my refresher, I got to hacking and re-wrote my normal mapping sketch. Here is my complete sketch along with vertex and fragment shaders:

The sketch:

PImage diffuseMap;
PImage normalMap;

PShape plane;

PShader normalMapShader;

void setup() {
  size(256, 256, P3D);
  
  diffuseMap = loadImage("crossColor.jpg");
  normalMap = loadImage("crossNormal.jpg");
  
  plane = createPlane(diffuseMap);
  
  normalMapShader = loadShader("texfrag.glsl", "texvert.glsl");
  shader(normalMapShader);
  normalMapShader.set("normalMap", normalMap);
}

void draw(){
  background(0);
  translate(width/2, height/2, 0);
  scale(128);
  shape(plane);
}

void mouseMoved(){
  updateCursorCoords();
}

void mouseDragged(){
  updateCursorCoords();
}

void updateCursorCoords(){
  normalMapShader.set("mouseX", (float)mouseX);
  normalMapShader.set("mouseY", height - (float)mouseY);
}

void mousePressed(){
  normalMapShader.set("useSpecular", 1);
}

void mouseReleased(){
  normalMapShader.set("useSpecular", 0);
}

PShape createPlane(PImage tex) {
  textureMode(NORMAL);
  PShape sh = createShape();
  sh.beginShape(QUAD);
  sh.noStroke();
  sh.texture(tex);
  sh.vertex( 1, -1, 0, 1, 0);
  sh.vertex( 1,  1, 0, 1, 1);    
  sh.vertex(-1,  1, 0, 0, 1);
  sh.vertex(-1, -1, 0, 0, 0);
  sh.endShape(); 
  return sh;
}

The vertex shader:

#define PROCESSING_TEXTURE_SHADER

uniform mat4 transform;
uniform mat4 texMatrix;

attribute vec4 vertex;
attribute vec2 texCoord;

varying vec4 vertTexCoord;

void main() {
  gl_Position = transform * vertex;
  vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
}

The fragment shader:

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

#define PI 3.141592658

uniform sampler2D normalMap;
uniform sampler2D colorMap;

uniform int useSpecular;

uniform float mouseX;
uniform float mouseY;

varying vec4 vertTexCoord;

const vec3 view = vec3(0,0,1);
const float shine = 40.0;

void main() {
  // Convert the RGB values to XYZ
  vec4 normalColor  = texture2D(normalMap, vertTexCoord.st);
  vec3 normalVector = vec3(normalColor - vec4(0.5));
  normalVector = normalize(normalVector);

  vec3 rayOfLight = vec3(gl_FragCoord.x - mouseX, gl_FragCoord.y - mouseY, -150.0);
  rayOfLight = normalize(rayOfLight);

  float nDotL = dot(rayOfLight, normalVector);

  vec3 finalSpec = vec3(0);

  if(useSpecular == 1){
    vec3 reflection = normalVector;
    reflection = reflection * nDotL * 2.0;
    reflection -= rayOfLight;
    float specIntensity = pow( dot(reflection, view), shine);
    finalSpec = vec3(1.0, 0.5, 0.2) * specIntensity;
  }

  float finalDiffuse = acos(nDotL)/PI;

  gl_FragColor = vec4(finalSpec + vec3(texture2D(colorMap, vertTexCoord.st) * finalDiffuse), 1.0);
}

Performance

I found using PShaders very exciting, since I could place all this computational work on the GPU rather than CPU. So I wondered about the performance vs my old sketch. I’m on a mac mini, and after running tests I found my original normal mapping sketch ran at 30FPS with diffuse lighting and it ran at 21FPS using diffuse and specular lighting. Using PShaders, I was able to render specular and diffuse lighting at a solid 60FPS. Keep in mind the first sketch is 2D and my new one is 3D, so I’m not sure if that comparison is fair.

No Demo? 😦

Sadly, Processing no longer allows exporting to applets, so I can’t even post a demo running in Java. The perfect solution would be to implement the PShader in Processing.js, which is something I’m considering….