Cocos2d-x 3D渲染技术 (三)

news2024/11/27 4:38:19

包围盒算法

说白了就是给物体装进一个盒子里,该盒子可以装下物体。目的是为了进行碰撞检测。

种类:

  1. 球状碰撞体
  2. 立方体碰撞体
  3. 胶囊碰撞体
  4. Mesh碰撞体

实现原理是OBB包围盒。

经常使用的两种碰撞算法是OBB包围盒和AABB包围盒算法。

OBB包围盒算法

方向包围盒(Oriented bounding box),简称为OBB。

OBB包围盒特点:始终沿着物体的主方向生成最小的一个矩形包围盒,并且可以随物体旋转,这就决定了它可以用来做精准的碰撞检测。

判断碰撞条件:两个多边形,在所有轴的投影都发生了重叠,则认为发生了碰撞;否则,认为没有发生碰撞。

OBB有很多中表达的方式。

cocos2dx中定义的表达方式为:

Vec3 _center;   // obb center
Vec3 _xAxis;    // x axis of obb, unit vector
Vec3 _yAxis;    // y axis of obb, unit vector
Vec3 _zAxis;    // z axis of obb, unit vector
Vec3 _extentX;  // _xAxis * _extents.x
Vec3 _extentY;  // _yAxis * _extents.y
Vec3 _extentZ;  // _zAxis * _extents.z
Vec3 _extents;  // obb length along each axis

_center :中心点

_xAxis :包围盒X轴方向单位矢量

_yAxis :包围盒Y轴方向单位矢量

_zAxis :包围盒Z轴方向单位矢量

_xAxis、_yAxis、_zAxis 为正交单位向量,定义了当前OBB包围盒的x,y,z轴,用来计算矢量投影的

_extentX :包围盒X轴坐标

_extentY :包围盒Y轴坐标

_extentZ :包围盒Z轴坐标

_extents :定义了OBB包围盒的长、宽、高

这里可以看出来,每一个OBB包围盒需要24个float类型的变量来存储,占了96个字节。这样相比AABB来说,的确会需要额外的内存空间。

减少开销的办法:只存储旋转矩阵的两个轴,只是在测试的时候,利用叉积计算第三个轴。

创建 OBB

cocos2dx 使用了两种创建方式

利用AABB

由一个AABB包围盒来确定最终OBB包围盒。

OBB::OBB(const AABB& aabb)
{
    reset();
    
    _center = (aabb._min + aabb._max);
    _center.scale(0.5f);
    _xAxis.set(1.0f, 0.0f, 0.0f);
    _yAxis.set(0.0f, 1.0f, 0.0f);
    _zAxis.set(0.0f, 0.0f, 1.0f);
    
    _extents = aabb._max - aabb._min;
    _extents.scale(0.5f);
    
    computeExtAxis();
}
协方差矩阵

利用协方差矩阵来确定一个方向包围盒。

通俗点就是将原基下的坐标转换到新基上,然后依照新基算出AABB包围盒,然后再转换到原基,这个时候就是原基的OBB包围盒了。

关于协方差和PCA的知识点可以参考:

PCA

里面讲得非常详细。

在这里插入图片描述

大部分引擎都使用到了该算法,cocos2dx中也使用了这个算法。如下:

//生成协方差矩阵

static Mat4 _getConvarianceMatrix(const Vec3* vertPos, int vertCount)
{
    int i;
    Mat4 Cov;

    double S1[3];
    double S2[3][3];

    S1[0] = S1[1] = S1[2] = 0.0;
    S2[0][0] = S2[1][0] = S2[2][0] = 0.0;
    S2[0][1] = S2[1][1] = S2[2][1] = 0.0;
    S2[0][2] = S2[1][2] = S2[2][2] = 0.0;

    // get center of mass
    for(i=0; i<vertCount; i++)
    {
        S1[0] += vertPos[i].x;
        S1[1] += vertPos[i].y;
        S1[2] += vertPos[i].z;

        S2[0][0] += vertPos[i].x * vertPos[i].x;
        S2[1][1] += vertPos[i].y * vertPos[i].y;
        S2[2][2] += vertPos[i].z * vertPos[i].z;
        S2[0][1] += vertPos[i].x * vertPos[i].y;
        S2[0][2] += vertPos[i].x * vertPos[i].z;
        S2[1][2] += vertPos[i].y * vertPos[i].z;
    }

    float n = (float)vertCount;
    // now get covariances
    Cov.m[0] = (float)(S2[0][0] - S1[0]*S1[0] / n) / n;
    Cov.m[5] = (float)(S2[1][1] - S1[1]*S1[1] / n) / n;
    Cov.m[10] = (float)(S2[2][2] - S1[2]*S1[2] / n) / n;
    Cov.m[4] = (float)(S2[0][1] - S1[0]*S1[1] / n) / n;
    Cov.m[9] = (float)(S2[1][2] - S1[1]*S1[2] / n) / n;
    Cov.m[8] = (float)(S2[0][2] - S1[0]*S1[2] / n) / n;
    Cov.m[1] = Cov.m[4];
    Cov.m[2] = Cov.m[8];
    Cov.m[6] = Cov.m[9];

    return Cov;
}
碰撞检测

