opengl如何让火力平稳无阻地移动

时间:2015-05-29 11:47:39

标签: c++ opengl particles

我使用freeglut在opengl中渲染,我创建了一个带有emmiter和forcefield的粒子系统,它们运行良好。

我要把火放在大学项目的旧火车上,我用纹理球来模拟它。

代码会很长,我很抱歉,但一切都很重要,这里是:

#include <GL/freeglut.h>

#include <GL/gl.h>
#include <GL/glu.h>
#include <SOIL/SOIL.h>
#include <glm/glm.hpp>

#include <deque> using namespace std;

typedef glm::vec3 Vector3;

struct Force { private :
    float strength;
    Vector3 orentation; public :
    Force(const Vector3 &v,const float &s)
        :strength(s),orentation(v)
    {
    }
    Force(const Force &f)
        :strength(f.strength),orentation(f.orentation)
    {

    }
    float getStrength() const
    {
        return strength;
    }
    Vector3 getOrentation() const
    {
        return orentation;
    } };

class Particle { public :
    Vector3 velocity,position;
    const float mass;
    float *deltatime;
    float lifebegin;
    Particle(const Vector3 &pos,const float &m,float *dt,const float &life)
        :velocity(Vector3()),position(pos),mass(m),deltatime(dt),lifebegin(life)
    {

    }

    void applyforce(const Force &f)
    {
        Vector3 force=f.getOrentation()*f.getStrength();
        Vector3 accleration=force/mass;
        velocity+=accleration*(*deltatime);
        proceedintime();
    }
    void proceedintime()
    {
        position+=velocity*(*deltatime);
    }
    virtual void draw()const=0;
    virtual bool collide(Particle *p)const=0;
    virtual ~Particle()
    {

    } }; class TexturedParticle : public Particle { protected :
    unsigned int textureid; public:
    TexturedParticle(const Vector3 &pos,const float &m,float *dt,const float &life,const unsigned int &tex)
        :Particle(pos,m,dt,life),textureid(tex)
    {

    }
    virtual ~TexturedParticle()
    {

    }

}; class Emmiter; class ForceField;

class ParticleSystem {
    friend class Emmiter;
    friend class ForceField; private :
    deque<Particle*> particles;
    float elapsedtime;
    float *deltatime;
    float LifeOfParticles;
    unsigned short particlesperframe; public :
    ParticleSystem(const float &life,float *dt)
        :elapsedtime(0),deltatime(dt),LifeOfParticles(life)
    {

    }
    void deleteparticles()
    {
        float div=fmod(elapsedtime,LifeOfParticles);
        if(div==0)
        {
            deque<Particle*>::const_iterator begin=particles.begin();
            deque<Particle*>::const_iterator end=particles.end();
            for(deque<Particle*>::const_iterator it=begin;it!=end;it++)
                delete (*it);
            particles.clear();
        }
    }

    void drawparticl1es()
    {
        deque<Particle*>::const_iterator begin=particles.begin();
        deque<Particle*>::const_iterator end=particles.end();

        for(deque<Particle*>::const_iterator it=begin;it!=end;it++)
        {
            (*it)->proceedintime();
            (*it)->draw();
        }
        elapsedtime+=(*deltatime);
    } };

class Emmiter { public :
    unsigned short ParticlesPerSecond;
    Vector3 position;
    ParticleSystem *ps;
    unsigned char count;
    float *deltatime;
    Particle *(*emitfunc)(const Emmiter &em);
    Emmiter(const unsigned short &parpersec,const Vector3 &pos,ParticleSystem *p,const unsigned char &c)
        :ParticlesPerSecond(parpersec),position(pos),ps(p),count(c)
    {

    }
    unsigned short particlesperframe()const
    {
        return ParticlesPerSecond*(*deltatime);
    }
    void emitparticles()const
    {
        unsigned short numpars=particlesperframe();
        Particle *p=0;
        for(unsigned char i=0;i<count;i++)
        {
            for(unsigned short j=0;j<numpars;j++)
            {
                p=emitfunc(*this);
                p->lifebegin=ps->elapsedtime;
                ps[i].particles.push_back(p);
            }
        }
    } }; class ForceField { private :
    Vector3 center;
    float radius,MaxAcceleration;
    unsigned char count;
    ParticleSystem *pars; public:
    ForceField(const Vector3 &cen,const float &r,const float &mf,ParticleSystem *ps,const unsigned char &c);
    void applyforceonparticle(Particle *p)const;
    void applyforceonsystems()const; };


ForceField::ForceField(const Vector3 &cen,const float &r,const float
&mf,ParticleSystem *ps,const unsigned char &c)
    :center(cen),radius(r),MaxAcceleration(mf),count(c),pars(ps) {

}

void ForceField::applyforceonsystems() const {
    for(unsigned char i=0;i<count;i++)
    {
        ParticleSystem p=pars[i];
        for(deque<Particle*>::const_iterator it=p.particles.begin();it!=p.particles.end();it++)
        {
            applyforceonparticle(*it);
        }
    } }

void ForceField::applyforceonparticle(Particle *p) const {
    Vector3 orentation=center-p->position;
    float strength=1-orentation.length()/radius;
    if(strength<=0)
        return;
    p->applyforce(Force(orentation,strength*MaxAcceleration*p->mass)); }

