[笔记][Unity3D]运行时从图集中读取小图

1,971 阅读1分钟
private static RenderTexture _getTexture2D_rt;
public static Texture2D GetTexture2DFromImage(Image image, Texture2D atlasTexture)
{
    RenderTexture targetTexture = null;
    if (Camera.main != null)
    {
        targetTexture = Camera.main.targetTexture;
    }
    if (_getTexture2D_rt == null)
    {
        _getTexture2D_rt = RenderTexture.GetTemporary(
                    atlasTexture.width,
                    atlasTexture.height);
    }
    else
    {
        _getTexture2D_rt.Release();
        _getTexture2D_rt.width = atlasTexture.width;
        _getTexture2D_rt.height = atlasTexture.height;
    }

    if (targetTexture != null)
    {
        Camera.main.targetTexture = null;
    }

    Material material = new Material(image.material);
    var clipRect = new Vector4(0, 0, 1, 1);//_ClipRect各方向的取值范围是0~1。
    material.SetVector("_ClipRect", clipRect);
    Graphics.Blit(atlasTexture, _getTexture2D_rt, material);
    RenderTexture previous = RenderTexture.active;
    // GL.Clear(true, false, Color.clear);
    RenderTexture.active = _getTexture2D_rt;

    Texture2D myTexture2D;
    //原图
    if (image.mainTexture == null || (image.sprite.rect.width == atlasTexture.width &&
        image.sprite.rect.height == atlasTexture.height))
    {
        int textureWidth = (int)image.sprite.texture.width;
        int textureHeight = (int)image.sprite.texture.height;
        myTexture2D = new Texture2D(textureWidth, textureHeight, TextureFormat.ARGB32, false);
        myTexture2D.ReadPixels(new Rect(0, 0, textureWidth, textureHeight), 0, 0);
    }
    //图集
    else
    {
        int textureWidth = (int)image.sprite.textureRect.width;
        int textureHeight = (int)image.sprite.textureRect.height;
        //image.sprite.textureRect.y 从底部网上算,但是ReadPixels从顶部往下算
        float textureY = 0;
        if(Application.platform == RuntimePlatform.WindowsEditor)
        {
            //pc(DirectX)的起始点位于左上
            textureY = image.sprite.texture.height - (image.sprite.textureRect.y + image.sprite.textureRect.height);
        }
        else if(Application.platform == RuntimePlatform.Android || Application.platform == RuntimePlatform.IPhonePlayer)
        {
            //移动平台(OpenGL)位于左下
            textureY = image.sprite.textureRect.y;
        }
        myTexture2D = new Texture2D(textureWidth, textureHeight, TextureFormat.ARGB32, false);
        myTexture2D.ReadPixels(new Rect(image.sprite.textureRect.x, textureY, textureWidth, textureHeight), 0, 0);

    }
    myTexture2D.Apply();
    return myTexture2D;
}

注意:

1、Graphics.Blit(atlasTexture, _getTexture2D_rt, material);

由于图集Image用的不是Unity自带的DefaultUIMaterial,所以要想截取正确显示的Texture,需要将material传到Graphics.Blit中去。

api:
public static void Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
Material to use. Material's shader could do some post-processing effect, for example.

2、从RenderTexutre中ReadPixels的时候需要注意DirectX和OpenGL两种渲染器下坐标系的区别

DirectX: 小图在图集中的坐标从左上角开始计算

OpenGL: 小图在图集中的坐标从右上角开始计算

3、若Material的Shader里引用了Unity底层属性,有可能需要手动赋值。

var clipRect = new Vector4(0, 0, 1, 1);//_ClipRect各方向的取值范围是0~1。
material.SetVector("_ClipRect", clipRect);

比如_ClipRect,这个一般在遮罩功能中使用,原来的赋值是在Image中进行,在脱离Image的情况下,需要手动对_ClipRect进行赋值,才能保证显示正常