Detail Maps and Asset Bundles

I’m trying to set a Detail Map on a gameobject and material that are loaded from an asset bundle. I enable the material’s “_DETAIL_MAP” keyword and set the texture, but it doesn’t have any effect. If I uncomment out the first line, it works just fine. The material that comes in the asset bundle has all defaults.

                //gameObject.GetComponent<MeshRenderer>().material = new Material(Shader.Find("HDRP/Lit"));
                Material material = gameObject.GetComponent<MeshRenderer>().material;
                material.EnableKeyword("_DETAIL_MAP");
                material.SetTexture("_DetailMap", texture);

I will try to reproduce this in an isolated project, but before I do, does it seem like there is anything obviously wrong with what I’m doing here?

I’ve attached a very small project that reproduces the issue. It comes with an asset bundle that was built in the same project. If you load the asset bundle and try to set the detail map on the gameobject inside the scene, it doesn’t work. If you just load the scene (not from the asset bundle) and apply the detail map, it works just fine.

7386377–901520–DetailTest.zip (1.7 MB)

Hi, try getting the shared material instead. When you get the .material, unity will create an instance, so you don’t modify the actual material:

Material material = gameObject.GetComponent<MeshRenderer>().sharedMaterial;

If you want to modify that particular instance, I think you need to reassign the material

Material material = gameObject.GetComponent<MeshRenderer>().material;
material.EnableKeyword("_DETAIL_MAP");
material.SetTexture("_DetailMap", texture);

gameObject.GetComponent<MeshRenderer>().material = material;

I appreciate the reply. Using sharedMaterial doesn’t seem to work in this case either, and neither does reassigning the material back to the MeshRenderer. Besides, keeping my code as is but just replacing “_DetailMap” with “_BaseColorMap” correctly sets the Base Map.

1 Like

Hi @mmkc
I’m investigating on this issue, and I may think this is an expected behavior :
I also tried reproducing the issue with built-in shaders and materials, and it the same there. I suspect that when you bundle a material in an asset bundle it get there with it’s shader and lives on its own. When you add the detail map keyword to the shader, in will not just set a toggle, it actually asks to use an other variant of the shader, an other code, that doesn’t exist in the assetbundle.
I tried manually creating a material with the detail map and adding it to the assetbundle so the needed shader variant is already there when the code is executed, and it worked.
The fact that it works when you uncommented the line where you create a new material is probably because when you do this, you’re not relying anymore on the shader embeded in the bundle.

This is because the “base” shader variant already deals with this map, and doesn’t need any additional keyword.

I will investigate further and ping around internally to be sure, but like mentioned, I think it is expected and you have to bundle all the needed shader variants that you intend to use.

Hmm OK so would it be safest to enable the detail map keyword on all materials before building the asset bundles?

Not really, as if you have the detail map keyword enabled, then the shader variant without it might be missing. Like I mentioned, add in your asset bundle at least one material with the detail map keyword enabled.

OK thank you. One question though… we have an asset bundle that contains many of the common shaders used by our many asset bundles, and build those asset bundles with this common bundle as a dependency. Can I just update the common one and it will try to look for it there?

Also, could I just add the correct combination of keywords to the shader variant collection that’s in that asset bundle?

One more question (sorry)… what other keywords would this apply to or how can I determine that? Normal maps? Height maps? Mask maps?

I’m not very used to how assetbundles work, but I found a better solution to your issues. Instead of creating a lot of materials, use a ShaderVariantCollection asset. Insert all the variants you need there and add the asset to your assetbundle.
Note that single keywords is not enough, a variant is a combinations of keywords.
If you inspect a HDRP/Lit material with the detail map you want to use in your attached project with the inspector in debug mode, it will have the following keywords : _DETAIL_MAP _NORMALMAP _NORMALMAP_TANGENT_SPACE
So using only the _DETAIL_MAP one is not enough.
We do have an editor function that fixes the material keywords depending on what is set up, but it won’t help with missing variants, and obviously, won’t work in a build : UnityEditor.Rendering.HighDefinition.HDShaderUtils.ResetMaterialKeywords(material)

So, to make your attached project work you need to :

  • add a ShaderVarianCollection asset with the HDRP/Lit shader and the above mentioned keywords combo as variant
  • add this asset to the assetbundle, and rebuild it
  • change the script so properly enable all the needed keywords on the material :
material.EnableKeyword("_DETAIL_MAP");
material.EnableKeyword("_NORMALMAP");
material.EnableKeyword("_NORMALMAP_TANGENT_SPACE");

I tried this locally and it worked. And like mentioned, you need to list all the variants that you need in the asset. You can use the API to automatically add variants, but there is not way that I know of to automatically find which ones you’ll need.