OpenSceneGraph学习笔记

news2024/9/20 10:41:13

目录

  • 引言
  • 第一章:OSG概述
    • 一、前言
      • (1)为什么要学习OSG?
      • (2)OSG的组成
      • (3)OSG的智能指针
      • (4)OSG的安装编译
    • 二、第一个OSG程序
      • (1)Hello OSG程序
      • (2)OSG渲染程序的基本流程
  • 第二章:数学基础
    • 一、坐标系统
      • (1)世界坐标系
      • (2)物体坐标系
      • (3)摄像机坐标系
    • 二、坐标系变化
      • (1)物体坐标系-世界坐标系变化
      • (2)访问器

引言

 本文为我学习OSG库的笔记,其目的是在已有计算机图形学的基础上,记录OSG的关键知识,以达到快速学习的目的。
 教材:OSG-DOC.pdf。

第一章:OSG概述

一、前言

(1)为什么要学习OSG?

  我在本科毕业时尝试使用DirectX去设计一个通用图形库,它旨在为Windows上应用程序提供图像计算和渲染服务,使得程序开发者不需要了解计算机图形学以及图像API,就能渲染出满意的图形。
 在设计过程中,学习DirectX12并不是我遇到最难的问题,而是如何设计通用图形库的架构,如何构建通用的Shader接口?如何提供和Shader配套的设置?如何组织场景对象?这些是需要思考的问题。
 OSG场景图像系统是使用OpenGL开发的库,它能让程序员更快速、便捷地创建高性能和跨平台的交互式图像程序。也许你会用OpenGL,但你是否能够设计出通用、高性能、跨平台的OpenGL封装库呢?
 无论是工作要使用OSG,还是学习如何设计图形引擎甚至游戏引擎,学习OSG都将使你受益匪浅。

(2)OSG的组成

 在系统的底层绘图硬件和相应的软件驱动程序之上,OSG封装了OpenGL。
在这里插入图片描述

 OSG由多个模块组成,它主要包括如下4个库(通读一遍下文即可)。
在这里插入图片描述
在这里插入图片描述

(3)OSG的智能指针

 OSG提供了智能指针类osg::ref_ptr来管理内存并防止内存泄漏,智能指针使用示例如下。

// 创建场景浏览器实例osgViewer::Viewer
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer(); // 推荐写法
osgViewer::Viewer* viewer = new osgViewer::Viewer(); // 不推荐写法

(4)OSG的安装编译

 在此不再赘述,可查阅OSG中文社区或非官方教程。

二、第一个OSG程序

(1)Hello OSG程序

 程序代码如下,本文所有代码具有注释,非关键点不再赘述。

#include<osgViewer/Viewer>

#include<osg/Node>
#include<osg/Geode>
#include<osg/Group>

#include<osgDB/ReadFile>
#include<osgDB/WriteFile>

#include<osgUtil/Optimizer>

int main()
{
	// 创建场景浏览器实例osgViewer::Viewer
	osg::ref_ptr<osgViewer::Viewer> viewer
		= new osgViewer::Viewer();

	// 创建一个场景组节点osg::Group
	osg::ref_ptr<osg::Group> root
		= new osg::Group();

	// 创建一个节点osg::node,并将牛模型读入到此节点中
	osg::ref_ptr<osg::Node> node
		= osgDB::readNodeFile("cow.osg");

	// 将node节点加入为group节点的子节点
	root->addChild(node.get());

	// 优化场景数据结构
	osgUtil::Optimizer optimizer;
	optimizer.optimize(root.get());

	// 将group节点设置为场景浏览器的场景数据
	viewer->setSceneData(root.get());

	// 初始化并创建窗口
	viewer->realize();

	// 开始渲染
	viewer->run();

	return 0;
}

 代码的学习,建议大家首先照着代码和注释打一遍,然后理解注释和代码,再然后删掉注释按照理解自己重写注释,最后删掉原代码按照注释还原代码。(找不到cow.osg文件的安装OSG-DATA并配置到环境变量,若使用了vcpkg可能会忽略环境变量,可在vs中进行设置)

(2)OSG渲染程序的基本流程

 根据上述程序,可知OSG场景渲染程序的基本流程如下:

步骤内容
1创建场景浏览器,即通过osgViewer::Viewer类创建对象,用于渲染场景
2加载模型和场景数据
3建立场景树,确定场景数据之间的关系
4执行渲染场景的循环

 OSG提供许多丰富功能可使用命令行使用,本文专注于图像库的使用和设计,因此不再叙述,详情可看教材原文。

第二章:数学基础

 虽然学习过计算机图形学,但学习OSG是怎样对图形学系统进行封装的,是非常有必要的。
 OSG中的向量类有如下种类。
