Kacper Pietrzak

Soft lightning in Phaser Graphics based implementation of dynamic lightning

Recommended Posts

Hey everyone!

I've implemented simple dynamic lightning in my project, but what I'm missing is soft light edges effect.

This what it looks like now: https://youtu.be/M-idTMGYvsw

I use sprites' masks to achieve that, code looks somewhat like this:

updateShowingLayer() {
+    this.showMaskGraphics.clear();
+    this.showMaskGraphics.lineStyle( 2, 0xffffff, 1 );
+    this.showMaskGraphics.beginFill( 0x00000000 );
+    this.showMaskGraphics.moveTo( this.player.x, this.player.y );
+
+    for ( let i = 0; i < NUMBER_OF_RAYS; i++ ) {
+      const rayAngle = mouseAngle - ( LIGHT_ANGLE / 2 ) + ( LIGHT_ANGLE / NUMBER_OF_RAYS ) * i;
+      let lastX = this.player.x;
+      let lastY = this.player.y;
+      for ( let j = 1; j <= RAY_LENGTH; j++ ) {
+        const landingX = Math.round( this.player.x - ( 2 * j ) * Math.cos( rayAngle ) );
+        const landingY = Math.round( this.player.y - ( 2 * j ) * Math.sin( rayAngle ) );
+        if ( !this.isTileBlocking( landingX, landingY ) ) {
+          lastX = landingX;
+          lastY = landingY;
+        } else {
+          break;
+        }
+      }
+      this.showMaskGraphics.lineTo( lastX, lastY );
+    }
+    this.showMaskGraphics.lineTo( this.player.x, this.player.y );
+    this.showMaskGraphics.endFill();
   }
   }

And then I just set masks in corresponding sprites to `this.showMaskGraphics`

The full code is available here: https://github.com/PiGames/Project-Nostradamus/blob/dynamic-lightning/src/objects/Flashlight.js

Does anyone have an idea how to make this light soft ?

Thanks

Share this post


Link to post
Share on other sites

 

 

Maybe you can apply a bluf filter to your sprites. Im not sure if this will work (WEBGL only) in your specific case, but it might be useful . Here it is applied to an example (you can uncomment the code in create):

 

Share this post


Link to post
Share on other sites

 

 

The blur filter should apply to a solid color too I believe (the border sprite around the first example is actually a solid copy of the dude). Another idea, maybe you can use a glow filter?:

 

Share this post


Link to post
Share on other sites

The thing is, the way that I'm handling dynamic lighting is creating mask that cuts off the shape of light from flashlight that is precisely calculated, so any effect that makes this light bigger, like glow filter, is useless for now. because it gets cut off, have to think about it :)

Share this post


Link to post
Share on other sites

