OSG编程指南<十四>:OSG纹理渲染之普通纹理、多重纹理、Mipmap多级渐远纹理及TextureRectangle矩阵纹理

1、纹理映射介绍

  物体的外观不仅包括形状,不同物体表面有着不同的颜色和图案。一个简单而有效地实现这种特性的方法就是使用纹理映射。在三维图形中,纹理映射(Texture Mapping)的方法运用广泛,使用该技术可以大大提高物体的真实感。

  OSG 是对底层 OpenGL API 的封装,OpenGL 本身有非常标准而高效的纹理机制。OSG 全面支持OpenGL 的纹理映射机制,因此,在 OSG 中使用纹理映射机制非常简单。纹理映射主要包括一维纹理、二维纹理、三维纹理、凸凹纹理、多重纹理、Mipmap 纹理、压缩纹理和立方纹理等。本文主要针对一维纹理、二维纹理、三维纹理、多重纹理、Mipmap 纹理和立方纹理等经常使用的几种纹理加以解释说明。

下面讲解一些纹理的基础知识,这些对于熟悉 OpenGL 的朋友来说,应该都是再基础不过了。

(1)纹理坐标:

enum WrapParameter
{
WRAP_S, //x 轴
WRAP_T, //y 轴
WRAP_R //z 轴
};

(2)纹理的包装模式:

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 //纹理镜像的重复映射
};

(3)纹理过滤方法:

enum FilterParameter
{
MIN_FILTER, //用于缩小
MAG_FILTER //用于放大
};

(4)纹理的过滤处理:

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 //选择最临近的纹理单元值
};

(5)纹理映射模式(处理纹理图像数据与物体本身的融合):

enum Mode
{
DECAL= GL_DECAL, //贴花
MODULATE= GL_MODULATE, //调整
BLEND= GL_BLEND, //混合
REPLACE= GL_REPLACE, //替换,覆盖
ADD= GL_ADD //添加
};

(6)纹理坐标的自动生成模式:

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//反射贴图
};

(7)贴图坐标:

enum Coord
{
S, //x
T, //y
R, //z
Q //w
};

(8)纹理的内部格式:

enum InternalFormatMode
{
USE_IMAGE_DATA_FORMAT, //使用贴图本身的格式
USE_USER_DEFINED_FORMAT,//使用用户自定义的格式,如 GL_R3G3B3 等格式
USE_ARB_COMPRESSION, //使用 ARB 协会出的贴图压缩格式
USE_S3TC_DXT1_COMPRESSION,//使用 S3TC_DXT1 压缩格式
USE_S3TC_DXT3_COMPRESSION,//使用 S3TC_DXT3 压缩格式
USE_S3TC_DXT5_COMPRESSION //使用 S3TC_DXT5 压缩格式
};

2、二维纹理

2.1 纹理坐标和纹理数据

  在所有的纹理映射中,二维纹理映射的过程最简单,也非常容易理解。在用户应用程序中创建二维纹理的步骤如下:

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

1.纹理坐标
  在前面几何体的绘制时已经提到用一个二维的向量数据来保存纹理坐标。设置纹理坐标比较简单,纹理坐标是与顶点一一对应的,很像数学中的映射。
  下面的代码段创建了一个 osg::Vec2Array 数组,用于保存纹理坐标,同时将其关联到 Geometry 实例的纹理单元 0。如果要对单一的 Geometry 设置多个纹理,只需要将多个纹理坐标数组关联到Geometry,并针对不同的数组指定不同的纹理单元即可。

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

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

2.纹理数据
  在大多数应用程序中,纹理数据都是从外部导入的图像文件。当没有必要导入图像时,可以生成一幅纹理数据贴图。这里使用从外部导入的纹理数据图形的方法。读取图像需要使用一个新类——osg::Image。osg::Image 继承自 osg::Object 类。面的代码将实现如何读取图像:

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

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

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

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

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

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