在这里插入图片描述

一、坐标系统

 坐标系是什么?坐标系是一个精确定位对象位置的框架,所有的图形变换都是基于一定的坐标系进行的。
 常见坐标系有世界坐标系、物体坐标系和摄像机坐标系。

(1)世界坐标系

 世界坐标系又称为全局坐标系,它描述的是整个场景中的所有对象,它为所有对象的位置提供一个绝对的参考标准,可以理解为绝对坐标系,因为所有对象的位置都是绝对坐标。

(2)物体坐标系

 物体坐标系是针对某一特定的物体建立的独立坐标系,它使得描述单独物体非常方便,比如建模师可能会在空间原点附近建模一个人体,这个人体模型就位于物体坐标系。
 建模师通常不在乎模型会被放到世界的哪个角落,它只需要在物体坐标系下建模好人物,然后生成人物的多个动画,当3D开发者使用时将模型变换到世界坐标即可。

(3)摄像机坐标系

 摄像机坐标系是和观察者相关的坐标系。摄像机坐标系和屏幕坐标系类似,但二者的差异在于摄像机坐标系处于3D空间中,而屏幕坐标系在2D平面里。
 摄像机坐标系描述的问题是:“哪些物体应该渲染并显示在屏幕上?”,主要包括物体是否在摄像机坐标系区域内、物体的渲染顺序和物体的遮挡剔除。
 OSG和OpenGL的世界坐标系都是左手坐标系,并且X轴都是向右,但OpenGL的Y轴向上且Z轴向你(即垂直指向屏幕外),而OSG的Z轴向上且Y轴垂直屏幕向里,具体见下图。
在这里插入图片描述

二、坐标系变化

(1)物体坐标系-世界坐标系变化

 三维实体对象需要经过一系列的坐标变化才能正确、真实地显示在屏幕上。
 每个物体对象都定义在自己的物体坐标系下,当渲染时,每个物体对象通过变化矩阵变换到世界坐标系中。
 如何在OSG中实现从物体坐标系到世界坐标系呢?OSG以节点组成场景树,每个节点都有自己的父节点和自己的变化矩阵,变化矩阵记录了如何从自己的坐标系变化到父节点坐标系,因此只需将该节点与根节点之间所有节点的变化矩阵相乘即可。
 如何实现上述遍历和计算过程呢?在OSG中有多种方式,如回调、访问器等。用访问器的好处是方便可控,每一帧都会自动计算矩阵变化,但缺点是回调在一定程度上不可操控,并且会增加额外开销而影响渲染效率。

(2)访问器

 访问器通过遍历的方式记录场景中节点的路径,并根据路径上的变化矩阵计算出世界坐标。下面以代码的形式展示访问器的使用。

#include<osgViewer/Viewer>

#include<osg/Node>
#include<osg/Geode>
#include<osg/Group>

#include<osgDB/ReadFile>
#include<osgDB/WriteFile>

#include<osgUtil/Optimizer>

// 手工如何计算?从节点到根节点,将变换矩阵逐个相乘计算最终结果。

// 定义新的节点访问器类,以实现对节点和场景树的自定义形式访问
// 新访问器类需要继承osg::NodeVisitor
class GetWorldCoordinateOfNodeVisitor : public osg::NodeVisitor
{
public:
	// 节点访问器类的构造函数,需要初始化osg::NodeVisitor
	// NodeVisitor::TRAVERSE_PARENTS表示访问目标节点和其父节点
	GetWorldCoordinateOfNodeVisitor() :
		osg::NodeVisitor(NodeVisitor::TRAVERSE_PARENTS), done(false)
	{
		/*
			osg::ref_ptr主要用于自动管理那些继承自osg::Referenced类的对象,
			而osg::VecN和osg::MatrixT类似整数和浮点数直接使用即可,
			但是osg::VecN*和osg::MatrixT*搭配new申请空间时,需要自己手动释放,否则会造成内存泄漏
		*/
		wcMatrix = new osg::Matrixd();
	}

	// 自定义访问节点和场景树的方式
	virtual void apply(osg::Node& node)
	{
		// done标识是否遍历到根节点,就像手算一样若遍历到根节点则逐层回退
		if (!done)
		{
			// 虽然说场景是树,但其实它是一个无环图,因为一个节点可能有多个父节点
			// 若一个场景需要渲染多棵同样的树,让树节点被多个具有不同变换的父节点引用即可
			if (0 == node.getNumParents())
			{
				// 若没有父节点则到达场景根节点,计算最终世界坐标并标识根节点已到达
				wcMatrix->set(osg::computeLocalToWorld(this->getNodePath()));
				done = true;
			}
			traverse(node);
		}
	}
	
