OSG-渲染状态、纹理映射(一)

1、渲染状态

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

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

1.1、osg::StateSet 类

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

图5-1 osg::StateSet 的继承关系图

        用户的应用程序需要在 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 对象提供一个 StateSct 实例。

        (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的指针时,就可以设置属性和模式了。

1.2、渲染属性和渲染模式

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

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

        1.设置渲染属性(Attribute)

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

  1. // 获取变量geomStateSet 指针  
  2. osg::StateSet *state = geom->getOrCreateStateSet();//创建并添加CullFace属性类  
  3. 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()设置允许或禁止某种模式。例如,下面的代码将打开雾效模式的许可:

  1. // 获取一个StateSet实例  
  2. osg::StateSet*state = geom->getOrCreateStateSet():  
  3. // 允许这个StateSet的雾效模式  
  4. 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 融合检验的属性,同时许可颜色融合模式。

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

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

1.3、状态继承

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

图5-2 光照状态继承图

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

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

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

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

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

1.4、渲染状态示例

        渲染状态(ClipeNode)示例的代码如程序清单5-1所示。

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);// 根据前面指定的包围盒创建六个裁剪平面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();
}void clipeNode_5_1(const string &strDataFolder)
{osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;traits->x = 40;traits->y = 40;traits->width = 600;traits->height = 480;traits->windowDecoration = true;traits->doubleBuffer = true;traits->sharedContext = 0;osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());osg::ref_ptr<osg::Camera> camera = new osg::Camera;camera->setGraphicsContext(gc.get());camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;camera->setDrawBuffer(buffer);camera->setReadBuffer(buffer);viewer->addSlave(camera.get());osg::ref_ptr<osg::Node> root = new osg::Node();// 加载模型string strDataPath = strDataFolder + "cessna.osg";osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);root = createClipNode(node.get());// 优化场景数据osgUtil::Optimizer optimzer;optimzer.optimize(root.get());viewer->setSceneData(root.get());viewer->realize();viewer->run();
}

运行程序,截图如图 5-3 所示

图5-3渲染状态示例截图

2、纹理映射

        物体的外观不仅包括形状,不同物体表面有着不同的颜色和图案。一个简单而有效地实现这种特性的方法就是使用纹理映射。
        OSG是对底层OpenGL API的封装,OpenGL本身有非常标准而高效的纹理机制。OSG全面支持OpenGL的纹理映射机制,因此,在OSG中使用纹理映射机制非常简单。纹理映射主要包含一维纹理、二维纹理、三维纹理、凹凸纹理、多重纹理、Mipmap纹理、压缩纹理和立方纹理等。
      纹理的基础知识:  

 /* 纹理坐标 */
enum WrapParameter
{WRAP_S, // x轴WRAP_T, // y轴WRAP_R, // z轴
};/* 纹理的包装模式 */
enum WrapMode
{CLAMP = GL_CLAMP, // 截取CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, // 边框始终被忽略CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER_ARB, // 它使用的纹理取自图像的边框,没有边框就使用    常量边框颜色REPEAT = GL_REPEAT, // 纹理的重复映射MIRROR = GL_MIRRORED_REPEAT_IBM, // 纹理镜像从重复映射
};/* 纹理过滤方法 */
enum FilterParameter
{MIN_FILTER, // 用于缩小MAG_FILTER  // 用于放大
};/* 纹理的过滤处理 */
enum FilterMode
{LINEAR = GL_LINEAR, // 以周围4个像素的平均值作为纹理LINEAR_MIPMAP_LINEAR = GL_LINEAR_MIPMAP_LINEAR, // 使用线性均和计算两个纹理的值LINEAR_MIPMAP_NEAREST = GL_LINEAR_MIPMAP_NEAREST, // 线性地改写临近的纹理单元值NEAREST = GL_NEAREST, // 取比较接近的像素作为纹理NEAREST_MIPMAP_LINEAR = GL_NEAREST_MIPMAP_LINEAR, // 在两个纹理中选择最临近的纹理,并取他们之间的线性均值NEAREST_MIPMAP_NEAREST = GL_NEAREST_MIPMAP_NEAREST // 选择最临近的纹理单元值
};/* 纹理映射模式(处理纹理图像数据与物体本身的融合) */
enum Mode
{DECAL = GL_DECAL,       // 贴花MODULATE = GL_MODULATE, // 调整BLEND = GL_BLEND,       // 混合REPLACE = GL_REPLACE,   // 替换,覆盖ADD = GL_ADD            // 添加
};/* 纹理坐标的自动生成模式 */
enum Mode
{OBJECT_LINEAR = GL_OBJECT_LINEAR,      // 物体线性,纹理贴图与移动物体保持固定EYE_LINEAR = GL_EYE_LINEAR,            // 产生移动物体的动态轮廓线SPHERE_MAP = GL_SPHERE_MAP,            // 球体贴图NORMAL_MAP = GL_NORMAL_MAP_ARB,        // 法线贴图,用于立方图纹理REFLECTION_MAP = GL_REFLECTION_MAP_ARB // 反射贴图
};/* 贴图坐标 */
enum Coord
{S, // xT, // yR, // zQ  // w
};/* 纹理的内部格式 */
enum InternalFormatMode
{USE_IMAGE_DATA_FORMAT,     // 使用贴图本身的格式USE_USER_DEFINED_FORMAT,   // 使用用户自定义的格式,如GL_R3G3B3USE_ARB_COMPRESSION,       // 使用ARB协会出的贴图压缩格式        USE_S3TC_DXT1_COMPRESSION, // 使用S3TC_DXT1压缩格式USE_S3TC_DXT3_COMPRESSION, // 使用S3TC_DXT3压缩格式USE_S3TC_DXT5_COMPRESSION, // 使用S3TC_DXT5压缩格式
};

2.1 二维纹理映射

         在所有的纹理映射中,二维纹理映射的过程最简单,也非常容易理解。在用户应用程序中创建二维纹理的步骤如下:
        <1> 指定用户几何体的纹理坐标;
        <2> 创建纹理属性对象并保存纹理图形数据;
        <3> 为StateSet设置合适的纹理属性和模式。
        1、纹理坐标
        设置纹理坐标比较简单,纹理坐标是与顶点一一对应的,很像数学中的映射,如图5-4所示。

图5-4 纹理坐标

        下面的代码段创建一个osg::Vec2Array数组,用于保存纹理坐标,同时将其关联到Geometry实例的纹理单元0,如果要对单一的Geometry设置多个纹理,只需要将多个纹理坐标数组关联到Geometry,并针对不同的数组指定不同的纹理单元即可。

  1. // 创建一个Geometry几何体对象  
  2. osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;  
  3. // 创建一个Vec2Array对象以保存纹理单元0的纹理坐标,并将其关联到geom  
  4. osg::ref_ptr<osg::Vec2Array> tc = new osg::Vec2Array;  
  5. geom->setTexCoordArray(0,tc.get());  
  6. tc.push_back(osg::Vec2(0.0f,0.0f));  
  7. tc.push_back(osg::Vec2(1.0f,0.0f));  
  8. tc.push_back(osg::Vec2(1.0f,1.0f));  
  9. tc.push_back(osg::Vec2(0.0f,1.0f));  

         osg::Geometry::setTexCoordArray()的第一个参数是纹理单元号,第二个参数是纹理坐标数组。用户不需要使用类同osg::Geometry::setTexCoordBinding()的函数输入点来绑定纹理数组。纹理坐标总是绑定到每个顶点的。

        在这里有一点要注意,OpenGL的早期版本并不支持多重纹理,而加入多重纹理的特性之后,OpenGL仍然支持非多重纹理的函数接口,以实现向下兼容。从本质上说,此时OpenGL将非多重纹理接口解释为使用纹理单元0对应所有纹理数据。与OpenGL不同,OSG并不支持非多重纹理接口。因此,用户程序必须指定一个纹理单元,以对应纹理坐标数据和纹理状态。如果要使用单一纹理,只需要指定纹理单元0即可。

  1. 纹理数据

        在大多数应用程序中,纹理数据都是从外部导入的图像文件。当没有必要导入图像时,可以生成一幅纹理数据贴图。这里使用从外部导入的纹理数据图形的方法。

        读取图像需要使用一个新类:osg::Image。osg::Image继承自osg::Object类,继承关系图如图5-5所示。

        下面的代码将实现如何读取图像:

  1. osg::ref_ptr<osg::Image> image = new osg::Image;  
  2. image->setFileName("tree.rgb");  

        在读取一个图像后,需要创建一个纹理对象来关联图像。osg::Texture2D属于osg::StateAttribute的派生类,用于管理OpenGL纹理对象,而Image用于管理图像像素数据。如果要使用2D图像文件作为纹理映射的图形,只要将文件名赋给Image对象并将Image关联到Texture2D即可。

        osg::Texture2D继承自osg::Texture。继承关系图如图5-6所示。

         下面的代码将实现图像关联到2D纹理对象上:

  1. // 将图像关联到Texture2D对象  
  2. osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;  
  3. tex->setImage(image.get());  

        在关联图像后,可以直接关联到渲染状态。值得注意的是:大量使用纹理贴图的程序往往需要实现更紧凑的内存管理。Image类继承自Referenced类,而Texture2D内部保存了一个指向Image的ref_ptr<>指针。在第一次渲染时,OSG创建了用于保存图像数据的OpenGL纹理对象,其结果是产生了两个纹理图像的副本,一个是Image对象,另一个由OpenGL拥有。简单的单环境(single-context) 场景渲染中,读者可以通过设置 Texture2D 解除对Image 的引用来降低内存损耗。如果当前引用Image对象的只有Texture2D对象那么OSG将释放Image及其内存空间下面的代码演示了设置Texture2D解除对Image引用的方法:

  1. //创建OpenGL纹理对象后,释放内部的ref_ptr<Image>,删除Image图像  
  2. tex->setUnRefImageDataAfterApply( true );  

        默认情况下,Texture2D 不会自动释放对 Image 的引用。在多环境(multi-context)场景渲染中这是一种期望行为,前提是纹理对象并没有在各环境中共享。

        3.纹理状态

        用户程序可以使用纹理状态函数接口为每个纹理单元指定渲染状态。纹理状态函数接口与非纹理状态的接口类似。用户可以使用osg::StateSet::setTextureAttribute()将一个纹理属性关联到 StateSet 对象setTextureAtribute()的第一个参数是纹理单元,第二个参数是继承自StateAttribute类的一种纹理属性,合法的纹理属性类共有6种,其中包括5种纹理类型(osg::TexturelD、osg::Texture2D、osg::Texture3D、osg::TextureCubeMap和osg:TextureRectangle)和一个用于纹理坐标的生成的类(osg::TexGen)。

        下面的代码将根据给定的 Texture2D 属性对象 tex和渲染状态StateSet 将 tex关联到状态并设置使用纹理单元0。

  1. // 创建一个Texture2D属性  
  2. osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D;  
  3. // 关联材质属性到材质单元0  
  4. state->setTextureAttribute(0, tex.get()):  

        与上面的程序类似,用户可以调用osg::StateSet::setTextureMode()方法来设置材质渲染模式,这个方法与 setMode()方法类似用户可以使用setTextureMode()来设置GL_TEXTURE_1D、GL_TEXTURE2D、GL_TEXTURE_3D、GL_TEXTURE_CUBE_MAP、GL_TEXTURE _RECTANGLE、GL_TEXTURE_GEN_Q、GL_TEXTURE_GEN_R、GL_TEXTURE_GEN_S及 GL_TEXTURE_GEN_T模式。

        与 setTextureAttribute()相似,setTextureMode()的第一个参数表示纹理单元。下面的代码段将禁止纹理单元1的2D映射:

  1. state->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF ):  

        当然,用户也可以使用 osg::StateSet::setTextureAttributesAndModes()来关联纹理渲染属性到StateSet,同时允许相应的纹理模式。如果属性是一个 TexGen 对象,那么 setTextureAttributesAndModes()将设置相应的坐标生成模式GL_TEXTURE_GEN_Q、GL_TEXTURE_GEN_R、GL_TEXTURE_GEN_S和GL_TEXTURE_GEN_T。对于其他纹理属性来说,这一模式是隐含的。例如,下面的代码中,由于第二个参数传入了一个Texture2D 对象作为纹理属性setTextureAttributesAndModes()将允许GL_TEXTURE_2D模式:

  1. // 创建一个Texture2D属性对象  
  2. osg::ref_ptr<osg::Texture2D> tex = new osg::Texture2D:  
  3. //在纹理单元0上,关联2D纹理属性并许可GLTEXTURE2D模式  
  4. state->setTextureAttributeAndModes(0, tex)  

        setTextureAttributeAndModes()的第三个参数的默认值为ON,即允许纹理渲染模式。与setAttributeAndModes()类似,读者可以对这个参数使用位或操作包括OVERRIDE、PROTECTED和INHERIT,以修改纹理属性的继承特性。读者还可以通过修改 setTextureMode()和setTextureAtribute()的第三个参数来指定这个继承标志。

2.2、二维纹理映射示例

        二维纹理映射(osg::Texture2D)示例的代码如序清单5-2所示

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();
}void texture2D_5_2(const string &strDataFolder)
{osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;traits->x = 40;traits->y = 40;traits->width = 600;traits->height = 480;traits->windowDecoration = true;traits->doubleBuffer = true;traits->sharedContext = 0;osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());osg::ref_ptr<osg::Camera> camera = new osg::Camera;camera->setGraphicsContext(gc.get());camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;camera->setDrawBuffer(buffer);camera->setReadBuffer(buffer);viewer->addSlave(camera.get());osg::ref_ptr<osg::Group> root = new osg::Group();// 读取贴图文件string strDataPath = strDataFolder + "Images\\primitives.jpg";osg::ref_ptr<osg::Image> image = osgDB::readImageFile(strDataPath);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();
}

        运行程序,截图如图 5-7 所示。

图5-7 二维纹理映射示例截图

2.3、多重纹理映射

        在进行标准的二维纹理映射处理时,一次把一幅纹理图像应用到一个多边形上。多重纹理允许应用几个纹理,在纹理操作管线中把它们逐个应用到同一个多边形上。多重纹理存在一系列的纹理单元,每个纹理单元执行单独的纹理操作,并把它的结果传递给下一个纹理单元,直到所有纹理单元的操作完成为止,最终显示处理后的效果。

        多重纹理映射非常广泛,它能实现一些高级的渲染技巧,如光照、贴花、合成和细节纹理等。在OSG中实现三维纹理主要有以下几个步骤:

  • 指定用户几何体的纹理坐标。
  • 创建多个纹理属性对象并保存纹理多个图形数据。
  • 为StateSet设置合适的纹理属性和模式。

        看起来和二维纹理映射的区别不大,简单地说就是多个二维纹理映射的叠加。但这里需要注意的是,对于不同的纹理属性对象需要指定不同的纹理单元及纹理坐标,否则就不会启用该纹理单元,或者该纹理单元会被覆盖。

2.4、多重纹理映射示例

        多重纹理映射示例的代码如程序清单5-3示

1.	void multTex_5_3(const string &strDataFolder)  
2.	{  
3.	    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
4.	    osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
5.	    traits->x = 40;  
6.	    traits->y = 40;  
7.	    traits->width = 600;  
8.	    traits->height = 480;  
9.	    traits->windowDecoration = true;  
10.	    traits->doubleBuffer = true;  
11.	    traits->sharedContext = 0;  
12.	  
13.	    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
14.	    osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
15.	    camera->setGraphicsContext(gc.get());  
16.	    camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
17.	    GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
18.	    camera->setDrawBuffer(buffer);  
19.	    camera->setReadBuffer(buffer);  
20.	    viewer->addSlave(camera.get());  
21.	  
22.	    osg::ref_ptr<osg::Group> root = new osg::Group();  
23.	  
24.	    string strDataPath = strDataFolder + "cow.osg";  
25.	    osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);  
26.	  
27.	    // 读取贴图  
28.	    strDataPath = strDataFolder + "Images/primitives.jpg";  
29.	    osg::ref_ptr<osg::Image> image = osgDB::readImageFile(strDataPath);  
30.	  
31.	    if (image.get())  
32.	    {  
33.	        //创建二维纹理  
34.	        osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;  
35.	        texture->setImage(image.get());  
36.	  
37.	        //设置自动生成坐标  
38.	        osg::ref_ptr<osg::TexGen> texgen = new osg::TexGen;  
39.	        texgen->setMode(osg::TexGen::SPHERE_MAP);  
40.	  
41.	        //设置纹理环境,模式为BLEND,  
42.	        osg::ref_ptr<osg::TexEnv> texenv = new osg::TexEnv;  
43.	        texenv->setMode(osg::TexEnv::BLEND);  
44.	        //设置BLEND操作的颜色  
45.	        texenv->setColor(osg::Vec4(0.6f, 0.6f, 0.6f, 0.0f));  
46.	  
47.	        //启用单元1自动生成纹理坐标,并使用纹理  
48.	        osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;  
49.	        stateset->setTextureAttributeAndModes(1, texture.get(), osg::StateAttribute::ON);  
50.	        stateset->setTextureAttributeAndModes(1, texgen.get(), osg::StateAttribute::ON);  
51.	  
52.	        //设置纹理环境  
53.	        stateset->setTextureAttribute(1, texenv.get());  
54.	  
55.	        //设置纹理状态  
56.	        node->setStateSet(stateset.get());  
57.	    }  
58.	  
59.	    root->addChild(node.get());  
60.	  
61.	    // 优化场景数据  
62.	    osgUtil::Optimizer optimizer;  
63.	    optimizer.optimize(root.get());  
64.	  
65.	    viewer->setSceneData(root.get());  
66.	    viewer->realize();  
67.	    viewer->run();  
68.	}  

运行程序,截图如图5-8 所示。

图5-8 多重纹理映射示例截图

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

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

相关文章

墙裂分享的免费好用api接口

企业工商三要素核验 API&#xff1a;通过输入企业名称、法人、注册号 /组织机构代码 /统一社会信用代码&#xff0c;验证三者是否匹配一致。企业工商四要素核验&#xff1a;传入企业名称、社会统一信用代码、法人名称、法人身份证&#xff0c;校验此四项是否一致。空气质量查询…

Python 数据库(一):使用 mysql-connector-python 操作 MySQL 数据库

大家好&#xff0c;我是水滴~~ 当涉及到使用 Python 操作 MySQL 数据库时&#xff0c;mysql-connector-python 库是一个强大而常用的选择。该库提供了与 MySQL 数据库的交互功能&#xff0c;使您能够执行各种数据库操作&#xff0c;如连接数据库、执行查询和插入数据等。在本文…

【业务中台-上线总结篇】

上线总结篇&#xff1a;从项目切换、问题处理、项目总结、项目验收环节展开 一、项目切换&#xff1a; 在上一章我们提到有制定详细规划的切换计划&#xff0c;在实际切换过程中&#xff0c;我们也是严格按照既定时间节点进行。然而&#xff0c;及时再详情的计划&#xff0c;也…

R语言——R函数、选项参数、数学统计函数(六)

目录 一、R函数 二、选项参数 三、数学统计函数 四、参考 一、R函数 1.lm() lm()是R语言中经常用到的函数&#xff0c;用来拟合回归模型。它是拟合线性模型最基本的函数 lm()格式如下&#xff1a; fit<-lm(formula,data) 其中&#xff0c;formula指要拟合的模型形式…

draw流程图工具导入云原生(CNCF)相关控件

目录 1、通过draw导入xml文件&#xff0c;获取云原生相关的空间 2、引用自己的资源链接&#xff1a; 1、通过draw导入xml文件&#xff0c;获取云原生相关的空间 导入资源图库&#xff0c;资源放在下方&#xff0c;大家可以下载&#xff1a; 2、引用自己的资源链接&#xff1a;…

Vue实现JSON字符串格式化编辑器组件

相信很多同学都用过网上的在线JSON格式化工具来将杂乱的JSON数据转换成易于我们阅读和编辑的格式。那么&#xff0c;你有没有想过自己动手实现一个这样的工具呢&#xff1f;今天&#xff0c;我将介绍如何使用Vue.js来构建一个简单的JSON格式化工具。 功能简述 支持格式化JSON字…

Linux使用yum命令安装postgrepsql

1.检查安装源 yum search postgresql 2.安装 yum install postgresql-server 3.启动数据库 service postgresql start 4.查看启动状态 service postgresql status 5.登陆测试 su - postgrep psql \l6.远程连接 6.1修改配置文件 在pg_hba.conf增加host all all 0.0.0…

解决Android AAPT: error: resource android:attr/lStar not found. 问题

错误信息 /xxx/gjc/.gradle/caches/transforms-2/files-2.1/930c42acd29d295ce5bc495c3b84423e/core-1.9.0/res/values/values.xml:104:5-113:25: AAPT: error: resource android:attr/lStar not found. not found 资源位置 场景 原Android studio中的项目都是在git上面拉的老项…

linux大于2TB磁盘分区方法