3.纹理状态
  用户程序可以使用纹理状态函数接口为每个纹理单元指定渲染状态。纹理状态函数接口与非纹理状态的接口类似。用户可以使用 osg::StateSet::setTextureAttribute()将一个纹理属性关联到 StateSet 对象。setTextureAttribute()的第一个参数是纹理单元,第二个参数是继承自 StateAttribute 类的一种纹理属性。

  合法的纹理属性类共有 6 种,其中包括 5 种纹理类型(osg::Texture1D、osg::Texture2D、osg::Texture3D、
osg::TextureCubeMap 和 osg::TextureRectangle)和一个用于纹理坐标的生成的类(osg::TexGen)。

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

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

  与上面的程序类似,用户可以调用 osg::StateSet::setTextureMode()方法来设置材质渲染模式,这个方法与 setMode()方法类似。用户可以使用 setTextureMode()来设置 GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3DGL_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 纹理映射:

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 模式:

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

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

2.2 示例效果

2.2.1 普通示例

  备注:osg默认会被图片大小非2的n次方的图片进行缩放处理。后续可与矩阵纹理进行对比

在这里插入图片描述

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <windows.h>
#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/TexEnv>
#include <osg/StateSet>
#include <osg/PrimitiveSet>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体

#include <osgUtil/Optimizer>

#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> 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->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();
}
2.2.2 光照和混合模式示例

在这里插入图片描述

在这里插入图片描述

//创建二维纹理状态对象
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());

	//关联Texture2D纹理对象,第三个参数默认为ON
	stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

	//启用混合
	stateset->setMode(GL_BLEND, osg::StateAttribute::ON);

	//关闭光照
	stateset->setMode(GL_LIGHTING, osg::StateAttribute::ON);

	return stateset.get();
}
2.2.3 光照关闭示例

在这里插入图片描述

//创建二维纹理状态对象
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());

	//关联Texture2D纹理对象,第三个参数默认为ON
	stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

	//启用混合
	stateset->setMode(GL_BLEND, osg::StateAttribute::ON);

	//关闭光照
	stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

	return stateset.get();
}

3、多重纹理

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

  多重纹理映射非常广泛,它能实现一些高级的渲染技巧,如光照、贴花、合成和细节纹理等。在OSG 中实现三维纹理主要有以下几个步骤:
(1)指定用户几何体的纹理坐标。
(2)创建多个纹理属性对象并保存纹理多个图形数据。
(3)为 StateSet 设置合适的纹理属性和模式。

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

2.2 示例效果

  原书上给的示例感觉不太合适不具备说明性,因此自己参照learnopengl实现了多重纹理混合示例。

在这里插入图片描述

2.2 源码

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <windows.h>
#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/TexEnv>
#include <osg/StateSet>
#include <osg/PrimitiveSet>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体

#include <osgUtil/Optimizer>

