博客
关于我
OSG学习:几何对象的绘制(二)——简易房屋
阅读量:796 次
发布时间:2023-02-26

本文共 7351 字,大约阅读时间需要 24 分钟。

OSG 三维渲染引擎编程实践:房屋建模示例

本文将介绍 OpenSceneGraph(OpenSceneGraph,简称 OSG)三维渲染引擎的使用方法,通过一个简易房屋建模的例子,详细讲解从项目配置到几何体绘制的整个流程。

1. 基本配置与环境搭建

在开始编程之前,需要完成以下步骤:

  • 安装 OSG 开发环境

    • 下载并安装 OSG 的开发包,通常包含必要的头文件和库文件。
    • 在项目中配置环境变量,确保编译器能够找到 OSG 的头文件和库文件。
  • 创建新项目

    • 使用合适的开发工具(如 Visual Studio)创建新项目。
    • 在项目中添加 OSG 的相关库,通常需要手动添加依赖项。
  • 配置项目

    • 通过项目配置文件(如 vcproj)添加必要的包含路径和库路径。
    • 确保所有 OSG 相关的头文件和库文件都被正确添加。
  • 2. 简易房屋建模

    在本节中,我们将通过代码示例详细讲解如何创建一个简单的房屋模型。房屋模型将由两个部分组成:房屋墙体和房屋顶部(屋顶)。

    2.1 房屋墙体建模

  • 顶点定义

    房屋墙体是一个人字顶,由 10 个顶点组成。顶点坐标如下:

    // 顶点坐标
    vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); // 顶部左前角
    vertices->push_back(osg::Vec3(0.0, 0.0, 0.0)); // 底部左前角
    vertices->push_back(osg::Vec3(6.0, 0.0, 4.0)); // 顶部右前角
    vertices->push_back(osg::Vec3(6.0, 0.0, 0.0)); // 底部右前角
    vertices->push_back(osg::Vec3(6.0, 4.0, 4.0)); // 顶部右后角
    vertices->push_back(osg::Vec3(6.0, 4.0, 0.0)); // 底部右后角
    vertices->push_back(osg::Vec3(0.0, 4.0, 4.0)); // 顶部左后角
    vertices->push_back(osg::Vec3(0.0, 4.0, 0.0)); // 底部左后角
    vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); // 顶部左前角
  • 法线定义

    每个顶点都需要对应的法线向量。以下是墙体各顶点的法线坐标:

    // 法线坐标
    (*normals)[0].set(-0.707, -0.707, 0.0); // 顶部左前角
    (*normals)[1].set(-0.707, -0.707, 0.0); // 底部左前角
    (*normals)[2].set(0.707, -0.707, 0.0); // 顶部右前角
    (*normals)[3].set(0.707, -0.707, 0.0); // 底部右前角
    (*normals)[4].set(0.707, 0.707, 0.0); // 顶部右后角
    (*normals)[5].set(0.707, 0.707, 0.0); // 底部右后角
    (*normals)[6].set(-0.707, 0.707, 0.0); // 顶部左后角
    (*normals)[7].set(-0.707, 0.707, 0.0); // 底部左后角
    (*normals)[8].set(-0.707, -0.707, 0.0); // 顶部左前角
  • 纹理坐标

    每个顶点需要对应的纹理坐标。以下是墙体各顶点的纹理坐标:

    // 纹理坐标
    (*texcoords)[0].set(0.0, 1.0); // 顶部左前角
    (*texcoords)[1].set(0.0, 0.0); // 底部左前角
    (*texcoords)[2].set(0.3, 1.0); // 顶部右前角
    (*texcoords)[3].set(0.3, 0.0); // 底部右前角
    (*texcoords)[4].set(0.5, 1.0); // 顶部右后角
    (*texcoords)[5].set(0.5, 0.0); // 底部右后角
    (*texcoords)[6].set(0.8, 1.0); // 顶部左后角
    (*texcoords)[7].set(0.8, 0.0); // 底部左后角
    (*texcoords)[8].set(1.0, 1.0); // 顶部左前角
  • 几何体创建

    将顶点、法线和纹理坐标设置到几何体对象中,并绘制为多段四边形条带图元:

    // 创建墙体几何体
    osg::Geometry *houseWall = new osg::Geometry;
    houseWall->setVertexArray(vertices.get());
    houseWall->setTexCoordArray(0, texcoords.get());
    houseWall->setNormalArray(normals.get());
    houseWall->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
    houseWall->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::QUAD_STRIP, 0, 10));
    houseWall->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile("wall.bmp")));
  • 2.2 房屋顶部(屋顶)建模

  • 顶点定义

    屋顶由 6 个顶点组成。顶点坐标如下:

    // 顶点坐标
    vertices->push_back(osg::Vec3(-0.2, -0.5, 3.5)); // 左前下角
    vertices->push_back(osg::Vec3(6.2, -0.5, 3.5)); // 右前下角
    vertices->push_back(osg::Vec3(0.8, 2.0, 6.0)); // 左后上角
    vertices->push_back(osg::Vec3(5.2, 2.0, 6.0)); // 右后上角
    vertices->push_back(osg::Vec3(-0.2, 4.5, 3.5)); // 左后下角
    vertices->push_back(osg::Vec3(6.2, 4.5, 3.5)); // 右后下角
  • 顶点绘制方式

    屋顶的绘制方式为多段三角形条带图元。以下是具体实现:

    // 创建屋顶几何体
    osg::Geometry *houseRoof = new osg::Geometry;
    houseRoof->setVertexArray(vertices.get());
    houseRoof->addPrimitiveSet(roof.get());
    houseRoof->addPrimitiveSet(roofSide.get());
  • 颜色定义

    屋顶使用颜色数组代替纹理贴图,颜色分布如下:

    // 颜色定义
    colors->push_back(osg::Vec4(0.25, 0.0, 0.0, 1.0));
  • 快速法线生成

    使用 OSG 自带的 SmoothingVisitor 工具快速生成法线:

    osgUtil::SmoothingVisitor smv;
    smv.smooth(*houseRoof);
  • 最终几何体创建

    将墙体和屋顶合并到一个叶节点中进行渲染:

    // 创建叶节点
    osg::Geode *geode = new osg::Geode;
    geode->addDrawable(houseWall);
    geode->addDrawable(houseRoof);
  • 3. 代码优化与结构分析

    本例中的代码主要优化了以下几个方面:

  • 数组数据管理

    • 动态数组:通过 push_back 动态添加数据,适用于未知长度或需要灵活扩展的场景。
    • 静态数组:在定义时指定长度,适用于已知且固定不变的数据。
  • 几何体绘制流程

    • 墙体:使用多段四边形条带图元绘制,结合纹理贴图实现高效渲染。
    • 屋顶:采用多段三角形条带图元结合颜色数组,实现简易的屋顶效果。
  • 性能优化

    • 使用 SmoothingVisitor 自动生成法线,减少手动计算工作量。
    • 通过合理设置顶点数组和纹理坐标,提升渲染效率。
  • 4. 代码示例总结

    以下是完整的代码示例:

    #include "stdafx.h"
    #include
    #include
    #include
    #include
    #include
    // 创建房屋墙体部分
    osg::Drawable *createHouseWall() {
    // 创建顶点数组
    osg::ref_ptr
    vertices = new osg::Vec3Array; // 添加顶点坐标 vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); vertices->push_back(osg::Vec3(0.0, 0.0, 0.0)); vertices->push_back(osg::Vec3(6.0, 0.0, 4.0)); vertices->push_back(osg::Vec3(6.0, 0.0, 0.0)); vertices->push_back(osg::Vec3(6.0, 4.0, 4.0)); vertices->push_back(osg::Vec3(6.0, 4.0, 0.0)); vertices->push_back(osg::Vec3(0.0, 4.0, 4.0)); vertices->push_back(osg::Vec3(0.0, 4.0, 0.0)); vertices->push_back(osg::Vec3(0.0, 0.0, 4.0)); // 创建法线数组 osg::ref_ptr
    normals = new osg::Vec3Array(10); // 添加法线坐标 (*normals)[0].set(-0.707, -0.707, 0.0); (*normals)[1].set(-0.707, -0.707, 0.0); (*normals)[2].set(0.707, -0.707, 0.0); (*normals)[3].set(0.707, -0.707, 0.0); (*normals)[4].set(0.707, 0.707, 0.0); (*normals)[5].set(0.707, 0.707, 0.0); (*normals)[6].set(-0.707, 0.707, 0.0); (*normals)[7].set(-0.707, 0.707, 0.0); (*normals)[8].set(-0.707, -0.707, 0.0); (*normals)[9].set(-0.707, -0.707, 0.0); // 创建纹理坐标数组 osg::ref_ptr
    texcoords = new osg::Vec2Array(10); // 添加纹理坐标 (*texcoords)[0].set(0.0, 1.0); (*texcoords)[1].set(0.0, 0.0); (*texcoords)[2].set(0.3, 1.0); (*texcoords)[3].set(0.3, 0.0); (*texcoords)[4].set(0.5, 1.0); (*texcoords)[5].set(0.5, 0.0); (*texcoords)[6].set(0.8, 1.0); (*texcoords)[7].set(0.8, 0.0); (*texcoords)[8].set(1.0, 1.0); (*texcoords)[9].set(1.0, 0.0); // 创建几何体 osg::Geometry *houseWall = new osg::Geometry; houseWall->setVertexArray(vertices.get()); houseWall->setTexCoordArray(0, texcoords.get()); houseWall->setNormalArray(normals.get()); houseWall->setNormalBinding(osg::Geometry::BIND_PER_VERTEX); houseWall->addPrimitiveSet(new osg::DrawArrays(osg::DrawArrays::QUAD_STRIP, 0, 10)); houseWall->getOrCreateStateSet()->setTextureAttributeAndModes(0, new osg::Texture2D(osgDB::readImageFile("../wall.bmp"))); return houseWall.release(); } // 创建房顶部分 osg::Drawable *createHouseRoof() { // 创建顶点数组 osg::ref_ptr
    vertices = new osg::Vec3Array; // 添加顶点坐标 vertices->push_back(osg::Vec3(-0.2, -0.5, 3.5)); vertices->push_back(osg::Vec3(6.2, -0.5, 3.5)); vertices->push_back(osg::Vec3(0.8, 2.0, 6.0)); vertices->push_back(osg::Vec3(5.2, 2.0, 6.0)); vertices->push_back(osg::Vec3(-0.2, 4.5, 3.5)); vertices->push_back(osg::Vec3(6.2, 4.5, 3.5)); // 创建三角形条带图元 osg::DrawArrays *roof = new osg::DrawArrays(osg::DrawArrays::TRIANGLES, 0, 6); osg::DrawElementsUInt *roofSide = new osg::DrawElementsUInt(osg::DrawElementsUInt::TRIANGLES, 6); (*roofSide)[0] = 0; (*roofSide)[1] = 2; (*roofSide)[2] = 4; (*roofSide)[3] = 5; (*roofSide)[4] = 3; (*roofSide)[5] = 1; // 创建颜色数组 osg::ref_ptr
    colors = new osg::Vec4Array; (*colors).push_back(osg::Vec4(0.25, 0.0, 0.0, 1.0)); // 创建几何体 osg::Geometry *houseRoof = new osg::Geometry; houseRoof->setVertexArray(vertices.get()); houseRoof->setColorArray(colors.get()); houseRoof->setColorBinding(osg::Geometry::BIND_OVERALL); houseRoof->addPrimitiveSet(roof.get()); houseRoof->addPrimitiveSet(roofSide.get()); osgUtil::SmoothingVisitor smv; smv.smooth(*houseRoof); return houseRoof.release(); } int main(int argc, char **argv) { // 创建叶节点并添加几何体 osg::Geode *geode = new osg::Geode; geode->addDrawable(createHouseWall()); geode->addDrawable(createHouseRoof()); // 创建查看器并渲染场景 osgViewer::Viewer viewer; viewer.setSceneData(geode.get()); return viewer.run(); }

    5. 代码优化总结

  • 数组管理

    • 动态数组适用于数据长度未知或需要频繁修改的场景。
    • 静态数组适用于数据长度固定且不会变更的场景。
  • 几何体绘制

    • 增加了法线生成工具 SmoothingVisitor,简化了手动计算。
    • 突出纹理贴图的使用,提升了渲染质量。
  • 结构优化

    • 将顶点、法线和纹理坐标分开管理,提升代码可读性。
    • 使用合适的图元类型(如 QUAD_STRIPTRIANGLES)优化渲染效率。
  • 通过以上优化,本文提供了一个完整的简易房屋建模示例,既详细解释了 OSG 的使用方法,又展示了代码优化的技巧。

    转载地址:http://qkvfk.baihongyu.com/

    你可能感兴趣的文章
    oracle账号共享
    查看>>
    Oracle重置序列(不删除重建方式)
    查看>>
    Oracle闪回技术(Flashback)
    查看>>
    oracle隐含参数的查看与修改
    查看>>
    oracle零碎要点---ip地址问题,服务问题,系统默认密码问题
    查看>>
    oracle零碎要点---oracle em的web访问地址忘了
    查看>>
    Oracle零碎要点---多表联合查询,收集数据库基本资料
    查看>>
    Oracle静默安装
    查看>>
    【Bert101】变压器模型背后的复杂数学【02/4】
    查看>>
    Oracle面试题:Oracle中truncate和delete的区别
    查看>>
    ThreadLocal线程内部存储类
    查看>>
    thinkphp 常用SQL执行语句总结
    查看>>
    Oracle:ORA-00911: 无效字符
    查看>>
    Text-to-Image with Diffusion models的巅峰之作:深入解读 DALL·E 2
    查看>>
    Tensorflow.python.framework.errors_impl.ResourceExhaustedError:无法分配内存[操作:AddV2]
    查看>>
    TCP基本入门-简单认识一下什么是TCP
    查看>>
    tableviewcell 中使用autolayout自适应高度
    查看>>
    Symbolic Aggregate approXimation(SAX,符号聚合近似)介绍-ChatGPT4o作答
    查看>>
    Orcale表被锁
    查看>>
    svn访问报错500
    查看>>