You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity(C#)快速设置大量连续颜色纹理像素的优化方案咨询

Unity中高效更新垂直柱状图纹理的优化方案

问题背景

我在Unity(C#)里要绘制一张约2000×200像素的自定义纹理,本质是垂直柱状图——每列1像素宽,上方是同色区块,下方是透明像素,每列的色块高度和颜色都不一样。这个纹理需要每秒更新一次,但当前实现设置像素的速度太慢,导致帧率波动,想找最快的实现思路。

现有实现代码

初始化阶段

Color32[] m_pixelData = new Color32[width * height];
Texture2D m_texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
Color32 m_transparent = new Color32(0,0,0,0);

每次更新时

for (int x = 0; x < width; x++)
{
    Color32 barColor = CalculateBarColorForThisColumn(x);
    int barHeight = CalculateBarHeightForThisColumn(x);

    for (int y = 0; y < height; y++)
    {
        int pixelIndex = y * width + x;

        if (y <= barHeight)
            m_pixelData[pixelIndex] = barColor;
        else
            m_pixelData[pixelIndex] = m_transparent;
    }
}
m_texture.SetPixels32(m_pixelData);

优化思路

1. 减少循环次数,避免重复赋值

当前代码对每个像素都执行一次赋值操作,完全可以优化:

  • 初始化时一次性将m_pixelData填充为透明色,后续更新无需再重复设置透明像素
  • 每列仅对上方barHeight个像素赋值颜色,跳过下方已为透明的像素

修改后的更新代码示例:

// 初始化阶段执行一次,填充全透明
Array.Fill(m_pixelData, m_transparent);

// 每次更新时
for (int x = 0; x < width; x++)
{
    Color32 barColor = CalculateBarColorForThisColumn(x);
    int barHeight = CalculateBarHeightForThisColumn(x);
    
    // 仅处理需要上色的像素
    for (int y = 0; y <= barHeight; y++)
    {
        int pixelIndex = y * width + x;
        m_pixelData[pixelIndex] = barColor;
    }
}
m_texture.SetPixels32(m_pixelData);

如果barHeight平均值远小于200,这个优化能直接减少大量循环操作(比如平均高度100的话,循环次数直接减半)。

2. 批量内存操作替代逐像素赋值

对于同色的连续像素,可以用Buffer.BlockCopy批量复制数据,比逐像素循环赋值更高效:

// 初始化阶段执行一次
Array.Fill(m_pixelData, m_transparent);

// 每次更新时
for (int x = 0; x < width; x++)
{
    Color32 barColor = CalculateBarColorForThisColumn(x);
    int barHeight = CalculateBarHeightForThisColumn(x);
    
    if (barHeight < 0) continue;
    
    // 创建对应高度的同色数组
    Color32[] colorBlock = new Color32[barHeight + 1];
    Array.Fill(colorBlock, barColor);
    
    // 计算字节偏移:每个Color32占4字节,起始位置为x列第0行
    int startByteOffset = x * 4 + 0 * width * 4;
    // 批量复制到目标数组
    Buffer.BlockCopy(colorBlock, 0, m_pixelData, startByteOffset, colorBlock.Length * 4);
}
m_texture.SetPixels32(m_pixelData);

这个方法在barHeight较大时,性能提升会更明显。

3. 后台线程分担计算压力

如果上述优化后仍存在帧率波动,可以将像素数据的计算逻辑移到后台线程:

  • Unity的Texture2D相关API只能在主线程调用,所以后台线程仅负责计算Color32数组,计算完成后再回到主线程更新纹理
  • 用线程安全的方式传递计算结果,比如简单的锁或者ConcurrentQueue

示例伪代码:

private object pixelDataLock = new object();
private Color32[] pendingPixelData;
private bool isDataReady = false;
private int width, height;
private Color32 m_transparent = new Color32(0,0,0,0);

// 触发更新的方法
public void TriggerTextureUpdate()
{
    Task.Run(() => {
        Color32[] newPixelData = new Color32[width * height];
        Array.Fill(newPixelData, m_transparent);
        
        for (int x = 0; x < width; x++)
        {
            Color32 barColor = CalculateBarColorForThisColumn(x);
            int barHeight = CalculateBarHeightForThisColumn(x);
            
            for (int y = 0; y <= barHeight; y++)
            {
                int pixelIndex = y * width + x;
                newPixelData[pixelIndex] = barColor;
            }
        }
        
        // 线程安全地传递数据到主线程
        lock (pixelDataLock)
        {
            pendingPixelData = newPixelData;
            isDataReady = true;
        }
    });
}

// 主线程Update中处理纹理更新
void Update()
{
    if (isDataReady)
    {
        lock (pixelDataLock)
        {
            m_texture.SetPixels32(pendingPixelData);
            m_texture.Apply();
            isDataReady = false;
        }
    }
}

4. 细节优化

  • 提前缓存width、height等固定值,避免循环中重复访问成员变量
  • 优化CalculateBarColorForThisColumn和CalculateBarHeightForThisColumn方法,比如缓存计算结果、减少重复逻辑,降低每列的计算开销

结论

当前实现并非最优,优先尝试减少循环次数的优化,这是成本最低、效果最显著的方案;如果仍不满足需求,再考虑批量内存操作;最后才是后台线程方案。多数情况下,前两种优化就能解决帧率波动问题。

内容的提问来源于stack exchange,提问作者Dogtanian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 13:33:02