【OSG学习笔记】Day 24: Texture2D 与 Image

Texture2D 与 Image

在 OSG(OpenSceneGraph)三维开发中,纹理贴图 是实现模型真实感渲染的核心手段,而 osg::Imageosg::Texture2D 是承载纹理数据、实现纹理渲染的两个关键类。

本文结合完整可编译运行的四边形纹理渲染源码 ,从类继承关系、核心功能、源码实战、工作流程四个维度,彻底讲透这两个类的用法与原理,让你一站式掌握 OSG 二维纹理渲染。

核心基础

OSG 所有核心类都继承自基类 osg::Object,遵循树形继承、职责分离 的设计思想,先理清 ImageTexture2D 的继承链,才能理解它们的分工:

1. osg::Image 继承关系

osg::Image纯数据载体类,只负责存储图片的像素、宽高、格式等原始数据,与渲染无关:

复制代码
osg::Object(OSG 所有对象基类)
└── osg::Image(图片原始数据容器)
  • 职责:读取/存储/管理图片原始像素数据(如 PNG/JPG/GIF 的像素数组、分辨率、通道数)。
  • 定位:相当于「图片文件的内存化身」,不参与渲染,只提供数据。

2. osg::Texture2D 继承关系

osg::Texture2D渲染状态类,负责将 Image 的数据交给 GPU,实现纹理采样渲染,继承链更长:

复制代码
osg::Object
└── osg::StateAttribute(渲染状态属性基类:纹理、光照、混合等)
    └── osg::Texture(纹理基类,封装通用纹理逻辑)
        └── osg::Texture2D(二维纹理子类,最常用)
  • 职责:将 Image 数据上传至 GPU,配置纹理渲染参数(过滤、环绕、数据更新方式)。
  • 定位:相当于「图片数据与 GPU 之间的桥梁」,是渲染纹理的核心。

3. 两个类的核心分工总结

类名 继承源头 核心作用 与渲染的关系
osg::Image 直接继承 Object 存储图片原始像素数据 无渲染能力
osg::Texture2D 继承 StateAttribute 管理 GPU 纹理,控制渲染效果 直接参与渲染

简单说:Image 管「数据」,Texture2D 管「渲染」,二者配合才能完成纹理贴图。


完整可运行源码(四边形纹理渲染)

以下是完整可直接编译、运行 的 OSG 代码,集成了 Image 加载、Texture2D 纹理绑定、四边形渲染全流程,是本文讲解的核心实践案例:

cpp 复制代码
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Vec3>
#include <osg/Vec2>
#include <osg/Texture2D>
#include <osg/StateSet>
#include <osg/Group>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osgViewer/Viewer>
#include <iostream>

// 创建一个带纹理坐标的四边形节点
osg::ref_ptr<osg::Node> createTextureQuad()
{
    osg::ref_ptr<osg::Geode> geode = new osg::Geode();
    osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();

    // 1. 设置四边形顶点(XY平面,Z=0)
    osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();
    vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));  // 左下角
    vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));  // 右下角
    vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));  // 右上角
    vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));  // 左上角
    geom->setVertexArray(vertices.get());

    // 2. 设置纹理坐标(UV映射,对应图片四个角)
    osg::ref_ptr<osg::Vec2Array> texCoords = new osg::Vec2Array();
    texCoords->push_back(osg::Vec2(0.0f, 0.0f));  // 纹理左下角
    texCoords->push_back(osg::Vec2(1.0f, 0.0f));  // 纹理右下角
    texCoords->push_back(osg::Vec2(1.0f, 1.0f));  // 纹理右上角
    texCoords->push_back(osg::Vec2(0.0f, 1.0f));  // 纹理左上角
    geom->setTexCoordArray(0, texCoords.get());    // 绑定到第0层纹理单元

    // 3. 设置法线(保证光照渲染正常)
    osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
    normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
    geom->setNormalArray(normals.get());
    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

    // 4. 绘制四边形图元
    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
    geode->addDrawable(geom.get());

    return geode.get();
}

// 创建Texture2D纹理状态(核心:绑定Image,配置纹理参数)
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);
    // 核心API:将Image数据绑定到Texture2D
    texture->setImage(image.get());

    // 优化纹理效果:线性过滤
    texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
    texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

    // 将纹理添加到状态集并开启
    stateSet->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);

    return stateSet;
}

int main()
{
    // 1. 创建OSG渲染窗口
    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
    osg::ref_ptr<osg::Group> root = new osg::Group();

    // ==================== Image 核心使用:加载图片数据 ====================
    osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");
    if (!image.valid())
    {
        std::cerr << "错误:图片加载失败,请检查路径!" << std::endl;
        return -1;
    }
    std::cout << "图片加载成功!宽:" << image->s() << " 高:" << image->t() << std::endl;

    // 2. 创建四边形节点
    osg::ref_ptr<osg::Node> quadNode = createTextureQuad();

    // 3. 绑定纹理到节点
    osg::ref_ptr<osg::StateSet> textureState = createTexture2DState(image);
    quadNode->setStateSet(textureState.get());

    // 4. 构建场景树
    root->addChild(quadNode.get());

    // 5. 优化场景并启动渲染
    osgUtil::Optimizer optimizer;
    optimizer.optimize(root.get());
    viewer->setSceneData(root.get());
    viewer->realize();

    std::cout << "渲染启动成功,鼠标操作:左键旋转,滚轮缩放,中键平移" << std::endl;
    return viewer->run();
}

