前言

  模型较大的时候,出现卡顿,那么使用LOD(细节层次)进行层次细节调整,可以让原本卡顿的模型变得不卡顿。
  本就是LOD介绍。

 

Demo

  

 

LOD

概述

  LOD也称为层次细节模型,是一种实时三维计算机图形技术,旨在通过根据物体在场景中的位置和重要性动态调整其渲染的详细程度,从而提高渲染效率和性能。
  视点离物体近时,能观察到的模型细节丰富;视点远离模型时,观察到的细节逐渐模糊。系统绘图程序根据一定的判断条件,选择相应的细节进行显示,从而避免了因绘制那些意义相对不大的细节而造成的时间浪费,同时有效地协调了画面连续性与模型分辨率的关系。

Osg::LOG节点

  在OSG中,LOD技术通过osg::LOD节点来实现。osg::LOD节点是一个特殊的场景节点,它可以包含多个子节点,每个子节点代表一个不同详细程度的模型。根据视点与物体的距离或屏幕上的像素大小,osg::LOD节点会选择相应的子节点进行渲染。

子节点的添加

  通过addChild方法,可以将不同详细程度的模型作为子节点添加到osg::LOD节点中。每个子节点都需要指定一个距离范围(或像素大小范围),在这个范围内,该子节点会被渲染。

距离和像素大小模式

  osg::LOD节点支持两种切换模式距离模式和像素大小模式。

  • 距离模式:根据视点到物体包围盒中心的距离来选择子节点;
  • 像素大小模式:根据物体在屏幕上的像素大小来选择子节点。

中心模式的设置

  对于距离模式,osg::LOD节点还支持两种中心模式:包围盒中心模式和自定义中心模式。包围盒中心模式使用物体的包围盒中心作为计算距离的点;自定义中心模式则允许用户指定一个自定义的中心点。
  LOD技术的优点和应用

  • 提高渲染效率:通过动态调整模型的详细程度,LOD技术可以显著减少需要渲染的多边形数量,从而提高渲染速度。
  • 优化内存使用:虽然OSG中的osg::LOD节点会一次性载入所有模型进入内存,但它只是有选择地进行绘制,这仍然有助于优化内存使用,因为不需要为每个模型都分配独立的内存空间。此外,OSG还提供了osg::PagedLOD节点,它支持动态分页加载,可以根据需要来加载模型文件,进一步优化内存使用。
  • 提升视觉效果:LOD技术可以在保证视觉效果的前提下,通过简化模型来减少渲染负担,从而允许开发者在场景中放置更多的物体或实现更复杂的视觉效果。
  • 广泛的应用场景:LOD技术适用于各种需要高效渲染的三维场景,如城市规划、地形渲染、游戏开发等。在这些场景中,物体的数量和详细程度往往非常高,使用LOD技术可以显著提高渲染性能和用户体验。

LOD技术的局限性

  尽管LOD技术具有诸多优点,但它也存在一些局限性。例如,在切换不同详细程度的模型时,可能会出现视觉上的跳跃现象,特别是当两个模型之间的详细程度差异较大时。此外,设计和管理不同详细程度的模型也需要一定的时间和资源投入。

其他

  OSG中的LOD技术是一种高效且灵活的三维渲染技术,它通过动态调整模型的详细程度来优化渲染性能和内存使用。在开发大规模三维场景时,LOD技术是一个不可或缺的工具。

 

LOD实现

  模型就不多说了,关键就是osg::Lod结点,如何添加的问题:

            // 添加模式,0~100范围内使用线模型
pLod->addChild(pGeode, 0, 100);

  

           // 添加模型,非设置的范围内的都是这个
// pLod->addChild(pGeode); // 不能使用,预想中是没设置的都使用这个,实际上这个函数实际无用,反正都不显示
pLod->addChild(pGeode, 100, 1000);

  

 

Demo关键源码

绘制部分

