1. 矩形纹理基础概念与OSG实现
在计算机图形学中,纹理映射是将2D图像映射到3D几何体表面的关键技术。传统Texture2D存在两个主要限制:纹理坐标必须归一化到[0,1]范围,以及纹理尺寸必须是2的幂次方。TextureRectangle正是为解决这些限制而生的扩展特性。
1.1 TextureRectangle核心特性
与常规Texture2D相比,TextureRectangle具有以下显著差异:
-
纹理坐标系统:
- Texture2D:强制要求归一化坐标(0.0-1.0)
- TextureRectangle:直接使用像素坐标系统,例如512x512的纹理可以直接使用(0,0)-(512,512)的坐标范围
-
尺寸限制:
- Texture2D:通常要求宽高为2^n(如1024x1024)
- TextureRectangle:支持任意尺寸(如513x1025)
-
功能限制:
- 不支持Mipmap层级
- 仅支持CLAMP类环绕模式(CLAMP_TO_EDGE/CLAMP_TO_BORDER)
- 滤波方式仅限NEAREST和LINEAR
cpp复制// 创建TextureRectangle对象示例
osg::ref_ptr<osg::TextureRectangle> texRect = new osg::TextureRectangle();
texRect->setImage(osgDB::readImageFile("texture.jpg"));
1.2 TexMat纹理矩阵的作用
TexMat(Texture Matrix)是控制纹理坐标变换的关键组件,在TextureRectangle应用中尤为重要:
-
坐标缩放:
cpp复制osg::ref_ptr<osg::TexMat> texmat = new osg::TexMat; texmat->setScaleByTextureRectangleSize(true); // 自动按纹理尺寸缩放 -
动态调整:
- 实现纹理滑动、旋转动画
- 多纹理混合时的坐标变换
重要提示:当同时使用TextureRectangle和TexMat时,建议始终启用setScaleByTextureRectangleSize(true),这是正确显示非2次幂纹理的关键设置。
2. 完整实现流程
2.1 场景搭建基础
首先创建带纹理坐标的几何体:
cpp复制osg::ref_ptr<osg::Geometry> createTexturedQuad() {
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
// 顶点坐标(XY平面)
osg::Vec3Array* vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(0,0,0));
vertices->push_back(osg::Vec3(1,0,0));
vertices->push_back(osg::Vec3(1,1,0));
vertices->push_back(osg::Vec3(0,1,0));
geom->setVertexArray(vertices);
// 纹理坐标(注意使用实际像素尺寸)
osg::Vec2Array* texcoords = new osg::Vec2Array;
texcoords->push_back(osg::Vec2(0,0));
texcoords->push_back(osg::Vec2(512,0)); // 假设纹理宽度512
texcoords->push_back(osg::Vec2(512,512));
texcoords->push_back(osg::Vec2(0,512));
geom->setTexCoordArray(0, texcoords);
geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
return geom;
}
2.2 纹理状态设置
配置TextureRectangle与TexMat的完整流程:
cpp复制osg::StateSet* createTextureState(osg::Image* image) {
osg::StateSet* stateset = new osg::StateSet();
// 1. 创建纹理对象
osg::TextureRectangle* texture = new osg::TextureRectangle();
texture->setImage(image);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
// 2. 配置纹理矩阵
osg::TexMat* texmat = new osg::TexMat;
texmat->setScaleByTextureRectangleSize(true);
// 3. 组合到状态集
stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(0, texmat, osg::StateAttribute::ON);
return stateset;
}
2.3 主程序集成
最终场景组装与渲染:
cpp复制int main() {
osgViewer::Viewer viewer;
osg::Group* root = new osg::Group();
// 加载纹理图像(支持非2次幂尺寸)
osg::Image* image = osgDB::readImageFile("texture.jpg");
// 创建带纹理的几何体
osg::Geode* geode = new osg::Geode();
geode->addDrawable(createTexturedQuad());
geode->setStateSet(createTextureState(image));
root->addChild(geode);
viewer.setSceneData(root);
return viewer.run();
}
3. 高级应用技巧
3.1 动态纹理更新
TextureRectangle特别适合需要频繁更新的纹理场景:
cpp复制// 每帧更新纹理内容
void updateTexture(osg::TextureRectangle* tex) {
osg::Image* image = tex->getImage();
unsigned char* data = image->data();
// 修改纹理数据(例如实现动态遮罩)
for(int y=0; y<image->t(); ++y) {
for(int x=0; x<image->s(); ++x) {
// 动态计算像素值
data[y*image->getRowSizeInBytes() + x*4 + 0] = ...; // R
data[y*image->getRowSizeInBytes() + x*4 + 1] = ...; // G
data[y*image->getRowSizeInBytes() + x*4 + 2] = ...; // B
}
}
image->dirty(); // 标记数据已更新
}
3.2 多纹理混合
结合TexMat实现复杂纹理效果:
cpp复制void setupMultiTexture(osg::StateSet* stateset) {
// 基础纹理(TextureRectangle)
osg::TextureRectangle* tex1 = new osg::TextureRectangle(...);
// 叠加纹理(常规Texture2D)
osg::Texture2D* tex2 = new osg::Texture2D(...);
// 配置纹理混合模式
osg::TexEnv* texenv = new osg::TexEnv;
texenv->setMode(osg::TexEnv::MODULATE);
stateset->setTextureAttributeAndModes(0, tex1, osg::StateAttribute::ON);
stateset->setTextureAttributeAndModes(1, tex2, osg::StateAttribute::ON);
stateset->setTextureAttribute(1, texenv);
// 为第二层纹理设置独立的纹理矩阵
osg::TexMat* texmat2 = new osg::TexMat;
texmat2->setMatrix(osg::Matrix::scale(0.5,0.5,1)); // 缩小第二层纹理
stateset->setTextureAttribute(1, texmat2);
}
4. 性能优化与问题排查
4.1 常见问题解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 纹理显示为纯色 | 未正确设置TexMat缩放 | 确认调用setScaleByTextureRectangleSize(true) |
| 纹理边缘闪烁 | 超出纹理坐标范围 | 检查纹理坐标值是否在纹理尺寸范围内 |
| 性能下降明显 | 频繁更新大尺寸纹理 | 考虑使用PBO(Pixel Buffer Object)优化上传 |
4.2 性能优化建议
-
纹理尺寸管理:
- 虽然支持任意尺寸,但建议宽度保持64字节对齐
- 超大纹理(>4096)考虑分块处理
-
状态变更优化:
cpp复制// 不好的做法:每帧创建新纹理对象 // 好的做法:复用纹理对象,只更新图像数据 texture->setImage(updatedImage); -
异步加载技巧:
cpp复制// 使用osg::ProxyNode预加载大纹理 osg::ProxyNode* proxy = new osg::ProxyNode; proxy->setFileName(0, "large_texture.jpg");
在实际项目中,TextureRectangle特别适合以下场景:
- 视频贴图(视频帧通常不是2的幂次方)
- 动态生成的图表/文字纹理
- 需要精确像素级控制的UI元素渲染
- 机器视觉应用中的相机图像直接渲染
通过合理使用TexMat,可以实现更复杂的纹理动画效果,比如:
- 纹理滑动(平移矩阵)
- 动态缩放(缩放矩阵)
- 透视变形(投影矩阵)
- 多纹理混合(多重纹理矩阵)
