Game 2D light and shadow ray casting with colors

Hello, I create sketch that uses ray casting with shadows and lights, but i want to tint every single light with own color, how i can do it ?

boolean lihghtsEnable = true;
PImage background, screen;
PGraphics lightMask;
ArrayList<Light> lights;
ArrayList<Wall> walls;
int pressedX, pressedY;

void setup() {
  size(800, 450);
  frameRate(100);
  textAlign(LEFT, TOP);
  background(0);

  walls = new ArrayList<Wall>();
  lights = new ArrayList<Light>();

  lightMask = createGraphics(width, height);
  background = loadImage("https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/4964b948864861.58a43fdc98e89.png");
  background.resize(width, height);
}

void draw() {
  // Draw background or game
  background(background);

  // Get game screen pixels
  screen = get();

  // Draw blackness
  fill(0, 240);
  noStroke();
  rect(0, 0, width, height);

  // Draw lights mask
  if (lihghtsEnable) {
    lightMask.beginDraw();
    lightMask.background(0);
    lightMask.blendMode(ADD);
    for (Light l : lights) {
      l.draw();
    }
    lightMask.endDraw();
  }

  // Apply mask to game screen pixels
  screen.mask(lightMask);

  // Draw game screen
  image(screen, 0, 0);

  // Draw walls
  for (Wall w : walls) {
    w.draw();
  }

  // Show FPS and others stuff
  fill(0, 255, 0);
  text(int(frameRate)+"\n"+"Lights: "+lihghtsEnable+"\nLeft mouse button - create wall\nRight mouse button - create light\nKey 'L' - enables lights\nKey 'C' - clear all walls", 0, 0);
}

class Light {
  int x, y, dist;
  color c = color(255), recoCol = color(random(100, 255), random(100, 255), random(100, 255));
  PGraphics mask;
  PImage texture;
  
  Light(int x, int y, int dist, color c) {
    this.x = x;
    this.y = y;
    this.dist = dist;
    this.c = c;
    texture = loadImage("light.png");
    texture.resize(dist*2, dist*2);
    mask = createGraphics(texture.width, texture.height);
  }
  
  void draw() {
    mask.beginDraw();
    mask.background(0);
    mask.fill(255);
    mask.noStroke();
    mask.beginShape();
    for (float i = 0; i < 360; i+=1) { // This is just test with angle ray scan, it is not accurate, later i will change it with cleverly ray render casting.
      PVector point = new PVector(x+dist*sin(radians(i)), y+dist*cos(radians(i)));
      for (Wall w : walls) {
        PVector pos = lineLine(w.x1, w.y1, w.x2, w.y2, x, y, x+dist*sin(radians(i)), y+dist*cos(radians(i)));
        if (pos.z == 1) {
          if (dist(x, y, point.x, point.y) > dist(x, y, pos.x, pos.y)) {
            point.x = pos.x;
            point.y = pos.y;
          }
        }
      }
      mask.vertex(point.x-x+dist, point.y-y+dist);
    }
    mask.endShape();
    mask.endDraw();
    mask.mask(texture);
    lightMask.image(mask, x-dist, y-dist);
  }
}

class Wall {
  int x1, y1, x2, y2;
  Wall(int x1, int y1, int x2, int y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
  }
  void draw() {
    strokeWeight(3);
    stroke(255);
    line(x1, y1, x2, y2);
  }
}

PVector lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
  float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
  float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
  PVector out = new PVector(0, 0, 0);
  if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
    out.x = x1 + (uA * (x2-x1));
    out.y = y1 + (uA * (y2-y1));
    out.z = 1;
    return out;
  }
  return out;
}

void mousePressed() {
  pressedX = mouseX;
  pressedY = mouseY;
}

void mouseReleased() {
  if (mouseButton == LEFT) {
    walls.add(new Wall(pressedX, pressedY, mouseX, mouseY));
  }
  if (mouseButton == RIGHT) {
    lights.add(new Light(mouseX, mouseY, 100, color(255, 255, 255)));
  }
}

void keyPressed() {
  if (key == 'l' || key == 'L') {
    lihghtsEnable = !lihghtsEnable;
  }
  if (key == 'c' || key == 'C') {
    for (int i = walls.size()-1; i >= 0; i--) {
      walls.remove(i);
    }
  }
}

