当前位置: 首页 > java >正文

【OSG学习笔记】Day 10: 字体与文字渲染(osgText)

在这里插入图片描述

osgText库简介

osgText 是OpenSceneGraph(OSG)中用于文本渲染的重要模块,支持在3D场景中添加静态/动态文字、自定义字体、文字样式(颜色、大小、对齐方式等)以及动态更新文本内容。通过结合OSG的场景图机制,可实现文字与3D模型的精准对齐、视角跟随等效果。

动态文字标签实现

1. 创建字体对象

#include <osgText/Text>
#include <osgDB/ReadFile>// 加载字体文件(需指定绝对路径或通过OSG_DATA_PATH环境变量查找)
osgText::Font* font = osgText::readFontFile("simhei.ttf"); // 示例:黑体
if (!font) {osg::notify(osg::FATAL) << "Failed to load font!" << std::endl;return nullptr;
}

2. 创建动态文本节点

osg::ref_ptr<osgText::Text> dynamicText = new osgText::Text;
dynamicText->setFont(font); // 设置字体
dynamicText->setCharacterSize(24); // 文字大小(像素)
dynamicText->setColor(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); // 文字颜色(RGBA)
dynamicText->setPosition(osg::Vec3(0.0f, 0.0f, 1.0f)); // 文字在3D场景中的位置
dynamicText->setAlignment(osgText::Text::CENTER_CENTER); // 居中对齐
dynamicText->setDrawMode(osgText::Text::TEXT); // 绘制模式(仅文字)

3. 动态更新文本内容

// 在场景更新回调中修改文本(例如每帧更新时间)
class TextUpdater : public osg::NodeCallback {
public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {osgText::Text* text = static_cast<osgText::Text*>(node);if (text) {// 动态生成文本(示例:显示当前时间)std::time_t now = std::time(nullptr);text->setText("当前时间:" + std::string(std::ctime(&now)));}traverse(node, nv); // 继续遍历场景}
};// 为文本节点添加更新回调
dynamicText->setUpdateCallback(new TextUpdater);

4. 将文本节点添加到场景图

osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(dynamicText); // 将文本节点加入根节点// 创建场景视图(示例:标准OSG窗口渲染)
osgViewer::Viewer viewer;
viewer.setSceneData(root);
viewer.run();

进阶功能:文字与3D对象绑定

1. 跟随模型位置

// 假设存在一个模型节点modelNode
osg::ref_ptr<osg::PositionAttitudeTransform> transform = new osg::PositionAttitudeTransform;
transform->addChild(dynamicText); // 文本作为子节点
transform->setPosition(modelNode->getWorldMatrices()[0].getTrans()); // 跟随模型世界坐标root->addChild(transform); // 将变换节点加入场景

2. 视角对齐(文字始终朝向摄像机)

dynamicText->setDataVariance(osg::Object::DYNAMIC); // 标记为动态更新
dynamicText->setInitialBound(osg::BoundingBox(-10, -10, -10, 10, 10, 10)); // 设置包围盒// 添加视角对齐回调(通过矩阵变换实现)
class ViewAlignedCallback : public osg::NodeCallback {
public:osg::Matrixd viewMatrix; // 摄像机视图矩阵virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {if (osg::CullVisitor* cv = dynamic_cast<osg::CullVisitor*>(nv)) {viewMatrix = cv->getViewMatrix();osg::Matrixd invView = viewMatrix.inverse();node->setWorldMatrices(osg::Matrixd::translate(node->getPosition()) * invView.getRotate());}traverse(node, nv);}
};dynamicText->setUpdateCallback(new ViewAlignedCallback);

五、常见问题与解决方案

问题描述解决方案
文字显示为方块/乱码检查字体文件路径是否正确,确保字体支持目标字符(如中文需使用中文字体)
文字不随模型移动将文本节点放入PositionAttitudeTransform节点中,并绑定模型变换矩阵
文字闪烁/裁剪调整文本节点的setInitialBound()范围,或启用深度测试:dynamicText->getOrCreateStateSet()->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
动态更新不生效确保更新回调(UpdateCallback)正确绑定,且文本节点的DataVariance设置为DYNAMIC

注意:

当你去掉 text->setText("hello"); 这一行代码后,动态显示失效,可能是因为以下原因:
初始文本未设置:osgText::Text 对象在创建时,若没有初始化文本内容,那么后续仅通过回调更新文本,可能无法正常显示,因为渲染系统可能不知道要显示什么内容。
回调未正确触发:回调类 TextUpdater 或许依赖于初始文本内容来进行后续更新操作,若初始文本为空,回调函数可能不会按预期工作。

实战

text.cpp

#include <osgViewer/Viewer>
#include <osgText/Text>
#include <osgDB/ReadFile>
#include <ctime>
#include <iostream>
#include <osg/PositionAttitudeTransform>
#include <osgUtil/CullVisitor>class TextUpdater : public osg::NodeCallback {osg::Matrixd viewMatrix; // 摄像机视图矩阵
public:virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) {osgText::Text* text = static_cast<osgText::Text*>(node);if (text) {std::time_t now = std::time(nullptr);text->setText("now time: " + std::string(std::ctime(&now)));}traverse(node, nv);}
};// 批量文字渲染函数
void batchTextRendering(osg::Group* root) {// 加载字体osgText::Font* font = osgText::readFontFile("../fonts/arial.ttf");if (!font) {std::cout << "read ttf error." << std::endl;return;}// 创建一个 Geode 节点用于批量管理文字标签osg::ref_ptr<osg::Geode> geode = new osg::Geode;// 定义要渲染的文字数量const int numTexts = 5;for (int i = 0; i < numTexts; ++i) {// 创建动态文本节点osg::ref_ptr<osgText::Text> text = new osgText::Text;text->setFont(font);text->setCharacterSize(20);text->setColor(osg::Vec4(0, 0, 1, 1)); // 设置文字颜色为蓝色text->setPosition(osg::Vec3(20.0f, i * 20, -(i + 1) * 20));text->setAlignment(osgText::Text::CENTER_CENTER);text->setText("Text ");text->setText("Text " + std::to_string(i));// 将文本节点添加到 Geode 节点geode->addDrawable(text);}// 将 Geode 节点添加到根节点root->addChild(geode);
}int main() {// 加载字体osgText::Font* font = osgText::readFontFile("../fonts/arial.ttf");if (!font) {std::cout <<"read ttf error." <<std::endl;return -1;}// 创建动态文本osg::ref_ptr<osgText::Text> text = new osgText::Text;text->setFont(font);text->setCharacterSize(24);text->setColor(osg::Vec4(1, 0, 0, 1)); // 红色text->setPosition(osg::Vec3(0, 10, 0));text->setAlignment(osgText::Text::CENTER_CENTER);text->setDataVariance(osg::Object::DYNAMIC);text->setText("hello");// text->setDrawMode(osgText::Text::TEXT); // 绘制模式(仅文字)text->setUpdateCallback(new TextUpdater);std::cout << "Text position: x=" << text->getPosition().x() << std::endl;std::cout << "Text position: y=" << text->getPosition().y() << std::endl;std::cout << "Text position: z=" << text->getPosition().z() << std::endl;// 创建一个 Geode 节点并将文本节点添加到其中osg::ref_ptr<osg::Geode> geode = new osg::Geode;geode->addDrawable(text);// // 创建一个 PositionAttitudeTransform 节点osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform;// // 设置旋转,使文本平面与 xz 平面平行osg::Quat rotation(osg::inDegrees(90.0), osg::Vec3(1.0, 0.0, 0.0));pat->setAttitude(rotation);pat->addChild(geode);// 创建场景根节点osg::ref_ptr<osg::Group> root = new osg::Group;root->addChild(pat);batchTextRendering(pat);// 渲染osgViewer::Viewer viewer;viewer.setSceneData(root);return viewer.run();
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.12)
project(OSG_Text)# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# 查找OpenSceneGraph核心组件
find_package(OpenSceneGraph REQUIRED COMPONENTS osg          # 核心库osgDB        # 文件读写osgViewer    # 查看器功能osgGA        # 图形上下文osgUtil      # 工具库osgText      
)# 包含头文件路径
include_directories(${OPENSCENEGRAPH_INCLUDE_DIR}
)# 创建可执行文件
add_executable(${PROJECT_NAME} text.cpp)# 链接OpenSceneGraph库
target_link_libraries(${PROJECT_NAME}${OPENSCENEGRAPH_LIBRARIES}# Windows需要额外链接$<$<PLATFORM_ID:Windows>:OpenThreads>
)# 配置调试模式
if(CMAKE_BUILD_TYPE STREQUAL "Debug")target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG)message(STATUS "Building in DEBUG mode")
endif()

