Я рендерил огонь в opengl, используя freeglut, я создал систему частиц с 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;
}
и вот текстура, которую я использую:
Огонь выглядит несколько хорошо, но у меня все еще есть пустое отверстие в середине, также частицы не двигаются плавно.
Я попробовал эти вещи:
1 — изменить силу и радиус силового поля: не сработало.
2 — увеличение количества частиц в секунду: не работает, все еще вижу дыру.
3-я сделал мои частицы больше: хорошо, это заставило огонь выглядеть лучше, и дыра почти исчезла (почти, она немного идет и приходит), но я получаю странные изгибы вокруг огня, и он все еще не движется плавно.
4-у меня двойная буферизация, и я сделал вызов glutPostRedisplay в функции ожидания, они все еще не двигаются плавно, я попытался удалить подсказку для PERSPECTIVE NICEST и SMOOTH_SHADING_MODEL, я использую glutSolidSphere, которая использует новые возможности opengl, и у меня есть Nvidia GT9400 с обновленным драйвером до последней версии, и все же они не двигаются плавно.
Так что же делать, чтобы решить эти две проблемы?
потому что это для университетского проекта, я не хочу код, «где ошибка» достаточно, я могу решить ее, когда я знаю это.
Алгоритм в порядке, но не код.
вот скриншот с выводом:
Как видите, в середине есть небольшая дыра, изогнутая вокруг костра.
на первый взгляд кажется, что вам нужно немного поиграть с glBlendFunc
https://www.opengl.org/sdk/docs/man2/xhtml/glBlendFunc.xml
вот хороший зритель того, что делают эти параметры