Jump to content

Soft lightning in Phaser Graphics based implementation of dynamic lightning


Kacper Pietrzak
 Share

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

Link to comment
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

Link to comment
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 :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...