OSG编程指南<十三>:OSG渲染状态

1、前言

  在 OSG 中存在两棵树,即场景树和渲染树。渲染树是一棵以 StateSet 和 RenderLeaf
为节点的树,它可以做到 StateSet 相同的 RenderLeaf 同时渲染而不用切换 OpenGL状态,并且做到尽量少但在多个不同 State 间切换。渲染树在 CullVisitor 的 cull 过程中逐渐创建。后面所讲到的纹理、光照及材质,都是关于渲染状态的控制。

2、渲染状态

  OSG 支持绝大部分的 OpenGL 固定功能管道(fixed function pipeline)渲染,如 Alpha 检验、Blending融合、剪切平面、颜色蒙板、面拣选(face culling)、深度和模板检验、雾效、点和线的光栅化(rasterization)等。OSG 的渲染状态也允许应用程序指定顶点着色(vertex shader)和片段着色(fragment shader)。

2.1 osg::StateSet 类

  osg::StateSet 类派生自 osg::Referenced,以实现更好的数据共享。也就是说,共享同一个 osg::StateSet的 osg::Node 或 osg::Drawable 类不需要额外的代码来清理其内存空间。

  用户的应用程序需要在 osg::StateSet 中设置渲染状态。可以将 StateSet 关联到场景图形中的任意一个节点(Node)或关联到 Drawable 类。正如大部分开发者所知,OpenGL 程序的开发需要尽量使状态量的变化实现最小化,并避免冗余的状态设置,StateSet 对象能够自动实现这些优化过程。在 OSG 遍历整个场景图形时,StateSet 类会对 OpenGL 的状态属性堆栈进行管理。因此,用户程序可以对不同的场景图形子树作不同的状态设置。在每个子树的遍历过程中,OSG 将会高效地执行保存和恢复渲染状态的操作。用户需要尽量使关联到场景图形的 StateSet 最少化。StateSet 越少,内存的占用也越少,OSG在一次场景图形遍历中所耗费的工作量也越少。

  OSG 将渲染状态分成两个部分,分别为渲染属性(Attribute)和渲染模式(Mode)。渲染属性也就是控制渲染特性的状态变量,如雾的颜色或 Blend 融合函数都是 OSG 的状态属性。OSG 中的渲染模式和 OpenGL 的状态特性几乎是一一对应的,这些特性在 OpenGL 中通过函数 glEnable()和 glDisable()进行控制。用户程序可以设置模式量以允许或禁止某个功能,如纹理映射、灯光等。简单来说,渲染模式是指渲染的某个功能,而渲染属性是这个功能的控制变量和参数。如果要设置渲染状态的值,用户程序需要执行以下两步操作:

(1)为将要设置状态的 Node 或 Drawable 对象提供一个 StateSet 实例。
(2)在 StateSet 实例中设置状态的渲染模式和渲染属性。

直接从某个 Node 或 Drawable 对象中获得一个 StateSet 实例可以使用下面的方法:

osg::StateSet* state = obj->getOrCreateStateSet();

  在上面的程序段中,obj 是一个 Node 或 Drawable 实例;getOrCreateStateSet()是这些类定义的方法,这个方法返回一个指向 StateSet 实例的指针,该实例属于 obj。如果 obj 还没有设置过与之关联的StateSet,那么这个方法返回一个新指针并将其关联到 obj 上。

  StateSet 继承自 Referenced 类。与 StateSet 关联的 Node 或 Drawable 类内部使用 ref_ptr<>来引用StateSet 实例,因此,不是长时间引用 StateSet 的情况下,也可以使用标准 C++指针来定义 state。如果state 变量是某个函数内的局部变量,且应用程序不会长时间引用这个StateSet,那么上面代码的使用是完全正确的。上面代码中的 state 变量指向该 obj 对象的 StateSet 指针,当应用程序获得了一个 StateSet的指针时,就可以设置属性和模式了。

2.2 渲染属性和渲染模式

  OSG 为每个状态属性定义了不同的类,以便应用程序采用。所有的属性类均继osg::StateAttribute,osg::StateAttribute 类是一个无法直接实例化的虚基类。

  OSG 将所有的属性和模式分为两大部分,即纹理(texture)和非纹理(non-texture)。本节将主要探讨非纹理渲染状态的设置。纹理渲染状态的设置将在后面的章节讨论。OSG 之所以为纹理属性的设置提供不同的接口,主要是因为纹理属性需要特别为多重纹理设置纹理单元(texture unit)。

1.设置渲染属性(Attribute)

  如果要设置一项属性,首先需要将修改的属性类实例化,设置该类的数值,然后用 osg::StateSet::setAttribute()将其关联到 StateSet。下面的代码段用于实现面剔除(face culling)的属性:

//获取变量 geom 的 StateSet 指针
osg::StateSet*state = geom->getOrCreateStateSet();
//创建并添加 CullFace 属性类
osg::CullFace*cf = new osg::CullFace( osg::CullFace::BACK );
state->setAttribute(cf);

  在上面的代码段中,geom 是一个 Geometry 几何类对象(当然也可以是任何其他派生自 Drawable和 Node 的对象),获取 geom 的 StateSet 指针后,代码创建了一个新的 osg::CullFace 对象,并将其关联到状态量。

2.设置渲染模式(Mode)
  用户可以使用 osg::StateSet::setMode()设置允许或禁止某种模式。例如,下面的代码将打开雾效模式的许可:

//获取一个 StateSet 实例
osg::StateSet*state = geom->getOrCreateStateSet();
//允许这个 StateSet 的雾效模式
state->setMode( GL_FOG, osg::StateAttribute::ON );

setMode()的第一个输入参数可以是任何一个在 glEnable()或 glDisable()中合法的 OpenGL 枚举量Glenum;第二个输入参数可以是 osg::StateAttribute::ON 或 osg::StateAttribute::OFF。事实上,这里用到了位屏蔽技术,这个技术将在后面讲解继承状态时说明。

3.设置渲染属性和模式

  OSG 提供了一个简单的、可以同时设置属性和模式的单一函数接口。在很多情况下,属性和模式之间都存在显著的关系。例如,CullFace 属性的对应模式为 GL_CULL_FACE。如果要将某个属性关联到一个 StateSet,同时要求打开其对应模式的许可,可以使用osg::StateSet::setAttributeAndModes()方法。

下面的代码段将关联 Blend 融合检验的属性,同时许可颜色融合模式。

//创建一个 BlendFunc 属性
osg::BlendFunc*bf = new osg::BlendFunc();
//关联 BlendFunc 并许可颜色融合模式
state->setAttributeAndModes( bf );

  setAttributeAndModes()的第二个输入参数用于允许或禁止第一个参数中渲染属性对应的渲染模
式,其默认值为 ON。这样,用户的应用程序只需用一个函数就可以方便地指定某个渲染属性,并许可其对应的渲染模式。

2.3 状态继承

  当读者设置节点的渲染状态时,这个状态将被赋予当前的节点及其子节点。如果子节点对同一个渲染状态设置了不同的属性参数,那么新的子节点状态参数将会覆盖原有的。也就是说,默认情况下子节点可以改变自身的某个状态参数或者继承父节点的同一个状态。

  状态继承特性在许多情况下都非常实用,但有时渲染可能需要更多特性。假设场景图形中有一个包含了实体多边形几何体的节点,如果要以线框模式来渲染场景图形,读者的程序就需要覆盖这种多边形渲染模式状态,而不论它出现在什么位置。OSG 允许用户根据场景图形中任意位置的渲染属性和模式需求单独改变原有的状态继承特性。可以选择以下几种枚举形式。

osg::StateAttribute::OVERRIDE:如果将一个渲染属性和模式设置为 OVERRIDE,那么所有的子节点都将继承这一属性或模式,子节点对它们的更改将会无效。
osg::StateAttribute::PROTECTED:这种形式可以视为 OVERRIDE 的一个例外。凡是设置为PROTECTED 的渲染属性或模式,均不会受到父节点的影响。
osg::StateAttribute::INHERIT:这种模式强制子节点继承父节点的渲染状态,其效果是子节点
的渲染状态被解除,而使用父节点的状态替代。

  读者可以对这些参数进行位或叠加操作,然后再作为 setAttribute()和 setMode()建立一个场景图形和 setAttributeAndModes()的第二个参数输入。下面的代码段将强制使用线框模式渲染场景图形:

//获取根节点的渲染状态 StateSet
osg::StateSet*state = root->getOrCreateStateSet();
//创建一个 PolygonMode 渲染属性
osg::PolygonMode*pm = new osg::PolygonMode)
osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
//强制使用线框渲染
state->setAttributeAndModes( pm,osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE );

  使用 PROTECTED 参量可以保证父节点的渲染状态不会覆盖子节点的渲染状态。例如,读者可能创建了一个发光的场景,其中包含有使用亮度照明的光源几何体,如果其父节点禁用了光照,那么光源几何体的渲染将会出错。这时,对光源几何体的 GL_LIGHTING 渲染状态使用 PROTECTED 就可以保证它依然可用。

3、渲染状态示例

3.1 效果

在这里插入图片描述

3.2 源码

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。#include <windows.h>
#include <osgViewer/Viewer>
#include <iostream>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Vec3>
#include <osg/Vec2>
#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/ClipNode>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osgUtil/DelaunayTriangulator> 
#include <osgUtil/TriStripVisitor>
#include <osgUtil/SmoothingVisitor>#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")osg::ref_ptr<osg::Node> createClipNode(osg::ref_ptr<osg::Node> subgraph)
{osg::ref_ptr<osg::Group> root = new osg::Group();osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();//多边形线形绘制模式,正面和反面都绘制osg::ref_ptr<osg::PolygonMode> polymode = new osg::PolygonMode();polymode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);//启用多边形线形绘制模式,并指定状态继承属性为 OVERRIDEstateset->setAttributeAndModes(polymode, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);//多边形线形绘制节点osg::ref_ptr<osg::Group> wireframe_subgraph = new osg::Group;//设置渲染状态wireframe_subgraph->setStateSet(stateset.get());wireframe_subgraph->addChild(subgraph.get());root->addChild(wireframe_subgraph.get());osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform;//更新回调,实现动态裁剪osg::ref_ptr<osg::NodeCallback> nc = new osg::AnimationPathCallback(subgraph->getBound().center(),osg::Vec3(0.0f, 0.0f, 1.0f), osg::inDegrees(45.0f));transform->setUpdateCallback(nc.get());//创建裁剪节点osg::ref_ptr<osg::ClipNode> clipnode = new osg::ClipNode();osg::BoundingSphere bs = subgraph->getBound();bs.radius() *= 0.4f;//设置裁剪节点的包围盒osg::BoundingBox bb;bb.expandBy(bs);//根据前面指定的包围盒创建 6 个裁剪平面clipnode->createClipBox(bb);//禁用拣选clipnode->setCullingActive(false);transform->addChild(clipnode.get());root->addChild(transform.get());//创建未被裁剪的节点osg::ref_ptr<osg::Group> clippedNode = new osg::Group;clippedNode->setStateSet(clipnode->getStateSet());clippedNode->addChild(subgraph.get());root->addChild(clippedNode.get());return root.get();
}int main()
{//添加到根节点osg::ref_ptr<osg::Group> root = new osg::Group();osg::ref_ptr<osg::Node> childNode = osgDB::readNodeFile("cessna.osg");osg::ref_ptr<osg::Node> node = createClipNode(childNode.get());root->addChild(node.get());//优化场景数据osgUtil::Optimizer optimizer;optimizer.optimize(root.get());osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();//方便查看在多边形之间切换,以查看三角网viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));viewer->addEventHandler(new osgViewer::StatsHandler());viewer->addEventHandler(new osgViewer::WindowSizeHandler());viewer->setSceneData(root.get());viewer->setUpViewInWindow(600, 600, 1000, 800);return viewer->run();
}

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

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

相关文章

正则表达式回溯陷阱

一、匹配场景 判断一个句子是不是正规英文句子 text "I am a student" 一个正常的英文句子如上&#xff0c;英文单词 空格隔开 英文单词 多个英文字符 [a-zA-Z] 空格用 \s 表示 那么一个句子就是单词 空格&#xff08;一个或者多个&#xff0c;最后那个单词…

java学习part21枚举

117-面向对象(高级)-枚举类的两种定义方式及练习_哔哩哔哩_bilibili 1.概念 2.enum关键字 枚举早期是用class内放n个public static final类变量供调用。 后来有了enum关键字&#xff0c;enum里的枚举变量都是public static final&#xff0c;但是必须都省略不写 正确方法 &…

【Java】7. 类型转换和类型判断

7. 类型转换 7.1 基本类型转换 顺箭头&#xff1a;隐式转换&#xff08;自动&#xff09; 逆箭头&#xff1a;强制转换&#xff08;可能造成精度丢失&#xff09; byte a 10; int b a; int c 1000; byte d (byte) c; System.out.println(d); // -24 7.2 包装类型与基…

Vue 双向数据绑定

之前通过v-bind来完成的数据绑定&#xff0c;属性值和表达式进行绑定&#xff0c;表达式的值发生变化了属性值也跟着发生变化。 单向数据绑定&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</titl…

C语言错误处理之“非局部跳转<setjmp.h>头文件”

目录 前言 setjmp宏 longjmp函数 使用方法&#xff1a; 实例&#xff1a;测试setjmp与longjmp的使用 前言 通常情况下&#xff0c;函数会返回到它被调用的位置&#xff0c;我们无法使用goto语句改变它的返回的方向&#xff0c;因为goto语句只能跳转到同一函数内的某个标号…

文件服务器迁移

文件服务器迁移还是比较简单的 win server加域 导出配额文件 选中所有项&#xff0c;点击导出 导出共享文件夹权限列表 导出文件夹的权限表&#xff0c;留作备用。需要用到“icacls” icacls c:\windows\* /save aclfile /t # C:\Windows 目录及其子目录中所有文件的 DAC…

pandas教程:USDA Food Database USDA食品数据库

文章目录 14.4 USDA Food Database&#xff08;美国农业部食品数据库&#xff09; 14.4 USDA Food Database&#xff08;美国农业部食品数据库&#xff09; 这个数据是关于食物营养成分的。存储格式是JSON&#xff0c;看起来像这样&#xff1a; {"id": 21441, &quo…

简单认识泛型【java】

目录 1.什么是泛型 2.引出泛型 2.1语法 3. 泛型类的使用 3.1 语法 4 泛型如何编译的 4.1 擦除机制 5.泛型的上下界 5.1 语法 6.注意 1.什么是泛型 一般的类和方法&#xff0c;只能使用具体的类型: 要么是基本类型&#xff0c;要么是自定义的类。如果要编写可以应用于…

2023第十四届蓝桥杯Java B组个人题解

&#x1f48e;蓝桥杯系列文章 欢迎大家阅读蓝桥杯文章专栏&#x1f344;&#x1f344; &#x1f525;2023第十四届蓝桥杯模拟赛第二期个人题解&#xff08;Java实现&#xff09; &#x1f525;2023第十四届蓝桥杯模拟赛第三期个人题解&#xff08;Java实现&#xff09; &#…

【Unity3D】MAX聚合广告SDK——Pangle广告接入(成了!成了!)

Pangle, App Monetization Simplified 注册 登录 创建应用 创建广告单元 将其应用ID和广告ID关联到MAX广告。 下载Pangle Unity Plugin包&#xff0c;新建一个空工程&#xff08;很重要&#xff09; Unity版本2019.4.0f1 gradle plugin 4.2.0 gradle版本6.7.1 build_tools 34.…

有没有不含亚硫酸盐的葡萄酒?

没有完全不含亚硫酸盐的葡萄酒&#xff0c;有机葡萄酒和生物动力葡萄酒中也会含有少量天然 亚硫酸盐。因为它是在发酵过程中产生的一种化合物&#xff0c;所以不可能酿造无亚硫酸盐的葡萄酒。 在大多数葡萄酒国家都有葡萄酒法律规定&#xff0c;如果葡萄酒超过特定的亚硫酸盐水…

HttpRunner原来还能这么用,大开眼界!!!

hook机制 Httprunner 框架中的 hook 机制相当于unittest框架中的 setup , teardown 函数&#xff0c;用来进行测试用例执行之前的环境初始化以及测试用例执行完毕之后的环境清理操作。 httprunner 中的 hooks 机制可以用在测试用例层级也可以用在测试步骤层级&#xff0c;其关键…

TDA4VM LInux SDK编译

文章目录 前言编译步骤前言 上篇TDA4VM EVM开发板调试笔记我们尝试把EVM开发板跑起来了,本篇主要记录,Linux SDK 的编译过程。 编译步骤 安装依赖: $ sudo apt-get update $ # Install packages required for builds $ sudo apt-get -f -y install \

【微服务专题】微服务架构演进

目录 前言阅读对象阅读导航前置知识笔记正文一、系统架构的演变1.1 单体架构1.2 单体水平架构1.3 垂直架构1.4 SOA架构1.5 微服务架构 二、如何实现微服务架构2.1 微服务架构下的技术挑战2.2 微服务技术栈选型2.3 什么是Spring Cloud全家桶2.4 Spring Cloud Alibaba版本选择 学…

慢 SQL 分析及优化

目录 分析慢 SQL SQL 优化 单表优化 多表优化 慢 SQL&#xff1a;指 MySQL 中执行比较慢的 SQL排查慢 SQL 最常用的方法&#xff1a;通过慢查询日志来查找慢 SQL MySQL 的慢查询日志是 MySQL 提供的一种日志记录&#xff0c;它用来记录在 MySQL 中响应时间超过阈值的语句&…

AI模型训练——入门篇(一)

前言 一文了解NLP&#xff0c;并搭建一个简单的Transformers模型&#xff08;含环境配置&#xff09; 一、HuggingFace 与NLP 自从ChatGPT3 问世以来的普及性使用&#xff0c;大家或许才真正觉察AI离我们已经越来越近了&#xff0c;自那之后大家也渐渐的开始接触stable diff…

esp32 esp-idf V5.1.1版本看门狗配置

idf.py menuconfig打开配置窗口 选择Component config选项

智慧城市交通大屏|助力解决城市交通问题

2017年起&#xff0c;数字孪生连续三年被Gartner列入“未来科技十大趋势”&#xff0c;由此可见数字孪生技术正屹立在数字化发展的风口之中。 数字孪生作为物理世界的数字映射&#xff0c;将流程、物体的信息利用数字技术实时映射到系统中&#xff0c;可以对某个设备、某个企业…

深入了解MD5加密技术及其应用与局限

一、MD5简介 MD5&#xff08;Message Digest Algorithm 5&#xff09;是一种单向散列函数&#xff0c;由美国密码学家罗纳德李维斯特&#xff08;Ronald Linn Rivest&#xff09;于1991年发明。它主要用于将任意长度的消息映射成固定长度的摘要&#xff0c;从而实现消息的完整…

高危行业vr施工安全教育平台提高员工的学习效果和兴趣

元宇宙作为一个虚拟的、无限的数字空间&#xff0c;为教育领域带来了前所未有的可能性。元宇宙突破了传统教育的物理空间限制&#xff0c;使学生和老师不受地域和时间的束缚&#xff0c;可以随时随地开展课堂学习。它还为学习者提供了更丰富、更沉浸式的学习体验&#xff0c;使…