在说之前,先来说说涂涂乐,涂涂乐的原理也类似于获取摄像头的画面,并且通过shader设置捕捉的画面的某一部分为模型的贴图来实现的。
首先是shader部分:
Shader "Custom/CameraTargetPatch" {
Properties {
_MainTex("Texture", 2D) = "white" { }
}
SubShader{
Pass{
Cull Back
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4x4 _MATRIX_MVP;
float _yScale;
float _xScale;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(appdata_base v){
v2f o;
float4 uvTmp;
// calculate new uv in camera image
uvTmp = mul(_MATRIX_MVP, float4(v.texcoord.x-0.5f,v.texcoord.y-0.5f,0,1));
uvTmp.x = uvTmp.x/uvTmp.w;
uvTmp.y = uvTmp.y/uvTmp.w;
uvTmp.z = uvTmp.z/uvTmp.w;
// some swap for different coordinate system
uvTmp.x = (uvTmp.x + 1.0f)/2.0f;
uvTmp.y = (uvTmp.y + 1.0f)/2.0f;
o.uv = uvTmp.xy;
//The position of the vertex should not be frozen, so use
//the standard UNITY_MATRIX_MVP matrix for that.
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
half4 frag(v2f i) : COLOR{
half4 texcol = tex2D(_MainTex, i.uv);
return texcol;
}
ENDCG
}
}
}
然后是代码部分:
// 设置模型贴图
public void SetTexture() {
// 在ARCamera下新建TextureCamera来捕捉画面,参数要和arcamera保持一直
cameraObj = new GameObject("TextureCamera");
cameraObj.transform.SetParent(NewFarmSceneControl.Instance.arCameraTran);
cameraObj.transform.localPosition = Vector3.zero;
cameraObj.transform.localRotation = Quaternion.identity;
cameraObj.transform.localScale = Vector3.one;
Camera renderCamera = cameraObj.AddComponent();
renderCamera.fieldOfView = NewFarmSceneControl.Instance.ARCamera.fieldOfView;
renderCamera.clearFlags = CameraClearFlags.SolidColor;
renderCamera.farClipPlane = 2000.0f;
renderCamera.nearClipPlane = 0.05f;
//只开启ARBackground层(这里是为了让该摄像头只获取ARCamra下的Background所拍到的画面)
renderCamera.cullingMask = LayerMask.GetMask("ARBackground");
RenderTexture renderTexture = new RenderTexture(Screen.width / 2, Screen.height / 2, 0);
renderCamera.targetTexture = renderTexture;
GetComponentInChildren().material.mainTexture = renderTexture;
hasSet = true;
P = GL.GetGPUProjectionMatrix(renderCamera.projectionMatrix, false);
V = renderCamera.worldToCameraMatrix;
GetComponentInChildren().material.SetFloat("_xScale", 1.0f);
GetComponentInChildren().material.SetFloat("_yScale", 1.0f);
if (boxPosTran == null) { boxPosTran = transform.FindChild("BoxPos"); }
// 获取模型的本地到世界的矩阵变换
M = boxPosTran.localToWorldMatrix;
MVP = P * V * M;
// 设置MVP矩阵
GetComponentInChildren().material.SetMatrix("_MATRIX_MVP", MVP);
}
上面方法中有个hasSet布尔值,这里设置为true的目的是为了在Update函数中实时获取摄像头的画面,保证每帧都渲染相同的贴图,否则会出现获取到贴图后,摄像头晃动时模型的贴图发生变化。
所以当hasSet为true时,在Update中也需要重新再设置MVP矩阵
void Update () {
if (hasSet) {
P = GL.GetGPUProjectionMatrix(NewFarmSceneControl.Instance.ARCamera.projectionMatrix, false);
V = NewFarmSceneControl.Instance.ARCamera.worldToCameraMatrix;
GetComponentInChildren().material.SetFloat("_xScale", 1.0f);
GetComponentInChildren().material.SetFloat("_yScale", 1.0f);
M = boxPosTran.localToWorldMatrix;
MVP = P * V * M;
GetComponentInChildren().material.SetMatrix("_MATRIX_MVP", MVP);
}
}
最终效果如下图所示
哇哈哈,是不是很酷炫!
喜欢的话请点个赞哦~~