热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用OpenInventor实现的NeHeOpenGL教程-第九课

用OpenInventor实现的NeHeOpenGL教程-第九课这节课我们将讨论如何在3D空间中移动物体,物体之间如何以透明方式显示。这节课我们还将首次
用OpenInventor实现的NeHe OpenGL教程-第九课


      

 

这节课我们将讨论如何在3D空间中移动物体,物体之间如何以透明方式显示。这节课我们还将首次讨论如何在OpenInventor中调用OpenGL函数。


在程序开始的部分我们新定义了一些变量,这些变量的作用和NeHe教程中定义的变量有相同的作用。

SoTranslation*              g_pZoomTrans = NULL; //用于放大/缩小场景

SoRotation*                 g_pTiltRotation = NULL;//倾斜场景中的物体

SoRotation*                 g_pAntiTiltRotation = NULL;//抵消掉场景中的倾斜

SoRotation*                 g_pStarSpinRotation = NULL;//旋转星星

double                      tilt = 3.1415926 / 2.0;//初始的倾斜角度

double                      zoom = -15.0;//初始Z轴位置

double                      spin = 0.0f;


const int iStartNums = 50; //定义星星的总数


和NeHe教程类似,我们也创建一个用来描述星星位置、颜色等信息的结构体

typedef struct tag_starts

{

     SoMaterial         *pStarColor; //用来指定星星的颜色

     SoTranslation   *pStarPos;    //用来指定星星距离中心的位置

     SoRotation         *pStarDir;    //用来指定星星当前的角度

     SoRotation         *pAntiStarDir; //用来抵消掉星星当前角度产生的影响

     float              dist;         //星星距离中心的位置

     float              angle;        //星星当前的角度

} STAR;

STAR stars[iStartNums]; //定义一个星星的数组变量


在函数BuildScene中,我们编写如下的代码。

void BuildScene(void)

