Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How would I go about limiting the max brightness when multiple lights overlap? #38

Open
Billydwilliams opened this issue Feb 7, 2019 · 12 comments

Comments

@Billydwilliams
Copy link

I used to use the old lighting style with pixi-layers a little over a year ago. I recently came upon this library and am trying to update my system to use it. It works great so far however I was wondering if there was a way to make it when multiple lights overlap so its not blindingly bright?

Kinda like some kind of limiter to hold back the combined power levels of the lights.

I'm not opposed to getting my hands dirty (I have many personalized pixi addons and build scripts), I just have no idea where to start or what kinds of terms to search to figure out if its even possible.

@ivanpopelyshev
Copy link
Contributor

ivanpopelyshev commented Feb 7, 2019

Try change alpha of whole light layer.

You can also set light tint of "dark" ambient background to "0x808080" instead of "0x0" how its done in pixi-lights big example (drag-sort). https://pixijs.io/examples/?v=v4.x#/layers/normals-drag.js

Adjusting alpha of lights and tint of dark background should balance things, theoretically.

@ivanpopelyshev
Copy link
Contributor

Another idea is to ditch normal-maps if you dont need them and use simpler approach https://pixijs.io/examples/?v=v4.x#/layers/lighting.js

@Billydwilliams
Copy link
Author

Billydwilliams commented Feb 8, 2019

I used to use that approach but we wanted to step up into diffuse lighting because we feel it will allow us to do more and allow for a better look overall.

Changing the light layer alpha doesn't seem to change anything on my setup nor does it seem to change anything in the demo.

Setting the "dark" ambient background to "0x808080" seems to increase the overall intensity of the lights but after adjusting the brightness values it seems like it just takes away from the darker end of the light spectrum rather than the brighter end.

I do appreciate you taking the time to give suggestions.
Maybe this is something I have to adjust the webgl shaders for? I'm not sure since I know very little about how webgl works.

Thanks again

@ivanpopelyshev
Copy link
Contributor

oh, right, you need to change alpha of the sprite that shows lighting texture.

@ivanpopelyshev
Copy link
Contributor

ivanpopelyshev commented Feb 8, 2019

Oops, sorry, I just realized that our lights are rendered directly , without extra renderTexture, and those lights dont have "alpha" component at all. You need to specify that lighting layer should be rendered in a texture, and use that texture for a sprite that you'll change the alpha :)

Its like you take sum of all lights in a texture, then dim it. Use useRenderTexture and getRenderTexture() from lighting group or lighting layer. Without understanding how pixi-layers (https://github.com/pixijs/pixi-display/) works there's no way to do that. This lighting plugins works on top of it. I know its not the best way for devs, and i'll improve pixi-layers in next version. However, there was a guy who made SHADOWS based on pixi-lights implementation in a day, so maybe its possible to understand my API's even on current level.

@Billydwilliams
Copy link
Author

Billydwilliams commented Feb 8, 2019

I've messed with the demo some. I'm sure that I'm doing something wrong.

While the games in focus + increases the sprites alpha and - decreases it
I marked my edits with //******** changes here

var WIDTH = 800, HEIGHT = 600;

// LAYERS plugin is here: https://github.com/pixijs/pixi-display/tree/layers
// LIGHTS plugin is here: https://github.com/pixijs/pixi-lights/tree/v4.x

var app = new PIXI.Application(WIDTH, HEIGHT);
document.body.appendChild(app.view);

var stage = app.stage = new PIXI.display.Stage();

// bg is first, its not lighted
var bg = new PIXI.extras.TilingSprite(PIXI.Texture.fromImage('required/assets/p2.jpeg'), WIDTH, HEIGHT);
bg.tint = 0x808080;
stage.addChild(bg);

// put all layers for deferred rendering of normals
var diffuseLayer = new PIXI.display.Layer(PIXI.lights.diffuseGroup);
stage.addChild(diffuseLayer);
var diffuseBlackSprite = new PIXI.Sprite(diffuseLayer.getRenderTexture());
diffuseBlackSprite.tint = 0;
// without the black sprite, lighted elements will be transparent to background. Try remove that line
stage.addChild(diffuseBlackSprite);
stage.addChild(new PIXI.display.Layer(PIXI.lights.normalGroup));

//******** changes start here
PIXI.lights.lightGroup.userRenderTexture = true;
var lightLayer = new PIXI.display.Layer(PIXI.lights.lightGroup);
var lightSprite = new PIXI.Sprite(lightLayer.getRenderTexture());
lightSprite.parentGroup = PIXI.lights.lightGroup;
stage.addChild(lightSprite);
stage.addChild(lightLayer);
//******** changes end here


var sortGroup = new PIXI.display.Group(0, true);
sortGroup.on('sort', function (sprite) {
    //green bunnies go down
    sprite.zOrder = -sprite.y;
});
// the group will process all of its members children after the sort
sortGroup.sortPriority = 1;
stage.addChild(new PIXI.display.Layer(sortGroup));

var dragGroup = new PIXI.display.Group(0, true);
// dragged objects has to processed after sorted, so we need a flag here too
dragGroup.sortPriority = 1;
stage.addChild(new PIXI.display.Layer(dragGroup));

// LIGHT and its movement
stage.addChild(new PIXI.lights.AmbientLight(null, 0.6));
var light = new PIXI.lights.PointLight(0xffffff, 1);
light.position.set(525, 160);
stage.addChild(light);
app.ticker.add(() => {
    light.position.copy(app.renderer.plugins.interaction.mouse.global);
});

var lightLoader = new PIXI.loaders.Loader();
lightLoader.baseUrl = 'https://cdn.rawgit.com/pixijs/pixi-lights/b7fd7924fdf4e6a6b913ff29161402e7b36f0c0f/';
lightLoader
    .add('block_diffuse', 'test/block.png')
    .add('block_normal', 'test/blockNormalMap.png')
    .load(onAssetsLoaded);

function onAssetsLoaded(loader, res) {
    for (var i=0; i<8; i+=2) {
        stage.addChild(createBlock(100 + i * 50, 100 + i*30));
    }
    for (var i=1; i<8; i+=2) {
        stage.addChild(createBlock(100 + i * 50, 100 + i*30));
    }
}

function createBlock(x, y) {
    var container = new PIXI.Container();
    //we need to sort them before children go to respective layers
    container.parentGroup = sortGroup;
    container.position.set(x, y);
    var diffuseSprite = new PIXI.Sprite(lightLoader.resources.block_diffuse.texture);
    diffuseSprite.parentGroup = PIXI.lights.diffuseGroup;
    diffuseSprite.anchor.set(0.5);
    var normalSprite = new PIXI.Sprite(lightLoader.resources.block_normal.texture);
    normalSprite.parentGroup = PIXI.lights.normalGroup;
    normalSprite.anchor.set(0.5);
    container.addChild(diffuseSprite);
    container.addChild(normalSprite);

    subscribe(container);

    return container;
}

/// === DRAG ZONE ===
function subscribe(obj) {
    obj.interactive = true;
    obj.on('mousedown', onDragStart)
        .on('touchstart', onDragStart)
        .on('mouseup', onDragEnd)
        .on('mouseupoutside', onDragEnd)
        .on('touchend', onDragEnd)
        .on('touchendoutside', onDragEnd)
        .on('mousemove', onDragMove)
        .on('touchmove', onDragMove);
}

function onDragStart(event) {
    if (!this.dragging) {
        this.data = event.data;
        this.oldGroup = this.parentGroup;
        this.parentGroup = dragGroup;
        this.dragging = true;

        this.scale.x *= 1.1;
        this.scale.y *= 1.1;
        this.dragPoint = event.data.getLocalPosition(this.parent);
        this.dragPoint.x -= this.x;
        this.dragPoint.y -= this.y;
    }
}

function onDragEnd() {
    if (this.dragging) {
        this.dragging = false;
        this.parentGroup = this.oldGroup;
        this.scale.x /= 1.1;
        this.scale.y /= 1.1;
        // set the interaction data to null
        this.data = null;
    }
}

function onDragMove() {
    if (this.dragging) {
        var newPosition = this.data.getLocalPosition(this.parent);
        this.x = newPosition.x - this.dragPoint.x;
        this.y = newPosition.y - this.dragPoint.y;
    }
}

//******** changes here
document.addEventListener('keydown', function(event) {
  	var target = lightSprite;
    if (event.which == 61) {//plus
        target.alpha += .1;
      	target.alpha = (target.alpha > 1) ? 1 : target.alpha;
    } else if (event.which == 173) {//minus
        target.alpha -= .1;
      	target.alpha = (target.alpha < 0) ? 0 : target.alpha;
    }
  	console.log("target's alpha is ", target.alpha);
});
//******** changes here

@ivanpopelyshev
Copy link
Contributor

ivanpopelyshev commented Feb 8, 2019

  1. useRenderTexture, not userRenderTexture
  2. lightSprite cant by in light layer because it tries to render light texture inside itself. No need to assign a group to it, do you want it to be rendered not in its place or what?
  3. lightSprite has to be added LAST because it renders after black background and it has to be rendered when light texture is ready (after lightLayer), otherwise you'll get texture of previous frame.

pixi-examples uses older version of pixi-lights so you can see that light shaders render everything upside-down if we render lights inside a texture. With latest version of pixi-lights that's not the case.

Hope you'll understand more about pixi-layers. The idea is to render elements out of their parent container, somewhere else. Group all shadow sprites, all light sprites, e.t.c. . Groups are global constants, layers are their anchors in the stage. That way you can make your scene tree logically and not depend on render order.

The second idea is to render layer insides into a texture that can be used further in stage tree, that's what allowed us to move old pixi-lights that hacks a renderer itself to vanilla pixijs. Hope I'll make even better architecture in future versions.

var WIDTH = 800, HEIGHT = 600;

// LAYERS plugin is here: https://github.com/pixijs/pixi-display/tree/layers
// LIGHTS plugin is here: https://github.com/pixijs/pixi-lights/tree/v4.x

var app = new PIXI.Application(WIDTH, HEIGHT);
document.body.appendChild(app.view);

var stage = app.stage = new PIXI.display.Stage();

// bg is first, its not lighted
var bg = new PIXI.extras.TilingSprite(PIXI.Texture.fromImage('required/assets/p2.jpeg'), WIDTH, HEIGHT);
bg.tint = 0x808080;
stage.addChild(bg);

// put all layers for deferred rendering of normals
var diffuseLayer = new PIXI.display.Layer(PIXI.lights.diffuseGroup);
stage.addChild(diffuseLayer);
var diffuseBlackSprite = new PIXI.Sprite(diffuseLayer.getRenderTexture());
diffuseBlackSprite.tint = 0;
// without the black sprite, lighted elements will be transparent to background. Try remove that line
stage.addChild(diffuseBlackSprite);
stage.addChild(new PIXI.display.Layer(PIXI.lights.normalGroup));

//******** changes start here
PIXI.lights.lightGroup.useRenderTexture = true;
var lightLayer = new PIXI.display.Layer(PIXI.lights.lightGroup);
var lightSprite = new PIXI.Sprite(lightLayer.getRenderTexture());
stage.addChild(lightLayer);
//******** changes end here


var sortGroup = new PIXI.display.Group(0, true);
sortGroup.on('sort', function (sprite) {
    //green bunnies go down
    sprite.zOrder = -sprite.y;
});
// the group will process all of its members children after the sort
sortGroup.sortPriority = 1;
stage.addChild(new PIXI.display.Layer(sortGroup));

var dragGroup = new PIXI.display.Group(0, true);
// dragged objects has to processed after sorted, so we need a flag here too
dragGroup.sortPriority = 1;
stage.addChild(new PIXI.display.Layer(dragGroup));

// LIGHT and its movement
stage.addChild(new PIXI.lights.AmbientLight(null, 0.6));
var light = new PIXI.lights.PointLight(0xffffff, 1);
light.position.set(525, 160);
stage.addChild(light);
app.ticker.add(() => {
    light.position.copy(app.renderer.plugins.interaction.mouse.global);
});

var lightLoader = new PIXI.loaders.Loader();
lightLoader.baseUrl = 'https://cdn.rawgit.com/pixijs/pixi-lights/b7fd7924fdf4e6a6b913ff29161402e7b36f0c0f/';
lightLoader
    .add('block_diffuse', 'test/block.png')
    .add('block_normal', 'test/blockNormalMap.png')
    .load(onAssetsLoaded);

function onAssetsLoaded(loader, res) {
    for (var i=0; i<8; i+=2) {
        stage.addChild(createBlock(100 + i * 50, 100 + i*30));
    }
    for (var i=1; i<8; i+=2) {
        stage.addChild(createBlock(100 + i * 50, 100 + i*30));
    }
}

function createBlock(x, y) {
    var container = new PIXI.Container();
    //we need to sort them before children go to respective layers
    container.parentGroup = sortGroup;
    container.position.set(x, y);
    var diffuseSprite = new PIXI.Sprite(lightLoader.resources.block_diffuse.texture);
    diffuseSprite.parentGroup = PIXI.lights.diffuseGroup;
    diffuseSprite.anchor.set(0.5);
    var normalSprite = new PIXI.Sprite(lightLoader.resources.block_normal.texture);
    normalSprite.parentGroup = PIXI.lights.normalGroup;
    normalSprite.anchor.set(0.5);
    container.addChild(diffuseSprite);
    container.addChild(normalSprite);

    subscribe(container);

    return container;
}

/// === DRAG ZONE ===
function subscribe(obj) {
    obj.interactive = true;
    obj.on('mousedown', onDragStart)
        .on('touchstart', onDragStart)
        .on('mouseup', onDragEnd)
        .on('mouseupoutside', onDragEnd)
        .on('touchend', onDragEnd)
        .on('touchendoutside', onDragEnd)
        .on('mousemove', onDragMove)
        .on('touchmove', onDragMove);
}

function onDragStart(event) {
    if (!this.dragging) {
        this.data = event.data;
        this.oldGroup = this.parentGroup;
        this.parentGroup = dragGroup;
        this.dragging = true;

        this.scale.x *= 1.1;
        this.scale.y *= 1.1;
        this.dragPoint = event.data.getLocalPosition(this.parent);
        this.dragPoint.x -= this.x;
        this.dragPoint.y -= this.y;
    }
}

function onDragEnd() {
    if (this.dragging) {
        this.dragging = false;
        this.parentGroup = this.oldGroup;
        this.scale.x /= 1.1;
        this.scale.y /= 1.1;
        // set the interaction data to null
        this.data = null;
    }
}

function onDragMove() {
    if (this.dragging) {
        var newPosition = this.data.getLocalPosition(this.parent);
        this.x = newPosition.x - this.dragPoint.x;
        this.y = newPosition.y - this.dragPoint.y;
    }
}

//******** changes here
stage.addChild(lightSprite);
document.addEventListener('keydown', function(event) {
  	var target = lightSprite;
    if (event.which == 61) {//plus
        target.alpha += .1;
      	target.alpha = (target.alpha > 1) ? 1 : target.alpha;
    } else if (event.which == 173) {//minus
        target.alpha -= .1;
      	target.alpha = (target.alpha < 0) ? 0 : target.alpha;
    }
  	console.log("target's alpha is ", target.alpha);
});

@Billydwilliams
Copy link
Author

Billydwilliams commented Feb 9, 2019

Ah I knew I was making a silly mistake(s).

I've actually been using pixi-display since PIXI 3 because it added zIndexing and zOrdering to PIXI. I upgraded to the newer version sometime last year and had to wrap my head around the layers and groups then. I actually have a bit of a custom build that I use that I actually posted on someone else's issue in the pixi-display github since I used to have a similar issue.
Setting the parent group was just because I didn't fully understand (I learn better through brute force and playing with examples xD). I've never been one to understand simply through reading. Playing with it is more fun anyway.

Thanks for taking time to help me out and explain everything! Hopefully this helps someone else in the future as well!

@Billydwilliams
Copy link
Author

Sadly these changes weren't able to solve my overall issue.
I have been doing some reading and research and was wondering if its possible for the shaders to have access to the diffuse layers pixels and then use that pixel data to say "dont make the finalColor brighter than this color"?
Sadly I'm not too familiar with shaders and their efficiencies.
I don't know if this is possible or if this would just be too demanding and costly to even be worth implementing.
Thanks again for your help.

@jonlepage
Copy link

if you talk about the saturation ? brightness, you can compute adjust
lightHeight brightness falloff, and maybe with a tiker, compute all light from her coor.
image

@Billydwilliams
Copy link
Author

Billydwilliams commented Feb 14, 2019

I appreciate the response, but I've messed with those variables to no avail. They don't help me much since Users will have lights that follow them around at night and even with moderate values it only takes 2 or 3 people grouped up, or two people under a light source to be way too bright (even fewer with snow based tiles behind them). I also cannot dim the lights too much otherwise the user lights become ineffective at helping them navigate in the dark. I wanted to make this lighting style work instead of the old basic lighting style because I wanted to be able to use normal maps in the near future.

@Billydwilliams
Copy link
Author

Billydwilliams commented Feb 14, 2019

These examples are using the light values I found that worked the best.
Sliders I used to hone in on a decent value:
image
Everything looks ok when they're separated a bit:
image
Starts getting pretty bad if they get close:
image

White tiles in the back, they're separated a bit:
image
White tiles in the back, they're close:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants