`
huozheleisi
  • 浏览: 1234503 次
文章分类
社区版块
存档分类
最新评论

用OpenInventor实现的NeHe OpenGL教程-第十九课

 
阅读更多

OpenInventor实现的NeHe OpenGL教程-第十九课

这节课我们将讨论怎样使用OpenInventor实现一个简单的粒子系统。我们在NeHe教程中可以看到,一个简单的粒子系统实现起来不像想象中那样的困难。只要能计算好一个粒子的当前状态(速度,加速度,颜色),就很容易完成一个粒子系统的效果。

NeHe教程中使用三角形带来显示每个粒子(GL_TRIANGLE_STRIP),OpenInventor提供了一个相对应的节点类SoTriangleStripSetSoTriangleStripSet节点是OpenInventor中显示速度最快的节点,特别适合显示大量粒子效果。

下面的代码首先定义一些全局变量。

SoMaterial* g_pParticleColor = NULL; //所有粒子的颜色

SoCoordinate3* g_pParticleCoord = NULL; //所有粒子的位置坐标

SoTextureCoordinate2* g_pParticleTexCoord = NULL; //所有粒子的材质坐标

SoTriangleStripSet* g_pParticleStripSet = NULL; //所有粒子的三角形面集

下面定义的变量和NeHe教程定义的变量名称和含义完全相同,不在赘述

#define MAX_PARTICLES 1000 // Number Of Particles To Create

bool rainbow = true; // Rainbow Mode?

float slowdown = 2.0f; // Slow Down Particles

float xspeed = 0; // Base X Speed (To Allow Keyboard Direction Of Tail)

float yspeed = 0; // Base Y Speed (To Allow Keyboard Direction Of Tail)

float zoom = -40.0f; // Used To Zoom Out

int col; // Current Color Selection

int delay; // Rainbow Effect Delay

typedef struct // Create A Structure For Particle

{

bool active; // Active (Yes/No)

float life; // Particle Life

float fade; // Fade Speed

float r; // Red Value

float g; // Green Value

float b; // Blue Value

float x; // X Position

float y; // Y Position

float z; // Z Position

float xi; // X Direction

float yi; // Y Direction

float zi; // Z Direction

float xg; // X Gravity

float yg; // Y Gravity

float zg; // Z Gravity

} particles; // Particles Structure

particles particle[MAX_PARTICLES]; // Particle Array (Room For Particle Info)

static float colors[12][3] = // Rainbow Of Colors

{

{1.0f,0.5f,0.5f},{1.0f,0.75f,0.5f},{1.0f,1.0f,0.5f},{0.75f,1.0f,0.5f},

{0.5f,1.0f,0.5f},{0.5f,1.0f,0.75f},{0.5f,1.0f,1.0f},{0.5f,0.75f,1.0f},

{0.5f,0.5f,1.0f},{0.75f,0.5f,1.0f},{1.0f,0.5f,1.0f},{1.0f,0.5f,0.75f}

};

float ParticleColorData[MAX_PARTICLES][3];

float ParticleLifeData[MAX_PARTICLES];

SbVec3f ParticleCoordData[MAX_PARTICLES * 4];

SbVec2f ParticleTexCoordData[MAX_PARTICLES * 4];

int ParticleNumVertices[MAX_PARTICLES];

下面的函数用来更新粒子系统中每个粒子的颜色,位置,材质等信息。计算方法和NeHe教程相同。

void UpdateParticle(void)

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active) // If The Particle Is Active

{

float x = particle[i].x; // Grab Our Particle X Position

float y = particle[i].y; // Grab Our Particle Y Position

float z = particle[i].z + zoom; // Particle Z Pos + Zoom

ParticleCoordData[i * 4 + 0].setValue(x + 0.5f,y + 0.5f,z);

ParticleCoordData[i * 4 + 1].setValue(x - 0.5f,y + 0.5f,z);

ParticleCoordData[i * 4 + 2].setValue(x + 0.5f,y - 0.5f,z);

ParticleCoordData[i * 4 + 3].setValue(x - 0.5f,y - 0.5f,z);

ParticleTexCoordData[i * 4 + 0].setValue(1,1);

ParticleTexCoordData[i * 4 + 1].setValue(0,1);

ParticleTexCoordData[i * 4 + 2].setValue(1,0);

ParticleTexCoordData[i * 4 + 3].setValue(0,0);

ParticleColorData[i][0] = particle[i].r;

ParticleColorData[i][1] = particle[i].g;

ParticleColorData[i][2] = particle[i].b;

ParticleLifeData[i] = 1 - particle[i].life;

particle[i].x += particle[i].xi / (slowdown * 1000);// Move On The X Axis By X Speed

particle[i].y += particle[i].yi / (slowdown * 1000);// Move On The Y Axis By Y Speed

particle[i].z += particle[i].zi / (slowdown * 1000);// Move On The Z Axis By Z Speed

particle[i].xi += particle[i].xg; // Take Pull On X Axis Into Account

particle[i].yi += particle[i].yg; // Take Pull On Y Axis Into Account

particle[i].zi += particle[i].zg; // Take Pull On Z Axis Into Account

particle[i].life -= particle[i].fade; // Reduce Particles Life By 'Fade'

if (particle[i].life < 0.0f) // If Particle Is Burned Out

{

particle[i].life = 1.0f; // Give It New Life

particle[i].fade = float(rand() % 100) / 1000.0f + 0.003f; // Random Fade Value

particle[i].x = 0.0f; // Center On X Axis

particle[i].y = 0.0f; // Center On Y Axis

particle[i].z = 0.0f; // Center On Z Axis

particle[i].xi = xspeed + float((rand() % 60) - 32.0f); // X Axis Speed And Direction

particle[i].yi = yspeed + float((rand() % 60) - 30.0f); // Y Axis Speed And Direction

particle[i].zi = float((rand() % 60) - 30.0f); // Z Axis Speed And Direction

particle[i].r = colors[col][0]; // Select Red From Color Table

particle[i].g = colors[col][1]; // Select Green From Color Table

particle[i].b = colors[col][2]; // Select Blue From Color Table

}

}

}

g_pParticleColor->diffuseColor.setValuesPointer(MAX_PARTICLES,(float *)ParticleColorData);

g_pParticleColor->transparency.setValuesPointer(MAX_PARTICLES,ParticleLifeData);

g_pParticleCoord->point.setValuesPointer(MAX_PARTICLES * 4,ParticleCoordData);

g_pParticleTexCoord->point.setValuesPointer(MAX_PARTICLES * 4,ParticleTexCoordData);

}

定时回调函数,用来不断更新粒子系统中每个粒子的颜色,位置等状态

void TimerSensorCB(void * data, SoSensor *)

{

UpdateParticle();

if(rainbow && (delay > 25))

{

delay = 0;

col++; // Change The Particle Color

if (col > 11)

col = 0; // If Color Is To High Reset It

}

delay++;

}

创建场景数据

void BuildScene(void)

{

//添加键盘回调事件

SoEventCallback* pEventCallback = new SoEventCallback;

pEventCallback->addEventCallback(SoKeyboardEvent::getClassTypeId(),

KeyboardEventCB,g_pOivSceneRoot);

g_pOivSceneRoot->addChild(pEventCallback);

//OpenGL回调事件

SoCallback *pGlCallback = new SoCallback();

pGlCallback->setCallback(GlRenderCB, NULL);

g_pOivSceneRoot->addChild(pGlCallback);

SoComplexity *pComplexity = new SoComplexity;

pComplexity->textureQuality = 0.6;

g_pOivSceneRoot->addChild(pComplexity);

//加载材质位图

SoTexture2 *Texture = new SoTexture2;

Texture->filename.setValue("../Data/Particle.png");

Texture->model = SoTexture2::MODULATE;

g_pOivSceneRoot->addChild(Texture);

SoMaterialBinding *pMaterialBinding = new SoMaterialBinding;

pMaterialBinding->value = SoMaterialBindingElement::PER_PART;

g_pOivSceneRoot->addChild(pMaterialBinding);

g_pParticleColor = new SoMaterial;

g_pOivSceneRoot->addChild(g_pParticleColor);

g_pParticleCoord = new SoCoordinate3;

g_pOivSceneRoot->addChild(g_pParticleCoord);

g_pParticleTexCoord = new SoTextureCoordinate2;

g_pOivSceneRoot->addChild(g_pParticleTexCoord);

g_pParticleStripSet = new SoTriangleStripSet;

for (int i = 0; i < MAX_PARTICLES; i++)

ParticleNumVertices[i] = 4;

g_pParticleStripSet->numVertices.setValues(0,MAX_PARTICLES,ParticleNumVertices);

g_pOivSceneRoot->addChild(g_pParticleStripSet);

//初始化粒子状态数组

for (int i = 0; i < MAX_PARTICLES; i++) // Initials All The Textures

{

particle[i].active = true; // Make All The Particles Active

particle[i].life = 1.0f; // Give All The Particles Full Life

particle[i].fade = float(rand() % 100) / 1000.0f + 0.003f; particle[i].r = colors[int(i * (12.0 / MAX_PARTICLES))][0]; particle[i].g = colors[int(i * (12.0 / MAX_PARTICLES))][1]; particle[i].b = colors[int(i * (12.0 / MAX_PARTICLES))][2]; particle[i].xi = float((rand() % 50) - 26.0f) * 10.0f; particle[i].yi = float((rand() % 50) - 25.0f) * 10.0f;

particle[i].zi = float((rand() % 50) - 25.0f) * 10.0f; particle[i].xg = 0.0f; // Set Horizontal Pull To Zero

particle[i].yg = -0.8f; // Set Vertical Pull Downward

particle[i].zg = 0.0f; // Set Pull On Z Axis To Zero

particle[i].x = 0;

particle[i].y = 0;

particle[i].z = 0;

}

UpdateParticle();

//定义粒子更新定时器

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

pTimerSensor->setInterval(0.001);

pTimerSensor->schedule();

}

下面的代码是键盘响应函数。

void KeyboardEventCB(void *userData, SoEventCallback *pEventCB)

{

const SoEvent *pEvent = pEventCB->getEvent();

if(SO_KEY_PRESS_EVENT(pEvent,LEFT_ARROW))

{

if(xspeed > -200)

xspeed -= 1.0f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,RIGHT_ARROW))

{

if(xspeed < 200)

xspeed += 1.0f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,UP_ARROW))

{

if(yspeed < 200)

yspeed += 1.0f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,DOWN_ARROW))

{

if(yspeed > -200)

yspeed -= 1.0f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAGE_UP))

{

zoom += 0.1f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAGE_DOWN))

{

zoom -= 0.1f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_ADD))

{

if(slowdown > 1.0f)

slowdown -= 0.01f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_SUBTRACT))

{

if(slowdown < 4.0f)

slowdown += 0.01f;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,TAB))

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active) // If The Particle Is Active

{

particle[i].x = 0.0f; // Center On X Axis

particle[i].y = 0.0f; // Center On Y Axis

particle[i].z = 0.0f; // Center On Z Axis

particle[i].xi = float((rand() % 50) - 26.0f) * 10.0f; particle[i].yi = float((rand() % 50) - 25.0f) * 10.0f; particle[i].zi = float((rand() % 50) - 25.0f) * 10.0f;

}

}

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_8))

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active && particle[i].yg < 1.5f)

particle[i].yg += 0.01f;

}

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_2))

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active && particle[i].yg > -1.5f)

particle[i].yg -= 0.01f;

}

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_6))

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active && particle[i].xg < 1.5f)

particle[i].xg += 0.01f;

}

}

else

if(SO_KEY_PRESS_EVENT(pEvent,PAD_4))

{

for (int i = 0; i < MAX_PARTICLES; i++)

{

if (particle[i].active && particle[i].xg > -1.5f)

particle[i].xg -= 0.01f;

}

}

else

if(SO_KEY_PRESS_EVENT(pEvent,RETURN))

{

rainbow = !rainbow;

}

else

if(SO_KEY_PRESS_EVENT(pEvent,SPACE))

{

rainbow = false;

delay = 0;

col++; // Change The Particle Color

if (col > 11)

col = 0; // If Color Is To High Reset It

}

pEventCB->setHandled();

}

现在编译运行我们程序,屏幕上显示出一个效果非常华丽的喷泉。按下左右方向键,喷泉可以左右转动。按下上下方向键,喷泉可以上下转动。按下PnUp/PnDn键,喷泉将放大或缩小。按下TAB键,喷泉将初始化一次。按下空格键,喷泉将修改颜色。效果和NeHe第十九课是相同的。

本课的完整代码下载。(VC 2003 Coin2.5

后记

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

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

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

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

我的联系方式:

E-mail: < openinventor@gmail.com > < openinventor@126.com >

Blog: < http://blog.csdn.net/RobinHao >

Site: < http://www.openinventor.cn >

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics