jump to navigation

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….

Game 2 for 1GAM: Tetrissing May 17, 2013

Posted by Andor Saga in 1GAM, Game Development, Open Source, Processing, Processing.js.
add a comment

tetrissing

Click to play!
View the source

I’m officially releasing Tetrissing for the 1GAM challenge. Tetrissing an open source Tetris clone I wrote in Processing.

I began working on the game during Ludum Dare 26. There were a few developers hacking on LD26 at the Ryerson Engineering building, so I decided to join them. I was only able to stay for a few hours, but I managed to get the core mechanics done in that time.

After I left Ryserson, I did some research and found most of the Tetris clones online lacked some basic features and has almost no polish. I wanted to contribute something different than what was already available. So, that’s when I decided to make this one of my 1GAM games. I spent the next 2 weeks fixing bugs, adding features, audio, art and polishing the game.

I’m fairly happy with what I have so far. My clone doesn’t rely on annoying keyboard key repeats, and it still allows tapping the left or right arrow keys to move a piece 1 block. I added a ‘ghost’ piece feature and kickback feature, pausing, restarting, audio and art. There was nothing too difficult about all this, but it did require work. So, in retrospect I want to take on something a bit more challenging for my next 1GAM game.

Lessons Learned

One mistake I made when writing this was over complicating the audio code. I used Minim for the Processing version, but I had to write my own implementation for the Processing.js version. I decided to look into the Web Audio API. After fumbling around with it, I did eventually manage to get it to work, but then the sound didn’t work in Firefox. Realizing that I made a simple matter complex, I ended up scrapping the whole thing and resorting to use audio tags, which took very little effort to get working. The SoundManager I have for JavaScript is now much shorter, easier to understand, and still gets the job done.

Another issue I ran into was a bug in the Processing.js library. When using tint() to color my ghost pieces, Pjs would refuse to render one of the blocks that composed a Tetris piece. I dove into the tint() code and tried fixing it myself, but I didn’t get too far. After taking a break, I realized I didn’t really have the time to invest in the Pjs fix and also came up with a dead-simple work-around. Since only the first block wasn’t rendering, I would render that first ‘invisible’ block off screen, then re-render the same block onscreen the second time. Fixing the issue in Pjs would have been nice. But that wasn’t what my main goal was.

Lastly, I was reminded how much time it takes to polish a game. I completed the core mechanics of Tetrissing in a few hours, but it took another 2 weeks to polish it!

If you like my work, please star or fork my repository on Github. Also, please post any feedback, thanks!