#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> 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());
	geom->setTexCoordArray(1, 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> image2 = osgDB::readImageFile("awesomeface.png");
	osg::ref_ptr<osg::Image> image1 = osgDB::readImageFile("container.jpg");

	//创建状态集对象
	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();

	//创建二维纹理对象
	osg::ref_ptr<osg::Texture2D> texture1 = new osg::Texture2D();
	texture1->setDataVariance(osg::Object::DYNAMIC);
	//设置贴图
	texture1->setImage(image1.get());
	//关联Texture2D纹理对象,第三个参数默认为ON
	stateset->setTextureAttributeAndModes(0, texture1.get(), osg::StateAttribute::ON);

	//创建二维纹理对象
	osg::ref_ptr<osg::Texture2D> texture2 = new osg::Texture2D();
	texture2->setDataVariance(osg::Object::DYNAMIC);
	//设置贴图
	texture2->setImage(image2.get());
	//关联Texture2D纹理对象,第三个参数默认为ON
	stateset->setTextureAttributeAndModes(1, texture2.get(), osg::StateAttribute::ON);

	//启用混合
	stateset->setMode(GL_BLEND, osg::StateAttribute::ON);

	//关闭光照
	stateset->setMode(GL_LIGHTING, 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::Node> node = createNode();

	//创建状态集对象
	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
	stateset = createTexture2DState();

	//使用二维纹理
	node->setStateSet(stateset.get());

	root->addChild(node.get());

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

	//方便查看在多边形之间切换,以查看三角网
	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();
}

4、Mipmap多级渐远纹理

  在一个动态的场景中,当一个纹理对象迅速远离视点时,纹理图像必须随着被投影的图像一起缩小。为了实现这种效果,可以通过对纹理图像进行过滤,适当对它进行缩小,以使它映射到物体的表面时不会产生抖动或者闪烁的效果。但这时还存在一个问题,就是当视点距离速度变大时,单个纹理缩小为单个像素之前,在经过一些过渡点时,经过过滤的纹理图像会变化非常明显。同时,也没有必要使用一张那么大的纹理数据了;当使用一个很大的、包含很多贴图的场景时,对渲染效率的影响是相当大的。

  为了避免这种突然变化的现象及不必要的渲染负担,可以预先指定一系列分辨率递减的纹理图像。使用 Mipmap 纹理映射必须指定全系列大小为 2 的整数次方的纹理图像,其范围为从最大值到 1×1 的纹理单元。例如,如果最高的纹理分辨率为 32×32,那么必须指定的纹理图像为 32×32、16×16、8×8、4×4、2×2、1×1。通常来说,较小的纹理图像是上一级分辨率的纹理图像的 4 个纹理单元的平均值。当然,这里也没有确定的计算方法,一般是这样计算的。

在 OSG 中使用 Mipmap 纹理映射主要包括下面几个步骤:

(1)将各层的纹理图像数据按照从大到小的顺序(且尺寸必须为 2 的幂次)依次存放到 unsigned char*数组中,将这个数组使用 setImage 送入 Image 对象。
(2)将各层纹理数据在数组中的偏移地址记录到一个 osg::Image::MipmapDataType 列表中,用于选择正确的层次细节纹理图像。
(3)使用 setMipmapLevels()将 MipmapDataType 送入 Image 对象。注意,这一步的次序和 setImage不能颠倒,否则可能无法正确显示各级别的纹理图像。

  在示例程序中,可以看到清晰的纹理图像的各个层次级别细节的明显过渡,这里用颜色代替了纹理。

备注:OpenGL描述如下:当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。然而,目前只有基本级别(Base-level)的纹理图像被加载了,如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。生成了纹理和相应的多级渐远纹理后,释放图像的内存并解绑纹理对象是一个很好的习惯。

4.1 示例效果

在这里插入图片描述

4.2 源码

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <windows.h>
#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/TexEnv>
#include <osg/StateSet>
#include <osg/PrimitiveSet>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体

#include <osgUtil/Optimizer>

#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::Geode> createQuad()
{
	osg::ref_ptr<osg::Geode> geode = new osg::Geode();

	osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
	geode->addDrawable(geom.get());

	//设置顶点
	osg::ref_ptr<osg::Vec3Array> vec = new osg::Vec3Array;
	vec->push_back(osg::Vec3(-10.0f, 0.0f, -10.0f));
	vec->push_back(osg::Vec3(-10.0f, 0.0f, 10.0f));
	vec->push_back(osg::Vec3(10.0f, 0.0f, 10.0f));
	vec->push_back(osg::Vec3(10.0f, 0.0f, -10.0f));

	geom->setVertexArray(vec.get());

	//设置法线
	osg::ref_ptr<osg::Vec3Array> nor = new osg::Vec3Array;
	nor->push_back(osg::Vec3f(0.0f, -1.0f, 0.0f));

	geom->setNormalArray(nor.get());
	geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE_SET);


	//设置纹理坐标
	osg::ref_ptr<osg::Vec2Array> tex = new osg::Vec2Array;
	tex->push_back(osg::Vec2f(0.0f, 0.0f));
	tex->push_back(osg::Vec2f(0.0f, 1.0f));
	tex->push_back(osg::Vec2f(1.0f, 1.0f));
	tex->push_back(osg::Vec2f(1.0f, 0.0f));

	geom->setTexCoordArray(0, tex.get());

	geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));

	return geode.get();
}

