项目起因:
前段时间自己在做的一个项目中需要实现一个类似AR的相机实现。举例来说就是App中打开照相机,然后屏幕中显示相机背景然后再显示一个模型,但是模型的世界坐标是不会变化的,不会随着手机的转动而跟着转动、
1.相机实现原理:
- 获取手机的姿态(陀螺仪)
- 实例化3D模型且固定
- 根据陀螺仪修改场景内相机的位置,姿态
2.ARCamera的结构:
;
3.代码实现:
ARCamera上的Cs脚本:
using UnityEngine;
using System.Collections;
public class CameraTrack : MonoBehaviour {
//PC上模拟陀螺仪的输入值
public Vector3 debugGro = new Vector3(0f,0f,0f);
public bool isDebug = false;
//用来定位的模型
public GameObject model;
// Use this for initialization
void Start () {
//陀螺仪的设置
Input.gyro.enabled = true;
Input.compensateSensors = true;
Input.gyro.updateInterval = 0.01f;
}
// Update is called once per frame
void Update () {
//根据陀螺仪返回的数据设置Camera的姿态。
gameObject.transform.localRotation = (isDebug ? Quaternion.Euler (debugGro) : Input.gyro.attitude)* new Quaternion(0,0,1,0);
//如果第一次启动就对定位的模型进行赋值,设置其在屏幕的中央。(此处为我的项目需要用到的,可以忽略。)
if(model.GetComponent().enabled == false){
StartCoroutine (ScriptTrue ());
}
}
IEnumerator ScriptTrue(){
yield return new WaitForSeconds (0.3f);
model.GetComponent ().enabled = true;
}
}
Quad上的Cs脚本。
using UnityEngine;
using System.Collections;
public class CameraSetting : MonoBehaviour {
public int Camertype;
public int CamerFPS;
public int CamerWidth;
public int CamerHeight;
private WebCamTexture t;
private WebCamDevice[] mWebCamDevices;
private bool isDebug;
private bool isNeedSetCamera = true;
// Use this for initialization
void Start () {
isDebug = gameObject.GetComponentInParent().isDebug;
StartCoroutine (initWebCamera());
}
IEnumerator initWebCamera(){
//权限判断,之前这里的写法没注意导致第一次进入会黑屏(iOS)
yield return Application.RequestUserAuthorization (UserAuthorization.WebCam);
if (Application.HasUserAuthorization(UserAuthorization.WebCam)){
//拿手机上的相机
mWebCamDevices = WebCamTexture.devices;
//CameraType 0后置。根据不同进行判断
string name = mWebCamDevices [Camertype].name;
t = new WebCamTexture (name);
//这里设置的分辨率都不是最终的,最终获取到的分辨率是根据相机自己的参数来的,它会给你匹配一个最接近你设置的。
t.requestedFPS = CamerFPS;
t.requestedHeight = CamerHeight;
t.requestedWidth = CamerWidth;
//设置Quad的贴图为相机拍摄到的。
GetComponent ().material.mainTexture = t;
t.Play ();
//下面一句必须放在Play后面,否则t.width等获得不到数据。
//旋转画布到正的位置,这里要特别注意,有些时候会出现画面横过来或者倒了。
gameObject.transform.localRotation = gameObject.transform.localRotation
* Quaternion.AngleAxis(t.videoRotationAngle,Vector3.forward);
}
}
// Update is called once per frame
void Update () {
//设置Quad画布的大小,如果写在play后面会导致部分时候获取的数据为1。应该是相机还没打开真正打开就开始获取了,所以数据有误,所以放在了这里。
if (isNeedSetCamera && t!=null && t.width >200 && t.height >200){
//判断是否翻转。
gameObject.transform.localScale = new Vector3(t.width*-1, t.height*(t.videoVerticallyMirrored?1:-1), 1f);
//计算场景内相机的视域(重要)
gameObject.GetComponentInParent ().fieldOfView =
Mathf.Atan(t.height / 2f / gameObject.transform.localPosition.z) * Mathf.Rad2Deg * 2-1;
isNeedSetCamera = (t.width <200 && t.height <200);
}
}
public void StopWebCamera(){
if (t != null) {
t.Stop ();
}
}
public void PauseWebCamera(){
if (t != null && t.isPlaying) {
t.Pause ();
}
}
public void StartWebCamera(){
if (t != null) {
t.Play ();
isNeedSetCamera = true;
}
}
}
4.关键语句(部分伪代码):
设置相机为陀螺仪姿态:
Input.gyro.attitude * new Quaternion(0,0,1,0);
画布旋转到摄像机的正的位置:
localRotation = localRotation * Quaternion.AngleAxis(t.videoRotationAngle,Vector3.forward);
判断是否翻转:
localScale = new Vector3(t.width*-1,t.height*(t.videoVerticallyMirrored?1:-1), 1f);
计算视域
Camera.fieldOfView = Mathf.Atan(t.height / 2f / Vector3.Distance(Camera.position,Quad.position)) * Mathf.Rad2Deg * 2;
4.总结:
代码都在上面,有了注释就不在进行分析了。相机的使用这块爬了不少的坑,主要就是在旋转上,对其旋转之类的坐标不是很了解,所以花了挺多时间。