Cocos2d摄像机详解
Cocos2d摄像机详解
摄像机的作用
在3D系统中都会有一个摄像机的概念,物体在显示之前需要先将物体的坐标转换到视角坐标,也就是摄像机坐标,然后再投影,最后还需对投影画面进行缩放到视口显示的大小。Coscos2d-x 3.x支持3D的,所以其肯定会有摄像机。在Cocos中摄像机由类Camera实现,主要作用有3个:1、设置视口,2、将全局坐标转换到摄像机坐标中,3、投影。
设置视口
视口,就是显示的位置,如果是显示在屏幕上,视口则是指在屏幕上显示的矩形区域。设置视口作用是设置渲染结果最终的显示方式、显示位置以及显示大小。渲染结果可以显示在屏幕的窗口上,也可以写入帧缓存中,Camera类支持这两种方式。摄像机默认情况显示到窗口,可以通过函数Camera::setFrameBufferObject设置渲染到帧缓存。视口位置和大小决定可最后投影生成的图像显示的位置和大小,所有显示到窗口的摄像机都使用同一个视口static Viewport Camera::_defaultViewport,可以通过函数static Camera::setDefaultViewport设置,帧缓存的视口可以通过函数Camera::setViewport设置。
Cocos也支持场景多视口显示,可以显示一个物体的多个视角,例如同时显示物体的正面视图和俯视图。
摄像机坐标
物体投影显示之前需要转换到摄像机坐标,这个功能在Cocos中实现还是比较简单的。在Cocos中每个继承至Node的UI节点都有局部坐标系,摄像机坐标系本身也只是一个局部坐标系,所以Camera只需要继承Node节点就可以实现该功能。具体坐标转换解释和原理请参考:Cocos2d-x 坐标系统详解。
为了更直观的设置摄像机,可以函数Camera::lookAt设置摄像机的方向。
投影
投影的作用是将3D物体,转换成2D坐标,转换后物体x,y坐标值都会压缩在[-1.0, 1.0]。显示的时候会将坐标拉伸到视口大小显示。
投影的方式有两种,一种是物体离摄像机越远越小,这种为透视投影,另一中是,远近物体都一样大的正交投影,默认情况下摄像机使用的是透视投影。
透视投影可以使用函数Camera::initPerspective设置。
正交投影可以使用函数Camera::initOrthographic设置。
2D显示
Cocos中使用Z=0平面显示2D元素,2D精灵、菜单、Label等z轴的值都为0。为了防止2D元素投影后图像会被拉伸或压缩,当视口宽高为w,h时,会将z=0平面的物体位置在(-w/2, -h/2)投影为(-1, -1),(w/2, h/2)投影为(1, 1) ,摄像机的位置为(w/2, h/2)投影后为(0, 0)。以下是默认摄像机初始化代码:
bool Camera::initDefault() { auto size = Director::getInstance()->getWinSize(); //create default camera auto projection = Director::getInstance()->getProjection(); switch (projection) { ...... case Director::Projection::_3D: { float zeye = Director::getInstance()->getZEye(); initPerspective(60, (GLfloat)size.width / size.height, 10, zeye + size.height / 2.0f); Vec3 eye(size.width/2, size.height/2.0f, zeye), center(size.width/2, size.height/2, 0.0f), up(0.0f, 1.0f, 0.0f); setPosition3D(eye); lookAt(center, up); break; } default: CCLOG("unrecognized projection"); break; } return true; } |
注意代码initPerspective的参数
float zeye = Director::getInstance()->getZEye(); initPerspective(60, (GLfloat)size.width / size.height, 10, zeye + size.height / 2.0f); Vec3 eye(size.width/2, size.height/2.0f, zeye), setPosition3D(eye); |
Zeye的值如下
float Director::getZEye(void) const { return (_winSizeInPoints.height / 1.154700538379252f);//(2 * tanf(M_PI/6)) } |
从代码中也可以看到近裁平面和远裁平面分别为10和zeye + size.height / 2.0f。这里可以看出默认情况下Cocos的3D显示超过2D平面后面size.height / 2.0f位置的内容将被裁剪。
使用摄像机
渲染与摄像机
Camera类本身是一个Node节点,可以通过addChild加入到场景树中任何节点上。每个摄像机都属于一个Scene,存放在变量 Scene* Camera::_scene中,在Scene类中存放着一个数组std::vector<Camera*> Scene::_cameras,存放了Scene下所有的摄像机。
当场景第一次渲染时会调用所有节点的Node::onEnter函数,Camera类的OnEnter函数中会寻摄像机所属的场景,让还将摄像机加入到场景摄像机数组中。代码如下:
void Camera::onEnter() { if (_scene == nullptr) { auto scene = getScene(); if (scene) { setScene(scene); } } Node::onEnter(); } |
Cocos的渲染都是在函数Scene::render中,在Scene::render中会为当前场景中的每个摄像机执行一次渲染。
void Scene::render(Renderer* renderer, const Mat4* eyeTransforms, const Mat4* eyeProjections, unsigned int multiViewCount)
{auto director = Director::getInstance();Camera* defaultCamera = nullptr;for (const auto& camera : getCameras()){if (!camera->isVisible())continue;Camera::_visitingCamera = camera;if (Camera::_visitingCamera->getCameraFlag() == CameraFlag::DEFAULT){defaultCamera = Camera::_visitingCamera;}for (unsigned int i = 0; i < multiViewCount; ++i) {if (eyeProjections)camera->setAdditionalProjection(eyeProjections[i] * camera->getProjectionMatrix().getInversed());if (eyeTransforms)camera->setAdditionalTransform(eyeTransforms[i].getInversed());director->pushProjectionMatrix(i);director->loadProjectionMatrix(Camera::_visitingCamera->getViewProjectionMatrix(), i);}camera->apply();//clear background with max depthcamera->clearBackground();//visit the scenevisit(renderer, transform, 0);renderer->render();camera->restore();for (unsigned int i = 0; i < multiViewCount; ++i)director->popProjectionMatrix(i);}………Camera::_visitingCamera = nullptr;
}
每个摄像机渲染时还有有一个multiViewCount循环,这里是设置视口矩阵,多次是因为可以设置多个视口,默认情况下这个循环只执行一次。循环中最后两行代码:
director->pushProjectionMatrix(i);
director->loadProjectionMatrix(Camera::_visitingCamera->getViewProjectionMatrix(), i);
这两行代码时将摄像机的投影视图矩阵(投影矩阵和视图矩阵相乘的结果)压入到导演类的栈中。
在执行渲染命令时,会从栈中取出该值,设置Uniform值,具体函数为GLProgram::setUniformsForBuiltins,代码如下:
void GLProgram::setUniformsForBuiltins(const Mat4 &matrixMV) { const auto& matrixP = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_PROJECTION); if (_flags.usesP) setUniformLocationWithMatrix4fv(_builtInUniforms[UNIFORM_P_MATRIX], matrixP.m, 1); …… if (_flags.usesMVP) { Mat4 matrixMVP = matrixP * matrixMV; setUniformLocationWithMatrix4fv(_builtInUniforms[UNIFORM_MVP_MATRIX], matrixMVP.m, 1); } …… } |
摄像机掩码
每个摄像机都可以设置一个CameraFlag,这是一个位枚举,
enum class CameraFlag { DEFAULT = 1, USER1 = 1 << 1, USER2 = 1 << 2, USER3 = 1 << 3, USER4 = 1 << 4, USER5 = 1 << 5, USER6 = 1 << 6, USER7 = 1 << 7, USER8 = 1 << 8, }; |
UI节点可以通过函数Node::setCameraMask设置摄像机掩码,可以使用或运算(“|”)同时设置多个掩码,这样可以同时在多个摄像机下可以,从而绘制多次。
绘制时可以将3D物体全部使用一个掩码,例如CameraFlag::USER1,然后单独设置一个摄像机并设置标识为CameraFlag::USER1,这样3D显示和2D显示可以分别使用两个摄像机控制,这样可以更容易的控制3D和2D的绘制。
摄像机使用步骤
1、创建摄像机,通过函数Camera::create()创建
2、设置摄像机位置,方向,视角(投影),分别通过函数Node::setPosition、Camera::lookAt、Camera::initPerspective设置。
3、设置摄像机表示,使用函数Camera::setCameraFlag
4、将摄像机添加到场景中。
3D透明
3D物体在绘制的时候使用深度缓存进行遮挡测试,无需关心先后绘制,所以不需要使用localZOrder和_globalOrder排序。但是3D物体如果有透明,只有按照物体在摄像机坐标系下Z轴由小到大绘制才可以正确显示透明。Cocos在绘制透明3D物体时,会生成一个_depth值,这个值代表了物体摄像机坐标系下z轴的值,绘制前,会使用该值对命令进行排序。
生成这个_depth值会使用到函数Camera::getDepthInView,代码如下:
float Camera::getDepthInView(const Mat4& transform) const
{Mat4 camWorldMat = getNodeToWorldTransform();const Mat4 &viewMat = camWorldMat.getInversed();float depth = -(viewMat.m[2] * transform.m[12] + viewMat.m[6] * transform.m[13] + viewMat.m[10] * transform.m[14] + viewMat.m[14]);return depth;
}
函数返回值depth是transform最后一列作为一个点,只进行位移转换到摄像机坐标系下,z轴的值。
绘制命令排序有如下代码:
std::stable_sort(std::begin(_commands[QUEUE_GROUP::TRANSPARENT_3D]), std::end(_commands[QUEUE_GROUP::TRANSPARENT_3D]), compare3DCommand);
static bool compare3DCommand(RenderCommand* a, RenderCommand* b)
{return a->getDepth() > b->getDepth();
}
Cocos2d摄像机详解相关推荐
- Cocos 技术派 05 | Camera 摄像机详解
往期技术派文章: 第01期<野蛮人大作战>从开发到上线 第02期 Cocos Creator 2.0 摄像机的灵活运用 第03期 插件推荐 CC_inspector + 第04期 实时竞技 ...
- unity摄影机depth模式_Unity3d摄像机详解
一 概述 Unity的相机用来将游戏世界呈现给玩家.你始终至少有一个相机在场景中,你也可以有多个.多相机可以给你一个双人分屏效果或创建高级的自定义效果.你可以让相机动起来,或用物理(组件)控制它们.几 ...
- Unity摄像机详解
摄像机包含五个组件: Transform 变换组件 Camera 摄像机:向玩家捕获并展示世界 Audio Listener 音频监听器:接受场景输入的音频源并通过计算机的扬声器播放声音. Flare ...
- Cocos2d坐标系详解
1.笛卡尔坐标系 左手坐标系(Direct3D坐标系),右手坐标系(Direct3D坐标系) 大拇指和食指分别对应x轴和y轴 2.UI坐标系 iOS/Android/Windows SDK中的通用UI ...
- Cocos2d之Texture2D类详解之将文件加载成Texture2D对象
一.声明 笔者以cocos2d框架cocos2d-x-3.3rc0版本的源码做分析.本文为笔者原创,允许转载和分享,只要注明文章出处即可. 二.简介 Texture2D类简介 Texture2D类允许 ...
- Cocos2d之Node类详解之节点树(二)
一.声明 本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可. 笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析.这篇文章承接上篇<Cocos2d之Node ...
- 海康、大华IP摄像机RTSP地址及格式详解
海康.大华IP摄像机RTSP地址及格式详解 在现代社会中,IP摄像机已经成为了安防行业中不可或缺的一部分.而对于信息技术人员来说,掌握摄像机RTSP地址及格式也显得尤为重要.本文将为大家详细介绍海康和 ...
- Three.js - 摄像机的使用详解(透视投影摄像机、正交投影摄像机)
一.两种摄像机的区别与比较 Three.js 库提供了两种不同的摄像机:透视投影摄像机和正交投影摄像机. 透视投影摄像机:这种摄像机的效果更贴近真实世界.也就是物体离摄像机越远,它们就会被渲染得越小. ...
- cocos2D捕鱼达人源代码初学者详解4数字滚动
数字滚动由两个类实现,数字类UINumber和数字滚动类UIRollNum,都是继承的CCSprite,都是精灵.数字类实现单个数字的上下滚动,数字滚动类实现了界面分数数字的位置设置,分数变化时把分数 ...
- 《Unity 4 3D开发实战详解》一6.7 物理引擎综合案例
本节书摘来异步社区<Unity 4 3D开发实战详解>一书中的第6章,第6.7节,作者: 吴亚峰 , 杜化美 , 张月霞 , 索依娜 责编: 张涛,更多章节内容可以访问云栖社区" ...
最新文章
- apache用户名和密码验证
- 使用C#+Linq+SQL快速开发业务
- Python基础教程:input()输入与数据类型转换
- vb.net2019- 机器学习ml.net情绪分析(2)
- c语言个人账册报告的课题来源,C语言个人账簿管理系统报告
- Ivan Fedorov:用已知无法想象未来 - Mixin Network开发者访谈
- 编程之美3——N!末尾有多少个0
- 如何使用硬盘安装debian8.3?
- Angular - ng-repeat高级用法
- 米斯特白帽培训讲义 工具篇 AWVS
- bootStrap库介绍
- [Luogu P2387] [NOI2014]魔法森林 (LCT维护边权)
- 你的早鸟票特权仅剩2天!百度、快手、商汤、图森等重磅嘉宾确认出席AI ProCon 2019...
- 常见的扫描类型有以下几种:Nmap的SYN、Connect、Null、FIN、Xmas、Maimon、ACK
- web安全day22:linux系统最基本知识最常用操作和命令
- 桌面计算机隐藏这么找,电脑桌面图标隐藏了怎么弄出来
- ES 数据导出和数据导入
- A micro Lie theory for state estimation in robotics002
- RMAN crosscheck command作用
- 防火墙阻止了从docker容器到外部的网络连接