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摄像机详解相关推荐

  1. Cocos 技术派 05 | Camera 摄像机详解

    往期技术派文章: 第01期<野蛮人大作战>从开发到上线 第02期 Cocos Creator 2.0 摄像机的灵活运用 第03期 插件推荐 CC_inspector + 第04期 实时竞技 ...

  2. unity摄影机depth模式_Unity3d摄像机详解

    一 概述 Unity的相机用来将游戏世界呈现给玩家.你始终至少有一个相机在场景中,你也可以有多个.多相机可以给你一个双人分屏效果或创建高级的自定义效果.你可以让相机动起来,或用物理(组件)控制它们.几 ...

  3. Unity摄像机详解

    摄像机包含五个组件: Transform 变换组件 Camera 摄像机:向玩家捕获并展示世界 Audio Listener 音频监听器:接受场景输入的音频源并通过计算机的扬声器播放声音. Flare ...

  4. Cocos2d坐标系详解

    1.笛卡尔坐标系 左手坐标系(Direct3D坐标系),右手坐标系(Direct3D坐标系) 大拇指和食指分别对应x轴和y轴 2.UI坐标系 iOS/Android/Windows SDK中的通用UI ...

  5. Cocos2d之Texture2D类详解之将文件加载成Texture2D对象

    一.声明 笔者以cocos2d框架cocos2d-x-3.3rc0版本的源码做分析.本文为笔者原创,允许转载和分享,只要注明文章出处即可. 二.简介 Texture2D类简介 Texture2D类允许 ...

  6. Cocos2d之Node类详解之节点树(二)

    一.声明 本文属于笔者原创,允许读者转载和分享,只要注明文章来源即可. 笔者使用cocos2d框架的cocos2d-x-3.3rc0版本的源代码做分析.这篇文章承接上篇<Cocos2d之Node ...

  7. 海康、大华IP摄像机RTSP地址及格式详解

    海康.大华IP摄像机RTSP地址及格式详解 在现代社会中,IP摄像机已经成为了安防行业中不可或缺的一部分.而对于信息技术人员来说,掌握摄像机RTSP地址及格式也显得尤为重要.本文将为大家详细介绍海康和 ...

  8. Three.js - 摄像机的使用详解(透视投影摄像机、正交投影摄像机)

    一.两种摄像机的区别与比较 Three.js 库提供了两种不同的摄像机:透视投影摄像机和正交投影摄像机. 透视投影摄像机:这种摄像机的效果更贴近真实世界.也就是物体离摄像机越远,它们就会被渲染得越小. ...

  9. cocos2D捕鱼达人源代码初学者详解4数字滚动

    数字滚动由两个类实现,数字类UINumber和数字滚动类UIRollNum,都是继承的CCSprite,都是精灵.数字类实现单个数字的上下滚动,数字滚动类实现了界面分数数字的位置设置,分数变化时把分数 ...

  10. 《Unity 4 3D开发实战详解》一6.7 物理引擎综合案例

    本节书摘来异步社区<Unity 4 3D开发实战详解>一书中的第6章,第6.7节,作者: 吴亚峰 , 杜化美 , 张月霞 , 索依娜 责编: 张涛,更多章节内容可以访问云栖社区" ...

最新文章

  1. apache用户名和密码验证
  2. 使用C#+Linq+SQL快速开发业务
  3. Python基础教程:input()输入与数据类型转换
  4. vb.net2019- 机器学习ml.net情绪分析(2)
  5. c语言个人账册报告的课题来源,C语言个人账簿管理系统报告
  6. Ivan Fedorov:用已知无法想象未来 - Mixin Network开发者访谈
  7. 编程之美3——N!末尾有多少个0
  8. 如何使用硬盘安装debian8.3?
  9. Angular - ng-repeat高级用法
  10. 米斯特白帽培训讲义 工具篇 AWVS
  11. bootStrap库介绍
  12. [Luogu P2387] [NOI2014]魔法森林 (LCT维护边权)
  13. 你的早鸟票特权仅剩2天!百度、快手、商汤、图森等重磅嘉宾确认出席AI ProCon 2019...
  14. 常见的扫描类型有以下几种:Nmap的SYN、Connect、Null、FIN、Xmas、Maimon、ACK
  15. web安全day22:linux系统最基本知识最常用操作和命令
  16. 桌面计算机隐藏这么找,电脑桌面图标隐藏了怎么弄出来
  17. ES 数据导出和数据导入
  18. A micro Lie theory for state estimation in robotics002
  19. RMAN crosscheck command作用
  20. 防火墙阻止了从docker容器到外部的网络连接

热门文章

  1. 坚果pro2刷回官方_锤子坚果Pro2手机降回6.0.3版本教程
  2. 基于易班API的Java开发入门教程
  3. C++报错 invalid operands to binary expression
  4. 纬衡、金蝶、腾讯、迅雷获深圳软件明星企业称号
  5. 【统计学】统计学专业术语
  6. 项目人力资源管理之编制项目人力资源管理计划
  7. PLC的IO点位是什么意思
  8. nRF51822 官方 Blinky 工程(多彩 LED)
  9. 各类邮箱谷歌邮箱、Outlook邮箱、雅虎邮箱的购买养号策略
  10. 现代软件工程 习而学的软件工程教育