{

     NeHe教程在这节课使用了一种 glBlendFunc(GL_SRC_ALPHA,GL_ONE) 的混合透明计算方法。这种透明方式在OpenInventor提供的透明类型中没有提供。不过没有关系,因为OpenInventor允许在程序中调用OpenGL的命令。当然我们不能直接调用OpenGL,因为我们知道在调用OpenGL函数的时候,当前线程必须要有一个合法的OpenGL Context,我们在编写OpenInventor程序的时候,根本就不需要考虑OpenGL Context的事情,这些事情都是OpenInventor内部的事情。如何在OpenInventor中使用OpenGL是一个比较高级的内容,读者可以查阅《The Inventor Mentor》一书中第17节课的内容。本节课我们使用SoCallback节点的方法来调用OpenGL。

首先我们要向场景中增加一个SoCallback节点,这个节点的作用是可以在它的回调函数中调用OpenGL函数。

     SoCallback *pGlCallback = new SoCallback();

     pGlCallback->setCallback(GlCB, NULL); //设置用户自定义回调函数GLCB

     g_pOivSceneRoot->addChild(pGlCallback);


     向场景中增加纹理对象

     SoTexture2 *pTexture = new SoTexture2;

     pTexture->filename.setValue("../Data/Star.png");

     g_pOivSceneRoot->addChild(pTexture);


     向场景增加一个平移节点,主要用于放大/缩小场景中的物体。

     g_pZoomTrans = new SoTranslation;

     g_pZoomTrans->translation.setValue(0,0,zoom);//在Z轴方向上移动

     g_pOivSceneRoot->addChild(g_pZoomTrans);


     向场景中增加一个旋转节点,使星星始终面向用户显示,作用和NeHe中的tilt作用一样。

     g_pTiltRotation = new SoRotation;

     g_pTiltRotation->rotation.setValue(SbVec3f(1,0,0),tilt);

     g_pOivSceneRoot->addChild(g_pTiltRotation);


     g_pAntiTiltRotation = new SoRotation;

     g_pAntiTiltRotation->rotation.setValue(SbVec3f(1,0,0),-tilt);

     g_pStarSpinRotation = new SoRotation;

     g_pStarSpinRotation->rotation.setValue(SbVec3f(0,0,1),spin);


     定义一个垂直于Z轴的平面,因为前面已经增加上一个纹理节点,所以纹理将贴在这个平面上,这个平面就显示成一个星星的图案。因为所有的星星都一样,不同的只是位置和旋转方向,所以我们可以定义一个Separator节点,将定义一个星星的所有节点都包含在这个Separator节点中,以后再共享使用这个节点。

     SoSeparator *pStarFaceSetSep = new SoSeparator;

     const float StarVertices[][3] = { {-1.0f, -1.0f, 0.0f}, {1.0f,-1.0f,0.0f}, {1.0f,1.0f,0.0f}, {-1.0f, 1.0f, 0.0f} };

     SoCoordinate3 *StarCoords = new SoCoordinate3;

     StarCoords->point.setValues(0, 4, StarVertices);

     pStarFaceSetSep->addChild(StarCoords);

     pStarFaceSetSep->addChild(new SoFaceSet);



     下面就要开始创建iStartNums个星星

     for(int i = 0; i

     {

         定义一个SoSeparator节点,用来保存当前这个星星的所有节点,这样做的目的是为了防止星星之间的节点会互相干扰。

         SoSeparator *pStarSep = new SoSeparator;

         g_pOivSceneRoot->addChild(pStarSep);


         随机定义星星的颜色

         stars[i].pStarColor = new SoMaterial;

         stars[i].pStarColor->diffuseColor.setValue((rand() % 256) / 256.0,(rand() % 256) / 256.0,(rand() % 256) / 256.0);

         stars[i].pStarColor->transparency = 0.3;

         pStarSep->addChild(stars[i].pStarColor);


         定义星星的旋转角度,初始都为0。注意OpenInventor使用角度的单位为弧度

         stars[i].angle = 0.0f;

         stars[i].pStarDir = new SoRotation;

         stars[i].pStarDir->rotation.setValue(SbVec3f(0,1,0),stars[i].angle);

         pStarSep->addChild(stars[i].pStarDir);


定义星星的位置

         stars[i].dist = (float(i) / (float)iStartNums) * 5.0f;

         stars[i].pStarPos = new SoTranslation;

         stars[i].pStarPos->translation.setValue(stars[i].dist,0,0);

         pStarSep->addChild(stars[i].pStarPos);


         取消掉前面设置的旋转,平移。只有这样,星星才能始终面向用户显示

         stars[i].pAntiStarDir = new SoRotation;

         stars[i].pAntiStarDir->rotation.setValue(SbVec3f(0,1,0),-stars[i].angle);

         pStarSep->addChild(stars[i].pAntiStarDir);


         pStarSep->addChild(g_pAntiTiltRotation);

         pStarSep->addChild(g_pStarSpinRotation);

         pStarSep->addChild(pStarFaceSetSep);

     }


     定义键盘回调节点,响应用户按键事件

     SoEventCallback* pEventCallback = new SoEventCallback;

     pEventCallback->addEventCallback(SoKeyboardEvent::getClassTypeId(),KeyboardEventCB,g_pOivSceneRoot);

     g_pOivSceneRoot->addChild(pEventCallback);


     在NeHe教程中,星星是不断旋转的。NeHe教程是通过在消息空闲的时候来旋转星星的。这种方式的显示效果比较好,但特别占用CPU的时间。读者可以运行所有NeHe的教程例子代码,可以发现所有的例子代码程序CPU占用时间均为100%.我们这里不采用NeHe的方式。我们使用一个定时器的方式来旋转星星。定时器可以使用Windows的SetTimer函数来创建。不过OpenInventor也提供定时器节点,这里我们使用OpenInventor提供的定时器节点来旋转星星。关于定时器节点,读者可以查阅《The Inventor Mentor》一书中第12章的内容。

     SoTimerSensor * Timer = new SoTimerSensor(TimerSensorCB, NULL);

     Timer->setInterval(0.001);

     Timer->schedule();

}


     下面我们编写SoCallback节点的响应函数,在OpenInventor遍历到SoCallback节点时会调用这个函数。在这个函数中,我们可以调用OpenGL命令,因为这时OpenGL Context是合法的。

void GlCB(void *data, SoAction *action)

{

     if (action->isOfType(SoGLRenderAction::getClassTypeId()))

     {

         glBlendFunc(GL_SRC_ALPHA,GL_ONE);//定义混合计算公式

         glEnable(GL_BLEND);              //启动混合运算

         glDisable(GL_DEPTH_TEST);        //禁止深度检测

     }

}


     下面的代码是定时器响应函数,定时器会每个一段时间调用一次这个函数,我们在这个函数中修改星星的位置、方向和颜色。计算方法和NeHe教程中是相同的。

void TimerSensorCB(void * data, SoSensor *)

{

     for(int i = 0; i

     {

         stars[i].dist -= 0.01f;

         stars[i].angle += (float(i) / (float)iStartNums) * 3.1415926 / 180.0;

         if(stars[i].dist <0.0f)

         {

              stars[i].dist &#43;&#61; 5.0f;

              stars[i].pStarColor->diffuseColor.setValue((rand() % 256) / 256.0,(rand() % 256) / 256.0,(rand() % 256) / 256.0);

         }

         stars[i].pStarPos->translation.setValue(stars[i].dist,0,0);

         stars[i].pStarDir->rotation.setValue(SbVec3f(0,1,0),stars[i].angle);

         stars[i].pAntiStarDir->rotation.setValue(SbVec3f(0,1,0),-stars[i].angle);

        

         spin &#43;&#61; 0.01f * 3.1415926 / 180.0;                                   

         g_pStarSpinRotation->rotation.setValue(SbVec3f(0,0,1),spin);

     }

}   


下面是键盘响应函数&#xff0c;我们在这个函数中响应上下箭头按键&#xff0c;Page_Up/Page_Down按键

void KeyboardEventCB(void *userData, SoEventCallback *pEventCB)

{

     const SoEvent *pEvent &#61; pEventCB->getEvent();


     if(SO_KEY_PRESS_EVENT(pEvent,UP_ARROW))

     {

         绕X轴旋转场景&#xff0c;注意&#xff0c;角度是弧度。

         tilt -&#61; 0.5f * 3.1415926 / 180.0;        

         g_pTiltRotation->rotation.setValue(SbVec3f(1,0,0),tilt);

         g_pAntiTiltRotation->rotation.setValue(SbVec3f(1,0,0),-tilt);

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,DOWN_ARROW))

     {

         tilt &#43;&#61; 0.5f * 3.1415926 / 180.0;

         g_pTiltRotation->rotation.setValue(SbVec3f(1,0,0),tilt);

         g_pAntiTiltRotation->rotation.setValue(SbVec3f(1,0,0),-tilt);

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAGE_UP))

     {   

         放大场景

         zoom -&#61; 0.2f;

         g_pZoomTrans->translation.setValue(0,0,zoom);

     }

     else

     if(SO_KEY_PRESS_EVENT(pEvent,PAGE_DOWN))

     {       

         缩小场景

         zoom &#43;&#61; 0.2f;

         g_pZoomTrans->translation.setValue(0,0,zoom);

     }

     pEventCB->setHandled();

}


剩下的代码和以前的代码都一样了。


现在编译运行我们程序&#xff0c;屏幕上显示一群不断旋转的星星&#xff0c;效果很cool。读者可以试试按下左右方向键和PnUp/PnDn键&#xff0c;观察星星产生的变化。效果和NeHe第九课是相同的。


本课的完整代码下载。&#xff08;VC 2003 &#xff0b; Coin2.5&#xff09;




后记

OpenInventor是一种基于OpenGL的面向对象的三维图形软件开发包。使用这个开发包&#xff0c;程序员可以快速、简洁地开发出各种类型的交互式三维图形软件。这里不对OpenInventor做详细的介绍&#xff0c;读者如果感兴趣&#xff0c;可以阅读我的blog中的这篇文章《OpenInventor 简介》。


NeHe教程是目前针对初学者来说最好的OpenGL教程&#xff0c;它可以带领读者由浅入深&#xff0c;循序渐进地掌握OpenGL编程技巧。到目前为止&#xff08;2007年11月&#xff09;&#xff0c;NeHe教程一共有48节。我的计划是使用OpenInventor来实现所有48节课程同样的效果。目的是复习和巩固OpenGL的知识&#xff0c;同时与各位读者交流OpenInventor的使用技巧。


因为篇幅的限制&#xff0c;我不会介绍NeHe教程中OpenGL的实现过程&#xff0c;因为NeHe的教程已经讲解的很清楚了&#xff0c;目前网络中也有NeHe的中文版本。我将使用VC 2003作为主要的编译器。程序框架采用和NeHe一样的Win32程序框架&#xff0c;不使用MFC。程序也可以在VC Express&#xff0c;VC 2005/2008中编译。我采用的OpenInventor开发环境是Coin&#xff0c;这是一个免费开源的OpenInventor开发库。文章 《OpenInventor&#xff0d;Coin3D开发环境》 介绍了如何在VC中使用Coin。我使用的Coin版本是2.5。读者可以到 www.coin3d.org 中免费下载。


读者可以在遵循GNU协议的条件下自由使用、修改本文的代码。水平的原因&#xff0c;代码可能不是最优化的&#xff0c;我随时期待读者的指正和交流。转载请注明。谢谢。

我的联系方式&#xff1a;

E-mail:

Blog:

Site:

推荐阅读
  • 本文介绍了在Mac上安装Xamarin并使用Windows上的VS开发iOS app的方法,包括所需的安装环境和软件,以及使用Xamarin.iOS进行开发的步骤。通过这种方法,即使没有Mac或者安装苹果系统,程序员们也能轻松开发iOS app。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • 本文详细介绍了PHP中与URL处理相关的三个函数:http_build_query、parse_str和查询字符串的解析。通过示例和语法说明,讲解了这些函数的使用方法和作用,帮助读者更好地理解和应用。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • Whatsthedifferencebetweento_aandto_ary?to_a和to_ary有什么区别? ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了Python语言程序设计中文件和数据格式化的操作,包括使用np.savetext保存文本文件,对文本文件和二进制文件进行统一的操作步骤,以及使用Numpy模块进行数据可视化编程的指南。同时还提供了一些关于Python的测试题。 ... [详细]
author-avatar
252575936_8ea84a
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有