OpenGL学习-----实用技术:场景管理与视锥体剔除

这篇具有很好参考价值的文章主要介绍了OpenGL学习-----实用技术:场景管理与视锥体剔除。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

场景管理:

场景树建立:

视锥体剔除:

视锥体:

包围盒:

球:

AABB:

判断是否在视锥体内:

包围盒为球体:

包围盒为AABB:

空间加速:


场景管理:

场景树建立:

场景树的建立,这里讲一下建树,树里面保存根据自己的model矩阵,这个矩阵需要根据父物体的model矩阵做出相对变化。出于学习目的:代码会以欧拉角的形式展示,也是只是用oop,而不是odp

属性的定义:

class Transform
{
protected:
    glm::vec3 m_pos ={0.0f,0.0f,0.0f};
    glm::vec3 m_eulerRot ={0.0f,0.0f,0.0f};
    glm::vec3 m_scale ={1.0f,1.0f,1.0f};
    glm::mat4 m_modelMatrix = glm::mat4(1.0f);
protected:
    glm::mat4 getLocalModelMatrix()
    {
        const glm::mat4 transformX = glm::rotate(glm::mat4(1.0f), glm::radians(m_eulerRot.x), glm::vec3(1.0f,0.0f,0.0f));
        const glm::mat4 transformY = glm::rotate(glm::mat4(1.0f), glm::radians(m_eulerRot.y), glm::vec3(0.0f,1.0f,0.0f));
        const glm::mat4 transformZ = glm::rotate(glm::mat4(1.0f), glm::radians(m_eulerRot.z), glm::vec3(0.0f,0.0f,1.0f));
        const glm::mat4 roationMatrix = transformY * transformX * transformZ;
        return glm::translate(glm::mat4(1.0f), m_pos)* roationMatrix * glm::scale(glm::mat4(1.0f), m_scale);
    }
public:
    void computeModelMatrix()
    {
        m_modelMatrix =getLocalModelMatrix();
    }
    void computeModelMatrix(const glm::mat4&parentGlobalModelMatrix)
    {
        m_modelMatrix = parentGlobalModelMatrix *getLocalModelMatrix();
    }
    [...]
}

那具体怎么更新模型矩阵呢?如果一个物体他有父物体那么他需要把父物体的旋转也应用上

   void forceUpdateSelfAndChild()
    {
        if(parent)
            transform.computeModelMatrix(parent->transform.getModelMatrix());
        else
            transform.computeModelMatrix();
        for(auto&& child : children)
        {
            child->forceUpdateSelfAndChild();
        }
    }

一个树节点的定义(简化版):

class Entity
{
public:
    std::list<std::unique_ptr<Entity>> children;
    Entity* parent =nullptr;
    Transform transform;
    Model* pModel =nullptr;

    [...]

    void addChild(TArgs&...args)
    {
        children.emplace_back(std::make_unique<Entity>(args...));
        children.back()->parent=this;
    }

    [...]
};

视锥体剔除:

在大量物体渲染的时候,我们需要限制GPU的使用,这里有项技术,就是视锥体剔除。

视锥体:

在之前csm的文章里,我怎么表示一个视锥体的呢?我主要是通过视锥体的八个点来表示一个视锥体(这些点可以根据相机的位置和方向进行求解或者根据NDC空间坐标反推世界空间的坐标),但是这里我们采用一个更加合适视锥体剔除的新的方法。

我们可以使用法线和平面到原点的距离来表示一个平面:

struct Plan
{
    glm::vec3 normal ={0.f,1.f,0.f};
    float  distance =0.f;   
    Plan()=default;   
    Plan(const glm::vec3&p1,const glm::vec3&norm)
    :normal(glm::normalize(norm)),
    distance(glm::dot(normal, p1))
    {}       
};

六个面组成一个视锥体:

struct Frustum
{
    Plan topFace;
    Plan bottomFace;
    Plan rightFace;
    Plan leftFace;
    Plan farFace;
    Plan nearFace;
};

创建一个视锥体:

Frustum createFrustumFromCamera(const Camera&cam,floataspect,floatfovY,floatzNear,floatzFar)
{
    Frustum  frustum;
    constfloat halfVSide = zFar *tanf(fovY *.5f);
    constfloat halfHSide = halfVSide * aspect;
    const glm::vec3 frontMultFar = zFar *cam.Front;
    frustum.nearFace={cam.Position+ zNear *cam.Front,cam.Front};
    frustum.farFace={cam.Position+ frontMultFar,-cam.Front};
    frustum.rightFace={cam.Position, glm::cross(cam.Up, frontMultFar +cam.Right* halfHSide)};
    frustum.leftFace={cam.Position, glm::cross(frontMultFar -cam.Right* halfHSide,cam.Up)};
    frustum.topFace={cam.Position, glm::cross(cam.Right, frontMultFar -cam.Up* halfVSide)};
    frustum.bottomFace={cam.Position, glm::cross(frontMultFar +cam.Up* halfVSide,cam.Right)};
    return frustum;
}