运行效果

在这里插入图片描述
几个有层次感的文字生成了。_

http://www.xdnf.cn/news/2153.html

相关文章:

  • ※※惯性时间常数与系统惯量定义、区别、联系
  • 水果成篮--LeetCode
  • Java—— 包装类
  • Arduino+ESP01S烧录
  • AudioVideoMerger 下载与使用
  • 计算机网络 | 应用层(1)--应用层协议原理
  • Git 工具的安装
  • django之优化分页功能(利用参数共存及封装来实现)
  • 一篇入门之-评分卡变量分箱(卡方分箱、决策树分箱、KS分箱等)实操例子
  • 软件开发架构设计原则详解(含案例)
  • 精益数据分析(28/126):解读商业模式拼图与关键指标
  • KTT入门
  • 现代化Android开发:Compose提示信息的最佳封装方案
  • qt事件过滤与传递机制
  • 关于图论的知识
  • 2025.4.26总结
  • GitOps进化:深入探讨 Argo CD 及其对持续部署的影响
  • 图像特征检测算法对比及说明
  • FPGA前瞻篇-数字电路基础-逻辑门电路设计
  • ssm乡村合作社商贸网站设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
  • 【C】初阶数据结构13 -- 快速排序
  • Pygame物理模拟:实现重力、弹跳与简单物理引擎
  • DAM-3B,英伟达推出的多模态大语言模型
  • IntelliJ IDEA 2025.2 和 JetBrains Rider 2025.1 恢复git commit为模态窗口
  • 23种设计模式-行为型模式之迭代器模式(Java版本)
  • 测试基础笔记第十三天
  • 工业摄像头通过USB接口实现图像
  • STL中emplace实现原理是什么?
  • 240426 leetcode exercises
  • springboot入门-controller层