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上色方法...