包围盒:

最简单的包围盒就是球体,以及稍稍进阶一点的AABB,接下来都会讲到

球:

球的表示就比较简单了,圆心和半径

AABB:

AABB的表示有两种:可以保存最大点和最小点(参考我的光追文章),也可以保存中心点和任意一个点

(需要注意,aabb需要在旋转缩放之后更新最大点/最小点/Extent)

然后排列组合就能求出AABB的八个顶点了。

struct AABB :public BoundingVolume
{
    glm::vec3 center{0.f,0.f,0.f};
    glm::vec3 extents{0.f,0.f,0.f};
    AABB(const glm::vec3&min,const glm::vec3&max)
        :BoundingVolume{},
        center{(max + min)*0.5f},
        extents{max.x-center.x,max.y-center.y,max.z-center.z}
    {}
    AABB(const glm::vec3&inCenter,floatiI,floatiJ,floatiK)
        :BoundingVolume{},center{ inCenter },extents{ iI, iJ, iK }
    {}
    [......]
};

判断是否在视锥体内:

核心是我们怎么判断他是否在视锥体内?

包围盒为球体:

这里采用的方法是:前面不是保存了每个面的法线吗,可以用法线投影求出球心到平面的距离,从而判断出是否在平面外。六个平面都判断一下就知道是不是在视锥体内了。

因为平面是无限延申的,而且点的位置的vec3其实也可以表示从原点指向点的一个向量,那我们点乘一下,求出其在法线上的投影,然后减去平面距离原点的距离就能得出结果。

glm::dot(normal, point) - distance;

判断球是否在视锥体内完整代码:

float getSignedDistanceToPlan(const glm::vec3&point)const
{
    return glm::dot(normal, point)- distance;
}
bool isOnOrForwardPlan(const Plan&plan)const
{
    returnplan.getSignedDistanceToPlan(center)>-radius;
}

bool isOnFrustum(const Frustum&camFrustum,const Transform&transform)constfinal
{
    [......]
    return (globalSphere.isOnOrForwardPlan(camFrustum.leftFace) &&
        globalSphere.isOnOrForwardPlan(camFrustum.rightFace) &&
        globalSphere.isOnOrForwardPlan(camFrustum.farFace) &&
        globalSphere.isOnOrForwardPlan(camFrustum.nearFace) &&
        globalSphere.isOnOrForwardPlan(camFrustum.topFace) &&
        globalSphere.isOnOrForwardPlan(camFrustum.bottomFace));
};

包围盒为AABB:

这里采用的方法是:计算 AABB( c) 的中心和它的范围 ( e)。然后我们将范围折叠e到平面法线上。在这一点上,我们有一条在两个方向上都有中心点c和范围的线r。然后我们计算线到平面的距离s,如果线的距离超过其长度的一半,则它不会相交。然后我们再重复算六个面就行

OpenGL学习-----实用技术:场景管理与视锥体剔除

判断AABB是否在视锥体内完整代码:

bool isOnOrForwardPlan(const Plan&plan)const
{
    const float r =extents.x* std::abs(plan.normal.x)+
            extents.y* std::abs(plan.normal.y)+extents.z* std::abs(plan.normal.z);
    return-r <=plan.getSignedDistanceToPlan(center);
}
bool isOnFrustum(const Frustum&camFrustum,const Transform&transform)constfinal
{
    [...]
    return (globalAABB.isOnOrForwardPlan(camFrustum.leftFace) &&
        globalAABB.isOnOrForwardPlan(camFrustum.rightFace) &&
        globalAABB.isOnOrForwardPlan(camFrustum.topFace) &&
        globalAABB.isOnOrForwardPlan(camFrustum.bottomFace) &&
        globalAABB.isOnOrForwardPlan(camFrustum.nearFace) &&
        globalAABB.isOnOrForwardPlan(camFrustum.farFace));
};

(注意:上述的所有算法的计算如果在相机空间进行更好理解也会更少错误)

空间加速:

如果我们每个物体都算一边其实也有点消耗的,一般得进行加速。这个其实可以做bvh或者八叉树等等。

(注意:空间加速树和场景管理树不一样,场景管理树一般根据逻辑划分父子节点,空间加速树根据位置划分父子节点)

BVH的话可以看我之前光照的文章光追渲染器开发记录:BVH加速结构构建与射线求交_This is MX的博客-CSDN博客

八叉树的简历其实需要找到所有物体的最大包围盒bound,然后xyz各进行一次划分,不断进行下去,直到子空间没有物体了或者只有一个物体之后就不划分了。可以参考这篇点云的文章:

Open3d之八叉树(Octree)_ancy_i_cv的博客-CSDN博客_octree python

