【问题标题】:Trying to optimise from setpixel/getpixel尝试从 setpixel/getpixel 进行优化
【发布时间】:2016-09-02 18:17:43
【问题描述】:

我正在尝试优化以下代码,因为循环多次以获取纹理的每个像素非常耗时!

我有 3 个纹理,在其他地方生成,需要合并然后保存到磁盘。

我尝试过使用 SetPixels 和 GetPixels,但这并不是特别有用,因为多次使用它只会替换整个纹理,而不仅仅是替换非透明像素。以下是我目前的功能:

public void savePNG()
{

    stationWidth = PlayerPrefs.GetInt("Station Width");
    stationHeight = PlayerPrefs.GetInt("Station Height");

    Debug.Log(stationWidth + ", " + stationHeight);

    Texture2D spaceStation = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    spaceStation = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);

    Color Test;

    floorTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    wallTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    roomTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);

    floorTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../floorTexture.png"), false);
    wallTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../wallTexture.png"), false);
    roomTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../roomTexture.png"), false);


    for(int row = 0; row < stationWidth*256; row ++)
    {
        for(int col = 0; col < stationHeight*256; col ++)
        {

            Test = floorTex.GetPixel(row,col);

            if(Test.a != 0.0f)
            {
                spaceStation.SetPixel(row, col, floorTex.GetPixel(row,col));
            }
            else
            {
                spaceStation.SetPixel(row, col, Color.white);
            }

            Test = wallTex.GetPixel(row,col);

            if(Test.a != 0.0f)
            {
                spaceStation.SetPixel(row, col, wallTex.GetPixel(row,col));
            }

            Test = roomTex.GetPixel(row,col);

            if(Test.a != 0.0f)
            {
                spaceStation.SetPixel(row, col, roomTex.GetPixel(row,col));
            }

        }
    }

    spaceStation.Apply();

    byte[] bytes = spaceStation.EncodeToPNG();
    File.WriteAllBytes(Application.dataPath + "/../SpaceStation.png", bytes);

}

我不知道从哪里开始,任何想法或建议将不胜感激!

附加

我尝试的 GetPixels 和 SetPixels 代码如下所示:

    spaceStation.SetPixels(floorTex.GetPixels(0, 0, stationWidth*256, stationHeight*256));
    spaceStation.SetPixels(wallTex.GetPixels(0 ,0, stationWidth*256, stationHeight*256));
    spaceStation.SetPixels(roomTex.GetPixels(0, 0, stationWidth*256, stationHeight*256));

每个 SetPixels 都完全覆盖了以前的那些,我希望透明像素根本不覆盖任何像素,这样细节就会显示出来

【问题讨论】:

  • GetPixelsSetPixels 能够产生任何效果。显示使用这些并导致您出现问题的代码。
  • 添加了对我不起作用的SetPixels 代码
  • 啊,好吧,你仍然需要运行你的算法。如果您只是想以透明度进行绘制,则应该有一个功能。对于其他效果,循环从GetPixels 返回的数组,一次更改一个像素,然后使用SetPixels 一次性应用所有效果。
  • 这似乎是最好的主意,我刚刚使用了颜色数组并在那里进行了所有编辑,使它快了大约 80% :) 将发布我的答案。有人告诉我,我可以使用 Graphics.Blit 变得更快,但我根本没有任何着色器经验,所以我要继续说下去,无论如何,80% 的时间节省了很多

标签: c# optimization unity3d textures texture2d


【解决方案1】:

我使用颜色数组而不是单独设置每个像素,只需要调用 GetPixels 3 次和 SetPixels 一次。功能现在快了大约 80%,这在所有方面都相当不错。有人告诉我,使用Graphics.Blit 和着色器可以获得更好的结果,但我真的对着色器一无所知,所以目前我不会采用这种方法。下面是代码现在的样子:

public void savePNG()
{
    stationWidth = PlayerPrefs.GetInt("Station Width");
    stationHeight = PlayerPrefs.GetInt("Station Height");

    Texture2D spaceStation = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    spaceStation = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);

    Color[] mainArray = new Color[(stationWidth*256)*(stationHeight*256)];
    Color[] wallArray = new Color[(stationWidth*256)*(stationHeight*256)];
    Color[] roomArray = new Color[(stationWidth*256)*(stationHeight*256)];

    floorTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    wallTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);
    roomTex = new Texture2D(stationWidth*256, stationHeight*256, TextureFormat.ARGB32, false);

    floorTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../floorTexture.png"), false);
    wallTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../wallTexture.png"), false);
    roomTex.LoadImage(File.ReadAllBytes(Application.dataPath + "/../roomTexture.png"), false);


    mainArray = floorTex.GetPixels(0,0,stationWidth*256, stationHeight*256);
    wallArray = wallTex.GetPixels(0,0,stationWidth*256, stationHeight*256);
    roomArray = roomTex.GetPixels(0,0,stationWidth*256, stationHeight*256);

    for(int i = 0; i < mainArray.Length; i++)
    {
        if(wallArray[i].a != 0.0f)
        {
            mainArray[i] = wallArray[i];
        }
        if(roomArray[i].a != 0.0f)
        {
            mainArray[i] = roomArray[i];
        }
    }

    spaceStation.SetPixels(mainArray);

    spaceStation.Apply();

    byte[] bytes = spaceStation.EncodeToPNG();
    File.WriteAllBytes(Application.dataPath + "/../SpaceStation.png", bytes);

}

【讨论】:

    【解决方案2】:

    我真的不知道 Texture2D 对象的 (get/set)Pixel 方法的性能。但在 .NET 世界中,我们更喜欢使用 System.Drawing.Bitmap 对象和 System.Drawing.Imaging.BitmapData 类和 BitmapData 的 Scan0 属性来快速访问像素值。

    在 SO 中已经有一个漂亮的 answer 演示了用法。

    【讨论】:

    • 谢谢,但不幸的是,如果我没记错的话,Unity 根本没有 System.Drawing!
    猜你喜欢
    • 2012-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    • 2017-03-11
    • 1970-01-01
    相关资源
    最近更新 更多