Textured Shadows Trick in Unreal Engine

This weekend I stumbled upon a reddit post about Dr. Facilier’s interesting shadow in The Princess and the Frog and it inspired me to experiment with Forward shading in Unreal Engine 4 to re-create a similar effect in real-time shading. OP pointed out that The Shadow Man’s shadow changes the wallpaper his shadow is cast on. A subtle but quite interesting effect!

With Forward rendering enabled we have a different shading pipeline to play with instead of UE4’s default deferred pipeline, the one I was interested in is the LightAttenuation buffer. The exact available data with Epic’s new Forward rendering is still mostly unknown to me, a good reason to try this new shading pipeline as a Sunday tech-doodle!

This trick was made possible due to a graphics binding bug where the LightAttenuationTexture was available in a pass it should not have been. This has since been fixed making this original implementation no longer possible. I have not looked into an alternative method for this trick.

The result, note the skull texture only rendered within the shadow bounds:

The implementation is really quite basic, I used the LightAttenuationTexture available only in Forward-rendering of the engine to find which part of affected by light. To access this buffer you need to use the Custom-node in the material editor, and apply the following code:

return Square(Texture2DSampleLevel(LightAttenuationTexture, LightAttenuationTextureSampler, UV, 0));

“UV” is an input parameter (so make sure it’s added to the param list of the custom node) in which we feed the ScreenAlignedUVs node output.

For those interested, I found this snippet in the engine’s shader folder at …/4.14/Engine/Shaders/Common.usf and contains the function  GetPerPixelLightAttenuation(float2 UV);

Below is the crude sample of the material used in the GIF:

There is not a whole lot going on, simply blending between the wallpaper and the skull pattern based on the light attenuation value of that pixel is screenspace. The texture samplers use my WorldUVs material function which I’ve posted about some time ago.

The effect visualized with two-tone instead of textures:

This LightAttenuationTexture may not be the perfect source for detailed lighting information, but did the trick for this simple effect recreation. Baked light for example needs to sample the lightmap data (as light attenuation is available from dynamic lights only) But this proved good enough for my specific case of the shadowed wallpaper.

Outlined Shadows

I’ve done multiple blog posts about rendering outlines in Unreal Engine in the past. So when I had this idea of outlining shadows instead of objects, I figured it would be fun to build it as a quick experiment.

To figure out where to draw the outline I use an approach very similar to my prior outline implementation, instead I sample the LightAttenuation buffer instead of the CustomDepth buffer and compare it to the light attenuation value or nearby pixel. This effect does NOT work in Deferred rendering!

35 Responses

  1. I’ve been searching for this kind of thing all over the internet, but I can’t see to be able to reproduce it correctly, does it still work in 4.24 ?

  2. I attempted to use this so objects could cast a shadow on a invisible plane (for casting shadows on the ground in Augmented Reality). I tried using a black base color and feeding what you have as the lerp alpha input instead directly into the material’s opacity. So far, does not look like I was hoping.

    Do you think I’m on the right track, or would you suggest a better way to cast only a shadow on something invisible?

    • I’d feel like there must be some kind of solutions for this available no? I have seen in 4.23 a new HDRI projection that casts some kind of blob shadows on the floor that doesn’t actually exist. That might be a good thing to check out I think its called: HDRI Backdrop.

  3. Hi Tom, this is a very popular post, unfortunately I’ve not being able to get it working and I would love too, I see much potential on this and would like to do more with it. Could you help me get running. I repeated exactly the graph, I removed WorldAlignUV from textures and used just simple texture coordinates since the objecs are mapped.

    The alpha section is exactly like image,
    under Custom node, I have pasted the code, and under input 0 renamed it to “UV”

    set a moveable spot light with a assigned the shader to the wall,

    RESULTS currently: texture B (wallpaper) displays and is mapped properly, shadow only goes to black, like normal behavior instead of texture swap, light attenuation power exp, is set to 2. There’s a weird flicker that will only than display texture A (tinted skull) when aiming character at its lower left side of wall with shader assigned, also swimming and flickering over texture B and streched at places. I’ve trying on fullscreen to see if was the issue of running on other viewport or not native resolution, same deal.

    Thanks for your time again this is a road block i’ve tried to overcome these last 3 or so months, as I see a great use for it as it was done on the movie Ponyo for backgrounds. Thanks for your time and will gladly share updates if i can get this running.

  4. Hey Tom, I need some advice. I would like to know how to modify a custom phong specular highlight material, so that when it receives a cast shadow the phong highlight dissapears.

    https://forums.unrealengine.com/development-discussion/rendering/5897-custom-specular-calculation

    (this is the forum post with the material)

    The “B” part of the first multiply node represents the specular color or the color of the highlight and the number “0” or the color black makes the highlight disappear.

    I would like to know how to make it so when the material receives a cast shadow it’s specular color to become black or phong highlight disappears.
    Do you know of way to do this?

    Thanks in advance.

    I also forwarded this message to “contact me” in case you don’t read/miss this.

  5. Hi Tom,
    Great stuff! I know someone asked about the WorldUV material function and while it has changed in 4.20, I am on a previous version and cant upgrade. I did download the file you attached to that comment but cannot find anything similar to that MF setup.
    Can I please get a closer hint or, even better, the deets of the material function itself?…Pretty please!

    • The node should be called WorldAlignedTexture (and there are a few variants for it too) it requires a “TextureObject” which is different from TextureSample node that you normally insert.

  6. Hey Tom,

    I know a lot have changed since 4.19 and it seems that LightAttenuationTexture does not contain anything anymore. Is there an updated way to reproduce the same tutorial ?

    Or any other way with recent versions of the engine to access the shadow ? I’m trying to control the colors of my shadows on different materials and could use a little help to point me on the right track !

    Thanks again for your great tuto

    • It probably still exists but may have been renamed. Make sure you are using the Forward renderer enable in the project otherwise this buffer is definitely not available. Also note that the Forward renderer isn’t as widely supported so you might want to find alternate techniques that are available in the Deferred renderer instead.

      To find the renamed buffers you have to dig into the .usf files that I discuss in the post itself. It may require some digging using a good search tool (I use Sublime Text)

      – Tom

      • Cool! This is exactly what I need! Any ideas on how to do this in Deferred mode? It could be super shitty, all I’m after is a mask like that.

    • Trex I’ve searched under Unreal 4.21, under common.ush

      Those lines are still there,
      Texture2D LightAttenuationTexture;

      as well as the one Tom provided,
      return Square(Texture2DSampleLevel(LightAttenuationTexture, LightAttenuationTextureSampler, UV, 0));

      So it should still be the same at least for the light attenuation command in 4.19.
      Hope that helps.

  7. Hello Tom. Where are you take function MF_WorldUV`s. I can saw what inside this function and create this material. Is this non secret information? Where see?

  8. Great work Tom!
    Very interesting technique… Thanks for sharing!

    I have been playing around with your shadow material and it worked great when I was using a DirectionalLight, however it doesn’t seem to work with SpotLight for me. But I see in your screenshot that you are using a SpotLight… Any idea what I may have missed?

    • Not sure! I think it’s a little glitchy and may also fail if you have multiple viewports open (eg. the static mesh editor) which was a pain to deal at the time. Trying using a single light source and see what happens.

  9. Hi Tom, I’m trying to use this technique to draw my own shadows and it seems to only work when in fullscreen / native resolution, when I use it in the editor viewport my shadows are drawns in the top left corner (using the exact code in your custom node).
    I’ve tried different operations on UVs to try to stretch them to current resolution using screenposition but I can’t find the attenuation texture dimension.
    Am I doing something wrong ? is there a way to get it working while playing in the editor ?

    Thanks a lot !

    • I had the same thing happen, it messes up whenever a secondary viewport opens up, eg. a new editor PIE viewport or even the static mesh editor. I don’t know of a solution, I lived through it while experimenting, but it’s a bit of an annoying bug.

  10. Hello, this effect is great and I would love to have a play around with it. If you don’t mind me asking what data does the LightAttPowerExp node hold? Unfortunately I have been unable to figure that out by reading your explanation

  11. Hi Tom!
    I’m using your great shader as a base for a “shadow catcher” (or “matte shadow”) material…
    Well, a sort of 🙂
    I’t working great with dynamic lighting but I’d like to add lightmaps as well.
    You’ve mentioned to access the LigthmapData: could you please tell us more about that?
    I suppose to get shadows from it in a similar way you did in the custom node.
    Many thanks!

    • Haven’t received lightmap data before using HLSL, it’s a different type of data (not a screen buffer, but a texture atlas from combined objects) so I don’t know how that is accessed tbh.

  12. Is there any way to access the scene texture gbuffer for the forward shading model? I would like to leverage the forward shader to expose the light vector for some of my materials, but my post-process materials break when I do so because my outline algorithm accesses the World Normal from the Scene Texture GBuffer, and I also need to access the Diffuse Color. Any tips?

    • That data is exclusive to the Deferred renderer (Unreal’s Default) Some type of renderers like Forward+ solve that I believe, but unfortunately Unreal is still mainly a Deferred only engine with Forward being a tiny stub which was mainly developed for the VR game(s) (like Epic’s Robo Recall)

  13. I’m looking forward to the borderlands cross hatch, I was just watching the tech demo’s on that and need an outline similar to it for a project. Keep up the good work Tom, Good name..

  14. I tried to recreate the BP that shows on the picture. But when i applying the changes at the material editor, it said there is compilation error in the nodes and might now display apporpriately in feature level SM5. Unreal didn’t tell me what the problem exactly is and the shader doesn’t work at all. Have you ran into the same problem?

  15. That’s really interesting! I was looking into messing with forward rendering and fragment shaders but i read in the forums that ue4 isn’t really open to such things. I’m just starting out with that kind of programming. Any tips for messing with ue4 with fragment shading?

    • Hi George,

      I linked to the shader folder in the blog in case you’re wondering where the shader files are located, that’s where I find the specific names of samplers and buffers if I need something for my Custom HLSL implementations. I believe there is almost no official docs on this unfortunately. There are some built-in custom implementation such as the SpiralBlur node which are good references for learning some syntax and let you play around.

      As for knowing what to look for, I did learn a lot about Forward and deferred shading a long time ago, having a basic understanding of how the pipeline works will help a lot in finding the shader variables you need for your own effects.

      • Hiya, just giving this a try now and only the texture plugged into B in the lerp is coming through. Wasn’t sure what to put for the parameter that’s hooked up to the Power node (you called it LightAttPowerExp) so I bypassed that node temporarily but I’m still not getting anything. I put UV in for the input of the custom node like you said, too.

        The only other difference I have is that I don’t have the MF_WorldUVs nodes. couldn’t find them. i assume these are the nodes you mentioned that are from a previous post. do i need them for this to work?

      • Were there steps you had to do in the editor for the project as a whole to get it to work? Like set up some settings for the lights? I set everything to movable: the lights and the object with the material and the object casting the shadow and it’s still not working.

        • It’s for Forward rendering only, which you must set in the Project Settings. Do note that Forward shading is very different from the default Deferred. In my case any other differences didn’t matter since it was just a solo experiment, but for a full project it’s a serious trade-off.

          Also, yes the MF_WorldUVs is custom, it’s basically doing a simple UV layout on the objects, this isn’t required and you could just use regular UVs node.

Leave a comment on this post!