Home Unreal Engine

Expanding DBuffer Decals

1
andrad
polycounter lvl 5
Offline / Send Message
andrad polycounter lvl 5
==============================
EDIT 2022-11-09
==============================
I'm cleaning up and consolidating a bunch of accounts, so the GitHub links in this thread will soon stop working. I have included my commits as patches attached to this post, so anyone still interested in this can take them and try to integrate them with their version of UE.
==============================

This post originated on the UE forums. a user at Polycount suggested I post the information here as well in order to reach more people and gain more feedback. I plan on keeping both threads in sync as I make progress. I also have some ideas on how to proceed, but would like to gauge other people's opinions before. I continue this tomorrow, for now I'm off watching the new season of Luke Cage. I want to see Misty's robot arm!

======================= Original message incoming =========================

I managed to implement metallic output for dbuffer decals. Below are some screenshots of how stuff looks. All the white dots and sprinkles in the buffer image are fully metallic decals. There's no dynamic lighting in the scene.




Note how this also allows placing non-metallic decals on metallic surfaces. This wasn't possible before where decals would inherit the metalness value of the underlying surface every time, which means you couldn't place a sign or a poster made out of paper on a metal wall without the poster becoming metal, too. The dark screws in the screenshots below are correctly rendered non-metallic even when placed on the metal floor.



How does it work?


There's a new option in the material settings under Decal Blend Mode: DBuffer Tanslucent Color,Normal,Roughness,Metal. Choosing this enables the metallic pin in the material attributes. From there it works as every other material does:

1. Connect pin
2. Save material
3. Profit!



I have not yet measured the performance impact, but it should be minimal. Connecting the metallic pin increases instruction count, but that's the case with every non-metallic vs. metallic material. There are some additional instructions in the shader files that are necessary to actually read the metallic value and write it into the respective buffers. But that's just some assignment and basic arithmetic operations. It doesn't get cheaper than this.

One reason I haven't measured performance is that the material is not yet optimized. I'll write about that in another post, in order to keep these from going too much TLDR.

So, what's the bad news?


There is unfortunately no way to get this working without modifications to UE source code. I tried to get this done by modifying the shaders only, but it simply does not suffice. Modifications are minimal, Git tells me I only had to change 24 lines of C++ code. But still, if you want to use this you have to use a custom engine build, as it is also not possible to pack this in a plugin.

Once I'm done with all I have planned, I'll tell you which commits you have to cherry-pick in order to integrate this in your engine builds. Alternatively, you can go pull the whole engine code from GitHub. The branch to check out is named decals-plus. This might not always be up to date and major breakage might occur. You have been warned.

What can you do?


If you'd be so inclined, I'd like you to help testing the stuff. There shouldn't be any problems really, but I would still like to know if there are problems on specific platforms or hardware configurations. I have compiled a demo project that you can play with. You can get it here:

Windows_64
Linux_64

Setups the project was successfully tested on:
Windows, Nvidia GTX 1060
Windows, Radeon HD 8850M
Ubuntu 18.04, Radeon HD 8850M

The Radeon card is a GCN 1.0 Southern Island chipset released in 2013. On Ubuntu the amdgpu free driver was used, running through Mesa 18.1. I haven't tested either the radeon driver nor any of the proprietary drivers and I didn't test with Nvidia on a Linux system at all. I suspect It works just as well, but I rather keep my Linuxes free of NV if I can help it.

I can't compile for Mac or consoles and if anyone who can would like to compile the modified UE code and give it a spin, I'd be tremendously grateful.

Future work


I should talk about that in another post. This one's long enough as it is.

