0
\$\begingroup\$

I am working on a script that changes the material of a selected object when hovered over. The object(s) in question have multiple materials attached. The problem is that only the first material is being changed and the others are staying the same. When a ray hits an applicable object it changes colour, when the ray is no longer on the object it changes back to the default colour. I am using SNAPS prototype assets from the asset store and cannot edit them to use one material, I cannot figure out how to make them use one material either, if there is a simpler way to do this please let me know.

Below is the code that I have attempted:

void Update()
    {
        if (_selection != null)
        {

            var selectionRenderer = _selection.GetComponent<Renderer>();
            Material[] materials = selectionRenderer.materials;
            foreach (Material mat in materials)
            {
                selectionRenderer.material = defaultMaterial;
            }

            _selection = null;
        }
        camera = Camera.main;
        Ray ray = camera.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit, Mathf.Infinity))
        {
            var selection = hit.transform;
            if (selection.CompareTag(selectableTag))
            {

                var selectionRenderer = selection.GetComponent<Renderer>();
                Material[] materials = selection.GetComponent<Renderer>().materials;
                if (selectionRenderer != null)
                {
                    foreach (Material mat in materials)
                    {
                        selectionRenderer.material = highlightMaterial;
                    }
                    _selection = selection;
                }

            }
        }
    }
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

What you are doing right is that you are using GetComponent<Renderer>().materials to get all the materials. But what you are doing wrong is that you are then set the material with selectionRenderer.material = highlightMaterial. This line uses the material property of the renderer, which always refers to the first material only.

In order to change every material on the object, you need to replace the whole .materials array.

However, there is likely a better way to do what you are trying to do. Accessing the materials of a renderer through those properties is discouraged, because whenever you do that, you are creating a completely new material in memory. That new material won't be shared with anything, so you will force the GPU to make a lot more draw calls.

A better way to temporarily change the way an object is being rendered is by using material property blocks. These allow you to override individual settings of one or more materials without switching the materials actually being used by the renderer. For example, when you want to change the emission color of all materials on a bunch of renderers to yellow:

foreach(var renderer in GetComponentsInChildren<Renderer>) {
    var propertyBlock = renderer.GetPropertyBlock();
    propertyBlock.SetColor("Emission", Color.yellow);
    renderer.SetPropertyBlock(propertyBlock);
}

Note: property names are stringly typed. What names are available and what they do depends on the shader. It is very well possible that the emission property of your particular shader is not called "Emission" but something else.

\$\endgroup\$
1

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .