OSG 渲染状态
前言:撸了两天代码,决定换一种学习方式试试。
渲染状态(Rendering State)
OSG支持绝大部分的OpenGL 固定功能管道(fixed function pipeline)渲染,例如 Alpha 检验,Blending 融合, 剪切平面,颜色蒙板,面拣选(face culling),深度和模板检验,雾效,点和线 的光栅化(rasterization),等等。OSG 的渲染状态也允许应用程序指定顶点着色(vertex shader)和片段着色(fragment shader)。 用户的应用程序需要在 osg::StateSet 中设置渲染状态。
OSG 将渲染状态分成两个部分,渲染属性(attribute)和渲染模式(mode)。
渲染属性也就是控制渲染特性的状态变量。例如,雾的颜色,或者 Blend 融合函 数都是 OSG 的状态属性。
OSG 中的渲染模式和 OpenGL 的状态特性几乎是一一 对应的,这些特性在 OpenGL 中通过函数 glEnable()和 glDisable()进行控制。用程序可以设置模式量以允许或禁止某个功能,例如纹理映射、灯光等。简单来说,渲染模式是指渲染的某个功能,而渲染属性是这个功能的控制变量和参数。
渲染的步骤如下:
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->setAttributeAndMode( bf );
setAttributeAndModes()的第二个输入参数用于允许或禁止第一个参数中渲 染属性对应的渲染模式。其缺省值为 ON。这样用户的应用程序只需用一个函数, 就可以方便地指定某个渲染属性,并许可其对应的渲染模式。
附上书上的一段代码:
//渲染状态(ClipeNode)
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/ClipNode>
#include <osg/PolygonMode>
#include <osg/MatrixTransform>
#include <osg/PositionAttitudeTransform>
#include <osg/AnimationPath>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <iostream>
osg::ref_ptr<osg::Node> createClipeNode(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::FILL);
//启用多边形线形绘制模式,并指定状态继承属性为OVERRIDE
stateset->setAttributeAndModes(polymode,osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);
//多边形线形绘制节点
osg::ref_ptr<osg::Group> wrieframe_subgraph = new osg::Group;
//设置渲染状态
wrieframe_subgraph->setStateSet(stateset.get());
wrieframe_subgraph->addChild(subgraph.get());
root->addChild(wrieframe_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()
{
//创建Viewer对象
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
//osg::ref_ptr<osg::Group> root = new osg::Group();
osg::ref_ptr<osg::Node> root = new osg::Node();
//加载模型
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cessna.osg");
root = createClipeNode(node.get());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
//设置场景数据
viewer->setSceneData(root.get());
//初始化并创建窗口
viewer->realize();
//开始渲染
viewer->run();
return 0;
}
运行出来是一个飞机的模型,大家也可以把注释代码打开看看效果;
改变一下模式,看看输出什么样的飞机:
polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::LINE); //线
polymode->setMode(osg::PolygonMode::FRONT_AND_BACK,osg::PolygonMode::POINT); //点
下面简单介绍贴二维纹理映射:
创建二维纹理的步骤如下:
1.指定用户几何体的纹理坐标
2.创建纹理属性对象并保存纹理图形数据
3.为StateSet设置合适的纹理属性和模式
附上一段代码:
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Image>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/TexEnv>
#include <osg/StateSet>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
//创建一个四边形节点
osg::ref_ptr<osg::Node> createNode()
{
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
//设置顶点
osg::ref_ptr<osg::Vec3Array> vc = new osg::Vec3Array();
vc->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
vc->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
vc->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
vc->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
geom->setVertexArray(vc.get());
//设置纹理坐标
osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
vt->push_back(osg::Vec2(0.0f, 0.0f));
vt->push_back(osg::Vec2(1.0f, 0.0f));
vt->push_back(osg::Vec2(1.0f, 1.0f));
vt->push_back(osg::Vec2(0.0f, 1.0f));
//第一个参数是纹理单元号, 第二个参数是纹理坐标数组
geom->setTexCoordArray(0, vt.get());
//设置法线 法线和光照有关
osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
geom->setNormalArray(nc.get());
//法线绑定方式:用一条法线绑定所有的顶点
geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
//添加图元
geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4));
//绘制
geode->addDrawable(geom.get());
return geode.get();
}
//创建二维纹理状态对象
osg::ref_ptr<osg::StateSet> createTexture2DState(osg::ref_ptr<osg::Image> image)
{
//创建状态集对象
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
//创建二维纹理对象
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
texture->setDataVariance(osg::Object::DYNAMIC);
//设置贴图
texture->setImage(image.get());
stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
return stateset.get();
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
osg::ref_ptr<osg::Group> root = new osg::Group();
//读取贴图文件
osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");
osg::ref_ptr<osg::Node> node = createNode();
//创建状态集对象
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
stateset = createTexture2DState(image.get());
//使用二维纹理
node->setStateSet(stateset.get());
root->addChild(node.get());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
//设置场景数据
viewer->setSceneData(root.get());
//初始化并创建窗口
viewer->realize();
//开始渲染
viewer->run();
return 0;
}