Search Unity

Shader attached to the Projector component of a game object cannot access the main directional light

Discussion in 'Shaders' started by cmbliu, Feb 19, 2019.

  1. cmbliu

    cmbliu

    Joined:
    Jun 4, 2017
    Posts:
    14
    Assume you have a shader that has two passes, with the first pass being ForwardBase, while the second pass being ForwardAdd.

    If that shader is attached to a material, and that material is attached to a non-projector gameobject like a cube. Then the lightmode follows the rules.

    The rules are:
    -Only the main directional light data are given in the ForwardBase pass
    -Other lights such as the second directional light or point lights will only have their data available in the ForwardAdd pass

    However, if that shader is attached to a material which is, in turn, attached to a projector component of a gameobject, then the lightmode will NOT follow the rule. Instead, the following will happen
    1. Regardless of whether a ForwardBase pass or a ForwardAdd pass, ONLY the closest light data is available IN ALL PASSES regardless of the type of light. For example, even if there is a main directional light, if the projector gameobject is closer to the point light, only the point light data will be available in ALL PASSES, even in the ForwardBase pass.
    2. In other words, ONLY one light (which is the closest one to the projector) in the scene can have its data passed to the shader, if the shader is attached to the projector component of a gameobject.

    You can reproduce this issue easily.
    1. Create an empty gameobject in the scene.
    2. Attach a projector component to the empty gameobject.
    3. Attach a multi-pass shader (with ForwardBase and ForwardAdd) to the projector component of the game object.
    4. Make a point light and a directional light in the scene.
    5. Make the point light a lot brighter than the directional light
    6. Move the projector gameobject very close to the point light
    7. You will find that _LightColor0 and _WorldSpaceLightPos0 will always contain the data of the closest light IN ALL PASSES, even in the ForwardBase pass.

    Everyone on the forum had stated that the point light data cannot be available in the ForwardBase pass.

    #include "UnityCG.cginc"
    uniform float4 _LightColor0; // color of light source (from "Lighting.cginc")

    Why can't I get access to the main directional light data in the ForwardBase pass when the projector gameobject is closer to a point light, and if the shader is attached to the projector component of a gameobject??? Is it a bug??? Please help me......
     
    Last edited: Feb 19, 2019
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    Projectors don't support lighting.

    Projectors are ancient, in fact they date back to the original release of Unity, and I doubt they've been touched much since they were first added. Just enough so they keep working, but not to add any new functionality. As such, they don't respect most of the more modern "rules" of Unity's rendering pipeline. Queue has no effect on them for example, they always render at the start of transparency queue, effectively their own queue that exists before 2501, but not 2500, and not the skybox. I doubt they pay attention to any of the shader tags. They certainly don't pay attention to the LightMode tag; every pass defined in the shader gets used by a projector when rendered, including deferred, meta, and shadow caster passes! And not when they're "supposed to", just one after another when the projector renders at the start of the transparency queue.

    The reality is projectors don't know about lighting. At all.

    Now to that you're going to say "but I'm getting lighting data", and yes, you very well may be. The thing is, projectors know so little about lighting they don't even know to unset the last values set by the lighting system. The fact a projector's shader gets that data is because a previous pass set that data and it was never set to anything else. You have a shader that is requesting that data, and the graphics APIs are simply passing along whatever data was last set. It's not that you're getting the closest light, you're getting the last light settings to have been previously rendered.
     
    Subliminum likes this.
  3. cmbliu

    cmbliu

    Joined:
    Jun 4, 2017
    Posts:
    14
    Dear bgolus,

    Thank you, many many thank you for answering my question.

    But please, let me ask 2 more questions:

    Question 1:
    As you had mentioned in your reply, the Unity developers have not been updating the projector features for a long time, does it mean the projector feature will become obsolete in the future? In other words, will the projector feature be removed from the future version of Unity?

    Question 2:
    If it is true that the projector feature will become obsolete and will be removed in the future, then what should I use instead??? I use projector in my game, because when the magic ice ball hit the ground, the projector will project a layer of thin ice on the area of impact. So, I want to have an icy ground that reflects the moonlight (directional light) and the lantern light (point light), may you please teach me how to do it without projector.

    Please help me, bgolus, I am very very sad and I want to cry. I really want to release a game on steam, I have been jobless for two years, this game project is my LAST HOPE, and my money almost run out. I can't even pay my rent. Please help me before I become homeless.
     
  4. Subliminum

    Subliminum

    Joined:
    Nov 9, 2017
    Posts:
    97
    Probably better off getting a supporting job then assuming releasing a game on steam will solve all your financial issues. Best of luck!
     
  5. CurlewStudios

    CurlewStudios

    Joined:
    May 22, 2016
    Posts:
    9
    I would instanciate a pre-made mesh, or maybe just a quad on which you can put any shader you want.

    Another alternative would be to dynamicly create a simple mesh, if you know your ground is uneven.

    If you are using deferred rendering you can probably find some nice decal system to modify color and smoothness with a texture, something like https://assetstore.unity.com/packag...fects/decalicious-deferred-decal-system-74175
    I haven’t tried it myself so i can’t recommend it, I’m sure there are others out there too.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    Unity has not yet said they're removing projectors, nor have they marked them as deprecated as they often do before removing a feature. However at the same time they do not support them in any form in either the LWRP or HDRP and they've been clearly a second class citizen of Unity's main rendering paths for a while now. As far as the built in rendering paths are concerned, I would expect the projector component to live on for a long time. At least as long as the built in forward rendering path lives (which they have not announced any plans to remove).

    Some form of deferred decals are certainly a good way to go if you have an uneven ground plane, especially if you're using deferred rendering already, though you can do something like deferred decals with the forward rendering path too. Unity's HDRP uses this style of projector already.

    The LWRP doesn't yet have anything, but it's been requested several times by us users, and Unity has said they are looking for some kind of replacement.

    Overall I'd say @Piggyback's suggestions are on the money. Alternatively you could write a shader that you pass the data about the main light, ambient lighting, and one point light manually from c#, though shadows would be difficult to support.
     
  7. cmbliu

    cmbliu

    Joined:
    Jun 4, 2017
    Posts:
    14
    Thank you so much everyone, especially bgolus.

    I have to use projector, because when the magic ice ball hits, all surrounding will be frozen, not just the ground. It cannot be done with just a quad or a pre-made mesh like Piggyback suggested. If there is a rock of some shape, enemies, my player character, tree or weapons, I will need to apply the icy texture on ALL of them, not just the ground. This can only be done using projector, or else I have to personally write a lot of C# code to do the logic. But I am running out of time and fund. I want to release something in a year, but I am an One-Man-Army. I am doing all 3D arts, all 3D animations, all songs, all music, all sounds, all drawing, all coding and all story writings, and I am making an Action RPG, not a casual game. Everyone looks down on me, and say this is impossible for one person, and I am here to prove them WRONG!! This is my destiny, my battle!!

    I think I will try to get the light data using C#, and manually pass the light data to the projector shader. I hope this works...
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    Note, to do this you will have to write a fully custom shader that takes the lighting data as custom input variables and does both the directional and point light in one pass. It's not as simple as "use a standard shader and set some values via c#". Because those shader properties used are part of Unity's internal lighting system, it won't let you modify them directly and instead throw a warning stating that fact.
     
  9. cmbliu

    cmbliu

    Joined:
    Jun 4, 2017
    Posts:
    14
    Thank you, bgolus.

    Yes, my Stage Controller will get a list of lights in the current stage (my game is a side scrolling Action RPG with the game world organised into stages). Then, my Action Manager will determine the position of Ice Magic Action. This will trigger the transfer of closest lights' data to the projector's shader. The projector's shader will be written by me to compute the diffuse and specular reflection in one pass.

    I also want to add ambient light to the projected texture. So I hope UNITY_LIGHTMODEL_AMBIENT works in the projector's shader; otherwise I don't know how I can get this information using C#.
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    You can extract the full ambient lighting SH to pass to the shader using this:
    https://github.com/keijiro/LightProbeUtility

    Note, you'll need to modify that script so it doesn't use the built in "unity_*" shader variable names as you can't set them from script anymore (for the reasons I mentioned above). However I wouldn't try to rewrite the in-shader ShadeSH9() function, but rather write something that overrides the built in values with your custom ones, ie:

    Code (CSharp):
    1. #include "UnityShaderVariables.cginc"
    2.  
    3. half4 custom_SHAr;
    4. half4 custom_SHAg;
    5. half4 custom_SHAb;
    6. half4 custom_SHBr;
    7. half4 custom_SHBg;
    8. half4 custom_SHBb;
    9. half4 custom_SHC;
    10.  
    11. #undef unity_SHAr
    12. #undef unity_SHAg
    13. #undef unity_SHAb
    14. #undef unity_SHBr
    15. #undef unity_SHBg
    16. #undef unity_SHBb
    17. #undef unity_SHC
    18.  
    19. #define unity_SHAr custom_SHAr
    20. #define unity_SHAg custom_SHAg
    21. #define unity_SHAb custom_SHAb
    22. #define unity_SHBr custom_SHBr
    23. #define unity_SHBg custom_SHBg
    24. #define unity_SHBb custom_SHBb
    25. #define unity_SHC custom_SHC
     
  11. cmbliu

    cmbliu

    Joined:
    Jun 4, 2017
    Posts:
    14
    Time to work. Thank you for everything, bgolus.