Unity3D相机

Posted little_fat_sheep

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D相机相关的知识,希望对你有一定的参考价值。

1 简介

        相机用于渲染游戏对象,每个场景中可以有多个相机,每个相机独立成像,每个成像都是一个图层,最后渲染的图层在最前面显示。

        相机的属性面板如下:

  • Clear Flags:设置清屏颜色,Skybox(天空盒)、Solid Color(纯色)、Depth Only(仅深度,画中画效果)、Don't Clear(不渲染);
  • Background:清屏颜色,当 Clear Flags 为 Skybox 或 Solid Color 时,才有 Background;
  • Culling Mask:剔除遮蔽,选择相机需要绘制的对象层;
  • Projection:投影方式,Perspective(透视投影)、Orthographic(正交投影);
  • Field of View:相机视野范围,默认 60°,视野范围越大,能看到的物体越多,看到的物体越小;
  • Clipping Planes:裁剪平面,视锥体中近平面和远平面的位置;
  • Viewport Rect:视口在屏幕中的位置和大小(相对值),以屏幕左下角为原点;
  • Depth:渲染图层深度,用于设置相机渲染图层的显示顺序;
  • Rendering Path:渲染路径,用于调整渲染性能(可以在 Stats 里查看性能)
  • Target Texture:将相机画面存储到一个纹理图片(RenderTexture)中。

        补充:基于 Target Texture 属性,可以使用 RawImage 显示次相机渲染的 RenderTexture,实现带主题边框的小地图、八倍镜等效果。 

2 应用

        本节将通过实现小地图案例展示相机的应用。

        1)游戏界面

        2)游戏对象层级结构

        3)Transform 组件参数

        1. 相机 Transform 组件参数

NameTypePositionRotationScaleViewport Rect
Main CameraCamera(0, 4, -5)(38, 0, 0)(1, 1, 1)(0, 0, 1, 1)
MinimapCameraCamera(0, 12, 0)(90, 0, 0)(1, 1, 1)(0.8, 0.7, 0.2, 0.3)

        2. 玩家 Transform 组件参数

NameTypePositionRotationScaleColor/Texture
PlayerEmpty(0, 0.25, -5)(0, 0, 0)(1, 1, 1)#228439FF
BottonCube(0, 0, 0)(0, 0, 0)(2, 0.5, 2)#228439FF
TopCube(0, 0.5, 0)(0, 0, 0)(1, 0.5, 1)#228439FF
GunCylinder(0, 0, 1.5)(90, 0, 0)(0.2, 1, 0.4)#228439FF
FirePointEmpty(0, 1.15, 0)(0, 0, 0)(1, 1, 1)——

        补充:Player 游戏对象添加了刚体组件。 

        3. 敌人 Transform 组件参数

NameTypePositionRotationScaleColor/Texture
Enemy1Empty(7, 0.25, 1)(0, 120, 0)(1, 1, 1)#F83333FF
Enemy2Enemy(-1, 0.25, 5)(0, 60, 0)(1, 1, 1)#EA9132FF
Enemy3Enemy(-5, 0.25, -1)(0, -40, 0)(1, 1, 1)#5DC2F4FF

        说明:Enemy1~Enemy3 都是由 Player copy 而来。

        4. 地面和炮弹 Transform 组件参数

NameTypePositionRotationScaleColor/Texture
PlanePlane(0, 0, 0)(0, 0, 0)(10, 10, 10)GrassRockyAlbedo
BulletSphere(0, 0.5, -5)(0, 0, 0)(0.3, 0.3, 0.3)#228439FF

        补充:炮弹作为预设体拖拽到 Assets/Resources/Prefabs 目录下,并且添加了刚体组件。

        4)脚本组件

        MainCameraController.cs

using UnityEngine;
 