linux大于2TB磁盘分区方法 通过parted命令进行分区。步骤如下&#xff1a; 查询要挂的有磁盘路径, 输入 parted -l 。 进入parted对/dev/vdb盘的交互方式&#xff1a;输入&#xff1a; parted /dev/vdb&#xff0c;再输入&#xff1a;print 查看磁盘信息。 修改磁盘格式为gp…

数据结构与算法python版本之线性结构之队列Quene

什么是队列&#xff1f; 队列是一种有次序的数据集合&#xff0c;其特征是&#xff1a;新数据项的添加总发生在一端&#xff08;通常称为“尾rear”端&#xff09;&#xff0c;而现存数据项的移除总发生在另一端&#xff08;通常称为“首front”端&#xff09;&#xff1b;当数…

详解静态网页数据获取以及浏览器数据和网络数据交互流程-Python

目录 前言 一、静态网页数据 二、网址通讯流程 1.DNS查询 2.建立连接 3.发送HTTP请求 4.服务器处理请求 5.服务器响应 6.渲染页面 7.页面交互 三、URL/POST/GET 1.URL 2.GET 形式 3.POST 形式 四.获取静态网页数据 1.requests库 点关注&#xff0c;防走丢&am…

粘接PI膜除了使用PI膜专用UV胶粘接,还可以使用热固化环氧胶来解决!

热固化环氧胶也是粘接聚酰亚胺&#xff08;PI&#xff09;膜的一种常见方法。 热固化环氧胶是一种在加热的条件下固化成坚固状态的胶水&#xff0c;在涂抹或涂覆胶水后&#xff0c;通过加热&#xff0c;胶水中的化学反应被触发&#xff0c;导致其硬化和固化&#xff0c;从而形…

C#语法进阶 Lambda

Lambda lambda:匿名方法 演变过程&#xff1a; 版本一&#xff1a; 首先创建一个委托&#xff1a; 创建委托需要调用的方法 调用 版本二&#xff1a;匿名方法 没有名字的方法&#xff0c;前面加一个delegate 这样写的话 可以访问局部变量 调用结果&#xff1a; 版本…

跑腿配送系统技术探析

概述 跑腿配送系统是一种基于现代科技的服务平台&#xff0c;通过智能化的技术手段&#xff0c;实现用户需求的快速响应和高效配送。本文将探讨该系统的核心技术原理&#xff0c;以及在实际开发中的一些代码示例。 技术原理 1. 用户请求与任务分配 跑腿配送系统的第一步是…

DFS,BFS算法

深度优先搜索&#xff08;DFS&#xff09;和广度优先搜索&#xff08;BFS&#xff09;是两种常见的图遍历算法。 深度优先搜索&#xff08;DFS&#xff09; 深度优先搜索是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过&a…

vue保姆级教程----深入了解 Vue Router的工作原理

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4…

YOLOv8改进Shape-IoU损失函数:考虑边界框形状和比例的更准确的指标

💡本篇内容:YOLOv8改进Shape-IoU损失函数:考虑边界框形状和比例的更准确的指标 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 💡参考论文地址:https://arxiv.org/abs/2312.17663 2023年12月最新发表论文:IoU最新的损失函数的…

【error C2146: 语法错误: 缺少“;”(在标识符“PVOID64”的前面)】

用VS2013编译报错&#xff1a; 有人说既然是找不到PVOID64加个typedef就行了。经过本人的试验发现&#xff0c;的确是vs的include顺序问题&#xff0c;调整include顺序保证dx目录在IncludePath目录的下面即可解决问题。 在vs中&#xff0c;选择工具->选项->项目和解决方…

uni静态资源引入及css图片图标引用规范

1、页面组件引入 单页面中的组件引入需经过导入——注册——使用三个步骤&#xff1b; <template><view><!-- 3.使用组件 --><uni-rate text"1"></uni-rate></view> </template> <script>// 1. 导入组件import un…

DoIP学习笔记系列:(八)车厂一般关于DoIP的相关测试分析

文章目录 1. 前言2. 基本项测试2.1 协议版本默认值2.2 车辆标识请求报文格式2.3 带EID的车辆标识请求报文格式2.4 带VIN的车辆标识请求报文格式2.5 否定响应码0x002.6 否定响应码0x012.7 否定响应码0x022.8 否定响应码0x042.9 路由激活应答码0x002.10 路由激活应答码0x012.11 路…