← Назад к вопросам

Как менять цвет Mesh в runtime программы используя один Draw Call?

1.3 Junior🔥 222 комментариев
#Рендеринг и графика

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Оптимизация смены цвета меша через один Draw Call

Для изменения цвета меша во время выполнения с одним Draw Call необходимо использовать подходы, которые позволяют менять визуальные свойства объектов без нарушения пакетной обработки (batching). Ключевой принцип — изменение данных в уже существующих буферах, а не создание новых материалов или свойств на объектах.

Основные подходы с одним Draw Call

1. Использование MaterialPropertyBlock

Это наиболее эффективный способ, так как позволяет изменять свойства шейдера (включая цвет) для каждого рендерера, не нарушая статическую или динамическую пакетную обработку. MaterialPropertyBlock изменяет свойства материала на уровне GPU, не создавая копий материала.

using UnityEngine;

public class MeshColorChanger : MonoBehaviour
{
    private Renderer meshRenderer;
    private MaterialPropertyBlock propertyBlock;

    void Start()
    {
        meshRenderer = GetComponent<Renderer>();
        propertyBlock = new MaterialPropertyBlock();
        
        // Получаем текущие свойства (если нужно)
        meshRenderer.GetPropertyBlock(propertyBlock);
        
        // Устанавливаем новый цвет
        propertyBlock.SetColor("_Color", Color.red);
        
        // Применяем изменения
        meshRenderer.SetPropertyBlock(propertyBlock);
    }
    
    public void ChangeColor(Color newColor)
    {
        propertyBlock.SetColor("_Color", newColor);
        meshRenderer.SetPropertyBlock(propertyBlock);
    }
}

2. Использование Graphics.DrawMeshInstanced

Для рендеринга множества мешей с разными цветами, но одним Draw Call, можно использовать инстансинг. Этот подход требует поддержки GPU Instancing в шейдере.

using UnityEngine;

public class InstancedColorChanger : MonoBehaviour
{
    public Mesh mesh;
    public Material material;
    public int instanceCount = 100;
    
    private Matrix4x4[] matrices;
    private MaterialPropertyBlock propertyBlock;
    private Vector4[] colors;

    void Start()
    {
        matrices = new Matrix4x4[instanceCount];
        colors = new Vector4[instanceCount];
        propertyBlock = new MaterialPropertyBlock();
        
        // Инициализация позиций и цветов
        for (int i = 0; i < instanceCount; i++)
        {
            matrices[i] = Matrix4x4.TRS(
                Random.insideUnitSphere * 10f,
                Quaternion.identity,
                Vector3.one
            );
            colors[i] = new Color(Random.value, Random.value, Random.value, 1f);
        }
        
        propertyBlock.SetVectorArray("_Colors", colors);
    }

    void Update()
    {
        // Рендеринг всех мешей одним Draw Call
        Graphics.DrawMeshInstanced(
            mesh, 
            0, 
            material, 
            matrices, 
            instanceCount, 
            propertyBlock
        );
    }
}

3. Изменение Vertex Colors через модификацию Mesh

Этот подход изменяет данные непосредственно в меше, что также сохраняет пакетную обработку:

using UnityEngine;

public class VertexColorChanger : MonoBehaviour
{
    private Mesh mesh;
    private Color[] colors;

    void Start()
    {
        MeshFilter meshFilter = GetComponent<MeshFilter>();
        mesh = meshFilter.mesh;
        colors = new Color[mesh.vertexCount];
        
        // Инициализация цветов вершин
        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = Color.white;
        }
        
        ApplyVertexColors();
    }
    
    public void ChangeAllVerticesToColor(Color newColor)
    {
        for (int i = 0; i < colors.Length; i++)
        {
            colors[i] = newColor;
        }
        ApplyVertexColors();
    }
    
    private void ApplyVertexColors()
    {
        mesh.colors = colors;
        
        // Важно: пересчитать границы, если меняется визуальное представление
        mesh.RecalculateBounds();
    }
}

Критические требования для сохранения одного Draw Call

  1. Один материал — все объекты должны использовать один и тот же материал
  2. Поддержка GPU Instancing — в шейдере должна быть включена опция "Enable GPU Instancing"
  3. Одинаковые настройки рендерера — идентичные параметры света, теней и отсечения
  4. Статические объекты — для статической пакетной обработки объекты должны быть помечены как Static

Практические рекомендации

  • MaterialPropertyBlock — лучший выбор для одиночных объектов или небольшого количества мешей
  • GPU Instancing — оптимален для большого количества одинаковых мешей с разными цветами
  • Избегайте material.SetColor() — этот метод создает копию материала, что ломает пакетную обработку
  • Проверяйте статистику — используйте окно Stats в редакторе для мониторинга количества Draw Calls

Пример шейдера с поддержкой инстансинга

Shader "Custom/InstancedColorShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        CGPROGRAM
        #pragma surface surf Lambert addshadow
        #pragma multi_compile_instancing
        
        sampler2D _MainTex;
        
        struct Input
        {
            float2 uv_MainTex;
        };
        
        UNITY_INSTANCING_BUFFER_START(Props)
            UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * 
                      UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Выбор конкретного подхода зависит от вашей задачи: для изменения цвета одного объекта используйте MaterialPropertyBlock, для множества одинаковых объектов — GPU Instancing, а если нужно менять цвет отдельных вершин — модифицируйте vertex colors. Все три метода сохраняют один Draw Call при правильной реализации.

Как менять цвет Mesh в runtime программы используя один Draw Call? | PrepBro