Replies

  • Millenia
    Offline / Send Message
    Millenia Polycount Sponsor
    Very interesting, cheers for sharing!
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3
    You are the hero we needed, but not the one we deserved :)
    Thank you for taking a look at improving DBuffer decals, I was about to do that as a project this summer but I would've had to start from zero, as I don't know any hlsl and ue4 source is horribly undocumented. It was something that I was heavily looking for.

    In my opinion, there wouldn't be a need to add custom extra nodes for blending/masking. Unless you want 100% control on that, with just a single opacity mask it would be enough for most stuff like bolts or random details like the sci-fi (kind of) pictures you posted.

    When opacity = 1: Overlay everything, color, metal, roughness and normal.
    When opacity = 0: Dont affect base color roughness and metal, but blend the decal's normal with the underlying normal.

    It would still be limited and probably some extra masking like you mentioned would be awesome, so I can't wait to see what's your final solution.

    Keep us updated and thank you again!
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Menchen Thanks! I'm afraid what you suggest is still not enough. Not only because of the examples I posted above and the limitations you mention, but also because of the fact that opacity still has to determine the weighting of the blend. Let's say I have an opacity of 0 and the decal normal blends with the underlying surface. Do they blend at equal weights, 50/50? That would mean a lot of detail gets lost or "washed out" because the normals would be flattened quite a bit in a lot of cases. If the decal normals overlay instead of blend, we lose the underlying information when we might like to have it.

    All in all, for the kind of fidelity I'm after, a single float value just does not contain enough information.

    What I'm currently trying to work with is this:

    The last pin called "MultiOpacity" accepts a 4-component vector that contains the opacity values for color, normal, roughness and metalness. If it is used, the input of the Opacity pin will be ignored, as it is not really needed anymore. This also allows for some optimization, because the reverse can also be true: If the Opacity pin is used, MultiOpacity will be ignored, opacity will be the same for all 4 attributes (basically how it is now) and we can save some shader instructions.

    The pin is at the bottom for now, because adding new material attribute pins in the middle requires some extra finnicky additional code that I do not yet completely understand. Getting this wrong means a whole lot of wrong shader code, so it's important that I do this right. What I'd like to do is put the MultiOpacity pin nearer to the other two opacity-related ones. I'd also like to hide it by default if the material is not a deferred decal, in order to not clutter the node with useless stuff.

    Getting all this to work will require some patience, though, because every code change warrants a near-complete engine recompile. So I have to contend with coding for ten minutes and waiting about an hour for compiling to finish before I can see what I broke this time. But I'm getting there.

    On a completely unrelated note: Two episodes in and still no robot arm. What gives!?
  • Johnnynapalmsc
    Offline / Send Message
    Johnnynapalmsc interpolator

    Hi Andrad,

    I hope you don't mind me posting in your thread and asking a few questions. The issue that I have is way more basic than anything you spoke about here, but I think this is the right thread to ask it. :)



    So I can't really figure out why I am getting these artifacts when I am using no alpha blending and using differed decals. It must have something to do with normals as that's the only channel I am using. Isn't this supposed to work with no normal masking?
    Also when it comes to base colour, how would I go about have a separate opacity channel for the masks only? Is the only way to do this to import the albedo of underlying texture and blend within this decal material? I suppose this is what your MultiOpacity will be doing?
  • Obscura
    Offline / Send Message
    Obscura grand marshal polycounter
    You need to use an opacity map.
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Johnnynapalmsc Hi! First off, I'm not quite sure what exactly your issue is. The only problem I can make out in the screenshots above is that the whole square mesh of your decal is slightly visible and I assume you want it to blend so that only the normals of the round thing are. That's because you don't put anything in the Opacity pin. Default opacity is 1, so the complete mesh is visible, which in your case means the whole square is overwriting the normal and roughness info of the mesh below. Buffer visualization viewmode will confirm that.

    As @Obscura pointed out, you need to provide an opacity map. A texture that is white where your decals are and black everywhere else is usually the way to go.

    I'm also not quite sure I understand every part of your second question, but separate opacity channels are not possible. That is indeed what I'm trying to change up there. Your decal will blend all of its attributes - color, roughness etc. - based on the one input you provide to the Opacity pin. If opacity is 0.4 for a given pixel, then the color of that pixel will be 40% your decal color and 60% the underlying surface color. Same with roughness and normals.
  • musashidan
    Offline / Send Message
    musashidan high dynamic range
    @Johnnynapalmsc You should check out @Millenia (who posted above) youtube video on setting this up from Max>UE4. I won't post the link here as I don't want to clutter up this thread. I also have a few Deferred decal tutorials on my YT channel.
  • Johnnynapalmsc
    Offline / Send Message
    Johnnynapalmsc interpolator
    Ok thanks very much, for some reason I thought that the blank area of the normal map would act as an alpha, resulting to surrounding area being completely see through.
    And lastly, in terms of the 2nd part of my question, here a super simple illustration on what I mean:

    So the normal map is inherited of the underlying material, but then on top of that there is a masked diffuse texture. I don't believe there is a way to do this currently with deferred decal, unless you use the underlying texture and blend it with the pink part within the material using alpha blending? Is it something that layered materials could achieve? Thanks in advance!
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3
    Ok thanks very much, for some reason I thought that the blank area of the normal map would act as an alpha, resulting to surrounding area being completely see through.
    And lastly, in terms of the 2nd part of my question, here a super simple illustration on what I mean:

    So the normal map is inherited of the underlying material, but then on top of that there is a masked diffuse texture. I don't believe there is a way to do this currently with deferred decal, unless you use the underlying texture and blend it with the pink part within the material using alpha blending? Is it something that layered materials could achieve? Thanks in advance!
    Independently masking each parameter (having a different opacity mask/map for albedo and another one for normals) is something you can't do. That's the main purpose of andrad's work in this thread
  • Johnnynapalmsc
    Offline / Send Message
    Johnnynapalmsc interpolator
    Menchen said:
    Ok thanks very much, for some reason I thought that the blank area of the normal map would act as an alpha, resulting to surrounding area being completely see through.
    And lastly, in terms of the 2nd part of my question, here a super simple illustration on what I mean:

    So the normal map is inherited of the underlying material, but then on top of that there is a masked diffuse texture. I don't believe there is a way to do this currently with deferred decal, unless you use the underlying texture and blend it with the pink part within the material using alpha blending? Is it something that layered materials could achieve? Thanks in advance!
    Independently masking each parameter (having a different opacity mask/map for albedo and another one for normals) is something you can't do. That's the main purpose of andrad's work in this thread
    Ok awesome, just wanted to make sure I'm not missing anything. Thanks!
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Johnnynapalmsc Since you asked about layered materials: EVERYTHING that's discussed in this thread and in the other one is already possible using layered materials. All the cool selective surface blending stuff is relatively easy going that route. It just has a number of drawbacks when it comes to performance and workflow, which is why I am trying to move away from it and started this work on decals.
  • Johnnynapalmsc
    Offline / Send Message
    Johnnynapalmsc interpolator
    andrad said:
    @Johnnynapalmsc Since you asked about layered materials: EVERYTHING that's discussed in this thread and in the other one is already possible using layered materials. All the cool selective surface blending stuff is relatively easy going that route. It just has a number of drawbacks when it comes to performance and workflow, which is why I am trying to move away from it and started this work on decals.
    Awesome thanks for that. I don't have access at the moment to UE4 as I'm away for a bit, but I assume you can use layered materials with deferred decals in UE4? For example the deferred decal layer would be providing the normal information and then I can alpha mask another material on top with all of my texture and colour information? Or am I misunderstanding this? For example if I have s screw in a hole, the hole would be deferred decal and screw would be masked out as the top layer and all of that would be applied to one mesh plane?
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Johnnynapalmsc You could probably do that, but usually you wouldn't use deferred decals at all for this. You would do all the blending in your material. A performant way to blend is based on vertex color, where your floating decal planes are identified by their color. From here on out I'm going to use the word "decal" to refer to those floating detail meshes. Look at this super simple example below.
    The pink color is the regular color of your mesh, grey is the color of metallic screws and the texture contains the roughness, metallic, normal and color masks for your decals.

    So, the node that goes directly into base color, the If node, says: "If vertex color red is above 0.5, use the regular color, otherwise blend between the regular color and the metal color according to the texture that knows where the metallic parts are." You then repeat this check for the other attributes, which means copy-pasting the If node (the 0.5 node is just there for demonstration purposes) three times and connecting them with additional lerps based on the texture mask.

    You have to make sure that you set the correct vertex colors inside your 3D modeling software and if you want to be extra diligent you also have to work with multiple UV channels, but the basic idea is just what I wrote above.
  • Nosslak
    Offline / Send Message
    Nosslak polycounter lvl 12
    Do you have any plans on implementing support for AO maps in the decals? Last time I tried UE didn't support it, but you could just get around it by including it the albedo, however that's not very PBR compliant.

    I'll give this a go once I get home, if you've got it all submitted and somewhat easily downloadable.
  • AXEL
    Offline / Send Message
    AXEL polycounter lvl 6
    Do you think there is any chance of ever getting POM in dbuffer decals ?
  • Obscura
    Offline / Send Message
    Obscura grand marshal polycounter
    Nosslak said:
    There's been support for POM for a while, my dude. Just add a ParallaxOcclusionMapping node (you don't need that weird workaround with projected UVs at all) and you're pretty much set to go. Here's an example of it in action (not the prettiest just a quick test I did a while back):
    You can see the simple shader setup for the decals here, nothing too fancy going on, just the POM node and a couple controls to change the strength and blending of the decals.


    Wow haven't checked that for a while. Thats some great news! Looks like things are starting to get together.
  • Toku
    Offline / Send Message
    Toku polycounter lvl 6
    Well this is good news, UE4 really needs a fully functioning PBR decal system! Last year I tried making a Star Citizen style environment and I found a hacky way to get the decals working, Basically it's 3 planes layered ontop of each other, the first one uses the DBuffer decal shader and effects the normal/roughness of the surface, 2nd albedo/metalness and 3rd is anAO. It requires 3 times the geo and 2 different mask textures so it isnt really ideal, considering you can achieve the same effect with a correct shader.


  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    Nosslak said:
    I'll give this a go once I get home, if you've got it all submitted and somewhat easily downloadable.
     @Nosslak Unfortunately, I don't. The code lives in this UE fork: https://github.com/POETindustries/UnrealEngine/tree/decals-plus and to use it, you would have to compile the engine yourself. I am very certain that I can't throw precompiled binaries of Unreal Engine around without unleashing the angry powers of Epic's lawyers.

    As for AO: I can look into it, but for dbuffer decals the chances are slim. The dbuffer is somewhat restrictive in the way data is stored and adding new information to it almost always requires additional render targets. I'm going to investigate regular decals in depth after I'm done with optimizing and profiling my changes to the dbuffer. Maybe there's a way to allow for Material AO in those. Both decal systems work substantially differently.
  • LiuYang
    Offline / Send Message
    LiuYang null
    thanks for the amazing work! 
  • Nosslak
    Offline / Send Message
    Nosslak polycounter lvl 12
    andrad said:
    Nosslak said:
    I'll give this a go once I get home, if you've got it all submitted and somewhat easily downloadable.
     @Nosslak Unfortunately, I don't. The code lives in this UE fork: https://github.com/POETindustries/UnrealEngine/tree/decals-plus and to use it, you would have to compile the engine yourself. I am very certain that I can't throw precompiled binaries of Unreal Engine around without unleashing the angry powers of Epic's lawyers.

    As for AO: I can look into it, but for dbuffer decals the chances are slim. The dbuffer is somewhat restrictive in the way data is stored and adding new information to it almost always requires additional render targets. I'm going to investigate regular decals in depth after I'm done with optimizing and profiling my changes to the dbuffer. Maybe there's a way to allow for Material AO in those. Both decal systems work substantially differently.
    I misunderstood what you meant in the first post, I thought you were saying you had it ready to go so anyone could easily try it via the download links. I'll look into compiling Unreal and giving it a try in the next few days. Edit: I tried using your link and it's just returning a 404 error, I found your account on Github but couldn't find the repository in question.

    Also thanks for trying to improve the decals!
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Nosslak Do you have your GitHub account linked to your Unreal user profile? You need to do that in order to see people's UE forks on GitHub.
  • Johnnynapalmsc
    Offline / Send Message
    Johnnynapalmsc interpolator
    andrad said:
    @Johnnynapalmsc You could probably do that, but usually you wouldn't use deferred decals at all for this. You would do all the blending in your material. A performant way to blend is based on vertex color, where your floating decal planes are identified by their color. From here on out I'm going to use the word "decal" to refer to those floating detail meshes. Look at this super simple example below.
    The pink color is the regular color of your mesh, grey is the color of metallic screws and the texture contains the roughness, metallic, normal and color masks for your decals.

    So, the node that goes directly into base color, the If node, says: "If vertex color red is above 0.5, use the regular color, otherwise blend between the regular color and the metal color according to the texture that knows where the metallic parts are." You then repeat this check for the other attributes, which means copy-pasting the If node (the 0.5 node is just there for demonstration purposes) three times and connecting them with additional lerps based on the texture mask.

    You have to make sure that you set the correct vertex colors inside your 3D modeling software and if you want to be extra diligent you also have to work with multiple UV channels, but the basic idea is just what I wrote above.
    Thank you very much for all of your help. Thought I might as well show what all of this lead me to creating, not trying to hi-jack this thread :D https://polycount.com/discussion/202641/oxygen-generator-room-jonas-roscinas-star-citizen-test/p1?new=1
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3
    Great! Thank you very much for your effort, specially considering how undocumented ue4's source is (besides the commented lines in code).

    Just a noob question: if you release an update on this for optimization purposes and such, would we have to recompile the whole engine again (because it's taking a lot of time right now), or just the classes that change?

    Also, for the ones asking of AO, I think it would be too hard as it seems like it would require extra rendering targets in DBuffer, which apparently isn't too hard to implement, but it would take even more memory and it would cause even more problems with opengl and such.

    For AO I would simply fake it by adding it to albedo; and in my opinion it would look even better despite not being totally phisically correct, as the dark color would be much more noticeable. When it finishes compiling I will test it.

    For normals, I don't like how it looks, as it is totally overriden. Again, when it finishes compiling I will test it, but I'm thinking about setting opacity to 50% in the areas in which the decal's normals should blend with the underlying normals, and increasing the vector strength of those same areas by 50% too to compensate:

    Will post when Visual Studio finally decides to finish the compiling process. It's taking ages despite being on a 4.5ghz i7 2600k . Hopefully by the time I'm 99 years old it has finished.
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    Menchen said:
    Just a noob question: if you release an update on this for optimization purposes and such, would we have to recompile the whole engine again (because it's taking a lot of time right now), or just the classes that change?
    Both, most likely. It usually is just the classes that have changed plus the classes that depend on those. But because the classes that deal with rendering are used pretty much everywhere in the engine, almost all of it has to be recompiled anyway. A full engine build should take between 1.5 and 3 hours on relatively high-end harware released within the last 2 years or so. I spend most of the time waiting for compilation to finish while I was working on this. In fact, I nearly finished building a whole kit for a new level while UE was compiling.

    Also, because you wrote about "releasing an update" I'd like to stress that none of this is "released" or finished or in any way complete and shippable. The optimizations I wrote about throughout this thread are an integral part of this extended decal system that I absolutely want to get done before I start thinking about integrating this into real, live projects.

    I'm going to write more about it once I finished the draft on the optimization post.
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Menchen Nice! That's using a POM node, right? I didn't have time to test that yet.
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3
    andrad said:
    @Menchen Nice! That's using a POM node, right? I didn't have time to test that yet.
    Yes. Didn't knew they added POM support for dbuffer decals until @Nosslak mentioned it, but it seems to work properly. Can't wait to further test it, having metallicness and multimasking feels like playing with a totally new engine (not joking, my entire workflow uses decals).

    Quick question, you also mentioned adding multimasking on translucent/non-dbuffer decals, right? I'm asking this because it would be interesting (specially for sci-fi stuff) if we could also get access to emissive to add little "light" decals, if you get what I mean. Stuff like this-> https://www.artstation.com/artwork/10P1o would be cool to have. The only problem would be that they wouldn't appear unless directly lit as they don't work with baked light. But I am considering making some kind of merge and adapt all the changes you made and getting them into an VXGI version of the engine for fully dynamic lighting only.

  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Menchen Yes, translucent decals are on my to-do list, too. In fact, emissive is one of the main reasons why I'd like to get this working. And for fully dynamic games they provide a pretty substantial performance improvement over dbuffer decals.

    Another thing: I don't know how well this is known, but the whole problem with decals not showing up in shadowed areas exists only if static lighting is enabled in the project settings. If we disable static lighting for the whole project, there are no problems with decals anywhere. The following screenshots demonstrate this:

    The pink square is a regular decal, blend mode set to translucent. Static lighting is enabled, but all lights in the scene are dynamic, which is why the pink color is at least somewhat visible in the shadowed areas. Nonetheless, we can observe the usual problem: The yellow stripes shine through in the shadows and even in direct sunlight some of the text beneath the decal is visible, because the sun shines on the decal at an angle !=90°. Observe what happens when static lighting is disabled in the project settings:

    None of that! The pink decal is completely opaque both in direct light as well as in the shadows. Just for shits and giggles, I disabled all lights in the scene and made the decal emissive:

    Full visibility. Now, we all know that dynamic GI in UE leaves a lot to be desired, but if a game doesn't make use of baked lighting, disabling static lighting project-wide has a lot of advantages and makes decals behave correctly everywhere.
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3

    I made a material function that heavily simplifies the process. It makes masking for multiopacity a lot easier, adds support for extra normal strength in the outer areas (as I explained in my previous post), and up to 3 "submaterials" as material functions (makes the process easier), and fake AO.

    It is spagghetied as fuck, but it works.


  • Hayden_Price
    Offline / Send Message
    Hayden_Price polycounter lvl 5
    Been having a play around with this the last few days and I'm loving it so far, thank you so much for working on it, it'll definitely change the way I work on future projects. I found that mesh decals, when aligned perfectly to the geometry, will Z-Fight in the Decals-Plus build of Unreal, even when using non-multimasked and 'stock' Unreal Deferred Decals (such as DBuffer Translucent Color,Normal,Roughness). This is easily worked around by ever so slightly offsetting the decal meshes along their face normals, so not a big problem, however the Z-Fighting doesn't occur in the current stable build of Unreal.

    Here is a mesh based DBuffer Translucent Color,Normal,Roughness decal in the current stable build of 4.19:


    And here is the exact same material setup and mesh in the Decal-Plus build (Once again Color,Normal,Roughness, no multi-masking either, just a single opacity mask plugged into the Opacity output, and static lighting disabled in Project settings):


    Bit hard to see in the screenshots, but the Decal-Plus build gets crazy Z-Fighting as opposed to the current stable build. I've been doing my best to keep up with the thread so I apologize if it's already been mentioned and I just missed it, or if it's a known side effect, I just thought I'd drop in in case it's not known/hasn't been mentioned/only happens to me.
  • Forest_Cat
    Offline / Send Message
    Forest_Cat polycounter lvl 5
    @andrad Great result! I'm looking forward to the moment when it will be added to the official version of UE4. By the way, how do you plan to solve the problem with Temporal AA which makes all small meshes disappear at a certain distance? At the official forum UE4 this issue was repeatedly raised, but I did not find an adequate working solution.
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    By the way, how do you plan to solve the problem with Temporal AA which makes all small meshes disappear at a certain distance? At the official forum UE4 this issue was repeatedly raised, but I did not find an adequate working solution.
    I don't. That's just the way TAA works. But this should rarely be a problem, because most finer decals will be mip-mapped away long before any AA solution renders them invisible.

    And I gotta be honest with you guys, I don't see much of a chance for Epic integrating this into upstream UE. It adds small but significant changes to the rendering code, comes with a performance cost for dbuffer decals even when the new blend modes are not used and it potentially breaks packaging projects. All of this might not be a problem for some projects, but then again, "might not be a problem for some" is exactly the use case that a custom engine build is better suited for than breaking a couple tens of thousand people's projects.
  • Nosslak
    Offline / Send Message
    Nosslak polycounter lvl 12

    andrad said:
    And I gotta be honest with you guys, I don't see much of a chance for Epic integrating this into upstream UE. It adds small but significant changes to the rendering code, comes with a performance cost for dbuffer decals even when the new blend modes are not used and it potentially breaks packaging projects. All of this might not be a problem for some projects, but then again, "might not be a problem for some" is exactly the use case that a custom engine build is better suited for than breaking a couple tens of thousand people's projects.
    I'm sorry to hear that as I just started to get into using it for the last few days and it seemed to be very promising. I did some tests and while it wasn't perfect the decals blended a lot nicer than the default option in Unreal. I guess this is a conclusion you came to after the optimization pass you were talking about or do you still need to do that?
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Nosslak Well, the performance impact is something that became known only after profiling, obviously, but it seemed relatively clear to me from the start that chances of merging this into official UE are slim. If it was only the metalness in dbuffer decals and nothing else, there might be a chance that Epic is willing to accept a pull request. But the selective blending part might be too big a change for their tastes.

    I think I'll create a pull request anyway, even if it's just to learn their reasoning for refusing it. But that'll happen after 4.20 comes out. I want to merge UE 4.20 to get an idea of the amount of merge conflicts this will produce. After having confirmed that everything works for the new UE version, I'll submit a pull request and we'll see what they have to say about it.
  • Menchen
    Offline / Send Message
    Menchen polycounter lvl 3
    Have you implemented yet multimasking for translucent decals? Or are you waiting for the stable 4.20 to put all the changes there?
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @Menchen I finished this a couple of days ago, but I haven't gotten around to writing everything up. I'm hoping I'm ready to post something by the end of the week.
  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    So, there's good news and bad news. The good news is that I got selective blending to work with regular decals as well, and the performance impact is pretty minimal. The bad news is that the workflow differs a little from dbuffer decals and both implementations don't have feature parity.

    The most severe limitation is that metallic opacity and roughness opacity will always be the same. The way gbuffer decals work seems to be coupled heavily with the general layout of UE's gbuffer, where roughness, specularity and metalness are stored in the same render target. Without heavy modifications to the whole render pipeline, opacity can be set separately only for each render target, which means I can blend [color], [normal] and [roughness+metal] separately.

    A somewhat mitigating factor is that this limitation is noticeable only in specific circumstances and can be worked around in most of those. Still, it's not optimal.

    Pics or it didn't happen

    You can activate selective blending my setting the decal blend mode to Selective, as seen in the screenshot below. This is a new blend mode I created for that purpose, so that the other modes are left unchanged and work as they did before. This blend mode shares all the limitations of the other gbuffer decals, most importantly: it doesn't work well with baked lighting.



    The first set of images shows (by now) nothing special, really. It blends the same way that dbuffer decals blend and nothing has to be changed in the material graph.




    All the metallic parts overwrite every attribute, the recesses and seams overwrite normals at the incline, but retain normals on the surface (like the large one to the right of the metallic strip), and most recessed shapes have a very weak roughness opacity in order to blend the underlying roughness with the one provided by the decal. You can see some faint traces of that in the dots on the bottom.

    This specific material/decal combination would look exactly the same when built with either gbuffer or dbuffer decals. There is virtually no difference that an observer could notice. Let's look at what happens when the same decal is placed on a metallic surface.

    If the normals of the first image look inverted to you, it's because I changed the light's direction in order to better make out the surface. Unfortunately, when viewed side-by-side with the image above this creates kind of an optical illusion that suggests the normals, and not the light, have changed.




    Apart from that, you might think there's nothing wrong with the first image. You'd be sorta kinda right, but only because the decal's roughness opacity is so weak that the adverse effects are barely noticeable. It's still enough to observe the problem in the buffer images, though.

    When looking at the metallic buffer, we can see that there's a ton of grey, which is generally not what we want. All those grey parts come from the roughness opacity, and what happens is this: When it comes time to blend the decal with the environment, the renderer takes the metallic value of the decal (0 for those grey spots), multiplies it with the opacity (the same for roughness and metalness), and blends it with the underlying, fully metallic surface. It comes down to something like 1 * 0.9 + 0 * 0.1 = 0.9, I don't know the exact blending operation off the dome. The point is that roughness blending always results in metalness blending which produces metalness values that are not 0 or 1, which violates PBR specs in most of the cases.

    The reason the images above look still kind of okay is that metalness can be blended to very light greys without producing non-PBR results right away. It's okay to have lightgrey metalness in areas that are covered by thin layers of dust for example, provided the reflectance value in the base color is still correct.

    Workarounds and Workwiths

    In summary, unwanted effects will happen if the intended metal opacity differs from the actual roughness opacity and the metal value of the underlying surface is different than the metal value of the decal.

    Let's break that sentence apart: If roughness opacity and metal opacity are the same, there's obviously no problem. Even though the wrong texture channel is used, metal opacity will still be calculated correctly. If roughness and metal opacity differ, but the metal value of the decal and the surface are the same, then the resulting value will stay the same. For non-metals 0 * 0.123 + 0 * 0.877 is still 0, still not metallic. The same but in reverse goes for two metals. If they differ, the amount of roughness opacity is proportional to the visible error, which means the error can be reduced if the opacity is low.

    With all that said, here are some ways to work around this problem:

    1. Don't blend roughness opacity independently. If you never blend roughness independently from metalness, you'll never run into this problem. Their opacities will always be linked and you get by with only one texture for both. This is very limiting, though. Forget blending roughness inside of seams and cracks or anywhere really. Roughness opacity will always have near white or near black values and more even blends cannot be achieved this way.

    2. Use metallic and non-metallic versions of decals. Let's say you have a seam in your decal sheet and that seam blends roughness opacity to simulate dust and dirt that have gathered in it. If this seam is not specifically set to be fully metallic, it will look good on non-metallic surfaces and bad on metallic ones, and vice versa. The solution to this is having two variations of that seam in the decal sheet - a metallic one and a non-metallic one. By placing the metallic seam on metallic surfaces, the problem goes away. The downside to this is that you have to know/decide in advance which surfaces of a mesh are going to be metallic and which ones are not. This severely limits modularity and it might force you to rework some of your meshes if, for example, art direction changes during development.

    3. Use weak roughness opacity. As stated, light greys in the metalness don't break PBR immediately, so you can limit yourself to having roughness opacity very low where it's independent of metal opacity. It will make metals slightly less metallic, but you might get away with it and keep the shading intact. Might also limit overall usefulness, though.

    4. Ignore it. Well, you can always decide to just not give a damn. After all, most of these decals consist of narrow seams, bolts and other very small elements that generally make up only a little portion of screen space and are seldom the center of the action. Who cares about bolts and seams? There's enemies to murder and worlds to save, and no one pauses to stare at a wall to admire the nice shading of that hex nut in the corner over there. In fact, go to Port Olisar and do just that. You will see tons of places where the decal shading might seem a bit off, but it really makes no difference to the overall experience.

    5. Use dbuffer decals. DBuffer decals don't suffer from this problem and blend every material attribute independently. You can always choose to use those instead. If your project makes use of baked lighting, you should be using dbuffer decals anyway, so this issue might not even be relevant to you at all.

    Feature Comparison

    I consider this expanded decal system to be feature-complete. I will look into AO since I have a feeling that it should be possible to get it working for decal materials, but that might just be my limited understanding of UE's rendering pipeline. I also might have another go at getting emissive working for dbuffer decals. For the time being though, I'm finished and here's how dbuffer decals differ from gbuffer decals:

    DBuffer
    • available attributes: Base Color, Metallic, Roughness, Normal
    • selectively blend color, normal, roughness, metal
    • works with dynamic and static lighting
    • adds constant overhead of about 25 instructions for every default lit surface material in the project, whether decals are in the scene or not (this is the cost of enabling dbuffer decals in the project settings)

    GBuffer
    • available attributes: Base Color, Metallic, Roughness, Emissive, Normal
    • selectively blends color, normal, roughnessmetal
    • works flawlessly with dynamic lighting, sucks most of the time for baked lighting

    Pick your poison, as they say.

    Performance

    Performance impact is practically negligible. I profiled FSceneRenderer_RenderMeshDecals and the frame time was 0.056 ms for the unmodified engine and 0.053 ms for selective blending. So there's no statistically significant difference. There's nothing like additional render targets going on and the only performance impact that's worth mentioning comes with evaluationg a ector instead of a scalar for decal opacity.

    Idiosyncrasies

    There are two things I'd like to mention before closing this bit.

    First, concerning sort order of mesh planes inside the same decal material. I have not yet figured out how to reliably control which decal plane gets rendered on top of another if both are part of the same material on the same mesh. I wrote previously about how to properly sort different decal materials on the same mesh, but this doesn't apply to stacking decal planes in the same material. For opaque and masked materials, sort order is defined by the geometry, i.e. if you place a polygon in front of another, it will be rendered in front of it, too. The same doesn't seem to be true for translucent materials. If I have a screw and I place it on top of a panel, both belonging to the decal in one material slot, then the sort order is, for all intents and purposes, undefined. It's not really, though, since the rendered order is consistent and this whole thing is deterministic, after all. I just haven't figured out yet how to control it. I suspect it is determind outside of UE, in the DCC app or the FBX exporter.

    Second, if you plan on using both gbuffer and dbuffer decals alongside each other, be aware that there is a predefined stacking order that you cannot control, not even by manually adjusting the decal actor's sort order: gbuffer decals are always rendered on top of dbuffer decals. It's easy to see why that is. Dbuffer decals are applied before the base pass, gbuffer decals are applied before lighting, well after the base pass. When the renderer deals with gbuffer decals, the dbuffer has been processed, its render targets destroyed and there is just no way to access it again to do any kind of sorting. You can see it in the image below, where every decal is a gbuffer decal except the graffito on the right, which gets drawn behind every other piece of decal.


  • frmdbl
    Offline / Send Message
    frmdbl polycounter
    @andrad

    I compiled the 4.20 version (from 2 days ago). I created a decal material with some quick textures.
    I think the setup should be ok, but no matter what I do, I can't get it to work properly.

    When I use the 'Selective' blend mode the Base Color just doesn't show, when I change to Dbuffer Translucent etc. (with Metallic) the metal isn't blended.
    I think the 'Multi Opacity' masks are fine. It's a vector4 with color, normal, roughness and metal masks in that order.

    The left one is 'Selective' , the right one is 'Dbuffer'.

  • Vexar
    Offline / Send Message
    Vexar polycounter lvl 3
    I am not an engineer but one of the best decal systems to study that I have used is in the BlackOps 3 renderer, which also includes volumetric decals that shrink wrap around models.

    In Black Ops3 they take the all the "grunge" decal layers and render them to one final texture so it reduces rendering overhead this is done at compile time and allows the level designer / environmental artists 5 layers to stack.



  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @frmdbl Can you post screenshots of the material graph and the blend mode settings of your decal material, and the decal response settings of the material on which the decal is projected?
  • frmdbl
    Offline / Send Message
    frmdbl polycounter
    @andrad
    Here's the material graph.
    In terms of the response setting for the underlying material, actually it's my bad, I didn't read your instructions carefully enough and it was the default 'Color Normal Roughness'

    however changing it to does make all channels blend for the Dbuffer mode, but the Base Color of the decal seems like a 0.5 grey rather than a white color as it should.


  • andrad
    Offline / Send Message
    andrad polycounter lvl 5
    @frmdbl All right, I'll have a look at it. Which decal blend mode did you set when the base color showed the correct value?
1
Sign In or Register to comment.