Adding to SkinnedMeshRenderer Materials array

Hey everyone,
I’m facing a huge problem that many days of googling has not answered. I am using a library for Unity called UMA that generates humans for me. Their have skinned mesh renderers that have two materials – the 1st seems to be for their body and the 2nd for their hair. I’m trying to add a red outline material (with an outline shader) to them so I can show the character as selected. If I simply replace the array of two with my outline material, I see only the outline. If I leave the two as-is and I add a 3rd material, it seems to apply to the hair only. So, somehow UMA created a set of meshes that is dependent on material INDEX. ONLY index 0 applies to the body but I cannot replace it, nor do I know enough about shaders to make a shader that will show the body AND the outline.
Is there a simple way for me to add my outline material but apply it to all meshes in the renderer? I don’t care about draw calls. Only a single object will ever be selected in the scene at any given time anyway.

One more thing to note… I want my technique to work for all other creatures in my game, besides UMA humans, so the materials could vary. A water elemental, for example, will have a completely different material/shader so I would like my solution to be something along the lines of adding an additional material for the outline and apply it to those same meshes rather than trying to make a whole new set of shaders to parallel all of my existing shaders.

Thanks in advance! :slight_smile:

Unfortunately that is not possible. The way submeshes works is like this:

  • For every submesh in the mesh you have to have one material in the materials / sharedMaterials array.
  • Each submesh is paired with the material from the materials / sharedMaterials array with the same index. So submesh 0 uses material 0 and submesh 1 uses material 1.
  • However if you add more materials than there are submeshes, only the last submesh will be re-rendered again for every additional material. So the submesh index is basically clamped.

It would be great if Unity would simply wrap the index around and keep going but unfortunately it doesn’t. That means when you have two submeshes but specify 4 materials Unity will render this:

  • submesh 0 with material 0
  • submesh 1 with material 1
  • submesh 1 with material 2
  • submesh 1 with material 3

That means a single skinned mesh renderer with submeshes can only render the mesh once with one set of materials. If you want to render the whole mesh again with different materials you would have to duplicate the object. Another way could be to actually create a “selected shader” that is a combination of your two shaders. So you can either switch the selection on / off by using a shader parameter or by replacing the materials with the selection material variant.

edit
Another way if merging of the shaders is too complicated would be to “manually” render the objects again using a replacement shader inside OnPostRender. This requires your objects to have a specific tag. When rendering the camera with a replacement shader and using a tag value only the object with that tag will be rendered again with the replacement shader. So you could dynamically assign the tag “selected” to the desired objects and they should get their outline.

Thank you for clarifying this for me Bunny! That’s so unfortunate though. You know what? My experience was that it worked like this:

submesh 0 with material 0
submesh **1** with material 1
submesh **1** with material 2
submesh **1** with material 3

Body was 0 and hair was 1. When I started adding more materials, it was only affecting the hair. Yeah, it’s funny that you mentioned you wished Unity “wrapped the indexes” because that’s exactly what I hoped for. I tried adding materials, hoping that would happen but :frowning: no…
I thought about duplicating the object but I have to handle animating both at exactly the same time. All my code has to be changed to loop through both. The combination of the two shaders sounds like my best bet but I’ve been shying away from that since I use different shaders throughout my players/mobs in the game. I don’t know a lot about shaders but, let’s say you were using the standard “built-in” shader… how hard would it be to add this shader script and make it switchable (on/off):

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Outlined/Silhouette Only" {
	Properties {
		_OutlineColor ("Outline Color", Color) = (0,0,0,1)
		_Outline ("Outline width", Range (0.0, 0.03)) = .005
	}
 
CGINCLUDE
#include "UnityCG.cginc"
 
struct appdata {
	float4 vertex : POSITION;
	float3 normal : NORMAL;
};
 
struct v2f {
	float4 pos : POSITION;
	float4 color : COLOR;
};
 
uniform float _Outline;
uniform float4 _OutlineColor;
 
v2f vert(appdata v) {
	// just make a copy of incoming vertex data but scaled according to normal direction
	v2f o;
	o.pos = UnityObjectToClipPos(v.vertex);
 
	float3 norm   = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
	float2 offset = TransformViewToProjection(norm.xy);
 
	o.pos.xy += offset * o.pos.z * _Outline;
	o.color = _OutlineColor;
	return o;
}
ENDCG
 
	SubShader {
		Tags { "Queue" = "Transparent" }
 
		Pass {
			Name "BASE"
			Cull Back
			Blend Zero One
 
			// uncomment this to hide inner details:
			//Offset -8, -8
 
			SetTexture [_OutlineColor] {
				ConstantColor (0,0,0,0)
				Combine constant
			}
		}
 
		// note that a vertex shader is specified here but its using the one above
		Pass {
			Name "OUTLINE"
			Tags { "LightMode" = "Always" }
			Cull Front
 
			// you can choose what kind of blending mode you want for the outline
			//Blend SrcAlpha OneMinusSrcAlpha // Normal
			//Blend One One // Additive
			Blend One OneMinusDstColor // Soft Additive
			//Blend DstColor Zero // Multiplicative
			//Blend DstColor SrcColor // 2x Multiplicative
 
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
 
half4 frag(v2f i) :COLOR {
	return i.color;
}
ENDCG
		}
 
 
	}
 
	Fallback "Diffuse"
}