cocos2d中的可见性检测

游戏的在进行一次渲染的时候,通常会提交大量的渲染对象给gpu。在这些需要渲染的对象中,并不是所有对象都会出现镜头中,即有一部分对象是不可见的。

通常有两种方式来完成不可见对象的剔除工作:

(1)直接交给图形库帮我剔除,即性能消耗在gpu端;

(2)在提交图元给gpu前,在游戏逻辑中进行剔除,即性能消耗在cpu端;

是将剔除操作放在cpu还是gpu来处理,没有一个具体的标准,个人认为,要根据实际情况,如果逻辑方面可以快速进行剔除,可以交由cpu来处理,但是gpu在这方面经过了优化,具有更快的处理能力,所以如果cpu端剔除操作过于复杂,可以交由gpu来处理。总之游戏开发中,cpu和gpu的性能都非常重要,所以应该在性能消耗方面尽量平衡,不应该让某一个端承担过多的任务。

opengl是剔除不可见的对象的过程:

一个最简单的模型,坐标点(x,y,z)-> 模型矩阵(modelMatrix)变换 -> 视图矩阵(viewMatrix)变换 -> 投影矩阵(projectMatrix)变换 -> 透视除法(除以齐次坐标中的w)-> 转换为齐次标准裁剪空间坐标 -> 在标准裁剪空间中进行裁剪,剔除不可见的图元

(1)首先在模型空间中定义坐标位置,模型坐标原点通常是(0,0,0),即世界坐标中心点,此时我们可以认为模型坐标系和世界坐标系是重合的,例如(2,2,2)表示相对模型坐标系原点的偏移位置,x正向偏移2,y正向偏移2,z正向偏移2,一个模型中的所有坐标点的变换都是相对于模型坐标系的原点进行设定的;

(2)然后通过视图矩阵将模型坐标系转换为世界坐标系,此时整个模型就显示在世界坐标系中了,例如视图矩阵是将某个模型向x正向偏移100,其实就是将整个模型的所有坐标点向x正向偏移100;

(3)投影矩阵是将世界坐标系中的所有3d坐标投影到2d平面上,游戏中主要采用透视投影,除此之外还有正交投影和斜视投影,正交投影形成的是一个平头截体(frustum),类似一个金字塔被削掉一部分顶部;

(4)透视投影之后形成的齐次坐标(x,y,z,w),为了形成近大远小的透视效果,需要将x/w,y/w,z/w,转换为标准裁剪空间的坐标;

(5)裁剪空间坐标系在不同的图形库中有所不同,opengl的裁剪空间坐标系是x:-1到+1,y:-1到+1,z:0到1的立方体。使用标准裁剪空间的目的:一是标准裁剪空间的剔除效率比在平头截体中剔除更快,相当于在2d矩形中进行剔除;二是标准裁剪空间可以独立于硬件设备。理论上,z坐标的保留在裁剪的时候是多余的,主要是为了后面的深度检测做准备;

备注:

(1)模型矩阵,视图矩阵,投影矩阵,按照规定的先后顺序可以依次结合,modelmatrix * viewmatrix * projectmatrix。由于矩阵乘法适用于结合律不适用于交换律,所以通常结合的方式是modelview矩阵或者viewproject矩阵,没有modelproject矩阵。计算方式有两种:左乘列向量(projectmatrix * viewmatrix * modelmatrix * vector),右乘行向量(vector * modelmatrix * viewmatrix * projectmatrix);

(2)裁剪过程不是简单的剔除不可见的坐标点,在剔除部分坐标点后,会导致一些图元被分割,因此opengl为帮助我们计算剔除后图元和裁剪空间的交点,交点会作为被分割的图元的新坐标点,用于后续的显示;

在游戏逻辑中检测不可见对象,在提交给gpu之前久提前进行了剔除(cocos2d为例):

Vec2 Camera::projectGL(const Vec3& src) const

{

Vec2 screenPos;

auto viewport = Director::getInstance()->getWinSize(); // 获取游戏的设计分辨率

Vec4 clipPos;

getViewProjectionMatrix().transformVector(Vec4(src.x, src.y, src.z, 1.0f), &clipPos); // 将坐标点通过透视投影进行变换,其实在cocos2d中,透视矩阵和一个相机定义的视图矩阵是结合在一起的

CCASSERT(clipPos.w != 0.0f, "clipPos.w can‘t be 0.0f!");

float ndcX = clipPos.x / clipPos.w; // 使用透视除法,转换成齐次标准裁剪空间坐标

float ndcY = clipPos.y / clipPos.w;

screenPos.x = (ndcX + 1.0f) * 0.5f * viewport.width; // 由于裁剪空间x坐标是从-1到1,要将裁剪空间的坐标映射到屏幕坐标,映射算法是(x - (-1)) / (1 - (-1)) * width

screenPos.y = (ndcY + 1.0f) * 0.5f * viewport.height;

return screenPos;

}

bool Renderer::checkVisibility(const Mat4 &transform, const Size &size)

{

auto scene = Director::getInstance()->getRunningScene();

//If draw to Rendertexture, return true directly.

// only cull the default camera. The culling algorithm is valid for default camera.

if (!scene || (scene && scene->_defaultCamera != Camera::getVisitingCamera()))

return true;

auto director = Director::getInstance();

Rect visiableRect(director->getVisibleOrigin(), director->getVisibleSize());

// transform center point to screen space

float hSizeX = size.width/2; // 用矩形中心点作为检测点

float hSizeY = size.height/2;

Vec3 v3p(hSizeX, hSizeY, 0);

transform.transformPoint(&v3p); // 使用模型视图矩阵去变换坐标,将其转换到世界坐标系中,cocos2d是将图元渲染在z为0的平面上

Vec2 v2p = Camera::getVisitingCamera()->projectGL(v3p); // 使用投影矩阵将世界坐标转换到屏幕坐标

  // 计算右上和右下两个坐标点在模型坐标系中旋转后的坐标,用来计算矩形旋转后的最大边界值

// convert content size to world coordinates

float wshw = std::max(fabsf(hSizeX * transform.m[0] + hSizeY * transform.m[4]), fabsf(hSizeX * transform.m[0] - hSizeY * transform.m[4]));

float wshh = std::max(fabsf(hSizeX * transform.m[1] + hSizeY * transform.m[5]), fabsf(hSizeX * transform.m[1] - hSizeY * transform.m[5]));

  // 增加可见范围的尺寸,检测可见性

// enlarge visible rect half size in screen coord

visiableRect.origin.x -= wshw;

visiableRect.origin.y -= wshh;

visiableRect.size.width += wshw * 2;

visiableRect.size.height += wshh * 2;

bool ret = visiableRect.containsPoint(v2p);

return ret;

}

时间: 2024-10-08 10:59:29

cocos2d中的可见性检测的相关文章

Cocos2D中Action的进阶使用技巧(一)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 假设认为写的不好请多提意见,假设认为不错请多多支持点赞.谢谢! hopy ;) 大家对Cocos2d中动作的使用大概都非常清楚了,事实上本身action的概念也不复杂. 可是在某些情况下,一般的动作不能满足我们需求的时候,我们就必须使用更高级的Action方法来解决这个问题. 比方,串行化(不是序列化哦,这是两个全然不同的概念)不同Action的执行-有些童鞋可能会说非常easy,直接用CCActionSequence不就结了,可是等等我

JS中的类型检测

JS中用于类型检测的函数有typeof.instanceof .Object.prototype.toString.constrcutor.duck type typeof用于检测基本类型和函数 有些特殊情况 null.数组.Date数据类型用typeof判断返回的是object instanceof用来判断对象类型,基于原型链,可以用来判断数组和Date数据类型

iOS中使用 Reachability 检测网络

iOS中使用 Reachability 检测网络 内容提示:下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测.   写本文的目的 了解Reachability都能做什么 检测3中网络环境 2G/3G wifi 无网络 如何使用通知 单个controller 多个controller 简单的功能: 仅在wifi下使用 Reachability简介 Reachablity 是一个iOS下... 如果你想在iOS程序中提供一仅在wifi网络下使用(Reeder)

AndroidPN中的心跳检测

在AndroidPN客户端里存在着心跳检测功能.就是每隔一段时间客户端向服务器端发送一个消息,以检测连接是否正常,发送的消息内容为: <presence id="h09Ke-13"></presence> 心跳检测频率时间在SmackConfiguration类中设置: 心跳检测任务的定义及开启在PacketWriter类中: 心跳检测的流程: 1.按心跳时间定时向服务发送心跳数据包: 2.如果发送失败,抛出发送失败的异常: 3.这个异常将被Persistent

cocos2d 中加入显示文字的三种方式(CCLabelTTF 、CCLabelBMFont 和CCLabelAtlas)

在 cocos2d 中有三个类能够在层或精灵中加入文字: CCLabelTTF CCLabelBMFont CCLabelAtlas      CCLabelTTF CCLabelTTF 每次调用 setString (即改变文字)的时候,一个新的OPENGL 纹理将会被创建..这意味着setString 和创建一个新的标签一样慢. 所以,当你须要频繁的更新它们的时候,尽可能的不用去使用标签对象.  而应该使用CCLabelAtlas或者是CCLabelBMFont. OK, 看下它的用法 CC

Cocos2D iOS之旅:如何写一个敲地鼠游戏(二):Cocos2D中的高清支持

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流之用,请勿进行商业用途.同时,转载时不要移除本申明.如产生任何纠纷,均与本博客所有人.发表该翻译稿之人无任何关系.谢谢合作! 高清显示和Cocos2D 好吧,有一个好消息 - 在最新版本的Cocos2D中包括了高清屏的完整支持,你可以用下面1-2-3步就可简单搞定! 在调用CCDirector的en

服务器的散热和Linux中温度的检测

当服务器被放在散热条件不好的条件下,这样会导致硬盘驱动过早损坏,并且服务器其他的组件也会很快出现故障.现代的服务器主板检测到CPU过热的时候,通常会限制CPU的频率,所以即使服务器没有完全损坏,也有可能达到一个无法使用的程度.有时候,组件过热也许会导致进程意外崩溃. 如果数据中心过热,可以购买一个可以检测温度的架装温度计. Linux提供了lm-sensors包工具. sensors 是一个调用检测传感器,用来检测服务器各个部分的温度 sh -c "yes|sensors-detect"

Cocos2D中Node的userObject实例变量使用时一个要注意的地方

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 我们知道在Cocos2D中,CCNode对象有一个ivar为userObject,它可以用来很方便的存储一些和该对象相关的数据,这样你就没必要费劲的去写子类继承了. 但是要注意的是,如果你的node添加到一个节点当做子类,比如: [someBaba addChild:myNode]; 那么在调用如下一句将myNode从父节点中删除时,其中的userObject也

【开发记录】iOS中使用 Reachability 检测网络

如果你想在iOS程序中提供一仅在wifi网络下使用(Reeder),或者在没有网络状态下提供离线模式(Evernote).那么你会使用到Reachability来实现网络检测. 写本文的目的 了解Reachability都能做什么 检测3中网络环境 2G/3G wifi 无网络 如何使用通知 单个controller 多个controller 简单的功能: 仅在wifi下使用 Reachability简介 Reachablity 是一个iOS下检测,iOS设备网络环境用的库. 监视目标网络是否可