I checked out the game, nice work! why not just add an extra glow graphics layer?:

    this.shadowLayer = this.player.game.add.image(0, 0, 'layer-background');
    this.shadowLayer.width = this.player.game.camera.width * 1.5;
    this.shadowLayer.height = this.player.game.camera.height * 1.5;
    this.shadowLayer.alpha = _FlashlightConstants.WORLD_SHADOW_ALPHA;

    this.flickerLayer = this.player.game.add.image(0, 0, 'layer-background');
    this.flickerLayer.width = _FlashlightConstants.RAY_LENGTH * 4.5;
    this.flickerLayer.height = _FlashlightConstants.RAY_LENGTH * 4.5;
    this.flickerLayer.anchor.setTo(0.5);

    this.hideMaskGraphics = this.player.game.add.graphics(0, 0);
    this.shadowLayer.mask = this.hideMaskGraphics;

    this.showMaskGraphics = this.player.game.add.graphics(0, 0);
    zombies.setAll('mask', this.showMaskGraphics);
    this.zombies = zombies;
    this.showMaskGraphics.filters=[this.glowFilter];

    //add the extra glow layer, the padding is necessary for a soft light effect
    this.glowGraphics = this.player.game.add.graphics(0, 0);
    this.glowGraphics.boundsPadding=200;
    this.glowFilter=new Phaser.Filter.Glow(this);
    this.glowGraphics.filters=[this.glowFilter];

 

      this.hideMaskGraphics.clear();
      this.hideMaskGraphics.moveTo(this.shadowLayer.x, this.shadowLayer.y);
      this.hideMaskGraphics.lineStyle(2, 0xfff000, 1);
      this.hideMaskGraphics.beginFill(0x00000000);
      this.hideMaskGraphics.lineTo(this.player.x, this.player.y);

        //the extra glow layer
      this.glowGraphics.clear();
      this.glowGraphics.lineStyle(1, 0xfff0f0,0.0 );
      this.glowGraphics.beginFill(0xffffff,Math.random()*0.05+0.02); //light flickering effect
      this.glowGraphics.lineTo(this.player.x, this.player.y);


      var mouseX = this.player.game.input.mousePointer.worldX;
      var mouseY = this.player.game.input.mousePointer.worldY;
      var mouseAngle = Math.atan2(this.player.y - mouseY, this.player.x - mouseX);

      for (var i = 0; i < _FlashlightConstants.NUMBER_OF_RAYS; i++) {
        var rayAngle = mouseAngle - _FlashlightConstants.LIGHT_ANGLE / 2 + _FlashlightConstants.LIGHT_ANGLE / _FlashlightConstants.NUMBER_OF_RAYS * i;
        var lastX = this.player.x;
        var lastY = this.player.y;
        for (var j = 1; j <= _FlashlightConstants.RAY_LENGTH; j++) {
          var landingX = Math.round(this.player.x - 2 * j * Math.cos(rayAngle));
          var landingY = Math.round(this.player.y - 2 * j * Math.sin(rayAngle));
          if (!this.isTileBlocking(landingX, landingY)) {
            lastX = landingX;
            lastY = landingY;
          } else {
            break;
          }
        }
        this.hideMaskGraphics.lineTo(lastX, lastY);
        //draw the glow layer
        this.glowGraphics.lineTo(lastX,lastY);
      }
      this.hideMaskGraphics.lineTo(this.player.x, this.player.y);
      this.hideMaskGraphics.lineTo(this.shadowLayer.x, this.shadowLayer.y);
      this.hideMaskGraphics.lineTo(this.shadowLayer.x + this.shadowLayer.width, 0);
      this.hideMaskGraphics.lineTo(this.shadowLayer.x + this.shadowLayer.width, this.shadowLayer.y + this.shadowLayer.height);
      this.hideMaskGraphics.lineTo(0, this.shadowLayer.y + this.shadowLayer.height);
      this.hideMaskGraphics.lineTo(this.shadowLayer.x, this.shadowLayer.y);
      this.hideMaskGraphics.endFill();

It should be possible , it looks quite nice imho, but it does affect performance significantly..... I've added the game.js file.

The glow effect also has some flickering effect so the flickeringLayer graphics might not be needed. The filter can be adjusted to add more glow to it (fiddling with the numbers).It also has an extra alpha attributethat could be set, so that the flickering is done within the fragment shader (possible performance benefit, not sure)... 

the filter:

Phaser.Filter.Glow = function (game) {
    'use strict';
    Phaser.Filter.call(this, game);
    this.uniforms.alpha = { type: '1f', value: 1.0 };

    this.fragmentSrc = [
        'precision lowp float;',
        'varying vec2 vTextureCoord;',
        'varying vec4 vColor;',
        'uniform sampler2D uSampler;',
        'uniform float alpha;',

        'void main() {',
            'vec4 sum = vec4(0);',
            'vec2 texcoord = vTextureCoord;',
            'for(int xx = -16; xx <= 12; xx++) {',
                'for(int yy = -8; yy <= 8; yy++) {',
                    'float dist = sqrt(float(xx*xx) + float(yy*yy));',
                    'float factor = 0.0;',
                    'if (dist == 0.0) {',
                        'factor = 8.0;',
                    '} else {',
                        'factor = 8.0/abs(float(dist));',
                    '}',
                    'sum += texture2D(uSampler, texcoord + vec2(xx, yy) * 0.004) * factor;',
                '}',
            '}',
            'gl_FragColor = sum * 0.025 + texture2D(uSampler, texcoord)*alpha;',
        '}'
    ];
};
  
Phaser.Filter.Glow.prototype = Object.create(Phaser.Filter.prototype);
Phaser.Filter.Glow.prototype.constructor = Phaser.Filter.Glow;
Object.defineProperty(Phaser.Filter.Glow.prototype, 'alpha', {

    get: function() {
        return this.uniforms.alpha.value;
    },
    set: function(value) {
        this.uniforms.alpha.value = value;
    }
});

Another possibility might be to add A bitmapData copy of the lightmask on the go and apply some filter to it.. But using filters is the only idea I can think of to do this..

game.js

Share this post


Link to post
Share on other sites

I am sorry, but I couldn't get your solution working, even game.js file that you attached doesn't seem to work for me I cannot tell the difference between the gameplay from original file and the one that you've send me. Also there are couple mistakes in code snippets that you've send, could you please send me only Flaslight.js file that works for you ? And again thanks for your time, I really appreciate :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.