Jump to content
Sign in to follow this  
CleverNickname

Multiple materials not working on ProBuilder objects

Recommended Posts

I have a shader effect that relies on different passes happening at different points in the rendering queue, and since every pass in a shader has to happen in the same spot in the queue, that means I have to split my shader into two shaders over two materials. On regular Unity objects and imported meshes, the effect works fine: I put two materials on the submesh, Unity throws a warning about having more materials than submeshes, but otherwise the effect works exactly as intended. With geometry created through ProBuilder, however, only the first material in the element 0 slot gets rendered. In fact, when running the scene, the inspector shows that the materials list reduces down to just the first material on the object, and when the scene is stopped, the materials list reverts back to normal. Is this a bug, or just an intentional limitation on ProBuilder meshes? If it's the latter, is there any sort of workaround?

 

I'm using Unity 5.1.2f1 and ProBuilder v3425 (v3519 won't work for me, but that's another issue), running on Windows 7.

Share this post


Link to post
Share on other sites

This is intentional behavior.  The workaround would be to just strip the ProBuilder script from those objects prior to applying the material.  

 

On a related note, the two material on one mesh thing seems like it's relying on undefined behavior on Unity's side.  I also don't follow on what the issue is with using one shader with multiple passes, could you shed a little more light on that reasoning?

Share this post


Link to post
Share on other sites

Sure. I'm trying to get an effect where two different textures are on the same object, and are blended based on how much light is hitting a given point. So, if half an object was illuminated and half wasn't, one half would get the "light" texture, one would get the "dark" texture, and there would be some blending in between where the lighting changes. My initial thought was that I could simply blend the two textures in each pass of the shader depending on that pass's lighting and then blend the final result, but that doesn't work; a spot which is heavily lit by one light but dimly lit by another will end up with a mixed light/dark texture, even though it should be fully light.

 

What I found works is to do an initial series of passes where I do a quick and basic rendering of all the lights in the scene, then save the result to a texture with a grab pass and use that texture as a lookup map for the total lighting on the object. However, there's a performance concern here: if I'm rendering lots of objects with this shader, it will get processed like this:

Lighting pass for object 1
Grab pass
Texturing pass for object 1

Lighting pass for object 2
Grab pass
Texturing pass for object 2

Lighting pass for object 3
Grab pass
Texturing pass for object 3

...

Grab passes are an expensive operation, and we're doing one for every object in the scene with this shader. What we'd much rather do instead is render all of our lighting passes first, do a single grab pass, and use that for all of our texturing passes:

Lighting pass for object 1
Lighting pass for object 2
Lighting pass for object 3
...

Grab pass

Texturing pass for object 1
Texturing pass for object 2
Texturing pass for object 3
...

In order for this to work, we need to have all the lighting passes be in the rendering queue before any of the texturing passes. However, as far as I'm aware, all passes within a shader are in the same place in the queue, and furthermore, a Unity material can only have one shader on it. Therefore, the only way that I can figure out to split the lighting passes from the texturing passes in the queue is to separate them out into two different shaders, put each on a different material, and apply both to the meshes I want to get this effect. Obviously not ideal, and if there's a better way I'm all for it, but this is all I can come up with for now. FWIW it does seem to work totally fine on non-Probuilder meshes, though I'm not sure if I can count on that behavior persisting through future Unity updates.

Share this post


Link to post
Share on other sites

Assuming you only have one submesh per-object, it wouldn't be too difficult to enable multiple materials.  The only reason it doesn't at the moment is because ProBuilder automatically tries to batch each mesh into as few submeshes as possible, which takes control over the sharedMaterials array.  Conversely, this would actually work just fine in Prototype since it doesn't bother batching submeshes (there isn't a way to make a submesh in Prototype, so it just skips that optimization step).

 

I'll look at that code again, there may be a way to avoid rebuilding the sharedMaterials array after all.

Share this post


Link to post
Share on other sites

This is intentional behavior.  The workaround would be to just strip the ProBuilder script from those objects prior to applying the material.  

What's the best way to go about doing this? I tried stripping them in the inspector and it results in the mesh no longer 

rendering or colliding, even though the mesh renderer and collider are still attached and enabled. I also found that I can attach multiple materials to the object at runtime with a script, but it still only renders the first material.

 

Assuming you only have one submesh per-object, it wouldn't be too difficult to enable multiple materials.  The only reason it doesn't at the moment is because ProBuilder automatically tries to batch each mesh into as few submeshes as possible, which takes control over the sharedMaterials array.  Conversely, this would actually work just fine in Prototype since it doesn't bother batching submeshes (there isn't a way to make a submesh in Prototype, so it just skips that optimization step).

 

I'll look at that code again, there may be a way to avoid rebuilding the sharedMaterials array after all.

Awesome! I appreciate the help.

Share this post


Link to post
Share on other sites

 

What's the best way to go about doing this? I tried stripping them in the inspector and it results in the mesh no longer 

rendering or colliding, even though the mesh renderer and collider are still attached and enabled.

Without looking, there's a menu item that I think is under Actions / Strip Selected Scripts or similar. 

Share this post


Link to post
Share on other sites

Resetting the sharedMaterials?  pb_Entity is also removed, right?

 

Yes, resetting the materials list to just the first material. The Pb_Entity and Pb_Object scripts are removed, just the Pb_Mesh remains. When I run the scene the mesh changes from pb_Mesh-xxxxxx to Combined Mesh (root: scene) if that helps.

Share this post


Link to post
Share on other sites

Oh!  The problem isn't ProBuilder then, it's Unity's static batching (that's what that "combined mesh" means).

 

Boy is my face red! You're right, my ProBuilder meshes had been marked as static and my other meshes weren't. It's actually Unity that isn't allowing multiple materials on non-static meshes. Thanks for all your help!

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×