unity3D AR涂涂乐制作浅谈

2016-05-26 来源: chenzhao 发布在  https://www.cnblogs.com/czaoth/p/5530459.html

unity3D AR涂涂乐制作浅谈

AR为现在是虚拟现实较为火爆的一个技术,其中有个比较炫酷的就是AR涂涂乐的玩法,这个技术可以把扫描到的图片上的纹理 粘贴到模型上实现为模型上色的功能,但是我们需要怎么才能实现其功能呢?大体的方法是将扫描到图片保存成纹理,在将纹理保存到模型的材质球上然后实现上色的功能。

那么有什么方式可以实现这个功能呢?我在参考的EsayAR的Demo以及在网上查找的方法基本都是采用通过Shader进行图片的处理,因此在这样的条件下,若有多个模型的UV张开图就要写不同的Shader进行进行图片的处理,这样的方式并不方便。那么有没有方法可以不写Shader来实现图片上UV展开图的颜色准确粘贴到模型上呢?于是我想为什么不可以在屏幕上设置一个扫描对准框,然后将扫描框的的内容保存成纹理呢?

那么我先配置好AR的环境,这里我用EsayAR来制作AR涂涂乐的效果,这里为了方便我直接用EsayAR Coloring3D的例子来做这个效果吧!我们先去创建一个空的Gameobject将EsayAR例子中的EasyImageTargetBehaviour类拖入空的GameObject里然后我们将其改名为ImageTargetNamecard,设置好我们的识别图片红色框是图片名字,黄色框是图片放置的位置,记得要Storage属性要设置成Assets,如下图所示:

然后将模型拖入到刚刚创建的ImageTargetNamecard下这样我们就做好了识别图以及模型,摆放的模型要注意的是这个模型必须是要有纹理展开图的模型,然后作为模型的识别图的图片必须对是对应着其纹理的展开的图片,例子里面我是使用一个展开过纹理的Cube模型,其中色块的位置正是UV展开的位置,展开的纹理图片如图下图所示:

现在弄好了AR环境了,那么我们开始做扫描用的对准框吧,这里我用的是UGUI来制作对准框,这里我给对焦框设定好大小,因为我使用的识别图片是1024*1024的所以我们的对焦框也要弄成正方形的520*520就可以了,然后在弄一个按钮在对准后帮模型“上色”!做好的效果大体就如下图所示一样:

那么开始写代码吧!这里我们在模型上添加一个InterceptTexture脚本,脚本内容如下:

using System;
using UnityEngine;
using System.Collections;
using System.IO;
using EasyAR;
using Image = UnityEngine.UI.Image;

public class InterceptTexture : MonoBehaviour {

    public Image scanTexture;
    public bool isrealRender = false;
    private Camera scanCamera;
    private RenderTexture renderTexture;
    private string pipingID;
    private string scanPath;
    private Rect scanRect;
    private bool isScanTexture = false;//是否开启实时渲染
    //ImageTargetBaseBehaviour targetBehaviour;

    // Use this for initialization
    void Start()
    {
        scanPath = Application.dataPath + "/StreamingAssets/Drwaing/";
        scanRect = new Rect(scanTexture.rectTransform.position.x - scanTexture.rectTransform.rect.width / 2, scanTexture.rectTransform.position.y - scanTexture.rectTransform.rect.height / 2,
           (int)scanTexture.rectTransform.rect.width, (int)scanTexture.rectTransform.rect.height);
        //targetBehaviour = GetComponentInParent<ImageTargetBaseBehaviour>();
        gameObject.layer = 31;

    }

    void Renderprepare()
    {
        if (!scanCamera)
        {
            GameObject obj = new GameObject("ScanCamera");
            scanCamera = obj.AddComponent<Camera>();
            obj.transform.parent = transform.parent;
            scanCamera.hideFlags = HideFlags.HideAndDontSave;
        }
        scanCamera.CopyFrom(Camera.main);
        scanCamera.depth = 0;
        scanCamera.cullingMask = 31;
        if (!renderTexture)
        {
            renderTexture = new RenderTexture(Screen.width, Screen.height, -50);
        }
        if (!isScanTexture)
        {
            scanCamera.targetTexture = renderTexture;
            scanCamera.Render();
        }
        if(isrealRender)
            GetComponent<Renderer>().material.SetTexture("_MainTex", renderTexture);
        //RenderTexture.active = renderTexture;
        //StartCoroutine(ImageCreate());

    }

    void OnWillRenderObject()
    {
        Renderprepare();
    }

    void OnDestroy()
    {
        if (renderTexture)
            DestroyImmediate(renderTexture);
        if (scanCamera)
            DestroyImmediate(scanCamera.gameObject);
    }

    public void ScanTextureClick()
    {
        StartCoroutine(ImageCreate());
    }

    IEnumerator ImageCreate()
    {
        isScanTexture = true;
        if (isScanTexture)
        {
            scanCamera.targetTexture = renderTexture;
            scanCamera.Render();
        }
        RenderTexture.active = renderTexture;
        Texture2D scantTexture2D = new Texture2D((int)scanRect.width, (int)scanRect.height, TextureFormat.RGB24, false);
        yield return new WaitForEndOfFrame();
        scantTexture2D.ReadPixels(scanRect, 0, 0, false);
        scantTexture2D.Apply();

        scanCamera.targetTexture = null;
        RenderTexture.active = null;
        GameObject.Destroy(renderTexture);

        byte[] bytes = scantTexture2D.EncodeToPNG();
        string savePath = scanPath + gameObject.name + ".png";
        File.WriteAllBytes(savePath,bytes);
        isScanTexture = false;
        isrealRender = false;        ///关闭实时渲染
        this.gameObject.GetComponent<Renderer>().material.SetTexture("_MainTex", scantTexture2D);
        Debug.Log("截图完成!");
    }
}

这里使用的UGUI来制作的扫描框,因此如果用NGUI的同学要自己改一下代码哟!因为公司极却摄像头,我就不做实时演示的截图了

通过对准框截图下来的图片如下:

然后看看我们的模型没贴纹理之前的样子,如下图所示:

然后是先附上贴非裁剪的正常纹理图,第二张是我们截图下来的做纹理的图片:

因为测试的时候用手机打开识别图,导致颜色有点变了,但是大体上位置都没有错!好了测试成功了~~~运行测试的时候千万被手抖哟!不如就没办法完美的对好UV位置!~~

因为是刚刚开始接触AR不久,所以可能做的不好望大家可以多多交流~第一次在公司写技术文章,写的不好请多多包含~

--Bě9oniǎ

这里还有现在普遍的shader上色方法...

相关文章