第二天,我们来实现下渲染3D奶牛模型。
实现「Hello Cow」3D 模型渲染
OpenSceneGraph(OSG)是轻量级高性能的 3D 图形引擎,本文以「Hello Cow」为例,从环境配置、代码编写、CMake 构建到运行验证,手把手完成第一个 OSG 3D 模型渲染程序,解决新手常见的环境变量、库依赖、编译链接等问题。
前置准备
昨天忘记配置环境变量,导致今天使用时候找不到对应的库。 这里配置一下。
1 配置永久环境变量
让系统和 CMake 识别 OSG 路径,编辑全局配置文件:
sudo vim /etc/profile
在文件末尾添加以下内容:
# OSG 环境变量配置
export OSG_ROOT=/usr/local/osg
export PATH=$OSG_ROOT/bin:$PATH
export LD_LIBRARY_PATH=$OSG_ROOT/lib:$LD_LIBRARY_PATH
export CMAKE_PREFIX_PATH=$OSG_ROOT:$CMAKE_PREFIX_PATH
export PKG_CONFIG_PATH=$OSG_ROOT/lib/pkgconfig:$PKG_CONFIG_PATH
保存后让配置立即生效:
source /etc/profile
# 验证配置(输出 /usr/local/osg 则生效)
echo $OSG_ROOT
2 配置系统动态链接库(解决运行时库依赖)
将 OSG 库路径写入系统配置,避免运行时「找不到共享库」:
# 创建 OSG 库配置文件
echo "/usr/local/osg/lib" | sudo tee /etc/ld.so.conf.d/osg.conf
# 更新动态链接库缓存
sudo ldconfig
# 验证(能查到 osgViewer 库则成功)
ldconfig -p | grep libosgViewer
核心代码
实现的代码已经上传gittee: hello_cow
1 代码功能说明
实现最基础的 3D 模型加载与渲染:
- 创建 OSG 视窗对象;
- 加载
cow.osg模型文件; - 优化场景数据并启动渲染循环;
- 包含错误提示,便于新手排错。
2 完整代码(hello_cow.cpp)
// 引入OSG核心头文件:分别对应视窗渲染、节点、几何体节点、组节点、文件读写、场景优化
#include <osgViewer/Viewer> // OSG的视窗渲染器,用于创建窗口和渲染场景
#include <osg/Node> // OSG所有场景节点的基类,是场景图的基本组成单元
#include <osg/Geode> // 几何体节点,用于承载可渲染的几何体(如点、线、面)
#include <osg/Group> // 组节点,可包含多个子节点,是场景图的分支节点
#include <osgDB/ReadFile> // OSG的文件读取工具,用于加载模型文件(.osg/.ive等格式)
#include <osgDB/WriteFile> // OSG的文件写入工具(本示例未使用,保留头文件供扩展)
#include <osgUtil/Optimizer> // OSG的场景优化器,用于优化场景图结构提升渲染效率
// 主函数:OSG程序的入口
int main() {
// 1. 创建视窗渲染器对象,负责创建窗口、处理事件、渲染场景
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
// 2. 创建根节点(组节点),作为整个场景图的根,所有其他节点都挂载在其下
osg::ref_ptr<osg::Group> root = new osg::Group();
// 3. 加载模型文件:加载OSG自带的示例模型cow.osg(需确保模型文件路径正确)
// readNodeFile返回osg::Node指针,失败时返回nullptr
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
// 4. 将加载的模型节点添加到根节点下,成为根节点的子节点
root->addChild(node.get());
// 5. 创建场景优化器,对场景图进行优化(如合并重复节点、优化渲染状态等)
osgUtil::Optimizer optimizer;
// 执行优化:传入根节点,优化整个场景图的结构和渲染性能
optimizer.optimize(root.get());
// 6. 将优化后的场景图设置给视窗渲染器
viewer->setSceneData(root.get());
// 7. 初始化视窗渲染器:创建窗口、初始化渲染上下文等
viewer->realize();
// 8. 启动渲染循环:处理窗口事件、持续渲染场景,直到窗口关闭
return viewer->run();
}
CMakeLists.txt
1 CMake 配置说明
- 手动指定 OSG 路径,避免 CMake 自动查找的坑;
- 区分「头文件目录」和「库文件目录」,规避「链接目录」警告;
- 自动复制模型/贴图文件到编译目录,简化运行步骤。
2 完整 CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(osg_cow_demo)
# 基础配置
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# ========== 核心:手动指定OSG路径(彻底避开find_package的坑) ==========
# 你的OSG安装根目录(固定写死,避免CMake猜路径)
set(OSG_INSTALL_DIR "/usr/local/osg")
# 1. 头文件目录(仅编译用,不参与链接)
include_directories(
${OSG_INSTALL_DIR}/include # OSG头文件
${CMAKE_CURRENT_SOURCE_DIR}
)
# 2. 库文件目录(仅告诉CMake去哪里找库,不直接链接)
link_directories(${OSG_INSTALL_DIR}/lib)
# 3. 生成可执行文件
add_executable(osg_cow osg_cow.cpp)
# 4. 链接OSG库文件(只写库名,不写目录!这是关键)
target_link_libraries(osg_cow
# 直接写OSG的库名(CMake会去link_directories里找)
osgViewer
osg
osgDB
osgUtil
OpenThreads
osgGA
)
# ========== 复制模型文件(可选) ==========
foreach(FILE cow.osg cow.png)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${FILE})
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/${FILE}
${CMAKE_CURRENT_BINARY_DIR}/${FILE}
COPYONLY
)
endif()
endforeach()
# 调试信息(确认路径)
message(STATUS "OSG头文件路径: ${OSG_INSTALL_DIR}/include")
message(STATUS "OSG库文件路径: ${OSG_INSTALL_DIR}/lib")
编译与运行「Hello Cow」
1 编译步骤(清除缓存,避免旧配置干扰)
# 进入项目目录(假设代码在 ~/osg_hello_cow 下)
cd ~/osg_hello_cow
# 彻底删除旧 build 目录(关键:清除缓存)
rm -rf build
# 新建 build 目录并编译
mkdir build && cd build
cmake .. # 无警告,仅输出调试信息
make -j$(nproc) # 编译生成 osg_cow 可执行文件
2 运行程序
# 在 build 目录下运行
./osg_cow
3 运行效果
- 弹出独立的 3D 视窗,显示带纹理的奶牛模型;
- 支持交互操作:
- 鼠标左键拖拽:旋转模型;
- 鼠标滚轮:缩放模型;
- 键盘 WSAD:平移视角;
- 关闭窗口:退出程序。
常见问题排查
1 CMake 提示「找不到 OSG 库」
- 确认
OSG_INSTALL_DIR路径正确(对应/usr/local/osg); - 重新执行
source /etc/profile加载环境变量。
2 编译报错「找不到 osgViewer/Viewer」
- 检查 OSG 头文件路径:
ls /usr/local/osg/include/osgViewer/Viewer; - 重新编译 OSG 并开启
BUILD_ALL_MODULES=ON。
3 运行时「找不到 libosgGA.so」
- 确认
/etc/ld.so.conf.d/osg.conf已配置; - 重新执行
sudo ldconfig更新动态链接库。
总结
- 环境配置核心:通过
OSG_ROOT配置路径,ldconfig解决运行时库依赖; - CMake 避坑:区分「头文件目录」和「库文件目录」,仅链接库名不链接目录;
- 新手关键:模型与贴图文件需同目录,文件名小写,编译前清除缓存;
- 交互基础:OSG 内置鼠标/键盘交互,无需额外代码即可操作模型。