相机截图(区域截图、长图、大图)

8 篇文章 1 订阅

        单独创建一个相机和画布实现截取一部分图,或者长图,传入的obj需含有“RectTransform”便于获取所截区域的大小,即obj的尺寸就是截图的尺寸。

        我传入的是Content对象,组件有ContentSizeFitter,因为适应宽高刷新不及时,如果不调用延时的话,可能会报错“Reading pixels out of bounds of the current active render texture”,如果没有该组件可以不用延时。

        在设置相机画布尺寸后延时DelayCaptureTexture渲染,为了适配好宽高,不然渲染出的是很小的图,是因为画布默认的宽高是窗口的大小,截出的有效区很小。

        相机清除时_camera.clearFlags = CameraClearFlags.Depth在真机上截图效果花屏,应改为CameraClearFlags.SolidColor。

    private Canvas _canvas;
    private Camera _camera;
    private RenderTexture _renderTexture;
    private RectTransform _rectTransformObj;
    private GameObject _objClone;
    private Texture2D _texture2D;


    public void DoScreenShot(GameObject obj)
    {
        CreateCanvas();
        DoCloneGameObject(obj);
    }
    //创建画布 画布的尺寸随相机视口大小
    private void CreateCanvas()
    {
        if (_canvas == null)
        {
            _canvas = new GameObject("ScreenShotCanvas").AddComponent<Canvas>();
            _canvas.renderMode = RenderMode.ScreenSpaceCamera;
            CreateCamera();
            _canvas.worldCamera = _camera;
            _canvas.gameObject.GetOrAddComponent<CanvasScaler>();
            _canvas.gameObject.GetOrAddComponent<GraphicRaycaster>();
        }
    }
    //创建相机
    private void CreateCamera()
    {
        _camera = new GameObject("ScreenShotCamera").AddComponent<Camera>();
        _camera.transform.localPosition = new Vector3(30, 0, -10);
        _camera.transform.localScale = Vector3.one;
        //_camera.clearFlags = CameraClearFlags.Depth;
        _camera.clearFlags = CameraClearFlags.SolidColor;
        _camera.backgroundColor = new Color32(0, 0, 0, 0);
        _camera.cullingMask = -1;
        _camera.orthographic = true;
        _camera.orthographicSize = 1.5f;
        _camera.depth = -2;
    }
    //克隆需要截图的对象
    private void DoCloneGameObject(GameObject obj)
    {
        _objClone = GameObject.Instantiate(obj) as GameObject;
        _rectTransformObj = _objClone.GetComponent<RectTransform>();
        _objClone.transform.SetParent(_canvas.transform);
        _rectTransformObj.pivot = new Vector2(0.5f, 0.5f);
        _rectTransformObj.anchorMax = new Vector2(0.5f, 0.5f);
        _rectTransformObj.anchorMin = new Vector2(0.5f, 0.5f);
        _rectTransformObj.sizeDelta = obj.GetComponent<RectTransform>().rect.size;
        _objClone.transform.localPosition = Vector3.zero;
        _objClone.transform.localScale = Vector3.one;
        StartCoroutine(DelaySetSize());
    }
    //延时设置_renderTexture的尺寸,相机的尺寸随_renderTexture的尺寸
     private IEnumerator DelaySetRenderTextureSize()
    {
        yield return new WaitForEndOfFrame();
        _renderTexture = new RenderTexture((int)_rectTransformObj.rect.width, (int)_rectTransformObj.rect.height, 24,
            RenderTextureFormat.ARGB32);
        _camera.targetTexture = _renderTexture;
        StartCoroutine(DelayCaptureTexture());
    }

    private IEnumerator DelayCaptureTexture()
    {
        yield return new WaitForEndOfFrame();

        _camera.RenderDontRestore();
        _renderTexture.filterMode = FilterMode.Bilinear;
        _camera.Render();

        _texture2D = new Texture2D((int)_rectTransformObj.rect.width, (int)_rectTransformObj.rect.height,
            TextureFormat.ARGB32, false);
        _texture2D.ReadPixels(new Rect(0, 0, _rectTransformObj.rect.width, _rectTransformObj.rect.height), 0, 0);
        _texture2D.Apply();

        ScreenShotImage();

        _camera.targetTexture = null;
        GameObject.Destroy(_objClone);
    }
    //生成图片并保存
    private void ScreenShotImage()
    {
        byte[] bytes = _texture2D.EncodeToPNG();
        _texture2D.Compress(true);
        _texture2D.Apply();

        string fileDir = Application.dataPath  + "/ScreenShot";
        if (!Directory.Exists(fileDir))
        {
            Directory.CreateDirectory(fileDir);
        }

        string filePath = fileDir + "/ScreenShotImage.png";
        File.WriteAllBytes(filePath, bytes);
        Debug.Log("截取了一张图片:"+ filePath);
    }

        上面使用的方法是基于画布的Canvas Scaler组件的缩放模式(UI Scale Mode)为Constant Pixel Size画布尺寸同像素大小及Camera的Target Texture。这种方法有个问题的,当传入的obj是关于文字排版时,克隆出的对象和主相机中的对象排版不一,尤其是文字下有下划线时更明显,其他情况下都是OK的。

        后面使用的截图方法缩放模式是Scale With Screen Size。

        截图读取像素从左下角开始,克隆对象轴点和锚点设置为0,代码段如下:

        _rectTransformObj.pivot = Vector2.zero;
        _rectTransformObj.anchorMax = Vector2.zero;
        _rectTransformObj.anchorMin = Vector2.zero;
        _rectTransformObj.sizeDelta = _cloneObjSize;
        _rectTransformObj.anchoredPosition3D = Vector3.zero;
        _objClone.transform.localScale = Vector3.one;
        _objClone.transform.localEulerAngles = Vector3.zero;