class BallParticle:public TexturedParticle { public :
    float radius;
    BallParticle(const Vector3 &pos,const float &r,const float &m,float *dt,const float &life,const unsigned int &tex)
        :TexturedParticle(pos,m,dt,life,tex),radius(r)
    {

    }
    void draw() const
    {
        glPushMatrix();
        glBindTexture(GL_TEXTURE_2D,textureid);
        glTranslatef(position.x, position.y, position.z);
        glutSolidSphere(radius,10,10);
        glPopMatrix();
    }
    bool collide(Particle *p) const
    {
        BallParticle *b=dynamic_cast<BallParticle*>(p);
        return (b)&&((b->position-position).length()<=b->radius+radius);
    } }; using namespace std;

float deltatime; float oldtime; float newtime; GLuint texture;
ParticleSystem pars(1,&deltatime); Emmiter
emmiter(100,Vector3(0,0,0),&pars,1); ForceField
f(Vector3(0,0.5f,0),0.5f,0.1f,&pars,1);


float myrand(const float &min,const float &max) {
    return min+(max-min)*(float(rand())/float(RAND_MAX)); }

Particle *emitfunction(const Emmiter &em) {
    float x=em.position.x,y=em.position.y;
    BallParticle *b=new BallParticle(Vector3(myrand(x-1,x+1),myrand(y-1,y+1),em.position.z),1,2,&deltatime,0.0f,texture);
    b->velocity=Vector3(0,1,0);
    return b; }

GLuint GetTextId(const char *texture_name) {
    return(SOIL_load_OGL_texture(
               texture_name,
               SOIL_LOAD_AUTO,
               SOIL_CREATE_NEW_ID,
               SOIL_FLAG_POWER_OF_TWO
               ));

}

/* GLUT callback Handlers */ void resize(int width, int height) {
    glViewport(0, 0, width, height);
    /*glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45,(4/3.0f),2,100);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    camera.LookAt();*/
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45,(float)width/(float)height,0.1f,100);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt( 0, 0, 10, 0, 0, 0, 0, 1, 0 ); }

void display() {
    glClear(GL_COLOR_BUFFER_BIT);
    newtime=glutGet(GLUT_ELAPSED_TIME)/1000;
    deltatime=newtime-oldtime;
    oldtime=newtime;
    pars.drawparticl1es();
    glutSwapBuffers(); } void idle() {
    emmiter.emitparticles();
    f.applyforceonsystems();
    pars.deleteparticles();
    glutPostRedisplay(); }

void init() {
    const GLfloat light_ambient[] = { 0.0f, 0.0f, 0.0f, 0.0f };
    const GLfloat light_diffuse[] = { 1, 0.5f, 0.0f, 1.0f };
    const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    const GLfloat light_position[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    const GLfloat mat_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
    const GLfloat high_shininess[] = { 100.0f };
    const GLfloat mat_emissive[]={1,0.5f,0,1};

    glClearColor(0, 0, 0, 0);
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP);
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP);

    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_TEXTURE_2D);

    glEnable(GL_TEXTURE_GEN_S);
    glEnable(GL_TEXTURE_GEN_T);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
    glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
    glMaterialfv(GL_FRONT,GL_EMISSION,mat_emissive);
    //glShadeModel(GL_SMOOTH);
    //glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE); }


int main(int argc, char *argv[]) {
    glutInit(&argc, argv);
    glutInitWindowSize(640,480);
    glutInitWindowPosition(300,200);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutCreateWindow("Fire");
    glutReshapeFunc(resize);
    glutDisplayFunc(display);
    glutIdleFunc(idle);
    init();
    oldtime=glutGet(GLUT_ELAPSED_TIME)/1000;
    emmiter.emitfunc=&emitfunction;

    emmiter.deltatime=&deltatime;
    texture=GetTextId("/home/abd/graphics/fire2.jpg");
    glutMainLoop();
    return EXIT_SUCCESS;
}

这是我使用的纹理: enter image description here

火势看起来有点好,但我中间还有一个空洞,粒子也不能顺利移动。

我试过这些东西:

1 - 改变力场的强度和半径:没有效果。

2 - 增加每秒的粒子数量:没有工作,仍然看到了洞。

3 - 我让我的粒子变得更大:它使火看起来更好,洞几乎消失了(差不多,它有点过去了)但是我在火周围得到奇怪的曲线并且它仍然没有顺利移动。

4 - 我有双缓冲,我在idle函数中调用了glutPostRedisplay,它们仍然没有顺利移动,我尝试删除了PERSPECTIVE NICEST和SMOOTH_SHADING_MODEL的提示,我使用了glutSolidSphere使用新的opengl功能,我有Nvidia GT9400和最新版本的更新驱动程序,但他们仍然没有顺利移动。

那么如何解决这两个问题?

PS

因为这是一个大学项目,我不想要代码,&#34;哪里是错误&#34;已经足够了,我知道它就能解决它。

算法没问题但不是代码。

修改

这是输出的屏幕截图:

enter image description here

正如你所看到的,中间有一个小洞,围着火焰弯曲。

1 个答案:

答案 0 :(得分:0)

乍一看,看起来你必须使用glBlendFunc

https://www.opengl.org/sdk/docs/man2/xhtml/glBlendFunc.xml

这是一个很好的观察者,了解这些参数的作用

http://www.andersriggelsen.dk/glblendfunc.php