关于碰撞检测,在另一篇中有提到,利用分离轴定理(separating axis theorem) 来进行检测。

cocos2dx 3D物理相关知识点汇总

cocos2dx中相关代码:

//将点映射到坐标上
float OBB::projectPoint(const Vec3& point, const Vec3& axis)const
{
    //点积的方式
    float dot = axis.dot(point);
    float ret = dot * point.length();
    return ret;
}
//计算 最大、最小的投影值
void OBB::getInterval(const OBB& box, const Vec3& axis, float &min, float &max)const
{
    Vec3 corners[8];
    box.getCorners(corners);
    float value;
    min = max = projectPoint(axis, corners[0]);
    for(int i = 1; i < 8; i++)
    {
        value = projectPoint(axis, corners[i]);
        min = MIN(min, value);
        max = MAX(max, value);
    }
}
//取边的矢量
Vec3 OBB::getEdgeDirection(int index)const
{
    Vec3 corners[8];
    getCorners(corners);
    
    Vec3 tmpLine;
    switch(index)
    {
        case 0:// edge with x axis
            tmpLine = corners[5] - corners[6];
            tmpLine.normalize();
            break;
        case 1:// edge with y axis
            tmpLine = corners[7] - corners[6];
            tmpLine.normalize();
            break;
        case 2:// edge with z axis
            tmpLine = corners[1] - corners[6];
            tmpLine.normalize();
            break;
        default:
            CCASSERT(0, "Invalid index!");
            break;
    }
    return tmpLine;
}
// 取面的方向矢量
Vec3 OBB::getFaceDirection(int index) const
{
    Vec3 corners[8];
    getCorners(corners);
    
    Vec3 faceDirection, v0, v1;
    switch(index)
    {
        case 0:// front and back
            v0 = corners[2] - corners[1];
            v1 = corners[0] - corners[1];
            Vec3::cross(v0, v1, &faceDirection);
            faceDirection.normalize();
            break;
        case 1:// left and right
            v0 = corners[5] - corners[2];
            v1 = corners[3] - corners[2];
            Vec3::cross(v0, v1, &faceDirection);
            faceDirection.normalize();
            break;
        case 2:// top and bottom
            v0 = corners[1] - corners[2];
            v1 = corners[5] - corners[2];
            Vec3::cross(v0, v1, &faceDirection);
            faceDirection.normalize();
            break;
        default:
            CCASSERT(0, "Invalid index!");
            break;
    }
    return faceDirection;
}

利用分离轴定理判断两个OBB是否碰撞

第一个包围盒的3个坐标轴、第二个包围盒的3个坐标轴,以及垂直于某一个轴的9个轴。(一共是15个轴)

垂直于某一轴的9个轴 : 取包围盒A的X轴方向的某一边矢量(不是X坐标轴,是x轴方向边的矢量),再取包围盒B的X轴方向的某一边矢量,对两个矢量做叉乘,叉乘结果就是垂直于A和B的矢量的方向矢量,这就是一个分离轴。如此重复,用包围盒A的X轴方向的某一边矢量依次叉乘包围盒B的X,Y,Z,然后再用包围盒A的Y和Z轴方向的某一边矢量再进行这一遍流程,总共得到9个轴。

bool OBB::intersects(const OBB& box) const
{
    float min1, max1, min2, max2;
    for (int i = 0; i < 3; i++)
    {
        getInterval(*this, getFaceDirection(i), min1, max1);
        getInterval(box, getFaceDirection(i), min2, max2);
        if (max1 < min2 || max2 < min1) return false;
    }
    
    for (int i = 0; i < 3; i++)
    {
        getInterval(*this, box.getFaceDirection(i), min1, max1);
        getInterval(box, box.getFaceDirection(i), min2, max2);
        if (max1 < min2 || max2 < min1) return false;
    }
    
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            Vec3 axis;
            Vec3::cross(getEdgeDirection(i), box.getEdgeDirection(j), &axis);
            getInterval(*this, axis, min1, max1);
            getInterval(box, axis, min2, max2);
            if (max1 < min2 || max2 < min1) return false;
        }
    }
    
    return true;
}
//检查点是否在OBB中
bool OBB::containPoint(const Vec3& point) const
{
    Vec3 vd = point - _center;

    float d = vd.dot(_xAxis);
    if (d > _extents.x || d < -_extents.x)
        return false;

    d = vd.dot(_yAxis);
    if (d > _extents.y || d < -_extents.y)
        return false;

    d = vd.dot(_zAxis);
    if (d > _extents.z || d < -_extents.z)
        return false;

    return true;
}