osg::Image 类:图片数据的「内存容器」

osg::Image 是 OSG 中处理图片原始数据的唯一入口,完整源码中通过 osgDB::readImageFile 加载图片 ,本质就是将磁盘文件转为 Image 对象。

1. 核心功能

  1. 加载图片 :通过 osgDB::readImageFile 读取磁盘图片(支持 PNG/JPG/BMP/GIF 等所有常见格式);
  2. 存储数据 :保存图片的宽、高、像素格式(RGB/RGBA)、原始像素数组
  3. 内存管理 :基于 OSG 智能指针(ref_ptr)自动释放内存,避免泄漏。

2. 源码中的核心用法

cpp 复制代码
// 加载磁盘图片,创建Image对象(纯数据载体)
osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");
// 判空校验
if (!image.valid()) {
    std::cerr << "图片加载失败!" << std::endl;
    return -1;
}
// 获取图片属性
int width = image->s();    // 图片宽度
int height = image->t();   // 图片高度

osg::Texture2D 类:GPU 渲染的「纹理桥梁」

osg::Texture2D 是二维纹理渲染的核心,本身不存储图片数据 ,而是通过 setImage() 绑定 Image 对象,将数据上传到 GPU 并配置渲染规则。

1. 核心功能

  1. 绑定 Image 数据 :关联 osg::Image,将像素数据传递给 GPU;
  2. 配置纹理参数:设置过滤模式(抗锯齿)、环绕模式、数据更新频率;
  3. GPU 资源管理:自动管理纹理显存,程序退出时自动释放。

2. 源码中的核心用法

cpp 复制代码
// 创建Texture2D对象
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
// 设置数据更新模式:动态纹理
texture->setDataVariance(osg::Object::DYNAMIC);
// 核心API:绑定Image数据(数据→渲染的关键衔接)
texture->setImage(image.get());
// 配置纹理过滤(抗锯齿,提升显示效果)
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);

Texture2D 与 Image 协同工作流程

在本文的完整源码中,Image 和 Texture2D 的配合流程是纹理渲染的核心,拆解如下:

完整工作流

  1. 加载图片 → 创建 Image
    调用 osgDB::readImageFile 读取磁盘图片,生成 osg::Image 对象,存储原始像素数据;
  2. 创建 Texture2D → 绑定 Image
    实例化 osg::Texture2D,通过 setImage 关联 Image,将图片数据交给纹理类;
  3. 纹理绑定到状态集 → 交给节点
    Texture2D 作为渲染状态,添加到 StateSet(状态集),再挂载到四边形节点;
  4. GPU 渲染 → 显示纹理
    OSG 渲染时,Texture2D 将数据上传至 GPU,结合顶点纹理坐标,完成四边形贴图。

源码对应核心代码

cpp 复制代码
// 步骤1:Image 加载图片原始数据
osg::ref_ptr<osg::Image> image = osgDB::readImageFile("Images/primitives.gif");

// 步骤2:Texture2D 绑定 Image 数据(核心衔接)
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
texture->setImage(image.get());

// 步骤3:纹理加入状态集,绑定到渲染节点
stateSet->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
quadNode->setStateSet(stateSet.get());

总结

  1. 继承关系Image 直接继承 Object(纯数据层),Texture2D 继承 StateAttribute(渲染状态层);
  2. 核心分工osg::Image 负责存储图片原始数据osg::Texture2D 负责将数据传递给 GPU 渲染
  3. 使用关键 :先通过 osgDB 加载 Image,再用 setImage 绑定到 Texture2D,最后挂载到节点完成渲染。
相关推荐
猹叉叉(学习版)4 小时前
【系统分析师_知识点整理】 12.系统设计
笔记·软考·系统设计·系统分析师
FinTech老王4 小时前
告别“sql_mode“噩梦:MySQL 8.0 vs 5.7兼容性全对比与升级避坑指南
android·sql·mysql
匆忙拥挤repeat4 小时前
Android Compose 渲染 UI 帧的三个阶段:组合、布局、绘制
android·ui
笨小古4 小时前
VLA学习笔记——持续更新中
学习·机器人·大模型·具身智能·vla
Rain_Rong4 小时前
禁止迅雷11更新
笔记
帅得不敢出门4 小时前
Android Studio同一个工程根据不同芯片平台加载不同的framework.jar及使用不同的代码
android·android studio·jar
孙严Pay4 小时前
快捷支付和网关支付,到底有啥不一样?
笔记·科技·计算机网络·其他·微信
prog_61034 小时前
【笔记】用cursor手搓cursor(四)
人工智能·笔记·大语言模型·agent