static void fillImage(unsigned char* ptr, unsigned int size)
{
	//黑色
	if (size == 1)
	{
		float r = 0.5f;
		osg::Vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
		*ptr++ = (unsigned char)((color[0]) * 255.0f);
		*ptr++ = (unsigned char)((color[1]) * 255.0f);
		*ptr++ = (unsigned char)((color[2]) * 255.0f);
		*ptr++ = (unsigned char)((color[3]) * 255.0f);
	}

	//白色
	if (size == 2)
	{
		osg::Vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//黄色
	if (size == 4)
	{
		osg::Vec4 color(0.0f, 1.0f, 0.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//红色
	if (size == 8)
	{
		osg::Vec4 color(1.0f, 0.0f, 0.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//粉红色
	if (size == 16)
	{
		osg::Vec4 color(1.0f, 0.0f, 1.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//黄色
	if (size == 32)
	{
		osg::Vec4 color(1.0f, 1.0f, 0.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//蓝绿色
	if (size == 64)
	{
		osg::Vec4 color(0.0f, 1.0f, 1.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//灰白色
	if (size == 128)
	{
		osg::Vec4 color(0.5f, 0.5f, 0.5f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}

	//蓝色
	if (size == 256)
	{
		osg::Vec4 color(0.0f, 0.0f, 1.0f, 1.0f);
		for (unsigned int r = 0; r < size; ++r)
		{
			for (unsigned int c = 0; c < size; ++c)
			{
				*ptr++ = (unsigned char)((color[0]) * 255.0f);
				*ptr++ = (unsigned char)((color[1]) * 255.0f);
				*ptr++ = (unsigned char)((color[2]) * 255.0f);
				*ptr++ = (unsigned char)((color[3]) * 255.0f);
			}
		}
	}
}

int main()
{
	osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();

	osg::ref_ptr<osg::Group> root = new osg::Group();

	//创建一个平面
	osg::ref_ptr<osg::Geode> geode = createQuad();

	osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();

	osg::ref_ptr<osg::Image> image = new osg::Image();

	//创建一个MipmapDataType列表,用来各层图片数据的偏移地址
	osg::Image::MipmapDataType mipmapData;

	//纹理的尺寸的最大值,必须为2的幂次
	unsigned int s = 256;

	//计算所需分配的数组的大小
	unsigned int totalSize = 0;

	for (unsigned int i = 0; s > 0; s >>= 1, ++i)
	{
		if (i > 0)
		{
			mipmapData.push_back(totalSize);
		}

		totalSize += s * s * 4;
	}

	//申请一个数据
	unsigned char* ptr = new unsigned char[totalSize];

	//设置image的尺寸大小,数据及数据格式
	image->setImage(256, 256, 256, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, ptr, osg::Image::USE_NEW_DELETE, 1);

	//将偏移地址传入imge对象
	image->setMipmapLevels(mipmapData);

	//向image中填充各层数据
	s = 256;
	for (unsigned int i = 0; s > 0; s >>= 1, ++i)
	{
		fillImage(ptr, s);

		ptr += s * s * 4;
	}

	//创建一个二维纹理对象
	osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
	//设置贴图
	texture->setImage(0, image.get());
	//设置边界处理为REPEATE
	texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
	texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
	//设置滤波
	texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST_MIPMAP_NEAREST);
	texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::NEAREST);
	//启用二维纹理对象
	stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

	geode->setStateSet(stateset.get());

	root->addChild(geode.get());

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

	//方便查看在多边形之间切换,以查看三角网
	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();
}

5、TextureRectangle矩阵纹理

  TextureRectangle 纹理映射是在 OpenGL 后来版本中的一个扩展——ARB_texture_rectangle,它也是一种二维纹理映射,但它与前面介绍的二维映射有很大的区别。
在这里插入图片描述

  使用 TextureRectangle 纹理映射时有以下几点需要注意:

纹理环绕模式。它并不支持所有的纹理包装模式,只能使用 CLAMP、CLAMP_TO_EDGE 或CLAMP_TO_BORDER,并不支持 REPEAT。
纹理滤波。它只支持NEAREST或者LINEAR,不支持Mipmap滤波,使用它时不能实现Mipmap纹理。
不支持纹理边框。

  对于没有图形学基础的开发人员来说,TextureRectangle 比较容易理解,也非常容易上手,可以简单理解为一个矩形纹理。

5.1 示例效果

在这里插入图片描述

5.2 源码

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。

#include <windows.h>
#include <osgViewer/Viewer>

#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Geometry>
#include <osg/Image>
#include <osg/TexGen>
#include <osg/Texture1D>
#include <osg/TexEnv>
#include <osg/TextureRectangle>
#include <osg/TexMat>
#include <osg/StateSet>
#include <osg/PrimitiveSet>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体

#include <osgUtil/Optimizer>

#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> 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::TextureRectangle> texture = new osg::TextureRectangle();
	texture->setDataVariance(osg::Object::DYNAMIC);
	//设置贴图
	texture->setImage(image.get());

	//设置纹理矩阵,并设置为根据矩阵纹理(TextureRectangle)的大小自动缩放
	//从而允许应用一个矩形纹理到一个纹理坐标不在0-1上
	osg::ref_ptr<osg::TexMat> texmat = new osg::TexMat;
	texmat->setScaleByTextureRectangleSize(true);

	//启用纹理及纹理矩阵
	stateset->setTextureAttributeAndModes(0, texmat.get(), osg::StateAttribute::ON);
	stateset->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

	//关闭光照
	stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

	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->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.mfbz.cn/a/202523.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

flink源码分析之功能组件(四)-slotpool组件I

简介 本系列是flink源码分析的第二个系列&#xff0c;上一个《flink源码分析之集群与资源》分析集群与资源&#xff0c;本系列分析功能组件&#xff0c;kubeclient&#xff0c;rpc&#xff0c;心跳&#xff0c;高可用&#xff0c;slotpool&#xff0c;rest&#xff0c;metrics&…

什么是供应链攻击?

随着企业越来越依赖技术、连接性和第三方&#xff0c;供应链攻击变得越来越普遍。这些攻击旨在通过供应商和业务合作伙伴损害公司。 供应链攻击可能对企业和组织构成重大威胁&#xff0c;损害其安全以及向客户提供的产品和服务的安全。 在本文中&#xff0c;我们将探讨供应链…

稳定的音频来了 — 使用人工智能创作音乐(for free)

今天&#xff0c;以稳定扩散&#xff08;Stable Diffusion&#xff09;和StableLM等开源AI工具和模型而闻名的Stability AI公司推出了其首个音乐和声音生成AI产品——StableAudio。音乐产业以其难以打入而闻名。即使您拥有才华和动力&#xff0c;您仍然需要创作和制作音乐所需的…

AppDelete 4.3.3(软件清理卸载工具)

AppDelete for Mac是一款运行在Mac平台上的强大软件卸载工具&#xff0c;AppDelete Mac版不仅可以删除应用程序&#xff0c;还可以删除小部件&#xff0c;首选项窗格&#xff0c;插件和屏幕保护程序及其相关文件&#xff0c;卸载快速又干净&#xff0c;仅需要简单的拖拽即可。 …

WEB渗透—反序列化(九)

Web渗透—反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩哔_…

编程中常见的技术难题——如何有效地解决编程中常见的技术难题?

文章目录 前言编程的重要性编程中常见的技术难题新手编程常见问题一、变量的命名规范二、语法错误三、逻辑错误四、代码复用五、代码优化 解决技术难题的方法后记 前言 在编写程序的过程中&#xff0c;总会遇到各种各样的技术难题&#xff0c;这些问题常常需要程序员们耗费大量…

面试题:海量PDF的OCR处理思路

关键点&#xff1a; 1000wPDF&#xff1a;数据量非常大。3天处理完&#xff1a;有时间限制。一篇PDF1~10s&#xff1a;可能需要以最高10s去做计算&#xff0c;这样时间才能保证留有富余。要求资源最大化利用&#xff1a;也就是尽可能节省服务器资源&#xff0c;能复用尽量复用&…

NB-IoT BC260Y Open CPU SDK⑤点亮一个LED

NB-IoT BC260Y Open CPU SDK⑤点亮一个LED 1、BC260Y gpio资源介绍2、相关API介绍3、调试信息串口打印3、实例分析 本章节将介绍BC260Y硬件GPIO相关操作 1、BC260Y gpio资源介绍 BC260Y-AA的sdk包中官方给出了16个可用IO 在ql_gpio.h文件中有定义如下/**********************…

SpringCloud原理】OpenFeign之FeignClient动态代理生成原理

大家好&#xff0c;前面我已经剖析了OpenFeign的动态代理生成原理和Ribbon的运行原理&#xff0c;这篇文章来继续剖析SpringCloud组件原理&#xff0c;来看一看OpenFeign是如何基于Ribbon来实现负载均衡的&#xff0c;两组件是如何协同工作的。 一、Feign动态代理调用实现rpc流…

并查集带权并查集

定义 : 并查集 : 一种数据结构&#xff0c;用于处理一些不相交集合的合并与查询问题&#xff1b; 例题 : 如 : 有n种元素&#xff0c;分属于不同的n个集合&#xff1b; 有两种操作 : 1.给出两个元素的亲属关系&#xff0c;合并两个集合(x与y是亲戚&#xff0c;亲戚的亲戚…

基于Java SSM框架+Vue实现实现大学生企业推荐网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现大学生企业推荐网站演示 摘要 大学生企业推荐系统采用B/S结构、java开发语言、以及Mysql数据库等技术。系统主要分为管理员和学生、企业三部分&#xff0c;管理员主要功能包括&#xff1a;首页、个人中心、学生管理、企业管理、招聘信息管理、个人简历…

【探索Linux】—— 强大的命令行工具 P.18(进程信号 —— 信号捕捉 | 信号处理 | sigaction() )

阅读导航 引言一、信号捕捉1. 内核实现信号捕捉过程2. sigaction() 函数&#xff08;1&#xff09;函数原型&#xff08;2&#xff09;参数说明&#xff08;3&#xff09;返回值&#xff08;4&#xff09;函数使用 二、可重入函数与不可重入函数1. 可重入函数条件2. 不可重入函…

MQTT发布_订阅架构(Pub_Sub)

MQTT发布/订阅架构&#xff08;Pub/Sub&#xff09; 本文中&#xff0c;将深入研究Pub/Sub架构&#xff0c;在软件架构中一个消息模式&#xff0c;它支持不同组件或系统之间以解耦的方式进行通信。 在前一片文章[MQTT简介]http://t.csdnimg.cn/6lNeZ中&#xff0c;对MQTT有一个…

Gitee-PicGo-Typora

Gitee-PicGo-Typora 问题引出 问题1&#xff1a;根据相关法律法规和政策&#xff0c;您的部分文件因存在敏感信息而无法显示 就在昨晚&#xff0c; 我在记笔记的时候&#xff0c;发现之前配置的七牛云图床出了问题&#xff1a; 1、根据相关法律法规和政策&#xff0c;您的部…

RabbitMQ消息模型之Routing-Topic

Routing Topic Topic类型的Exchange与Direct相比&#xff0c;都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key的时候使用通配符&#xff01;这种模型Routingkey一般都是由一个或多个单词组成&#xff0c;多个单词之间以”…

Mysql安全之基础合规

一、背景 某次某平台进行安全性符合型评估时&#xff0c;列出了数据库相关安全选项&#xff0c;本文特对此记录&#xff0c;以供备忘参考。 二、安全配置 2.1、数据库系统登录时的用户进行身份标识和鉴别&#xff1b; 1&#xff09;对登录Mysql系统用户的密码复杂度是否有要…

Stream API练习题

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 考虑到Stream API在实际…

2023-11-30 事业-代号s-资质-香港公司-带注册服务商-盛森国际-分析

摘要: 基于合法避税及其他因素&#xff0c;考虑在香港注册公司. 选择的服务商为盛森国际&#xff0c;对该公司做彻底的背调和服务分析, 以规避潜在的风险. 并分析该公司在香港代注册的服务商中的行业竞争力, 以保证其服务的质量及成本的控制. 盛森国际官方资料: 官网: 注册香港…

Nuxt.js:下一代Web开发框架的革命性力量

文章目录 一、Nuxt.js简介二、Nuxt.js的特点1. 集成Vue.js和Node.js2. 自动代码分割和优化3. 服务端渲染&#xff08;SSR&#xff09;4. 强大的路由管理5. 丰富的插件系统 三、Nuxt.js的优势1. 提高开发效率2. 降低维护成本3. 提高用户体验 四、Nuxt.js在实际应用中的案例1. 电…

YOLOv5独家原创改进:自研独家创新FT_Conv,卷积高效结合傅里叶分数阶变换

💡💡💡本文自研创新改进:卷积如何有效地和频域结合,引入分数阶傅里叶变换(FrFT)和分数阶Gabor变换(FrGT),最终创新到YOLOv5。 使用方法:1)直接替换原来的C2f;2)放在backbone SPPF后使用;等 推荐指数:五星 在道路缺陷检测任务中,原始map为0.8,FT_Conv为0.82 …
最新文章