And I want it to look something like this, I mean the colors, to make it look real when the lights come together when they’re colored in different colors. :slight_smile:

2 Likes

Like on this image…

you need to switch from masks to multiply blending modes or direct shaders.

boolean lihghtsEnable = true;
PImage background, screen;
PGraphics lightMask;
ArrayList<Light> lights;
ArrayList<Wall> walls;
int pressedX, pressedY;

void setup() {
  size(800, 450);
  frameRate(100);
  textAlign(LEFT, TOP);
  background(0);

  walls = new ArrayList<Wall>();
  lights = new ArrayList<Light>();

  lightMask = createGraphics(width, height);
  PGraphics blank =  createGraphics(width, height);
  blank.beginDraw();
  blank.background(255);
  blank.endDraw();
  background = blank.get();
  background.resize(width, height);
}

void draw() {
  // Draw background or game
  background(background);

  // Get game screen pixels
  screen = get();

  // Draw blackness
  fill(0, 240);
  noStroke();
  rect(0, 0, width, height);

  // Draw lights mask
  if (lihghtsEnable) {
    lightMask.beginDraw();
    lightMask.background(0);
    lightMask.blendMode(ADD);
    for (Light l : lights) {
      l.draw();
    }
    lightMask.endDraw();
  }

  // Apply mask to game screen pixels
  

  // Draw game screen
  blendMode(BLEND);
  image(screen, 0, 0);
  blendMode(MULTIPLY); //multiply lightmask with screen 
//black x black = black, 
//white x balck = black 
//white x white = white, etc
  image(lightMask, 0, 0);
  blendMode(BLEND);
  // Draw walls
  for (Wall w : walls) {
    w.draw();
  }

  // Show FPS and others stuff
  fill(0, 255, 0);
  text(int(frameRate)+"\n"+"Lights: "+lihghtsEnable+"\nLeft mouse button - create wall\nRight mouse button - create light\nKey 'L' - enables lights\nKey 'C' - clear all walls", 0, 0);
}
class Light {
  int x, y, dist;
  color c = color(random(100, 255), random(100, 255), random(100, 255));
  PGraphics mask;
  PImage texture;
  
  Light(int x, int y, int dist, color c) {
    this.x = x;
    this.y = y;
    this.dist = dist;
    this.c =  color(random(100, 255), random(100, 255), random(100, 255));
    texture = loadImage("light.png");
    texture.resize(dist*2, dist*2);
    mask = createGraphics(texture.width, texture.height);
  }
  
  void draw() {
    mask.beginDraw();
    mask.background(0);
    mask.fill(c);
    mask.noStroke();
    mask.beginShape();
    for (float i = 0; i < 360; i+=1) { // This is just test with angle ray scan, it is not accurate, later i will change it with cleverly ray render casting.
      PVector point = new PVector(x+dist*sin(radians(i)), y+dist*cos(radians(i)));
      for (Wall w : walls) {
        PVector pos = lineLine(w.x1, w.y1, w.x2, w.y2, x, y, x+dist*sin(radians(i)), y+dist*cos(radians(i)));
        if (pos.z == 1) {
          if (dist(x, y, point.x, point.y) > dist(x, y, pos.x, pos.y)) {
            point.x = pos.x;
            point.y = pos.y;
          }
        }
      }
      mask.vertex(point.x-x+dist, point.y-y+dist);
    }
    mask.endShape();
//same deal here
    mask.blendMode(MULTIPLY);
    mask.image(texture,0,0);
    mask.blendMode(BLEND);
    mask.endDraw();
    
    lightMask.image(mask, x-dist, y-dist);
  }
}

result:
image

3 Likes

Thanks a lot, working better than I expected :star_struck::heart_eyes::smiling_face_with_three_hearts::innocent:

1 Like

Nice solution – that looks great!

I seem to recall that PixelFlow also several examples of colored light raycasting.

1 Like

Hi, first happy new year !

i found your code and i wanted to try it in my code (i use java with eclipse).

i get a java nullPointerException for the line : screen = get();
I think it’s because it’s not initialize but i don’t know how to.

Can you explain to me ?

Thanks for your time

ok i think i found it, i can use screen = createImage(x,y,RGB); instead of screen = get();
But it’s really laggy and dont give the same output. any idea ?