public class MainCameraController : MonoBehaviour 
	private Transform player; // 玩家
	private Vector3 relaPlayerPos; // 相机在玩家坐标系中的位置
	private float targetDistance = 15f; // 相机看向玩家前方的位置
 
	private void Start() 
		relaPlayerPos = new Vector3(0, 4, -8);
		player = GameObject.Find("Player/Top").transform;
	
 
	private void LateUpdate() 
		CompCameraPos();
	
 
	private void CompCameraPos()  // 计算相机坐标
		Vector3 target = player.position + player.forward * targetDistance;
		transform.position = transformVecter(relaPlayerPos, player.position, player.right, player.up, player.forward);
		transform.rotation = Quaternion.LookRotation(target - transform.position);
	
 
	// 求以origin为原点, locX, locY, locZ 为坐标轴的本地坐标系中的向量 vec 在世界坐标系中对应的向量
	private Vector3 transformVecter(Vector3 vec, Vector3 origin, Vector3 locX,  Vector3 locY,  Vector3 locZ) 
		return vec.x * locX + vec.y * locY + vec.z * locZ + origin;
	

        说明: CameraController 脚本组件挂在 MainCamera 游戏对象上。 

        MinmapCameraController.cs

using UnityEngine;

public class MinimapController : MonoBehaviour 
	private Transform player; // 玩家
	private float height = 12; // 相机离地面的高度
	private bool isFullscreen = false; // 小地图相机是否全屏
	private Rect minViewport; // 小地图视口位置和大小(相对值)
	private Rect FullViewport; // 全屏时视口位置和大小(相对值)
 
	private void Start() 
		player = GameObject.Find("Player/Top").transform;
		minViewport = GetComponent<Camera>().rect;
		FullViewport = new Rect(0, 0, 1, 1);
	

	private void Update() 
		if (Input.GetMouseButtonDown(0) && IsClickMinimap()) 
			if (isFullscreen) 
				GetComponent<Camera>().rect = minViewport;
			 else 
				GetComponent<Camera>().rect = FullViewport;
			
			isFullscreen = !isFullscreen;
		
	
 
	private void LateUpdate() 
		Vector3 pos = player.position;
		transform.position = new Vector3(pos.x, height, pos.z);
	

	public bool IsClickMinimap()  // 是否单击到小地图区域
		Vector3 pos = Input.mousePosition;
		if (isFullscreen) 
			return true;
		
		if (pos.x / Screen.width > minViewport.xMin && pos.y / Screen.height > minViewport.yMin) 
			return true;
		
		return false;
	

        说明: MinimapController 脚本组件挂在 MinimapCamera 游戏对象上。 

        PlayerController.cs

using UnityEngine;

public class PlayerController : MonoBehaviour 
	private MinimapController minimapController; // 小地图控制器
	private Transform firePoint; // 开火点
	private GameObject bulletPrefab; // 炮弹预设体
	private float tankMoveSpeed = 4f; // 坦克移动速度
	private float tankRotateSpeed = 2f; // 坦克转向速度
	private Vector3 predownMousePoint; // 鼠标按下时的位置
	private Vector3 currMousePoint; // 当前鼠标位置
	private float fireWaitTime = float.MaxValue; // 距离上次开火已等待的时间
	private float bulletCoolTime = 0.15f; // 炮弹冷却时间
 
 	private void Start() 
		minimapController = GameObject.Find("MinimapCamera").GetComponent<MinimapController>();
		firePoint = transform.Find("Top/Gun/FirePoint");
		bulletPrefab = (GameObject) Resources.Load("Prefabs/Bullet");
	
 
	private void Update() 
		Move();
		Rotate();
		Fire();
	
 
	private void Move()  // 坦克移动
		float hor = Input.GetAxis("Horizontal");
        float ver = Input.GetAxis("Vertical");
		if (Mathf.Abs(hor) > float.Epsilon || Mathf.Abs(ver) > float.Epsilon) 
			Vector3 vec = transform.forward * ver + transform.right * hor;
			GetComponent<Rigidbody>().velocity = vec * tankMoveSpeed;
			Vector3 dire = new Vector3(hor, ver, 0);
			dire =  dire.normalized * Mathf.Min(dire.magnitude, 1);
		
	
 
	private void Rotate()  // 坦克旋转
		if (Input.GetMouseButtonDown(1)) 
			predownMousePoint = Input.mousePosition;
		 else if (Input.GetMouseButton(1)) 
			currMousePoint = Input.mousePosition;
			Vector3 vec = currMousePoint - predownMousePoint;
			GetComponent<Rigidbody>().angularVelocity = Vector3.up * tankRotateSpeed * vec.x;
			predownMousePoint = currMousePoint;
		
	
 
	private void Fire()  // 坦克开炮
		fireWaitTime += Time.deltaTime;
		if (Input.GetMouseButtonDown(0) && !minimapController.IsClickMinimap() || Input.GetKeyDown(KeyCode.Space)) 
			if (fireWaitTime > bulletCoolTime) 
				BulletInfo bulletInfo = new BulletInfo("PlayerBullet", Color.red, transform.forward, 10f, 15f);
				GameObject bullet = Instantiate(bulletPrefab, firePoint.position, Quaternion.identity);
				bullet.AddComponent<BulletController>().SetBulletInfo(bulletInfo);
				fireWaitTime = 0f;
			
		
	

        说明: PlayerController 脚本组件挂在 Player 游戏对象上。  

        BulletController.cs

