Cocos2d摄像机详解

2023-11-07

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 depth
        camera->clearBackground();
        //visit the scene
        visit(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::setPositionCamera::lookAtCamera::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();
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Cocos2d摄像机详解 的相关文章

  • 在 OpenGL 中移动相机时出现故障

    我正在为 iPhone 编写一个基于图块的游戏引擎 除了以下故障之外 它基本上可以正常工作 基本上 相机将始终将玩家保持在屏幕中央 并且它会移动以正确跟随玩家并在静止时正确绘制所有内容 然而 当玩家移动时 玩家行走的表面瓷砖会出现故障 如下
  • Android ACTION_IMAGE_CAPTURE 与内存中的 EXTRA_OUTPUT

    当我打电话时用相机拍照时 File file new File getFilesDir getAbsolutePath myImage jpg Uri outputFileUri Uri fromFile file cameraIntent
  • 如何在cocos2dx 3.0中调用callfunc函数

    我必须将 Objective C 转换为 C 11 我坚持使用以下语法 我已在 testcpp 中引用并尝试以下语法 这是我尝试过的代码 this gt runAction Sequence create blink CallFunc cr
  • 如何在 AVCaptureDevice 上准确设置快速快门速度(曝光时间)?

    我正在开发适用于 IOS 13 的相机应用程序 为此 我将 AVCaptureSession 与 AVCaptureVideoPreviewLayer 结合使用 到目前为止一切正常 现在我想让用户从给定的典型快门速度值数组 以 1 3 曝光
  • 在无头模式下独立运行 Unity,同时捕获屏幕截图

    我需要创建一个在无头模式下运行的统一项目 使用 batchmode 命令 但它必须捕获屏幕截图 例如每一秒并将它们写到一个文件中 我知道在无头模式下 您需要强制调用 Camera Render 才能渲染任何内容 在捕获第一个屏幕截图后 时间
  • Marshmallow 中的手电筒控制

    我对最新 Marshmallow 版本中的相机 更具体地说是手电筒 有疑问 在任何棉花糖之前的版本上 我需要执行以下操作来打开 关闭闪光灯 private void turnFlashOn final Camera camera int f
  • Ionic-Angular.js 拍照并发送到服务器:空图像

    因此 我设法使用自定义指令通过 Angular js 将图像上传到我的服务器 我还成功地实现了 Cordova 的相机功能 现在我尝试连接两者 但是当将图像发送到服务器时 它们被存储为空 我认为问题在于我使用输入字段来获取图像 并且它获取了
  • 如何构建自定义摄像机应用程序?

    我正在尝试开发一个自定义摄像机录像机 当我的设备在 Activity 的 beginRecording 中执行 start MediaRecorder 方法时 应用程序崩溃 我不知道出了什么问题 因为我遵循谷歌API指南 http deve
  • 在 Cocos2dx 中使用 CREATE_FUNC

    谁能解释一下为什么我们需要在Cocos2dx中使用CREATE FUNC 我在HelloWorld示例中看到了它 但不太理解它 请告诉我更多细节 谢谢 我们不需要使用它 它是一个扩展为以下内容的辅助宏 define a create fun
  • 如何使用键盘和鼠标控制相机 - Three.js

    我在 WEB GL 中有一个带有 Three js 的 3D 环境 并且我曾经使用 Orbitcontrols js http codepen io nireno pen cAoGI http codepen io nireno pen c
  • 有没有办法在 iOS 上获取相机流的亮度级别?

    我正在使用 iPhone iPad 摄像头获取视频流并在视频流上进行识别 但随着光照的变化 它会对鲁棒性产生负面影响 我已经在不同的光线下测试了不同的设置 并且可以让它工作 但尝试在运行时调整设置是我所需要的 我可以对每一帧进行简单的亮度检
  • 在android中,将相机预览流到视图上

    我想将 Android 相机的相机预览流式传输到视图上 目的是随后使用 onDraw 将各种内容添加到视图中 我不需要随时实际捕捉图像 它不必是最高质量或每秒最大数量的帧 有谁知道如何做到这一点 将其添加到您的 xml 中
  • 从相机、SurfaceView 或 SurfaceHolder 连续获取图像数据

    所以我设置了这个相机预览Camera SurfaceView and SurfaceHolder 我也有一个ImageView我将在其中放置相机图像的修改版本 并且我希望它更新 假设每秒更新一次 当我从 res 加载图像时 所有代码都已准备
  • 如何更改某些功能以兼容 iOS 10 或更低版本的 Snapchat 中的某些功能,例如相机视图控制器

    我正在制作一个视图控制器来制作像 snapchat 相机一样的相机视图控制器 我下面的代码在 iOS 11 或更高版本上完美运行 老实说 我并没有真正掌握我的代码 因为我只是按照这个像相机视图控制器这样的 snapchat 的教程进行操作
  • HTC One M8 - 使用第二个后置摄像头

    我有一台 HTC One M8 设备 它有 2 个后置摄像头和一个额外的前置摄像头 我的问题是尝试访问第二个后置摄像头 我已经成功制作了一个应用程序 它同时运行 2 个摄像头 1 个前置摄像头和 1 个后置摄像头 但问题是我无法访问第二个后
  • 不调用 PreviewCallback 和带缓冲区的 PreviewCallback

    我对 Android 4 0 x 的预览回调有疑问 我设置了一个相机 创 建一个表面来显示相机图像on previewCallback 事件 一切正常 但对于 Android 4 0 x 则不然onPreviewCallback被称为 也不
  • 将 Spritekit 游戏转换为 Android?

    是否有任何新选项可以将 spritekit 游戏转换为 Android 游戏 似乎唯一的选择是用 Java 重新编码所有内容或使用 Cocos2D LibGDX 等 很不幸的是 不行 而且带有 Apportable 服务的跨平台 Sprit
  • 在相机视图上叠加一个框架,然后保存并使用生成的照片(捕获的内容和叠加框架)

    我希望我的应用程序中有一个功能 让您为自己或其他有名望的人拍摄一张照片 例如 通缉 然后用户拍摄照片 叠加层和照片将合并为一张 生成的图像可在代码中使用 任何想法从哪里 如何开始 有什么教程之类的 Cheers 它可能会有点棘手 处理应用于
  • 如何在Android中没有Intent且没有任何视图窗口的情况下拍照

    大家好 我正在尝试弄清楚如何通过按下按钮来拍照 而不显示任何预览 我的想法是 我想要拍摄并保存照片 但之前或之后没有照片的视觉预览 到目前为止 我能够获取拍照并将其保存到磁盘的代码 没有任何问题 但如果没有表面视图或预览 我似乎无法做到这一
  • VideoCapture 未检测到 uEye 摄像头

    我的 uEye 相机遇到了一个问题 使用我的笔记本电脑摄像头 id 0 或 USB 上的网络摄像头 id 1 此行完美运行 TheVideoCapturer open 1 TheVideoCapturer 属于 VideoCapture 类

随机推荐

  • 堆栈相关应用算法

    栈的压入 弹出序列 题目描述 输入两个整数序列 第一个序列表示栈的压入顺序 请判断第二个序列是否可能为该栈的弹出顺序 假设压入栈的所有数字均不相等 例如序列1 2 3 4 5是某栈的压入顺序 序列4 5 3 2 1是该压栈序列对应的一个弹出
  • 什么能力很重要: 维持自己的框架的能力

    作者 纸箱之神 链接 https www zhihu com question 305507128 answer 637314109 有人答努力 坚持 选择 自律 学习 这些都很重要 但是我觉得 还有一个能力是凌驾于所有这些答案之上的 那就
  • 使用idea创建项目的步骤

    使用idea创建项目的步骤 1 点击桌面安装好的idea 2 由于我之前创建好了 所以打开的时候直接就进去 接下来 创建idea 3 File new project 4 点击project 创建项目 5 进行下一步 6 继续下一步 7 会
  • 渲染性能优化之几种LOD层次细节总结

    CIM 城市信息模型 这种大场景或者说特大场景LOD是非常重要的 城市 BIM 地形都非常系需要 根据公司的项目需求这里做了一下总结各种LOD的技术 算法 策略 首先 我们公司对于城市这种大场景我们提出使用如下技术 1 组织 符合3Dtil
  • Kr的pandas技巧笔记

    最近在做一个数据集的可视化项目 又捡起了pandas和python 这里把实际用到的技巧干货写下来 防止忘记再次去网上各种查和看documentation 同时也分享给大家 1 找出nan项 DataFrame isnull 返回一个大小和
  • Windows中的时间(SYSTEMTIME和FILETIME)

    上一篇中介绍了C运行库中的时间处理函数 这一篇介绍Windows SDk中提供的时间函数 两种时间系统之间没有本质区别 事实上CRT时间是用Windows时间实现的 当然这是说的VC实现 同样提供本地时间和UTC时间之间的转换 不过CRT中
  • C++socket编程(二):系统socket库介绍

    什么是套接字 套接字是一个介质 由操作系统控制 下面演示下windows和linux中的socket接口建立的代码 通用 ifdef WIN32 include
  • 出现rpm: command not found

    解决方法 sudo apt get install alien 转载于 https www cnblogs com xDan p 5411083 html
  • fiddler抓取,Android真机测试

    1 配置Fiddler抓取并解密HTTPS包 Fiddler默认是不抓取HTTPS包的 需要进行相应的配置 打开Fiddler 选择 Tools gt Fiddler Options 2 在弹出的对话框中选择 HTTPS 选项卡 3 勾选
  • 【Jupyter】下,Tensorflow 1.8 载入 MNIST 数据集

    导入包 import tensorflow as tf from tensorflow examples tutorials mnist import input data mnist input data read data sets M
  • 前端基础3——JavaScript基础用法

    文章目录 一 基本使用 1 1 内部方式 1 2 外部导入方式 1 3 css标签调用js脚本 触发事件 二 Windows对象 2 1 对象属性 2 2 对象方法 三 数据类型 3 1 字符串处理 3 2 数组处理 3 3 对象处理 四
  • 逆水寒7.25服务器维护,逆水寒7月4日更新维护公告 角色交易功能上线

    原标题 逆水寒7月4日更新维护公告 角色交易功能上线 逆水寒7月4日更新了什么内容 7月4日正式服中加入了角色交易的新功能 另外天江之战活动也正式开启 以下来看下完整的更新公告 各位自在同门 为了保证服务器的运行稳定和服务质量 逆水寒 将于
  • 【Unity每日灵感】第二期:复刻FreeFlyCamera插件内置脚本,如何实现丝滑好用的相机漫游?

    目录 一 插件效果 相机部分 提示部分 二 步骤拆解 视角旋转 移动与升降 加速 锁定及重置 三 脚本复刻 旋转 位置变换 增加体验感的细节 栏目Up Up 专门针对我自己平日里一些在项目中使用的好玩的点子 或者尚未实现的有趣功能复刻 第二
  • 微信小程序SLAM AR零基础入门教程

    鬼灭之刃花街篇 开播在即 今天带大家零基础使用Kivicube制作一个炭治郎的SLAM AR云手办 可以通过微信小程序将AR版的炭治郎放置在家中 提前感受鬼灭的氛围 先上个GIF大家看看动态的展示效果 在这里先科普一下本次教程使用到的AR技
  • Kendo UI开发教程(19): Kendo MVVM 数据绑定(八) Style

    Style绑定可以通过ViewModel绑定到DOM元素CSS风格属性 例如 1
  • 免费云服务器

    三丰云 免费虚拟主机 免费云服务器 https www sanfengyun com 一个良心服务器 可以通过活动 免费使用 特别适合大学生或者想尝试一下后端学习的朋友 今天我就分享一下使用这个服务器的感受 首先我选着配置了 CentOS
  • 51单片机播放音乐(二):DA转换播放任意波形

    51单片机播放音乐 二 DA转换播放任意波形 原理 DA转换器 仿真电路图 音频文件转成C语言数组代码 单片机代码 仿真输出波形 本文完整源码 用数字信号带动蜂鸣器同一时间只能发出一种频率的声音 也没有和弦 会比较难听 而我们听到的音乐可以
  • PNG透明窗体全攻略(控件不透明)vc++程序指导

    这两天在研究透明窗体 总算略有小成 网上大部分文章都是介绍到把窗体弄透明就没有下文 其实窗体透明并不难 难就难在透明的窗体上还要放控件 今 天我就把窗体透明一直到控件不透明怎么制作一块给写了吧 先截张图诱惑下你们 如果你没兴趣就没必要再看下
  • stm32 使用keil无实物(软件)仿真,虚拟串口通讯

    准备 1 keil 2 vspd虚拟串口 3 sscom串口助手 4 CubeMX 哪里报错no read permission 把哪里map一下 map 0x40000000 0x400077FF read write APB1 map
  • Cocos2d摄像机详解

    Cocos2d摄像机详解 摄像机的作用 在3D系统中都会有一个摄像机的概念 物体在显示之前需要先将物体的坐标转换到视角坐标 也就是摄像机坐标 然后再投影 最后还需对投影画面进行缩放到视口显示的大小 Coscos2d x 3 x支持3D的 所