这里就暂时不详细展开了文章来源地址https://www.toymoban.com/news/detail-422769.html

到了这里,关于OpenGL学习-----实用技术:场景管理与视锥体剔除的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA基础知识-实用建模技术

    目录 学习目标: 学习内容: 1.过程连续赋值 2.改写参数 3.条件编译和执行 4.时间尺度 5.常用的系统任务 学习时间: 学习总结 提示:这里可以添加学习目标 1.掌握怎样在模块调用时用defparam语句重新定义参数值J解释条件编译和Verilog 插述部件的执行。· 2.认识和理解系统任务

    2024年02月10日
    浏览(30)
  • SpringBoot【开发实用篇】---- 整合第三方技术(缓存)

    企业级应用主要作用是信息处理,当需要读取数据时,由于受限于数据库的访问效率,导致整体系统性能偏低。 应用程序直接与数据库打交道,访问效率低 为了改善上述现象,开发者通常会在应用程序与数据库之间建立一种临时的数据存储机制,该区域中的数据在内存中保

    2024年02月05日
    浏览(41)
  • 技术分享:PHP读取TXT文本内容的五种实用方法

    在Web开发中,我们经常需要读取和处理文本文件。PHP作为一种流行的服务器端脚本语言,提供了多种方法来读取TXT文本内容。本文将介绍五种不同的PHP教程,帮助您学习如何使用PHP读取TXT文本内容。PHP读取文件内容在实际开发当中,还是比较常见的,所以今天我就给大家分享

    2024年01月18日
    浏览(32)
  • 深入理解Java核心技术:Java工程师的实用干货笔记

    💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】 🤟 基于Web端打造的:👉轻量化工具创作平台 💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 在Java工程师的职业生涯中,深入理解Java核心技术是至关重要的。这不仅仅是为了更好地编写代码,还有

    2024年02月04日
    浏览(35)
  • 【100个 Unity实用技能】 | Unity 的 LOD技术(多细节层次)

    🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本文由 呆呆敲代码的小Y 原创,首发于 CSDN 🙉 🎄 学习专栏推荐:Unity系统学习专栏 🌲 游戏制作专栏推荐:游戏制作 🌲Unity实战100例专栏推荐:Unity 实战100例 教程 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📆 未来很长

    2024年02月07日
    浏览(30)
  • 【Docker】Linux中Docker技术入门与实战及实用的常规命令

    目录 一、引言 1. 说明: 2. Linux介绍 3. Docker简介 二、Docker三要素 1. 镜像(Image) 2. 容器(Container) 3. 仓库(Repository) 三、实现案例 1. 创建 2. 设置镜像 3. 开启设置 4. Docker安装 5. Docker使用及命令 每篇一获 Linux 是开源的,因此是免费的。 Web 主机仅需要支付技术支持即可安

    2024年02月01日
    浏览(25)
  • AI 时代,程序员无需焦虑 | 《服务端开发:技术、方法与实用解决方案》

    ChatGPT 横空出世后,“AI 即将取代程序员” 的观点一度引发热议,至今尚未完全冷却。作为一名服务端开发工程师,同时也是 ChatGPT 的“忠实”用户,经过将近一年的使用,今天抽空写一篇文章,谈谈自己对“AI 时代,程序员何去何从?”这一问题的看法。 2022 年 11 月 30 日

    2024年02月04日
    浏览(40)
  • Linux与C/C++服务器开发:深入探索网络编程与实用技术(文末送书)

    🎥 屿小夏 : 个人主页 🔥个人专栏 : 书籍推荐 🌄 莫道桑榆晚,为霞尚满天! 本文将探讨构建高性能Linux C/C++服务器的关键技术和最佳实践,包括优化服务器性能、处理并发和并行性、高效管理内存,以及利用高级网络功能等。我们还将推荐一本相关的书籍《Linux C/C++服务

    2024年02月04日
    浏览(34)
  • 【实用技巧】掌握人工智能语音转换的核心技术,轻松实现多语言语音转换和语音合成

    作者:禅与计算机程序设计艺术 【实用技巧】掌握人工智能语音转换的核心技术,轻松实现多语言语音转换和语音合成 1.1. 背景介绍 随着全球化的加速,跨文化交流需求日益增长,多语言语音转换和语音合成技术应运而生。人工智能技术的发展为语音合成和转换提供了便利

    2024年02月08日
    浏览(45)
  • AI 时代,程序员无需焦虑 | 《服务端开发:技术、方法与实用解决方案》(文末送书福利4.0)

    ChatGPT 横空出世后,“AI 即将取代程序员” 的观点一度引发热议,至今尚未完全冷却。作为一名服务端开发工程师,同时也是 ChatGPT 的“忠实”用户,经过将近一年的使用,今天抽空写一篇文章,谈谈自己对“AI 时代,程序员何去何从?”这一问题的看法。 2022 年 11 月 30 日

    2024年02月11日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包