ID:2141928
 
If you're using a lighting plane with BLEND_MULTIPLY, you'll want to use a backdrop.

Any part of the plane not covered by lights has an alpha of zero. If your lights fade out from a center like a soft spotlight, the lighting plane will end up with alpha values between 0 and the max (which I'll call 1 rather than 255, for simplicity).

The problem here is that you're likely using a color matrix for your lighting plane, and color matrices work differently depending on whether you're drawing an icon directly or whether it's being used for a plane or a KEEP_TOGETHER group. The plane has already been drawn on, so all of the color values conform to what you'd call premultiplied alpha. That is, where that red light you used is at half strength, it shows up on the plane as rgba(0.5,0,0,0.5) instead of rgba(1,0,0,0.5); the red, green, and blue channels have alreay taken alpha into account.

Color matrices work differently with premultiplied alpha; they'll first change the color back to its original non-premultiplied form, then do the matrix, then multiply back by the new alpha value. So if you have a matrix designed to turn the alpha all the way up to 1, those half-red pixels turn to rgba(1,0,0,1) which is full red, and your spotlight no longer fades out gracefully like you wanted.

A backdrop solves all of that.

obj/lighting_plane
plane = 2 // or whatever plane you use for lighting
screen_loc = "CENTER"
blend_mode = BLEND_MULTIPLY
// this has an ambient light of 20% gray added to all lights
color = list(null, null, null, "#0000", "#333f")

New(client/C)
C.screen += src

var/obj/O = new
O.blend_mode = BLEND_OVERLAY // this is important so it doesn't inherit
O.icon = 'blackness.dmi' // a black icon using your regular world.icon_size
O.layer = BACKGROUND_LAYER
overlays += O
UpdateView(C)

// call this any time client.view changes!
proc/UpdateView(client/C)
var/vx,vy
var/obj/O = new
var/list/L = overlays.Copy()
O.appearance = L[1]
if(isnum(C.view)) vx = vy = (2*C.view) + 2
else
L = splittext(C.view, "x")
vx = L[1] + 1
vy = L[2] + 1
O.transform = matrix(vx, 0, 0, 0, vy, 0)
overlays = list(O)

With the backdrop in place, you won't have any oddities where the lights fade out, because all of the lights (which presumably have BLEND_ADD) are drawn over an opaque black surface. The lighting plane is then totally opaque when you multiply it over your scene, which is good because you don't really want it changing the scene's alpha values anyway.

Savvy users may wonder: Why not use a screen_loc of "SOUTHWEST to NORTHEAST" instead? The reason is that this is represented as multiple tiles, which all have to be turned from appearances to icons (currently screen objects don't cache repeated tiles, which is less than ideal), put through the sorting process, and drawn, whereas by using transform you can have only a single very large icon. The engine likes that a heck of a lot better.
My lighting example that most people start with does this, the darkness is actually a solid white square scaled to cover the entire screen.
Ah I think this explains a few things. The plane master color matrix was really weirding me out.
I ran into this when helping out Exentriks Gaming with an issue regarding a spotlight that fell off gradually. Took me forever to figure out the problem, until I finally realized (when running in the debugger) that the premultiplied alpha was the issue. The matrix shader was adapting to it properly; the problem was I was fixed on the assumption that my matrix should simply set alpha to 1 and be done with it.
screen_loc = "CENTER"


?
?

Yep. Valid.
In response to Ter13
The CENTER keyword can also be used. This can be used alone to completely center the object, or as either the x or y component. If the map covers an even number of tiles in either direction, pixel offsets will be applied automatically.

Never came across that in the reference before. The learning never stops.

Just started playing with the plane system though. I think I'm going to have fun with this as I gain a better understanding...
screen_loc var (movable atom)
The edges of the map may also be referenced by using directions, such as "3,NORTH". For convenience, the order of coordinates is arbitrary when using directions, so one may specify y before x as in "NORTH,WEST". In expressions such as the latter, you may also leave out the comma.

The CENTER keyword can also be used. This can be used alone to completely center the object, or as either the x or y component. If the map covers an even number of tiles in either direction, pixel offsets will be applied automatically.
In response to Ter13
Yeah, my bad. I didn't mean for the second part of my post to come off like that. (I hadn't touched anything plane-related before today, is what I was getting at.)