private Texture2D GetScreenTexture(Camera _camera,RectTransform _rectTransformObj)
    {
        RenderTexture rt = RenderTexture.GetTemporary((int)(_canvasSize.x), (int)(_canvasSize.y));
        _camera.targetTexture = rt;
        _camera.Render();

        RenderTexture.active = rt;
        Texture2D _texture2D;

        _texture2D = new Texture2D((int)(_rectTransformObj.rect.width), (int)(_rectTransformObj.rect.height/0.95f), TextureFormat.ARGB32, false);
        _texture2D.ReadPixels(new Rect(0, 0f, (int)(_rectTransformObj.rect.width), (int)(_rectTransformObj.rect.height/0.95f)), 0, 0);
        _texture2D.Apply();
        
        byte[] bytes = _texture2D.EncodeToPNG();
        _camera.targetTexture = null;
        RenderTexture.active = null; 
        RenderTexture.ReleaseTemporary(rt);

        string fileDir = Application.dataPath  + "/ScreenShot";
        if (!Directory.Exists(fileDir))
        {
            Directory.CreateDirectory(fileDir);
        }

        string filePath = fileDir + "/ScreenShotImage.png";
        File.WriteAllBytes(filePath, bytes);
        Debug.Log("截取了一张图片:"+ filePath);
        return _texture2D;
    }

        上面这种是截取canvas内指定的区域的图片,如果obj尺寸大于canvas的宽或高则等比缩放到canvas内。这种方法有个问题是缩小比例越大,截图放大后就失真越多越模糊。

private void CaptureTexture(int surplusWidth, int surplusHeight, int addX = 0, int addY = 0)
    {
        
        Texture2Ds texture2Ds = new Texture2Ds();
        texture2Ds._X = addX;
        texture2Ds._Y = addY;
        if (surplusWidth <= (int)(_canvasSize.x))
        {
            if (surplusHeight <= (int)(_canvasSize.y))
            {
                texture2Ds._Texture2D = DoCaptureTexture(surplusWidth, surplusHeight);
                _listTexture2D.Add(texture2Ds);
            }
            else
            {
                texture2Ds._Texture2D = DoCaptureTexture(surplusWidth, (int)(_canvasSize.y));
                _listTexture2D.Add(texture2Ds);
                
                _rectTransformObj.anchoredPosition3D = new Vector3(_rectTransformObj.anchoredPosition3D.x,
                _rectTransformObj.anchoredPosition3D.y - (int)(_canvasSize.y), 0);
                CaptureTexture(surplusWidth, surplusHeight - (int)(_canvasSize.y), addX, addY + (int)(_canvasSize.y));
            }
        }
        else if (surplusWidth > (int)(_canvasSize.x))
        {
            if (surplusHeight <= (int)(_canvasSize.y))
            {
                texture2Ds._Texture2D = DoCaptureTexture((int)(_canvasSize.x), surplusHeight);
                _listTexture2D.Add(texture2Ds);
                
                _rectTransformObj.anchoredPosition3D = new Vector3(_rectTransformObj.anchoredPosition3D.x-(int)(_canvasSize.x),
                    _rectTransformObj.anchoredPosition3D.y, 0);
                CaptureTexture(surplusWidth-(int)(_canvasSize.x), surplusHeight, addX+(int)(_canvasSize.x), addY );
            }
            else
            {
                texture2Ds._Texture2D = DoCaptureTexture((int)(_canvasSize.x), (int)(_canvasSize.y));
                _listTexture2D.Add(texture2Ds);
                
                _rectTransformObj.anchoredPosition3D = new Vector3(_rectTransformObj.anchoredPosition3D.x-(int)(_canvasSize.x),
                    _rectTransformObj.anchoredPosition3D.y - (int)(_canvasSize.y), 0);
                CaptureTexture(surplusWidth-(int)(_canvasSize.x), surplusHeight - (int)(_canvasSize.y), addX+(int)(_canvasSize.x), addY + (int)(_canvasSize.y));
            }
        }
        
        
    }

    private Texture2D DoCaptureTexture(int width,int height)
    {
        RenderTexture rt = RenderTexture.GetTemporary((int)_canvasSize.x,(int)_canvasSize.y);
        _camera.targetTexture = rt;
        _camera.Render();
        RenderTexture.active = rt;
        
        Texture2D texture2D = new Texture2D(width, height, TextureFormat.ARGB32, false);
        texture2D.ReadPixels(new Rect(0, 0f, width, height), 0, 0);
        texture2D.Apply();
        
        _camera.targetTexture = null;
        RenderTexture.active = null;
        RenderTexture.ReleaseTemporary(rt);
        
        return texture2D;
    }

    //拼图
    private void MergeTexture2D()
    {
        _texture2D = new Texture2D((int)_cloneObjSize.x, (int)_cloneObjSize.y);

        int w = 0;
        int h = 0;
        for (int i = 0; i < _listTexture2D.Count; i++)
        {
            //取图
            Color32[] color = _listTexture2D[i]._Texture2D.GetPixels32(0);
            //赋给新图
            if (i > 0)
            {
                if (_listTexture2D[i]._X > _listTexture2D[i - 1]._X)
                    _texture2D.SetPixels32(w += _listTexture2D[i - 1]._Texture2D.width, h,
                        _listTexture2D[i]._Texture2D.width, _listTexture2D[i]._Texture2D.height, color); //宽度
                else
                    _texture2D.SetPixels32(w, h += _listTexture2D[i - 1]._Texture2D.height,
                        _listTexture2D[i]._Texture2D.width, _listTexture2D[i]._Texture2D.height, color); //高度
            }
            else
            {
                _texture2D.SetPixels32(w, h, _listTexture2D[i]._Texture2D.width, _listTexture2D[i]._Texture2D.height, color);
            }
        }
        //应用
        _texture2D.Apply();

        byte[] bytes = _texture2D.EncodeToPNG();

        string fileDir = Application.dataPath  + "/ScreenShot";
        if (!Directory.Exists(fileDir))
        {
            Directory.CreateDirectory(fileDir);
        }

        string filePath = fileDir + "/ScreenShotImage.png";
        File.WriteAllBytes(filePath, bytes);
        Debug.Log("截取了一张图片:"+ filePath);
    }

        调用方法CaptureTexture((int)_cloneObjSize.x, (int)_cloneObjSize.y);传入克隆对象的宽高,传入值必须是整型,因为像素是整型,画布尺寸也需是整型。在调用前先动态获取下_canvasSize的尺寸,如果在创建画布后就获取它的尺寸,还来不及适应,获取的值就不对,裁剪时尺寸会对不上,从而裁剪不完全。

        上面这种方法是不用缩放obj的,即使obj的尺寸远远大于画布尺寸,思路是先裁剪再拼图,这种就不会失真了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值