A better way to fake point light shadows

The Problem:

Unfortunately blender doesn’t have integrated support for point light shadows. Sometimes, it would be nice to have them, and so we look for ways to fake them.

Previous Solutions:

  1. The most obvious and most used way is to have a very wide angle spotlight in the roof of the room that should have the point light.
  2. A slight improvement on this is to use the spotlight as ‘shadows only’ and have a point light up there to do the actual lighting.

Both of these methods fall flat on their faces when you need to move around the point light in all three dimensions.

  1. A couple of weeks ago, @Viper-MIJP thread, which faked point-light shadows by having a wide angle spotlight always pointing at the camera.
    This works pretty well, except that when you’re facing across the direction of the light, there aren’t any shadows. Also, wide angle spotlights sometimes have issues with shadows at the extremes of their angles.

All of these methods also fail at large distances, where the pixels of the shadow-map become large on-screen.
So I thought about this, got out a pen and paper, and came up with a better way.

How it works:
- When you’re facing away from a light, the shadows you see are your shadows and anything between you and the light.

  • When you’re across a light (ie the light is perpendicular to you), you only see shadows being cast in that direction, or objects between you and the light.
  • When you face towards a light, you can see shadows in all directions from the light.

The method by Viper-MIJP solves the first of these cases, by having the light pointing at you.
To solve the second case, we can point the light in the direction you’re facing (incidentally, this also solves the first case)
To sold the third case, well, you need an actual point light, but the shadows you’re most likely to notice are those facing you. (ie objects between you and the light)

So we want the light to:
Face the direction you’re looking when you’re not facing it
Face towards you when you are facing it.
And of course, sudden jumps are not desirable as they are noticeable.

And of course, the view angle of the camera affects how much of the scene needs to be shadowed.
So we can end up with the code:

import bgeimport math


def face_the_right_way(cont):
    l = cont.owner
    c = bge.logic.getCurrentScene().active_camera


    dist, vect_to_cam, _ = l.getVectTo(c)
    vect_of_cam = c.getAxisVect([0,0,-1])
    
    view_angle = c.fov
    cur_angle = vect_to_cam.angle(vect_of_cam) * 180 / math.pi
    
    l.spotsize = view_angle * (1.1+l.spotblend)


    if cur_angle < view_angle/2:
        l.alignAxisToVect(-vect_of_cam)
    else: 
        ratio = (cur_angle-view_angle/2) / (180 - view_angle/2)
        final_vec = - vect_to_cam.lerp(vect_of_cam, 1 - ratio)
        l.alignAxisToVect(final_vec, 2)

Attach this to a spot-light light running every frame, set it to ‘shadows only,’ put it in the same location as a point light, and you have a point light that will cast shadows

Difference between code here and in the blend
The code here uses the active camera.
The code in the blend uses the first camera, to allow you view it from top-view.

I forgot to remove a print statement from the code in the blend!


Attached is a sample blend, and here are some shots of the effect from overhead:


Summary:
Pros:

  • Theoretically indistinguishable from a point light for 180 degrees of camera rotation.
  • Works in 3D situations, you can move above and below the light, and it looks just as good
  • Shadows never ‘pop’ in. They always transition in smoothly.

Cons:

  • Shadow map flickers as the light rotates. Probably would work better with variance shadowtype, but it doesn’t work on my graphics card.
  • another 180 degrees camera rotation where it doesn’t look like a point light
  • method for deciding spot-size based on camera view-angle works when facing away and across, but it should scale up to ~160 when facing the camera.

More Things To Do:
If I get around to it, here are a few things I’d like to try implementing:

  • As the player gets further away, scale the cone of the spot-lamp so the shadow has more pixels where the player is. Adapt angle based on direction facing.
  • Write a system like Viper’s where any point lights in the scene are auto-shadowed like this one, if they are close to the player.

Attachments

Light Test.blend (612 KB)

Appears to work perfectly with variance shadows,

