Secondary Maps (Detail Maps / Detail Normal) possible?

Hi,
is there a way to use a secondary normal map with different scale like it is in Unity? This is a feature I really miss sometimes or maybe I simply did not find the way to do it yet.

For example you could use a grid bump texture to do floor tiles and mix a tiling marble bump map for details instead of using an absurdly huge texture. My personal use case would embossing like it is done on gift cards. Use a server generated embossing normal map and add paper details with a separate detail normal.

Here is the Unity documentation for that feature:
https://docs.unity3d.com/Manual/StandardShaderMaterialParameterDetail.html

Is this possible somehow at the moment? How are opinions on that as a possible Babylon feature?
Feedback is greatly appreciated. Maybe it is worth to put a feature request if enough people would benefit from it.

(I don’t know if it is possible within the limitations of WebGL and available textures. Also i don’t know the performance implications in that case. I am not too deep into the PBR shader and modifying it. Otherwise I would have made a prototype myself to test it. I guess it would not be too difficult to implement this if you know what you are doing :sweat_smile:)

2 Likes

It is a good idea but this is the first time (if my memory serves me right). That we got the ask

The cost will be to read one more texture which could be ok depending on the complexity of your material.

If others are interested we should open a feature request on the repo for sure

3 Likes

As this exists into well-known engine like Unity3D & Unreal (by the way, Unreal detail map workflow here), this could be a nice feature for artists used to it.

I don’t use it personnally ('should use it sometimes I guess…) but I think people making characters or game assets use this often.

Example from the “Realistic Rendering” demo of UE:

door global relief using an UV channel, wood details another one

2 Likes

Pinging @PatrickRyan to get his thoughts

1 Like

@GreyWorks, I agree with you that this is a missing feature that can go a long way toward optimization. I spoke with @Deltakosh about it and we will put this in as a feature for 4.1 because it’s too close to the release of 4.0 to get this in. Thanks for suggesting the feature as we are always trying to determine where to allocate our resources and requests from the community always top the list!

2 Likes

@GreyWorks please create a feature request on our repo :slight_smile:

That’s great news! Thank you! :slight_smile:
I will create the feature request but it may take until Monday as my workday just ended.

Have a great weekend!

For completeness: I opened the feature request. It can be found here.
As it is my first submitted issue/feature request I hope to have done it correctly.

2 Likes

Soo… after a lot of time has passed I got far enough into the source code to make it happen for my use case!

TLDR first because this is going to be a big post:

I extended the PBRMaterial with a second and third normal map. They are applied upon each other.

I would be glad if someone could look into my code to give me feedback on how to improve on it.
As far as I have seen you could do something similar with the PBRCustomMaterial but I have to change the maps within the lifetime of the material. I couldn’t figure out how to do that with the existing methods.

One normal map:

Three normal maps combined:

I don’t have a playground because it is typescript, custom classes, etc but you can find a demo project with the full source code on my github.

So here is what I have done. This is going to be very technical:

After digging a lot in the PBRCustomMaterial, PBRMaterial and PBRBaseMaterial I finally understood it well enough to replicate what PBRCustomMaterial does. :smile:

PBRMaterialExt.ts
I added tow textures for detail normal maps. They can be scaled independently from the main bumpmap as they use separate uniforms for the texture matrices. Also I added a multiplier for each texture so you can use the same texture with different strengths on multiple materials as detail texture.
After one of these textures change the defines and for the shader are rebuilt. The custom NameResolve also looks if the shader was already built with that specific set of defines and makes the engine use the cached version if so.
AttachAfterBind sets the textures, matrices and infos for the textures if they are ready. I wanted to use the same method as PBRBaseMaterial uses but there is no way to access the UniformBuffer without duplicating a huge part of the material. As these values can be set on the effect directly I used this solution.
Also I use a custom garbage collection that relies on hasTexture() on my other project so I updated that method to look for the detail textures.

Replacement bumpFragment
For the shader I updated the bumpFragment. I could have written an updated fragment into the shader part store or replace the current one but I couldn’t find a reason for that at the moment as it is used as include in the fragment shader code anyway.
What I did here is to use the same method as the regular tangent space normal uses. It is applied on the already altered normal from the regular bumpMap. This is a bit on the heavy side processing wise but it is exactly the blending method I need in my case.
By the way… the uv coordinates are calculated in the vertex shader code for the other maps… but as they use vMainUV1 for the first set of UVs and this is a varying I used that in the fragment shader directly. Works in my use case :sweat_smile:

Shortcomings:

  • It only works for tangent space normal maps
  • I might have missed some edge cases where the shader might crash. I am not a pro at shader designing at the moment :sweat_smile:
    – I think I forgot something to make the bumpFunctions accessible when the regular bumpMap is not set.
  • The detail normals always use the first UV set
  • Parallax Occlusion is only calculated on the main bumpMap. Would have to alter the bumpFunctions to change that.

Conclusion

This works surprisingly well for my case but as mentioned above it does not cover all possible uses. If yours is similar feel free to use it. :slight_smile:
As it is I can’t make a pull request or anything because too much is missing. That said you can surely use this to improve on it and fully flesh it out. Sadly I don’t have enough time on my hand to work through all of it.

I still would be glad if at least one detail map would become a default feature but now I can use this in the meantime.
Also: This was a great practice on building my own material extensions.

PS: Did you know it is really easy to make a customProceduralTexture with a pixelshader that calculates a normal map from a bumpmap? I accidentally learned that in the process of researching all this stuff. :stuck_out_tongue:

6 Likes

Congratulations! This is really cool to have you in the community and you did a very good job explaining what you did

On a side note, we recently added a new node to the Node Material: NormalBlendBlock which let you blend multiple normal maps inside a node material :slight_smile:

1 Like

Thank you for the kind words :slight_smile:. It means a lot!

The problem with the Node Material is that it can’t modify the PBR Material yet, am I correct?
I really need this effect for the PBR Material. But as said: It was a great exercise and it is good to understand how the Material system and shaders work.

I hope to be allowed to share some more of the project I am working on someday. At the moment I sadly can’t talk about it publicly. :slightly_frowning_face:

Correct