using UnityEngine;
using UnityEngine.UI;

public class BulletController : MonoBehaviour 
	private BulletInfo bulletInfo; // 炮弹信息

	private void Start () 
		gameObject.name = bulletInfo.name;
		GetComponent<MeshRenderer>().material.color = bulletInfo.color;
		float lifeTime = bulletInfo.fireRange / bulletInfo.speed; // 存活时间
		Destroy(gameObject, lifeTime);
	

	private void Update () 
		transform.GetComponent<Rigidbody>().velocity = bulletInfo.flyDir * bulletInfo.speed;
	

	public void SetBulletInfo(BulletInfo bulletInfo) 
		this.bulletInfo = bulletInfo;
	

        说明:BulletController 脚本组件挂在 Bullet 游戏对象上(代码里动态添加)。   

        BulletInfo.cs

using UnityEngine;

public class BulletInfo 
	public string name; // 炮弹名
	public Color color; // 炮弹颜色
	public Vector3 flyDir; // 炮弹飞出方向
	public float speed; // 炮弹飞行速度
	public float fireRange; // 炮弹射程

	public BulletInfo(string name, Color color, Vector3 flyDir, float speed, float fireRange) 
		this.name = name;
		this.color = color;
		this.flyDir = flyDir;
		this.speed = speed;
		this.fireRange = fireRange;
	

        5)运行效果

计算机视觉——相机内外参、相机标定

参考技术A 一直在做图像处理,也经常听到相机内参相机外参,我却没深入理解什么是相机内外参,什么是相机标定。

1、 相机内参数 是与相机自身特性相关的参数,比如相机的焦距、像素大小等;

摄像机 内参矩阵 反应了相机自身的属性,各个相机是不一样的,需要标定才能知道这些参数。作用:告诉我们摄像机坐标的点在1的基础上,是如何继续经过摄像机的镜头、并通过针孔成像和电子转化而成为像素点的。

摄像机内参 (Camera Intrinsics) 矩阵:(需要注意的是,真实的镜头还会有径向和切向畸变,而这些畸变是属于相机的内参的)

           fx   s    x0

    K =  0    fy   y0

            0   0    1

其中,fx,fy为焦距,一般情况下,二者相等,x0、y0为主点坐标(相对于成像平面), s为坐标轴倾斜参数,理想情况下为0 。

内参矩阵的参数含义:

f:焦距,单位毫米

dx:像素x方向宽度,单位毫米,1/dx:x方向1毫米内有多少个像素(dx、dy为一个像素的长和高)

f/dx:使用像素来描述x轴方向焦距的长度

f/dy:使用像素来描述y轴方向焦距的长度

u0,v0,主点的实际位置,单位也是像素(原点的平移量)

2、 相机外参数 是在世界坐标系中的参数,比如相机的位置、旋转方向等。相比于不变的内参,外参会随着相机运动发生改变。