you tubing now

Feedback video

what is you GFX slot sdfgeof?

Pci express?

or?

integrated?

hows the performance on it logic wise? I assume rasterizer wise its not different that a spotlight

@BPR:
Yup, variance shadows really make it look good.
For reference: it looks best in either top view (where the orange plane is completely visible) or in camera view (where you can’t see it at all)

I am running blender on a laptop, but it’s graphics card is pretty decent: Radeon 7670M. It can handle most things I chuck at it, just not this for some reason. It displays fine in the viewport, but when I actually start the game, it has made no difference, they look like simple shadows.

@Superflip:
Your assumption about the rasterizer is correct. In terms of logic, it’s very very little. Yes, the code has to run every frame, but the math is very simple. You’ll hit the ceiling with number of lights/rasterizer a long time before you’ll run out of logic.

yeah, it uses about the same resources as a normal spot,

60 fps, and 0 latency on my gpu, then I doubled the shadow buffer, looks nice, but also shot my gpu latency to 28 or so ms, 56 fps?

Looks really good :smiley:

Great work!

All you need now is a godray filter and people making horror games are set.

Sweet job!

What version of Blender is this for? I’m using 2.69 still because 2.7 has a few bugs. The spot size was set to 2 degrees, which I think is incorrect. I set it up to 110 to fill the FOV, but it is clipping at certain angles.

2.7->2.69 flips normals of halos, and messes up spot lights angle, probably does more then that I am guessing,

@Mr PutuLips:
The soptsize depends on the view angle of the camera, but actually this is bad, as narrow camera’s have noticeable issues when facing the light. I normally have a 110 degree view angle, and with the blend property of the spotlamp, this works out to about 140 degrees spotlight angle. The relevant line of code is line 13 (l.spotsize = )

You must also remember that this is an approximation, and there will be some noticeable differences. (namely when facing the light)

Hmm, there appears to be a bug setting the size of spot-lamps. Sometimes they set the size correctly, other times they refuse to change. How annoying. I don’t think this is a problem with my script, so I’ll poke and see if I can narrow down the cause.

And a foretaste of what is coming:

Dang, man, that’s pretty solid-looking. It’s not really unlimited, but that’s better than really unlimited, I’d think. Nice work!

Are you planning on using shadow-only lamps? I find they don’t mix correctly with normal lights, so I’d assume not (since your results seem to be correct).

They are shadow only lamps!!
And yeah, not unlimited in how many are available at the same time, but it is unlimited in the number around the scene that you can have. They just won’t all be there all the time.

I downloaded Blender 2.7 and it’s working now. I think the way lamp data is stored in 2.69 is different to 2.7. There is still some clipping at certain positions and angles (as you said, because it is an approximation).

The system in that YouTube video looks great! That had better get implemented some time as default buffer shadows for point lamps ^^

Cool works smooth on my intel cpu with gpu inside 128 mb vram 60fps and fading realy saves performance

Any progress on the system?

The system is still at the front of things I intend to do. Unfortunately I’m very busy at the moment in real life, so haven’t opened blender for a couple of days. I expect it will be a couple of weeks before I can find the time to do any developing.

Actually, there is a nearly complete system on my hard drive that is just waiting for a few bugs to be ironed out. It not only does point light shadows, but is a complete light solution.
Current features:

  • Removes every light (except sun lights) in the scene!
  • Replaces any lights (point + spot) with lights of the correct value if they are within a certain distance of the player.

Current bug:

  • Setting the cone-size of the spot lamps works perfectly. Setting the cone-size of the shadow lamps is failing for some reason. The value is changing, but the actual lamp isn’t changing size. I’m hoping it’s just the current blender version, so I’m updating now and will let you know how it goes.

Hmm, updating didn’t help, but I may know where the problem is…

thx! looks like a good solution, i tryed the demo but couldnt figure it out…

would be nice to have any news on your nice system man :slight_smile:

cheers, Tobi