// 绘图
{
#if 1
// 第一个模型
for(int partIndex = 0; partIndex < kMode.listPart.size(); partIndex++)
{
// 创建一个用户保存几何信息的对象
osg::ref_ptr<osg::Geometry> pGeometry = new osg::Geometry;
// 创建四个顶点的数组
osg::ref_ptr<osg::Vec3Array> pVec3Array = new osg::Vec3Array;
// 添加四个顶点
pGeometry->setVertexArray(pVec3Array.get()); // 创建四种颜色的数据
osg::ref_ptr<osg::Vec4Array> pVec4Array = new osg::Vec4Array;
// 添加四种颜色
pGeometry->setColorArray(pVec4Array.get());
// 绑定颜色
pGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); double r, g, b;
r = 1.0f;
g = 1.0f;
b = 0.0f;
for(int elementShellIndex = 0; elementShellIndex < kMode.listPart.at(partIndex).listElementShell.size(); elementShellIndex++)
{
// x y z
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).z)); // r g b a(a设置无效,估计需要其他属性配合)
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); }
// 注意:此处若不绑定画笔,则表示使用之前绑定的画笔 // 为唯一的法线创建一个数组 法线: normal
osg::ref_ptr<osg::Vec3Array> pVec3ArrayNormal = new osg::Vec3Array;
pGeometry->setNormalArray(pVec3ArrayNormal.get());
pGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
pVec3ArrayNormal->push_back(osg::Vec3(0.0, -1.0, 0.0)); // 由保存的数据绘制四个顶点的多边形
pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, kMode.listPart.at(partIndex).listElementShell.size() * 4));
// pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); // 向Geode类添加几何体(Drawable)
osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
pGeode->addDrawable(pGeometry.get());
#if 1
// 线宽模式
{
osg::ref_ptr<osg::StateSet> pStateSet = pGeometry->getOrCreateStateSet();
osg::ref_ptr<osg::PolygonMode> pPolygonMode = new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
pStateSet->setAttribute(pPolygonMode);
}
#endif
// 添加模式,0~100范围内使用线模型
pLod->addChild(pGeode, 0, 100);
// 只是用一个部件
break;
}
#endif
#if 1
// 第一个模型
for(int partIndex = 0; partIndex < kMode.listPart.size(); partIndex++)
{
// 创建一个用户保存几何信息的对象
osg::ref_ptr<osg::Geometry> pGeometry = new osg::Geometry;
// 创建四个顶点的数组
osg::ref_ptr<osg::Vec3Array> pVec3Array = new osg::Vec3Array;
// 添加四个顶点
pGeometry->setVertexArray(pVec3Array.get()); // 创建四种颜色的数据
osg::ref_ptr<osg::Vec4Array> pVec4Array = new osg::Vec4Array;
// 添加四种颜色
pGeometry->setColorArray(pVec4Array.get());
// 绑定颜色
pGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); double r, g, b;
r = 1.0f;
g = 1.0f;
b = 0.0f;
for(int elementShellIndex = 0; elementShellIndex < kMode.listPart.at(partIndex).listElementShell.size(); elementShellIndex++)
{
// x y z
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n1).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n2).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n3).z));
pVec3Array->push_back(osg::Vec3(kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).x,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).y,
kMode.hashNid2Node.value(kMode.listPart.at(partIndex).listElementShell.at(elementShellIndex).n4).z)); // r g b a(a设置无效,估计需要其他属性配合)
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0));
pVec4Array->push_back(osg::Vec4(r, g, b, 1.0)); }
// 注意:此处若不绑定画笔,则表示使用之前绑定的画笔 // 为唯一的法线创建一个数组 法线: normal
osg::ref_ptr<osg::Vec3Array> pVec3ArrayNormal = new osg::Vec3Array;
pGeometry->setNormalArray(pVec3ArrayNormal.get());
pGeometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
pVec3ArrayNormal->push_back(osg::Vec3(0.0, -1.0, 0.0)); // 由保存的数据绘制四个顶点的多边形
pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, kMode.listPart.at(partIndex).listElementShell.size() * 4));
// pGeometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); // 向Geode类添加几何体(Drawable)
osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
pGeode->addDrawable(pGeometry.get());
// 添加模型,非设置的范围内的都是这个
// pLod->addChild(pGeode); // 不能使用,预想中是没设置的都使用这个,实际上这个函数实际无用,反正都不显示
pLod->addChild(pGeode, 100, 1000);
// 只是用一个部件
break;
}
#endif
pGroup->addChild(pLod);
}
 

工程模板v1.34.0

  

 

入坑

入坑:复制osg::ref_ptr相关类失败

问题

  想深度复制一个模型,做lod,复制编译不过
  

原理

  加了osg::ref_ptr,不能常规方法复制,也尝试使用get()之后在*取其类实体(非指针),也报错。
  下面是浅拷贝的示例:
  

  无法直接深拷贝,osg::ref_ptrosg::Geometry 本身并不提供直接的深拷贝功能,因为 osg::ref_ptr 只是一个智能指针,它管理对象的生命周期,但并不关心对象的具体内容或如何复制这些内容。深拷贝通常涉及到对象的逐字段复制,这通常需要在对象类内部实现,或者通过外部函数来实现。
  在 OpenSceneGraph(OSG)中,osg::Geometry 类没有内置的深拷贝方法,因此需要自己实现深拷贝逻辑。这通常包括复制几何体的所有属性,如顶点数组、颜色数组、法线数组、纹理坐标数组、图元集等。

解决

  直接复制前面一段代码,区别得地方调整下,当作新模型加入。

入坑二:lod添加节点不设置范围的不显示

问题

  lod添加节点不设置范围的不显示

原理

  这个函数看起来就是没用,改成都设置范围

解决

  