AABB包围盒算法

轴对称包围盒。

cocos2dx 中,为每一个Spite3D都提供了获取AABB包围盒的接口。

记得转换一次,因为获取的AABB包围盒的坐标系是自己,所以需要转换到世界坐标上。

const AABB& Sprite3D::getAABB() const
{
    Mat4 nodeToWorldTransform(getNodeToWorldTransform());
    
    // If nodeToWorldTransform matrix isn't changed, we don't need to transform aabb.
    if (memcmp(_nodeToWorldTransform.m, nodeToWorldTransform.m, sizeof(Mat4)) == 0 && !_aabbDirty)
    {
        return _aabb;
    }
    else
    {
        _aabb.reset();
        if (_meshes.size())
        {
            Mat4 transform(nodeToWorldTransform);
            for (const auto& it : _meshes) {
                if (it->isVisible())
                    _aabb.merge(it->getAABB());
            }
            
            _aabb.transform(transform);
            _nodeToWorldTransform = nodeToWorldTransform;
            _aabbDirty = false;
        }
    }
    
    return _aabb;
}

其他

碰撞算法用得最多的就是AABB和OBB,但也有其他的算法。比如Box2D,Bullet都有自己的一系列算法和检测机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/49982.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript -- 01. 基础语法介绍

文章目录基础语法1 Hello World2 JS的编写位置3 基本语法3.1 多行注释3.2 单行注释3.3 区分大小写3.4 空格和换行会被忽略3.5 以分号结尾3.6 字面量3.7 变量3.8 变量的内存结构3.9 常量3.10 标识符基础语法 JS的基本语法 1 Hello World js的三种输出方式 <!DOCTYPE html&g…

精彩回顾 | 云原生系统软件的产业应用

11月18日&#xff0c;2022年第五届中国金融科技产业大会暨第四届中新&#xff08;苏州&#xff09;数字金融应用博览会“基础软件与云原生系统软件”分论坛成功举办。该论坛由由中国计算机学会CTO CLUB&#xff08;苏州&#xff09;承办&#xff0c;江苏省金融科技云原生融合创…

如何用 Python 做一个简单的翻译工具?

前言 平时经常在网上翻译一些单词&#xff0c;突发奇想&#xff0c;可不可以直接调某些免费翻译网站的接口呢&#xff1f;然后做一个图形界面的翻译小工具&#xff1f;下面开始实践 &#xff08;文末送读者福利&#xff09; 1.先找一下有哪些免费翻译的接口 百度了一下关键字…

神经架构搜索的综合调查:挑战和解决方案(二)

4 PERFORMANCE COMPARISON NAS 是一项很有前途的研究。在本节中&#xff0c;我们根据主流搜索方法 [27, 28] 对现有 NAS 的性能进行分类和比较&#xff0c;同时还根据第 3 节报告了它们使用的优化策略。这些搜索方法主要包括以下内容&#xff1a;强化学习&#xff08;RL&#…

操作系统学习笔记(Ⅲ):内存

目录 1 内存管理 1.1 内存基础知识 1.内存 2.进程运行 1.2 内存管理的概念 1.3 覆盖与交换 1.覆盖 2.交换 3.区别 1.4 连续分配管理方式 1.单一连续分配 2.固定分区分配 3.动态分区分配 1.5 动态分区分配算法 1.首次适应算法 2.最佳适应算法 3.最坏适应算法 …

网络安全与IP安全

网络安全 是指网络系统的硬件&#xff0c;软件以及系统中的数据收到的保护。 保护的基本属性为&#xff1a;机密性&#xff0c;身份认证&#xff0c;完整性和可用性&#xff1b; 基本特征&#xff1a;相对性&#xff0c;时效性&#xff0c;相关性&#xff0c;不确定性&#xf…

React项目实战之租房app项目(六)渲染房源列表axios优化封装顶部搜索栏列表找房模块之条件筛选

前言 目录前言一、地图找房模块-获取并渲染房源列表1.1 房源列表示例图1.2 实现步骤1.3 代码示例二、axios优化2.1 问题概述2.2 配置生产环境和开发环境2.3 axios优化三、封装顶部搜索导航栏四、列表找房模块-导入顶部导航栏组件五、列表找房模块-条件筛选&#xff08;上&#…

Python将Excel文件插入Mysql数据库(脚本)