	// 要返回最终变换矩阵的地址,因此该类没有处理osg::Matrixd可能造成的内存泄漏
	osg::Matrixd* giveUpDaMat()
	{
		return wcMatrix;
	}

private:
	bool done;
	osg::Matrix* wcMatrix;
};

// 访问节点node计算其最终变换矩阵
osg::Matrixd* getWorldCoords(osg::Node* node)
{
	/*
	* 若使用osg::ref_ptr<osg::node>作为参数,则该函数可能使得计数增加,使得node永远不能释放,造成内存泄漏
	* 因此若一个函数不应记录某参数,则应向其传入osg::T*即类型指针,并因此要检查此指针是否为空
	*/ 

	// 创建自定义访问器对象,由于其被节点所引用,因此应使用new申请方式
	GetWorldCoordinateOfNodeVisitor* ncv = new GetWorldCoordinateOfNodeVisitor();
	if (node && ncv)
	{
		// 将访问器应用到节点,节点会引用该访问器
		node->accept(*ncv);

		// 返回访问器的遍历结果
		return ncv->giveUpDaMat();
	}
	else
	{
		return NULL;
	}
}

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

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

相关文章

关于升级WIN11后,点击功能面板空白的解决方法

方法一&#xff1a;先试试按下WinX,选择Windows powershell(管理员)&#xff0c;输入sfc /scannow等待系统自动扫描完成后重启系统看看是否修复。 方法二&#xff1a;打开微软Windows11镜像下载官网&#xff1a;Download Windows 11&#xff0c;下载对应版本和语言的ISO系统镜…

人工智能与伦理挑战:多维度应对策略

人工智能技术近年来取得了迅猛发展&#xff0c;广泛应用于医疗诊断、金融分析、教育辅助、自动驾驶等各个领域&#xff0c;极大地提升了生产效率和服务质量&#xff0c;推动了科技进步和商业创新。然而&#xff0c;伴随其普及和应用的泛滥&#xff0c;AI也带来了数据隐私侵犯、…

【银河麒麟操作系统】虚机重启lvs丢失现象分析及处理建议

了解银河麒麟操作系统更多全新产品&#xff0c;请点击访问麒麟软件产品专区&#xff1a;https://product.kylinos.cn 环境及现象描述 40台虚机强制重启后&#xff0c;其中8台虚机找不到逻辑卷导致启动异常&#xff0c;后续通过pvcreate 修复重建pv&#xff0c;激活vg和lv并修复…

基于python的图像去水印

1 代码 import cv2 import numpy as npdef remove_watermark(image_path, output_path):# 读取图片image cv2.imread(image_path)# 转换为灰度图gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用中值滤波去除噪声median_filtered cv2.medianBlur(gray, 5)# 计算图像的梯…

Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置”

1. Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置” 文章目录 1. Spring MVC 中的拦截器的使用“拦截器基本配置” 和 “拦截器高级配置”2. 拦截器3. Spring MVC 中的拦截器的创建和基本配置3.1 定义拦截3.2 拦截器基本配置3.3 拦截器的高级配置 4. Spr…

初识C++|类和对象(中)——类的默认成员函数

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; &#x1f379;初始C|类与对象&#xff08;上&#xff09;-CSDN博客 4. 类的默认成员函数 默认成员函数就是⽤⼾没有显式实现&#xff0c;编译器会⾃动⽣成的成员函数称为默认成…

数字孪生技术栈:简单选three.js,复杂选unity3D,基本不会错。

数字孪生项目中涉及到3D模型交互的部分&#xff0c;选择什么技术栈呢&#xff0c;一般来说遵循这个原则&#xff1a;简单的应用可以选择Three.js&#xff0c;而复杂的应用则更适合选择Unity3D。 Three.js是一个基于WebGL的开源JavaScript库&#xff0c;用于在Web浏览器中创建和…

IP地址定位与GPS定位:技术解析与应用比较

IP地址定位和GPS定位是比较常见的定位技术。本文将与大家探讨这两种技术的工作原理、优缺点及其在实际应用中的比较和融合。 IP地址定位 IP地址定位的工作原理 IP地址&#xff08;InternetProtocolAddress&#xff09;是分配给联网设备的唯一标识符。IP地址定位通过分析设备…

基于springboot3实现单点登录(一): 单点登录及其相关概念介绍

引言 应网友要求&#xff0c;从本文开始我们将实现一套基于springboot3springsecurity的单点登录认证系统。 单点登录的实现方式有多种&#xff0c;接下来我们会以oauth2为例来介绍和实现。 单点登录介绍 单点登录&#xff08;Single Sign-On&#xff0c;简称SSO&#xff0…

LeetCode-环形链表、环形链表 II