OSG开发笔记(三十一):OSG中LOD层次细节模型介绍和使用的更多相关文章

  1. angular学习笔记(三十一)-$location(2)

    之前已经介绍了$location服务的基本用法:angular学习笔记(三十一)-$location(1). 这篇是上一篇的进阶,介绍$location的配置,兼容各版本浏览器,等. *注意,这里介绍 ...

  2. angular学习笔记(三十一)-$location(1)

    本篇介绍angular中的$location服务的基本用法,下一篇介绍它的复杂的用法. $location服务的主要作用是用于获取当前url以及改变当前的url,并且存入历史记录. 一. 获取url的 ...

  3. Django笔记三十一之全局异常处理

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十一之全局异常处理 这一篇笔记介绍 Django 的全局异常处理. 当我们在处理一个 request 请求时,会尽可能的对接口数据的格式 ...

  4. Django开发笔记三

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.基于类的方式重写登录:views.py: from ...

  5. Modbus库开发笔记之十一:关于Modbus协议栈开发的说明(转)

    源: Modbus库开发笔记之十一:关于Modbus协议栈开发的说明

  6. Modbus库开发笔记之十一:关于Modbus协议栈开发的说明

    对于Modbus协议栈的整个开发内容,前面已经说得很清楚了,接下来我们说明一下与开发没有直接关系的内容. 首先,关于我为什么开发这个协议栈的问题.我们的初衷只是想能够在开发产品时不用每次都重写这一部分 ...

  7. iOS开发笔记7:Text、UI交互细节、两个动画效果等

    Text主要总结UILabel.UITextField.UITextView.UIMenuController以及UIWebView/WKWebView相关的一些问题. UI细节主要总结界面交互开发中 ...

  8. Hi3516开发笔记(一):海思HI3516DV300芯片介绍,入手开发板以及Demo测试

    前言   目前主流国产芯片为RV11XX.RK33XX.Hi35XX系列,本系列开启Hi3516系列的开发教程.   Hi3516DV300芯片介绍   Hi3516DV300为专业行Smart IP ...

  9. Android UI开发第三十一篇——Android的Holo Theme

    好长时间没写Android UI方面的文章了,今天就闲扯一下Android的Holo主题.一直做android开发的可能都知道,Android 系统的UI有过两次大的变化,一次是android 3.0 ...

  10. Java学习笔记三十一:Java 包(package)

    Java 包(package) 一:包的作用: 如果我们在使用eclipse等工具创建Java工程的时候,经常会创建包,那么,这个包是什么呢. 为了更好地组织类,Java 提供了包机制,用于区别类名的 ...

随机推荐

  1. MFC 静态拆分视图窗口

    今天学习了MFC中拆分窗口,现将方法记录下. 想要在窗口视图中拆分成左右两个视图窗口,首先要注意的是拆分后要加载到左右的视图要符合动态创建的类, 也就是要在自己创建的视图类中添加动态创建机制宏. 类内 ...

  2. C#ListView类的继承

    ListView控件类新加方法 新建一个类myListView class myListView : System.Windows.Forms.ListView { //添加自定义的方法 -- //设 ...

  3. 如何在 Nuxt 中动态设置页面布局

    title: 如何在 Nuxt 中动态设置页面布局 date: 2024/8/24 updated: 2024/8/24 author: cmdragon excerpt: 摘要:本文介绍如何在Nux ...

  4. idea下spring切换jdk版本

    1.首先打开项目配置设置 2. 修改project中的配置 3. 修改modules中的配置 这个方法不需要修改pom.xml文件 如果有问题请指正   及时修改 2022年9月10日16:42:16

  5. Vue开发转到React开发,Prettier - Code formatter失效的问题

    Vue转到React,Prettier - Code formatter失效,按下Ctrl+S无效,需要手动格式化一次 然后选择默认的格式化方式 之后按下Ctrl+S就可以进行格式化啦!!!

  6. C语言:应用程序增加库函数rand的步骤

    rand函数用来生成随机数,函数原型为int rand( void ); 返回值为生成的随机数,范围0~32767.在调用rand之前可以用srand函数初始化随机数发生器来生成更随机的数. 可以通过 ...

  7. Openstack-删除卷:您被禁止执行 删除卷 (僵尸卷)

    您被禁止执行 删除卷 (僵尸卷) 您被禁止执行 删除卷: 7f23a26a-27f2-4504-9191-0f5630a5bff5, 卷一直在创建,但实例已经被删除了 [root@controller ...

  8. yum命令提示error: rpmdb: BDB0113 Thread/process,解决方法

    最近在做RHCE的题目,yum命令装vdo时,使用yum install命令的时候,提示error: rpmdb: BDB0113 Thread/process,具体错误如下: [root@node2 ...

  9. Redis 入门 - 图形化管理工具如何选择,最全分类

    工欲善其事必先利其器,上一章Redis服务环境已经搭建完成,现在就需要一个趁手的工具,有个好工具可以做到事半功倍. Redis图形化管理工具五花八门,可供选择的很多,大家可以根据自己的需求应用场景进行 ...

  10. SQL Server 中的 NUL 设备/NIL设备

    SQL Server 中的 NUL 设备/NIL设备 在 SQL Server 中,有一个特殊的设备叫做 NUL(注意,不是 NULL),它类似于文件系统中的"黑洞".NUL 设备 ...