目录前言最近接到一个需求&#xff0c;就是将多个Eccel文件&#xff08;表头相同&#xff1b;每个都非常大&#xff0c;约60多万行&#xff0c;每个都是&#xff01;&#xff01;&#xff09;先合并在一起&#xff0c;再做一些处理&#xff0c;但是Excel表格一个文件根本存不下…

Python矩阵乘法 二重循环实现 + 列表推式

这是python 矩阵乘法的简单例子 col 2 row 2 a [[1, 2], [3, 4]] b [[5, 6], [7, 8]] c [[0, 0], [0, 0]] “”" a b c 二维矩阵初始化 c [[0 for col in range(col)] for row in range(row)] a [[0 for col in range(col)] for row in range(row)] b [[0 for c…

Android热修复,精简学习

接入热修复 接入热修复流程如下&#xff1a; 配置开发环境在控制台创建应用在客户端创建新工程签名配置加密信息编写代码发布带有热修复功能的客户端版本 配置开发环境 在控制台创建应用 在控制台创建 mPaaS 应用。此时&#xff0c;本地还没有带签名的 APK&#xff0c;因此…

面试:插件化相关---broadcastReceiver

实现原理 1 采用的模型 Android中的广播使用了设计模式中的观察者模式&#xff1a;基于消息的发布 / 订阅事件模型因此&#xff0c;Android将广播的发送者 和 接收者 解耦&#xff0c;使得系统方便集成&#xff0c;更易扩展 2 模型讲解 模型中有3个角色&#xff1a; 消息订阅…

小程序项目结构

pages 用来存放所有小程序的页面utils 用来存放工具性质的模块(例如:格式化时间的自定义模块)app.js 小程序项目的入口文件app.json 小程序项目的全局配置文件app.wxss 小程序项目的全局样式文件project.config.json 项目的配置文件sitemap.json 用来配置小程序及其页面是否允许…

【数据结构-查找】散列表

文章目录1 线性探测法1.1 查找成功时的 ASL1.2 查找失败时的 ASL1.3 散列表的装填因子 α2 拉链法1 线性探测法 1.1 查找成功时的 ASL 查找元素 47&#xff08;散列函数&#xff1a;3&#xff09;次数&#xff1a;1查找元素 7&#xff08;散列函数&#xff1a;11&#xff09;次…

云上办公便捷、安全,就用华为云桌面

云上办公便捷、安全&#xff0c;就用华为云桌面&#xff01; 根据IDC提出的“未来工作空间”的概念&#xff0c;未来工作空间意味着将打破时空与地域的限制&#xff0c;让员工随时随地工作。未来工作空间也将成为企业整体数字化转型战略中的必要组成部分。 恰逢其时&#xff0…

kubernetes Pod详解

文章目录Pod生命周期创建和终止pod的创建过程pod的终止过程初始化容器钩子函数容器探测重启策略Pod调度定向调度NodeNameNodeSelector亲和性调度NodeAffinityPodAffinityPodAntiAffinity污点和容忍污点&#xff08;Taints&#xff09;容忍&#xff08;Toleration&#xff09;Po…

【测试沉思录】19. 如何设置 JMeter 线程组?

作者&#xff1a;宋赟 编辑&#xff1a;毕小烦 最近有不少测试同学问我 JMeter 线程组如何设置并发的问题&#xff0c;发现很多人对线程组里的参数不是很清楚&#xff0c;今天就科普一下 JMeter 线程组的信息&#xff0c;也简单介绍一下不同场景的并发策略。 1. 线程组是什么 …

制作一个简单HTML游戏网页(HTML+CSS)米哈游 1页 带轮播图

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 游戏官网 | 游戏网站 | 电竞游戏 | 游戏介绍 | 等网站的设计与制作 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结构 …

K3S +Helm+NFS最小化测试安装部署只需十分钟

作者&#xff1a;郝建伟 k3s 简介 官方文档&#xff1a;k3s 什么是k3s k3s 是一个轻量级的 Kubernetes 发行版 它针对边缘计算、物联网等场景进行了高度优化。 k3s 有以下增强功能&#xff1a;打包为单个二进制文件。 使用基于 sqlite3 的轻量级存储后端作为默认存储机制。…

【附源码】计算机毕业设计JAVA专利查询与发布系统设计与实现

【附源码】计算机毕业设计JAVA专利查询与发布系统设计与实现 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1…

BI数据分析

一 前言 数据分析一般需要掌握Excel、SQL&#xff0c;再强大的需要掌握python等。目前市面上的数据BI工具&#xff0c;就是用来优化复杂的数据分析过程&#xff0c;解放相关人员的生产力的&#xff0c;不再为了一个需求而漫长等待和重复多次沟通&#xff0c;从而让人员可以更多…