godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

Add mesh decals and multi-opacity decals #2188

Open MenchenFive opened 3 years ago

MenchenFive commented 3 years ago

Describe the project you are working on

Sci-Fi environment and asset rendering, following what's been called the 'Star Citizen Workflow'.

Describe the problem or limitation you are having in your project

Before going into further detail, I would like to introduce you to the basics of the 'SC' Workflow. It's been popularized by Star Citizen since they make extensive use of it; but it was before used in Alien Isolation, and has lately been used in games like Doom Eternal and Cyberpunk2077, since it does help in achieving cutting edge visuals.

The traditional workflow consists mostly in artists generating a high-poly mesh then baking normals and AO onto a low poly mesh, and then painting textures and materials (with software like substance painter, i.e.) onto those models.

With the SC workflow, all materials are tileable and no baking is involved, base materials like rubber, metal... are applied directly onto the model; and then, all the details needed for the models are applied using decal atlases over the model using small meshes floating over the base model.

This might be like a convoluted way of doing things, but it does have many advantages, like not having to bake maps, reusing of tileable base materials while keeping control of the details over it, texture resolution uniformity across different objects with varying sizes, and pretty much infinite detail, since it doesn't matter how much close you get the camera to the assets, the decals and materials will always look high-res. Some examples of that in this Doom Eternal Digital Foundry video (6:20)

This is still a niche workflow, and most 'public' engines like UE4 or unity still don't fully support it, all AAA companies do this using their custom engines. UE4 does support mesh decals for instance, but no multiopacity.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I propose the following features:

MESH DECALS: The ability to add decal-like materials/shader onto meshes: maybe a blend mode, maybe a specific material shader. I am not talking about the decal nodes that will be featured in 4.0 that act like a projection volume, but meshes belonging to the 3d model. Projection would not be needed, since mesh decals' meshes are already really close to the 'base' mesh shape; the important focus here is the ability to blend (or simulate the blend) the material of the floating mesh with the base one, just like decal nodes do.

MULTI OPACITY for decals: This would be not just for mesh decals but also for decal nodes if possible. Sometimes it is undesireable for the normal and AO maps to be masked by the same opacity mask as albedo, metallic and roughness. As an example, the caved-in area around a screw: we want the metal of the screw to override the underlying material, but we also want the outer area not to do so, and instead apply only normals and AO, to give the illusion of a slight decrease.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

For mesh decals, either a shader/material that is able to take care of multi-opacity and decal-like blending; or another blend mode, or a new parameter in the existing shader/visualshader/materials that allows to set up decal-like blending with whatever is behind, and allows to set up multiopacity.

Multiopacity ideally would be a texture input, letting different color channels control the opacity of each material channel.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Editing godot source code allows quite easily to skip opacity masking for normal and orm textures in decal nodes by skipping the mix functions in scene_forward.glsl around line 2120, but further features will require more work.

Is there a reason why this should be core and not an add-on in the asset library?

It does open the window for new workflows and editing, if the decal node comes by default, I would see no reason why multimasking of decal-like shaders for meshes would be something unwanted.

Calinou commented 3 years ago

As far as I know, there are no plans to add mesh decals (for 4.0 at least). I doubt they can be made nearly as efficient as decal textures themselves.

As for multi-opacity, isn't this already possible by using an alpha channel in the desired texture maps? I'm not sure if we can actually add one more texture slot to the decal atlas due to Vulkan texture limits.

Jummit commented 3 years ago

Most of the times it makes sense to place mesh decals directly inside the modeling application instead of in the game engine, as it provides better UV editing and geometry placing tools. I do this for some of my models:

Decal atlas: ![image](https://user-images.githubusercontent.com/28286961/105898737-143eac80-601a-11eb-81b2-ad2fc1a419a4.png) The model in Blender: ![image](https://user-images.githubusercontent.com/28286961/105899044-74cde980-601a-11eb-995a-c1185f439996.png) Look in-game: ![image](https://user-images.githubusercontent.com/28286961/105898233-7b0f9600-6019-11eb-864d-b7944db8dc12.png)

The only thing that is missing is a G-Buffer, which would allow modifying only the normal, roughness or metallic values. This could probably be its own proposal, and maybe implemented as a render pipeline in 4.0.

In the example I used vertex colors to work around that limitation.

clayjohn commented 3 years ago

I guess the big question for me is, why should Godot use mesh decals instead our cluster-based decals? Keep in mind, Godot uses a forward renderer where mesh decals will be significantly more expensive than in engines like UE4.

Further, what is the benefit to users? You pointed out that this is a niche workflow limited to a few AAA in-house engines. To me that is an indication that the feature must be used very carefully by experienced artists who understand all the performance implications. For an easy to use engine like Godot doesn't it make more sense to provide a simpler solution that is extremely fast (but maybe less robust)?

As always. We care about the practical benefits rather than vague hypotheticals. Especially when we are talking about replacing a workflow that we have already decided on.

MenchenFive commented 3 years ago

@Calinou AFAIK, current 4.0 decal implementation only 'listens' to albedo texture alpha map; normal and orm will still be masked by albedo alpha, not by their own alpha channels, even if set. This being said, I agree that setting up the alpha channel in those texture maps is a much more straightforward approach to multiopacity than the extra masking texture I proposed. Maybe it will be problematic on normal maps due to special texture compression, but nothing that couldn't be worked around.

@clayjohn I do understand the concerns about wasting time on a 'niche' feature; since this workflow mostly caters to realistic hard-surface projects (that is probably why all of the games I mentioned are sci-fi), it's understandable that indie or smaller developers are not very concerned about (or even know) this approach. However, the concept of floating geo and floating textures are something that lots of 3d artists know about. Mesh decals allow for variation and high quality detail in a very non-destructive way.

It is much more straightforward to set up some decals in the 3D modelling suite, than having to re-bake normals, disp, and ao maps everytime something changes. It is also handier to do so in the modelling suite during the asset making process than having to jump between modelling and game engine suite just to set up the decals. Also many use plugins like the popular DECALMachine for blender, which exports texture atlases. Specially if we take into consideration that heavy use of mesh decals allow for heavy reuse of textures and materials, and get rid of the need for one baked set of texture per object, the workflow doesn't only allow for easier asset making, level of detail, and visual quality, but also for memory (and possibly performance) optimizations in some regards.

Again, I do understand the fears of wasting time on something not every user demands, and that it is harder to do so in forward rendering compared to how UE4 handles it (rendering them on an extra buffer, then compositing them over the 'standard' buffers), but if unity users can achieve acceptable results with URP (unity's forward rendering pipeline), there must be ways. Again, full projection is not really needed, just a way to blend the overlaying object with the base one.

Calinou commented 3 years ago

Maybe it will be problematic on normal maps due to special texture compression, but nothing that couldn't be worked around.

Godot is able to handle both RGTC and "standard" VRAM compressed normal maps (or uncompressed normal maps), so that should be fine.

and-rad commented 2 years ago

Since this made it onto the decal tracker for 4.0, I thought I'd provide some more background info on the whole multi opacity thing.

Here is a link to the Polycount thread were I discuss implementing multi opacity for decals in UE4. It contains a couple use cases too. It's a long read and not only about multi opacity, but it might help with the decision on whether it's realistic for Godot to have it. In the end it's about an increase in fidelity and artistic control.

The technical details discussed in that thread are probably mostly useless, though, since the implementation revolved heavily around UE being a deferred renderer. My knowledge of forward rendering is too limited to make any prediction about the performance impact on Godot.

Here is another Polycount thread on mesh decals and what they might be good for as well as the reddit thread that, as far as I can say, sparked all the mesh decal hype.

Calinou commented 2 years ago

I was thinking about decal blending again, and realized that we can probably get the best of both worlds (more flexibility while keeping the same amount of textures):

I started work on this a while ago, but didn't have time to finish it so far: https://github.com/Calinou/godot/tree/decal-normal-orm-improve-blending

[^1]: This min() behavior is what's used in the master branch to combine material AO maps and SSAO together. It looks good without requiring any configuration from the user (such as increasing Environment's Ao Channel Affect as was required in 3.x).

and-rad commented 2 years ago

Sounds like an interesting approach. It might get you halfway there, what about roughness an metallic?

WickedInsignia commented 11 months ago

This would be incredibly useful. Clustered decal nodes are great, but for all sorts of modeling techniques being able to use floating mesh decals allows for very high levels of detail without much poly or texture cost. Ideally this would be done on the material side, rather than as a unique node.

Koalamana9 commented 10 months ago

@clayjohn with new deferred render, will it be possible to use a lot of geometry decals without significant impact on performance?

clayjohn commented 10 months ago

@clayjohn with new deferred render, will it be possible to use a lot of geometry decals without significant impact on performance?

Potentially. They will certainly be more performant than they would be with a forward renderer

Koalamana9 commented 10 months ago

They will certainly be more performant than they would be with a forward renderer

Is it really that bad in forward renderer? Most mesh decals use alpha from vertex colors in the geometry to blend opaque material, are there any tricks in Godot to make rendering transparent materials a bit faster?

clayjohn commented 10 months ago

Its not that it is slow per se Its that the cost of rendering a mesh decal in a forward renderer is the same cost as rendering the mesh.

The entire benefit of mesh decals is that you can add detail to geometry before lighting is calculated. With a forward renderer you have to just render the mesh decal as another mesh which means you duplicate the cost of doing the lighting. It is the same cost as drawing another mesh. If you want to blend albedo, then thinks get really expensive since you don't store albedo in a forward renderer.

AresDevult commented 7 months ago

Doom Eternal uses forward rendering with no G-Buffer and has a lot of mesh decals and renders them very efficiently, I think it's a good example to learn from.

To achieve this, the following geometry pass renders each of the decals’s ID into an 8-bit render target. Later during shading, this texture is sampled to retrieve the ID which is used to retrieve a projection matrix bound with each draw call. The matrix projects the pixel’s position from world space into texture space. These coordinates are then used to sample the decal and blend with the underlying material. This is extremely fast and allows artists to go crazy with massive amounts of decals. Because the IDs are rendered to an 8-bit texture, the maximum amount of decals per mesh would theoretically be 255.

clayjohn commented 7 months ago

Doom Eternal uses forward rendering with no G-Buffer and has a lot of mesh decals and renders them very efficiently, I think it's a good example to learn from.

To achieve this, the following geometry pass renders each of the decals’s ID into an 8-bit render target. Later during shading, this texture is sampled to retrieve the ID which is used to retrieve a projection matrix bound with each draw call. The matrix projects the pixel’s position from world space into texture space. These coordinates are then used to sample the decal and blend with the underlying material. This is extremely fast and allows artists to go crazy with massive amounts of decals. Because the IDs are rendered to an 8-bit texture, the maximum amount of decals per mesh would theoretically be 255.

The Doom Eternal method of rendering decals has the same limitation as ours. Namely you have no additional geometry so you are essentially just splitting textures onto a surface. We could do something more similar to DoomEternal, but it is much more limiting than our current approach (we support way more than 255 decals per mesh).

In my response above I am talking about what UE4 calls "mesh decals" which are decals that contain geometry and are not projected onto a surface. Unfortunately Doom Eternal also calls their decals "mesh decals" even though they contain no geometry and are really just a way of implementing projective decals (our clustered decals are also projective decals, as are UE4s deferred decals).

Mesh decals with actual geometry require deferred rendering. But that doesn't mean we can't improve our current decal system to get closer to what the OP proposes.

Calinou's comment above describes a few changes that sound like they might bridge the gap between what the OP wants and what Godot currently offers

AresDevult commented 7 months ago

Unfortunately Doom Eternal also calls their decals "mesh decals" even though they contain no geometry and are really just a way of implementing projective decals

No! They are geometry based mesh decals! They literally use pieces of geometry that are manually placed by artist onto another mesh, there is no projection at all. https://www.artstation.com/artwork/Ye8o4d There are clever tricks in the shader to render them efficiently in forward renderer, please study this in more detail this is really important.

Current projection decals in Godot are suitable for planar or slightly curved surfaces, but for props where you need to wrap the model on all sides they are useless, it is very important that Godot can effectively render mesh decals because for many this is the main workflow in 3D modeling, especially given the popularity of plugins such as DECALmachine for Blender.

clayjohn commented 7 months ago

@AresDevult Thank you for the link. It looks like the artist references "geodecals" and "projected decals". So I read through the "Rendering of Doom Eternal" slides again and can see that they indeed have 2 types of decals. What they call "geometry decals" and what they call "binned decals". Binned decals appear to be the same as what we use, while Geometry decals indeed are a little more complex and much closer to what we have been calling "mesh decals" in this proposal.

Their exact approach to geometry decals requires a few things that Godot doesn't support right now (i.e. fully bindless rendering, mesh authoring in engine, etc.). But overall it does the following:

  1. Author mesh and decals together (mesh can have up to 254 decals)
  2. Calculate and store 2x4 projection matrix for each decal for each mesh to transform from world space pixel position to decal UV coord
  3. After depth prepass render all geometry decals into a thin G-buffer (only store decal ID) (no blending/transparency)
  4. When rendering mesh, sample the G-buffer to get decal index, use that to lookup the decal properties (only one decal per pixel)

Limitations

  1. Only 254 decals per mesh
  2. only 1 million projections total (one projection is created for each mesh+decal combo. So if one decal is used on 1000 meshes, thats 1000 projections)
  3. Can't alter geometry of mesh (unlike UE4 mesh decals), so only works well for flat geometry
AresDevult commented 7 months ago

Just to be clear I'm not implying that Godot should use this approach for mesh decals as in Doom Eternal, it's just a nice example on how geometry decals can be used in forward renderer. I still think that the best option for Godot is to implement deferred rendering, this will improve the performance of many complex scenes and allow the use of classic mesh decals.

Calinou commented 7 months ago

I still think that the best option for Godot is to implement deferred rendering, this will improve the performance of many complex scenes and allow the use of classic mesh decals.

An option for deferred rendering is planned, but I don't know if we'll go the route of adding a decal system that is clearly deferred-only in nature. It means switching back and forth between forward and deferred will be harder for established projects.

MichaelWengren commented 7 months ago

@Calinou With deferred rendering a G-Buffer will be available and it should be possible to blend mesh decals in albedo instead of drawing them separately

AresDevult commented 6 months ago

I'm curious if it possible to somehow use current projection based decals but with custom geometry instead of just square bounding box? The idea is to make decals in a 3D modeling program and then apply a shader in Godot that makes a projection from the specified pieces of geometry onto another mesh without additional drawing to the depth buffer. Here is an example of mesh decals that are made from pieces of geometry that wrap around the corners of concrete, they all use the same texture and are rendered in one pass. 04_meshdecal

Calinou commented 6 months ago

I'm curious if it possible to somehow use current projection based decals but with custom geometry instead of just square bounding box?

Godot always uses a bounding box for decal projection, so no.