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纹理对象,第三个参数默认为ONstateset->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纹理对象,第三个参数默认为ONstateset->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纹理对象,第三个参数默认为ONstateset->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纹理对象,第三个参数默认为ONstateset->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());//设置边界处理为REPEATEtexture->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();
}
相关文章:

OSG编程指南<十四>:OSG纹理渲染之普通纹理、多重纹理、Mipmap多级渐远纹理及TextureRectangle矩阵纹理
1、纹理映射介绍 物体的外观不仅包括形状,不同物体表面有着不同的颜色和图案。一个简单而有效地实现这种特性的方法就是使用纹理映射。在三维图形中,纹理映射(Texture Mapping)的方法运用广泛,使用该技术可以大大提高物…...

Langchain-Chatchat的安装过程
参考:LLMs之RAG:LangChain-Chatchat(一款中文友好的全流程本地知识库问答应用)的简介(支持 FastChat 接入的ChatGLM-2/LLaMA-2等多款主流LLMs多款embe_一个处女座的程序猿的博客-CSDN博客 1、安装过程中出现了 GPU驱动版本 是11.8 而 python -c "…...
Windows系列:Windows Server 2012 R2 安装VMware Tools的正确姿势(实现物理机和虚拟机文件互传)
Windows Server 2012 R2 安装VMware Tools的正确姿势(实现物理机和虚拟机文件互传) 安装环境安装步骤一. 安装补丁下面进入教程首先打开虚拟机,点击"虚拟机"选项中的"安装VMware Tools"点击确定如果出现下图中的问题,说明虚拟机中缺少更新程序,我们需…...

最长连续递增序列
最长连续递增序列 描述 : 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。 连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l < i < r,都有 …...

FreeRTOS入门--任务
目录 一、什么是任务 二、创建任务---xTaskCreate函数 三、任务的删除 四、任务优先级 1.阻塞状态(Blocked) 2.暂停状态(Suspended) 3.就绪状态(Ready) 五、Delay 六、调度算法 一、什么是任务 在FreeRTOS中,任务就是一个函数,原型如下ÿ…...

4个解决特定的任务的Pandas高效代码
在本文中,我将分享4个在一行代码中完成的Pandas操作。这些操作可以有效地解决特定的任务,并以一种好的方式给出结果。 从列表中创建字典 我有一份商品清单,我想看看它们的分布情况。更具体地说:希望得到唯一值以及它们在列表中出…...
【已解决】AttributeError: module ‘gradio‘ has no attribute ‘Image‘
问题描述 AttributeError: module gradio has no attribute Image 不知道作者用的是哪个gradio版本,最新的版本报错AttributeError: module gradio has no attribute outputs , 换一个老一点的版本会报错AttributeError: module gradio has no attribute…...
高级软件工程15本书籍
如果您想学习软件工程技能并提高您的专业知识,那么这里是您的最佳选择。我们有一本很棒的书,可以极大地增强您在软件工程方面的知识。 1)干净的代码 Robert C. Martin 写了一本名为“干净代码:敏捷软件工艺手册”的书。在本书中&…...

计网Lesson3 - 计算机网络评价指标与封包解包
文章目录 计算机网络的性能指标1. 速率2. 带宽3. 吞吐量4. 时延5. 时延带宽积6. 往返时间7. 利用率8. 数据的解包和封包 计算机网络的术语实体协议服务 计算机网络的性能指标 1. 速率 数据…...
深度学习好文记录,反复学习
recent update time:2023.12.2 深度学习入门 - 知乎、这本书也很好,作者写的专栏不错。 机器学习,深度学习一些好文_一只菜得不行的鸟的博客-CSDN博客 卷积神经网络学习路线(五)| 卷积神经网络参数设置,提…...

CSS浅谈动画性能
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 目的一、举个栗子二、性能分析1.从图层分析2.性能分析 总结 目的 为了探究使用动画时,『transform』和『width、height、margin等』的差异 一、举个栗子…...

万能的视频格式播放器
今天博主给大家带来一款“万能”的视频播放器——VLC Media Player,支持的文件格式非常多,大家快来一起看看吧! VLC Media Player 是一款可播放大多数格式,而无需安装编解码器包的媒体播放器。可以播放 MPEG-1、MPEG-2、MPEG-4、D…...

设计模式---第五篇
系列文章目录 文章目录 系列文章目录前言一、知道观察者模式吗?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、知道观察者模式吗? 答:观察者模式是定义对…...

.NET8构建统计Extreme Optimization Numerical Libraries
为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持,使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合,为技术和统计…...

07-原型模式-C语言实现
原型模式: Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.(用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象。 ) UML图࿱…...
深度学习与深度迁移学习有什么区别?
深度学习包含深度迁移学习,它们都利用了深层神经网络(Deep Neural Network,DNN)来处理数据,并从中学习特征。但是,它们也有一些区别。 深度学习是一种机器学习方法,它通过多层神经网络来自动学…...

创建Asp.net MVC项目Ajax实现视图页面数据与后端Json传值显示
简述回顾 继上篇文章创建的mvc传值这里说明一下Json传值。在mvc框架中,不可避免地会遇到前台传值到后台,前台接收后台的值的情况(前台指view,后台指controller),有时只需要从控制器中返回一个处理的结果&a…...
1089 Insert or Merge (插入排序,相邻归并排序,附模拟实现)
注意点1:判断插入排序不能从头开始判断是否为目标数组, 比如:初始为1 2 3 4 3,目标数组也为1 2 3 4 3,则如果是从头开始推的,则下一步会变成1 2 3 4 3,而下一步应该是 1 2 3 3 4。所以我们应该…...

基于算能的国产AI边缘计算盒子8核心A53丨17.6Tops算力
边缘计算盒子 8核心A53丨17.6Tops算力 ● 可提供17.6TOPS(INT8)的峰值计算能力、2.2TFLOPS(FP32)的高精度算力,单芯片最高支持32路H.264 & H.265的实时解码能力。 ● 适配Caffe/TensorFlow/MxNet/PyTorch/ ONNX/…...

Eaxyx 让圆球跟随鼠标移动
如果出现2023,代表配置成功: 进入Eaxy官方网站,点击文档: 选择 函数->绘图函数->initgraph: 可以看见initgraph()函数有如下三个参数: 现在我们想生成一个1280*720大小的窗口: 我们需写如下代码: 但…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...