一、环形链表 . - 力扣&#xff08;LeetCode&#xff09; 判断是否有环&#xff0c;使用快慢指针&#xff0c;开始时都指向头节点&#xff0c;快指针每次走两部&#xff0c;慢指针每次走一步&#xff0c;如果在走的过程中&#xff0c;慢指针和快指针相同&#xff08;也就是快指…

STM32第九课:STM32-基于标准库的42步进电机的简单I/O控制(附电机教程,看到即赚到)

一&#xff1a;步进电机简介 步进电机又称为脉冲电机&#xff0c;简而言之&#xff0c;就是一步一步前进的电机。基于最基本的电磁铁原理,它是一种可以自由回转的电磁铁,其动作原理是依靠气隙磁导的变化来产生电磁转矩&#xff0c;步进电机的角位移量与输入的脉冲个数严格成正比…

从Mac电脑硬盘驱动器恢复数据的3种方法[终极指南]

如果您的MacBook的启动磁盘损坏&#xff0c;并且您没有另一台Mac来安装恢复软件并恢复数据&#xff0c;该怎么办&#xff1f;相反&#xff0c;您随身携带的是 Windows 操作系统。 起初&#xff0c;您可能会陷入僵局&#xff0c;因为您无法启动到Mac并使用Mac恢复软件恢复数据&…

移动UI:具备什么特征,可以被认定为科技风格。

移动UI设计在科技风格上通常具备以下特征&#xff1a; 1. 清晰简洁的排版&#xff1a; 科技风格的移动UI通常采用清晰简洁的排版&#xff0c;注重信息的层次感和结构化&#xff0c;以便用户能够快速、直观地获取所需信息。 2. 几何形状和线条&#xff1a; 科技风格的移动UI常…

【大模型书籍】从零开始大模型开发与微调:基于PyTorch与ChatGLM(附PDF)

哈喽各位&#xff0c;今天又来给大家分享大模型学习书籍了&#xff0c;今天是这本<从零开始大模型开发与微调&#xff1a;基于PyTorch与ChatGLM 书籍PDF分享>&#xff0c;大模型是深度学习自然语言处理皇冠上的一颗明珠&#xff0c;也是当前AI和NLP研究与产业中最重要的方…

移动UI:任务中心的作用,该如何设计更合理?

任务中心是移动应用中用于展示和管理用户待办任务、提醒事项、用户福利、打卡签到等内容的功能模块。合理设计任务中心可以提升用户体验和工作效率。 以下是一些设计任务中心的合理建议&#xff1a; 1. 易于查看和管理&#xff1a; 任务中心的设计应该使用户能够快速、直观地…

python基础知识点(蓝桥杯python科目个人复习计划69)

做些基础题 第一题&#xff1a;微生物增值 题目描述&#xff1a; 假设有两种微生物x和y。 x出生后每隔3分钟分裂一次&#xff08;数目加倍&#xff09;&#xff0c;y出生后每隔2分钟分裂一次&#xff08;数目加倍&#xff09;。 一个新出生的x&#xff0c;半分钟之后吃掉一…

STM32学习(1)--STM32介绍

STM32介绍 1.STM32简介2.ARM3.STM32F103C8T64.外设5.命名规则6.系统结构7.引脚定义8.启动配置9.最小系统电路 课程知识及代码来源均来自b站 江协科技&#xff0c;下学期即将做毕设&#xff0c;需要用到STM32单片机&#xff0c;在这个假期我将持续学习相关知识 1.STM32简介 ST…

免费压缩归档助手,一键搞定你的文件烦恼!

在这个数字化时代&#xff0c;我们每天都在与各种文件打交道。无论是工作文档、个人照片还是珍贵的视频&#xff0c;管理这些文件成了一项必不可少的任务。但你是否还在为文件的压缩、加密和解压缩而烦恼&#xff1f;别担心&#xff0c;我们为你带来了一款方便好用的免费软件—…

如何降低老年人患帕金森病的风险?

降低老年人患帕金森病风险的方法 避免接触有害物质&#xff1a;长期接触某些化学物质、农药或其他有害物质可能会增加患帕金森病的风险。应减少这些物质的暴露&#xff0c;例如在工作或生活中采取防护措施。 健康饮食&#xff1a;均衡饮食&#xff0c;多吃富含抗氧化剂的食物&a…

Richtek立锜科技可用于智能门铃的电源管理解决方案

新型的智能门铃不仅能满足呼叫、提醒的需要&#xff0c;还能在线监控、远程操作、闯入通知、记录过程&#xff0c;系统构成相对复杂&#xff0c;与传统门铃相比有了很大的改变。 从电源管理的角度来观察&#xff0c;满足这样需求的系统构成也相对复杂&#xff1a; 处于外置状态…