前言

  模型较大的时候,出现卡顿,那么使用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. C语言基础- Hello World

    第一个C语言程序 Hello World # include <stdio.h> //#关键标识符,表示用用头文件:include:引入都文件关键字 // stdio.h:系统标准输入.输 ...

  2. Microsoft Build Next-Gen Windows Dev (placeholder)

    This is a placeholder page for Microsoft Build after party in next Month. Will update once I get det ...

  3. Python3将web服务和脚本做成开机自启

    1.将bwService文件放到 /etc/init.d/下 bwService文件(类型是文件) #!/bin/bash # # This shell script takes care of st ...

  4. 新一代监控神器Prometheus+Grafana介绍及使用

    一.介绍 1.什么是Prometheus? 普罗米修斯是一个开源的系统监控及报警工具,在2016年加入了 Cloud Native Computing Foundation,是继Kubernetes之 ...

  5. Go plan9 汇编:内存对齐和递归

    Go plan9 汇编系列文章: Go plan9 汇编: 打通应用到底层的任督二脉 Go plan9 汇编:手写汇编 Go plan9 汇编:说透函数栈 0. 前言 在 Go plan9 汇编系列文 ...

  6. 6.2 XXE和XML利用

    pikaqu靶场xml数据传输测试-有回显,玩法,协议,引入 1.构造payload 写文件 <?xml version="1.0" encoding="UTF-8 ...

  7. 记一次 公司.NET项目部署在Linux环境压测时 内存暴涨分析

    一:背景 讲故事 公司部署在某碟上的项目在9月份压测50并发时,发现某个容器线程.内存非正常的上涨,导致功能出现了异常无法使用.根据所学,自己分析了下线程和内存问题,分析时可以使用lldb或者wind ...

  8. 教你一招,测试人员如何通过AI提高工作效率!

    伴随着AI技术的兴起,像OpenAI推出的ChatGPT.Microsoft发布的Microsoft 365 Copilot.阿里的通义千问.百度的文心一言.华为的盘古大模型等.很多测试人员开始担心, ...

  9. duxapp:基于Taro使用模块化开发,提升开发效率

    duxapp是基于Taro二次开发的模块化框架 使用这个框架,结合框架提供的UI库和工具库,能帮助你快速且高质量的完成项目,且能实现同时开发小程序.H5.APP(React Native),并且保证各 ...

  10. YAML编写应用的资源清单文件(十五)

    上面我们在 Kubernetes 中部署了我们的第一个容器化应用,我们了解到要部署应用最重要的就是编写应用的资源清单文件.那么如何编写资源清单文件呢?日常使用的时候我们都是使用 YAML 文件来编写, ...