摄像机的旋转平移属于外参,用于描述相机在静态场景下相机的运动,或者在相机固定时,运动物体的刚性运动。因此, 在图像拼接或者三维重建中,就需要使用外参来求几幅图像之间的相对运动,从而将其注册到同一个坐标系下面来 。(最近我在研究多幅图像的图像拼接)

摄像机 外参矩阵 :包括旋转矩阵和平移矩阵。作用:告诉我们现实世界点(世界坐标)是怎样经过旋转和平移,然后落到另一个现实世界点(摄像机坐标)上。

旋转矩阵和平移矩阵共同描述了如何把点从世界坐标系转换到摄像机坐标系。

旋转矩阵 :描述了世界坐标系的坐标轴相对于摄像机坐标轴的方向

平移矩阵:描述了在摄像机坐标系下,空间原点的位置

摄像机 外参(Camera Extrinsics) 矩阵:

其中, R是旋转矩阵,t是平移向量.

3、相机标定(或摄像机标定):一句话就是世界坐标到像素坐标的映射,其中世界坐标是人为定义的。

相机标定的目的是确定相机的一些参数的值。通常,这些参数可以建立定标板确定的三维坐标系和相机图像坐标系的映射关系,换句话说, 你可以用这些参数把一个三维空间中的点映射到图像空间,或者反过来。相机需要标定的参数通常分为内参和外参两部分。

标定就是已知标定控制点的世界坐标和像素坐标我们去解算这个映射关系,一旦这个关系解算出来了我们就可以由点的像素坐标去反推它的世界坐标,当然有了这个世界坐标,我们就可以进行测量等其他后续操作了。上述标定又被称作隐参数标定,因为它没有单独求出相机的内部参数,如相机焦距,相机畸变系数等。

一般来说如果仅仅只是利用相机标定来进行一些比较简单的视觉测量的话,那么就没有必要单独标定出相机的内部参数了。至于相机内部参数如何解算,相关论文讲的很多。

在图像测量过程以及机器视觉应用中,为确定空间物体表面某点的三维几何位置与其在图像中对应点之间的相互关系,必须建立相机成像的几何模型,这些几何模型参数就是相机参数。在大多数条件下这些参数必须通过实验与计算才能得到,这个求解参数的过程就称之为相机标定(或摄像机标定)。

无论是在图像测量或者机器视觉应用中,相机参数的标定都是非常关键的环节,其标定结果的精度及算法的稳定性直接影响相机工作产生结果的准确性。因此,做好相机标定是做好后续工作的前提,提高标定精度是科研工作的重点所在。

4、畸变(distortion)是对直线投影(rectilinear projection)的一种偏移。简单来说直线投影是场景内的一条直线投影到图片上也保持为一条直线。畸变简单来说就是一条直线投影到图片上不能保持为一条直线了,这是一种光学畸变(optical aberration),可能由于摄像机镜头的原因。

     畸变矩阵:告诉我们为什么像素点并没有落在理论计算该落在的位置上,还产生了一定的偏移和变形!!!

参考链接:

计算机视觉-相机内参数和外参数_liulina603的专栏-CSDN博客_相机参数

https://blog.csdn.net/paulkg12/article/details/121393602

https://blog.csdn.net/weixin_40787463/article/details/105785593

https://blog.csdn.net/guyuealian/article/details/102502213

https://blog.csdn.net/weixin_41480034/article/details/121759128

旋转矩阵(Rotate Matrix)的性质分析 - caster99 - 博客园 (cnblogs.com)

SLAM入门之视觉里程计(2):相机模型(内参数,外参数) - Brook_icv - 博客园 (cnblogs.com)

以上是关于Unity3D相机的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D c# 2D Game Camera Follow 正在跳跃

[Unity] 在Unity中实现小地图(Minimap)

unity3d (2d!) - 相机以玩家为中心,但永远不会超过“地图”边界

微信小程序 camera 系统相机 组件

如何在unity3D中平滑移动相机?

小地图制作