Search Unity

Procedural static point lights (with source)

Discussion in 'Works In Progress - Archive' started by Zuntatos, Aug 20, 2015.

  1. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    As a lot of Unity features, point light shadows are either fully dynamic or they require static geometry and baking in the editor. If you're procedurally generating terrain, and want to light it statically, you're out of luck.

    I haven't been able to find something to fix this in unity3d yet, probably because it 'needs' things like command buffers to be implemented properly, and those are fairly recent. So I made my own system.

    Early test results: http://imgur.com/a/ftDRl

    Demo file: https://drive.google.com/file/d/0B7PRzgpiVzsAR293b2lsWTNxLVk
    It's a self-extracting 7z archive, as the demo is 110 MB due to a heap of sphere meshes and simple zip was S***ty at compressing. I promise, no malware :)

    Demo consists of 2000 spheres being lit by 55 point lights (so complex and realistic!)

    Source code link: https://bitbucket.org/Zuntatos/piplight/

    Edit: Big rewrite/deletion etc. Added demo file
    Edit2: Some demo information
    Edit3: Added source
     
    Last edited: Sep 17, 2015
    forestrf, Simie, hippocoder and 4 others like this.
  2. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    I would be very interested in a system like this. I work with a lot of architectural scenes that make heavy use of dynamic lighting. Some of the missing features that would be a must have for me would be a working recieve/cast shadow option and also support for cookies on point lights. I never use spot lights but directional lights would be nice to have as well. Looks very good and thanks for sharing. Looking forward to any updates on this.
     
  3. boundingbox

    boundingbox

    Joined:
    Mar 31, 2013
    Posts:
    30
    A system that fully replaces unity lights with ones that are more accessible through script would be nice. I really wish there was a way to get access to the shadow maps generated by lights for other random purposes. Area lights ( sphere, square, tube, etc with textures would be nice as well.
     
  4. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Cubemap cookies for point lights should be rather simple to add. A working receive shadow option won't work, as this needs deferred rendering to work properly. Cast shadow - maybe, have to look into it. Layers would work for casting shadows already, but again not receiving shadows. Directional lights should be doable as well, just need to figure out some orthogonal camera math.

    That's exactly what I thought.

    Area lights could be added as they are in the command buffer examples from unity itself, but it'll be madness to add cookies and to add proper area light shadows (except maybe lerping between different point lights).
     
  5. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Cubemap cookies would be the most important for me, as I use them on just about every light. Good to hear that they would be simple to add. Any updates on this? Thanks!
     
  6. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Haven't been working much on it. I've replaced most of the custom code with versions that already existed in the builtin .cginc's, but it turns out half of the values that are used in there will be overwritten if set manually via commandbuffer.setglobalvector and similar. I had to make some 'duplicate' values for those. There's also no documentation for half the code (which variables used for what, what are in them, when are they set by unity).

    Using the .cginc's added support for using the GGX option present in them instead of normalized blinn-phong & the same soft shadows as default lights. It also optimized the code a bit and probably added compatibility options.

    There's support for cookies in the .cginc's in the form of a _LightTexture0 cubemap in combination with a #define POINT_COOKIE iirc.

    TL;DR: Basically rewrote shader to use unity's code, more options/fps.

    P.S: Found interesting things as well. Shadow bias hardcoded to 0.97 (probably 0.03 as setting) in the .cginc for point lights, no wonder that option didn't work for me :)

    P.S2: It looks like practically all performance difference between my lights are the builtin lights (when updating) comes from Camera.RenderToCube() blitting 6 times into the cubemap for some reason
     
  7. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    I've actually written a runtime lightmapper that can bake a decent sized scene in ~5 seconds, would be interested to see what you have done, perhaps there is some areas where we could help each other :)
     
  8. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    It's not really what I'd call lightmapping. It merely attempts to do exactly the same as the builtin lights + their shadows, and then add in options to re-use the shadowmap from previous frames for a big performance boost.

    On progress:

    Added support for rendering the lights in-editor. (Workaround with an editorwindow to grab the camera)
    Added support for non-HDR rendering
    Added most of the inspector interface, mimics builtin lights now.
    Added gizmo's
    Added soft shadows option
    Added shadow strength option
    Added culling mask; select which layers cast shadows.
    Added an optional range check to discard pixels that are known to be out of the light's range
    Added an option to discard pixels that are known to be shadows
    Both these discards improve performance on my simple test scenes on PC. Need some complex scenes to test further.

    Fixed shadow bias not working due to builtin-cginc-port
    Changed level of detail algorithm to be more sensible (could lower detail while inside a big light's range)

    I'll have to find some sample cookies to add support for them, didn't find any at my first google search.
     
  9. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    True about the lightmapping, although lightmapping could be natural progression. Nevertheless I am quite excited to try these lights out and see if I can't learn a thing or two from them!
     
  10. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Zuntatos, I've got some cookies I could send you if you still need some.
     
  11. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
  12. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Cookies working now, including rotation, doesn't require a shadowmap update to rotate it as well.
    Fixed the work-around to make lights work on the scene view so now it works properly
    Added 'refresh all shadows' button to light inspector.
    Added option in right click menu to make a new light.
    --Maybe something else, can't remember.
     
  13. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Nice, sounding good. What are your plans for releasing it?
     
  14. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Not sure. Atleast before any form of release i'd have to refactor some code to have sensible naming, optimize some and make sure the editor plays with them nicely.

    For the release itself, I'm still in doubt whether to release it to the asset store for free / for money (what price?) / just post it on a public bitbucket for everyone.

    If I'd go for the asset store, I'd add spot light support as well, to make it more of a whole replacement. (Directional static is a possibility, but it won't have cascades and it'd be limited to a box area)
     
    Last edited: Sep 3, 2015
  15. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Sounds good. I'll be on the look out for updates. I'm definitely interested in it.
     
  16. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Updated OP with a simple demo scene
     
  17. Haagndaaz

    Haagndaaz

    Joined:
    Feb 20, 2013
    Posts:
    232
    My machine wont allow me to download the demo, perhaps its just my machine though (IT at work locks some connecctions down)
     
  18. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Worked well for me. On my work machine which I was getting 47 fps with unity lights and 212 fps with the custom static lights. Huge difference, very nice work.
     
  19. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Hi Zuntatos. Any updates on this? I've got a project I'm working on that could make great use of this. Would love to test it out. Thanks!
     
  20. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Eeeh didn't work on it at all past days. I'll do a quick refactor in the coming days somewhere and put the source up for free
     
  21. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    jason-fisher and Simie like this.
  22. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Thank you! I am having an issue when I create the light I get this error message

    NullReferenceException: Object reference not set to an instance of an object
    CustomLightEditor.OnInspectorGUI () (at Assets/Editor/CustomLightEditor.cs:34)
    UnityEditor.InspectorWindow.DrawEditor (UnityEditor.Editor editor, Int32 editorIndex, Boolean forceDirty, System.Boolean& showImportedObjectBarNext, UnityEngine.Rect& importedObjectBarRect, Boolean eyeDropperDirty) (at C:/buildslave/unity/build/Editor/Mono/Inspector/InspectorWindow.cs:1211)
    UnityEditor.DockArea:OnGUI()
     
  23. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Oh, forgot to add some explaining on how to set it up. Adding it to the readme now

    Edit: Copy of it:

    Add a PipLightRenderer component to any camera you desire to have support for these lights. It'll add the command buffers to the camera itself.
    Then add the PipLight component to any gameobject intended to cast light, and it should work.
     
  24. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Yep, that fixed it. Awesome, thanks a lot.
     
  25. jctz

    jctz

    Joined:
    Aug 14, 2013
    Posts:
    47
    Thanks for this Zuntatos, it looks awesome. This isn't an option for those of us in iOS land and who are stuck with forward rendering, is it?
     
  26. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Indeed, it doesn't work with forward rendering. It may be able to support it - but I wouldn't have a clue, only using deferred.
     
  27. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Did some testing and got this mostly implemented into my project. Its got 3.1 million triangles with over 100 of the custom lights all casting shadows on everything. Maintains a solid ~90 fps with a full stack of image effects. I did mix in some standard unity lights where it mattered to have GI. Works very well!

    Only issue I've run into is that the light speculars are causing some crazy blowouts with bloom. Anyway to adjust this?
     
  28. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Good to hear it works well!

    I think that's a problem with both normal mapping providing a pixel-wide specular highlight and the standard bloom effect being a bit meh. Afaik it shouldn't be an issue specific to this code. Not sure how to fix it.
     
  29. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Understood. Playing around with it I was able to basically remove the blowouts using a clamp effect on the camera. Thanks again for sharing this. Great stuff.
     
  30. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Hey Zuntatos, I'm having an issue with no shadows showing in builds. Everything works fine in the editor and the light itself works in the build, just no shadows are being cast. Tried it with 5.2 and 5.1 in an empty scene with no success. Any ideas? Thanks!
     
  31. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Errrm.. in an empty scene? :S

    Working fine in editor, and then not working in a build is very weird. This did work for you before didn't it?

    The only 'real' thing i can think of is running out of VRAM, denying the shadowmap creation. But that shouldn't happen in an empty scene. Unless you try a 8192x8192 shadowmap on a lower/mid end gpu; it takes up 805 MiB VRAM, and 4096x4096 takes up 201 MiB VRAM, 2048x2048 50 MiB etc.

    Other things may be wrong layer masks or the light being created in a frame before the frame with the rest of the objects, but again, that doesn't really apply to an empty scene.

    Try in a new project as well if it's not the VRAM thingy, if it works there we know it's project settings.
     
  32. zelmund

    zelmund

    Joined:
    Mar 2, 2012
    Posts:
    437
    hi there. tried to do all that in instructions but getting this after add PipLightRenderer

    NullReferenceException
    UnityEngine.Material..ctor (UnityEngine.Shader shader) (at C:/buildslave/unity/build/artifacts/generated/common/runtime/ShaderBindings.gen.cs:168)
    PipLightRenderer.OnEnable () (at Assets/CustomLight/PipLightRenderer.cs:89)
    UnityEditorInternal.InternalEditorUtility:InspectorWindowDrag(Object[], Boolean)
    UnityEditor.DockArea:OnGUI()

    using 5.2
     
  33. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Ah, welp. Forgot to add a part to the readme.

     
  34. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Thanks for the reply! Sorry, I should have added that the empty scene was in a fresh project as well. Nothing in the project except the custom lights and the scene was just a plane, a sphere, and a single light. I also tried on 2 different computers with no success.
     
  35. zelmund

    zelmund

    Joined:
    Mar 2, 2012
    Posts:
    437
    i put all shaders in their slots and set sphere and nothing ((
     
  36. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    I did a quick video of what I am seeing happen. I don't know how much help it will be, but you can check it out here


    That video might help you out in setting it up.
     
  37. zelmund

    zelmund

    Joined:
    Mar 2, 2012
    Posts:
    437
    ok. found out that i forgot to set DEFERED lighting...

    and thx for video. ))
     
  38. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    Alright, I'll take a look in a moment. Should be working.

    Edit 1: Can recreate, yey!

    Edit 2: Manually including the piplight/light shader makes it work.
     
    Last edited: Sep 29, 2015
  39. DeveloperRLA

    DeveloperRLA

    Joined:
    Jun 17, 2013
    Posts:
    53
    Hopefully this isn't to dumb of a question, but how would I do that?

    Edit:
    Nevermind, got it to work in a test scene by adding the shaders to the Always Included Shaders in the graphics options. Thanks a lot for the help!!
     
    Last edited: Sep 29, 2015
  40. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    In Project Settings -> Graphics there is a list with always included shaders.

    But it shouldn't be necessary as I've just pushed an update to the git.

    Changed the system so that it now uses 6 materials pre-made instead of dynamically creating/deleting them when enabled/disabled. This also makes unity automatically include the shader.

    Also added default references for the renderer script, so if you update you may want to re-add the renderer components to the camera. (sidenote, i hope the sphere reference holds, and the metadata file holds)

    Another random update, I may have fixed some of the scene view lighting inconsistencies where it would stop rendering all of them.
     
  41. elias_t

    elias_t

    Joined:
    Sep 17, 2010
    Posts:
    1,367
    Hi Zuntatos. Very interesting and practical approach.

    Do you think it could work with a directional light?
     
  42. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    It would work, but it would be different than unity's approach to a directional light. You can't have shadow cascades (involves moving, thus re-rendering), and the directional light would be position dependent (can't move with player, as moving is re-rendering). Shouldn't be very hard to implement; just need some orthogonal math in there.

    One of the issues would be finding out what casts shadows; I don't know of a way to do that efficiently in unity. So it would only casts shadows from objects inside of the 'light box' onto objects in it.

    Another is that the resolution would look rather limited; Covering a 200x200 area with a 2kx2k shadowmap would give pixels of 10 cm. So it pretty much requires some system to move directional lights in a chunk/tile-based method to keep higher resolution close to you.

    It's something I'll eventually add as my game would benefit from it, but it's not a priority at this moment.
     
  43. elias_t

    elias_t

    Joined:
    Sep 17, 2010
    Posts:
    1,367
    Ok, makes sense. Thanks for the thorough reply!
     
  44. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    Have you messed around with any of the light events?

    http://docs.unity3d.com/ScriptReference/Rendering.LightEvent.html

    Do you know if this brings in a cubemap from the current point light's shadow buffer, or if it is just the scene perspective shadow buffer?

    Have you attempted anything with camera render layers, so where it renders a static map for static geometry, but renders in realtime for a limited set of render layers?
     
  45. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    @Plutoman I haven't messed around with the LightEvent ones yet, as they aren't able to do what I needed; stop refreshing shadowmaps every frame.

    I suppose the shadowmap events would give access to drawing into the shadowmap for point/cube lights, but I'm not sure how it'd work for directional lights with the cascades and such. A perspective shadow map is only used to gather shadows from cascades in directional lighting (At least, in deferred). That's probably what the screenspacemask events are for.

    I've implemented camera render layers with the idea of rendering only selected layers into the shadowmap. Extending the system to have 2 shadowmaps with 1 being 'static' and one 'dynamic' should be entirely possible.
    Take note though that it may be more performant to combine this solution together with an unity point light at the same position as my performance from the custom lights per frame is about half of unity's up close but gets better from further away (unity lights seem to use the same resolution for all distances, while my lights have some LoD support in them).

    Random update note; I've modified the system to have a function named "QueueUpdate" taking an argument with a facemask so you can update only one side of a point light's shadow. Together with some helper functions that calculate which faces a bounding sphere is in. Lowers the cost of updating by 0% up to ~60% effectively, depending on the type of updates. But it's a bit integrated into my game now, so it'd take some time to update the bitbucket with the code. I'll probably do so if there's interest.

    I've also thought about changing the system to either use a command buffer for each light or to stop using command buffers entirely and render them as plain meshes with a materialpropertyblock & high shader queue. Reason for that is that current code does generate a couple dozens bytes of garbage per light per frame (I hate the garbage collector). And a bit because currently the entire buffer is remade a lot. But it's not a priority so not happening for now.
     
  46. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    Well.. The reason I asked about the light event is because you could grab the shadowmap with the event using normal shadowcasting - then just disable the shadowcasting on the light. That stops the refresh! Then process the cached shadowmap normally.

    That could not be used with a selective render, though, sadly, since the selection is based upon the meshes, not the light... which seems kinda odd, to be honest.
     
  47. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    I may be missing something, but how are you planning on stopping the refresh by getting the shadowmap in a shader? The call to update the shadowmap happens somewhere in c++ land and I don't see a way to cancel it. It's probably cleared by the time the command buffer is called as well.

    PS; What do you mean with the selective render thing?
     
  48. Plutoman

    Plutoman

    Joined:
    May 24, 2013
    Posts:
    257
    Oh, I meant more like -

    in frame 1, you use a command buffer to grab the shadowmap using the point light shadow rendering.
    In frame 2, you see you have a cached shadowmap, so in the Update call, you disable the light's shadowcasting (just on the Unity light source). Since the render would come after the Update call, that should work fine.

    The shadowcasting of a light can just be disabled by the light API, no need for it to be in a shader!

    I can think of multiple ways to set that up, too. OverlapSphere for physics layers that are moveable physics types, and if one is in range (return value is > 0), re-enable shadow-casting.

    By selective render, I mean, that if you use a command buffer + the build in shadow rendering, there is no mechanism to render shadows onto certain layers, the only thing it respects for shadows is the mesh renderer settings. Unlike a RenderToCubemap, which respects the camera's render layers. That doesn't quite make sense to me, as it should be easy to add to their shadow rendering, but.. the intent being that you cache a shadowmap for all the static geometry, and render the realtime shadows using Unity's more optimized methods (I never figured out why theirs is faster, either).

    I originally had a system like this working, but it got to be a pain to maintain with my code-base so I dropped it and went with full realtime shadows. Got too busy dealing with other issues. Figured I'd just need higher sys requirements.
     
  49. macdude2

    macdude2

    Joined:
    Sep 22, 2010
    Posts:
    686
    Anyone else feeling some instant lightmaps with the proxy gi module?
     
  50. Zuntatos

    Zuntatos

    Joined:
    Nov 18, 2012
    Posts:
    612
    @Plutoman

    Your frame 2 seems to be implying some mixed rendering of an unity light and the shadows with a custom light. That's quite troublesome, more of that onwards.

    If in frame 1, you mean to grab the shadowmap of a unity light there's some problems:

    1) For unity3d point lights, there is only 1 rendertexture per resolution. Unity renders into them in the middle of the rendering phase before they're rendering the light itself. The performs gains they have is probably due to this (rendertexture re-using) combined with their rendering being on the actual rendering thread. Plus they can bypass some of the internal wrapper code and possibly specify more options. Oh, and this method uses tons less VRAM. But it's also the reason why we can't have a 'manual refresh' mode in a simple way; they'd have to restructure the light rendering.

    2) None of the command buffer functions actually return a value, so you can't actually get the rendertexture that way.

    Rendering shadows onto certain layers requires use of a stencil buffer and marking the layers receiving shadows as values in it, and then you need to split the point light rendering in 2 passes; one with shadows onto the marked pixels and one without shadows onto the unmarked pixels.

    Rendering shadows from certain layers simply makes use of the camera's render layers as you said, to limit what is drawn into the shadowmap.

    That would be slightly problematic. The static light would draw light in the shadow of the dynamic light, and the same the other way around. (simple) Options are either:
    1) Render the light as custom static when everything is static; turn it off and create a unity3d light with shadows when something dynamic is in range.

    2) Extend the implementation of these lights. Add a second shadow map that you only refresh with dynamic geometry (possibly only some faces) while keeping the first with static geometry. Shader-wise this shouldn't make much of an impact (as long as you don't go to 2k+ sized maps) as it's simply an extra texture fetch and multiplying the 'shadow values' with eachother. But yeah, then you get the lower performance per render of these lights. If enough of the geometry is static, it may be worth it though. (Imagine a point light needing 400 draw calls to update, of which only ~20 are dynamic).

    Note: All I'm talking about is in deferred shading, I've no clue about forward as it's not an option for my project.