如何将粒子系统角度扩展扩展到 3 个维度

How to expand particle system angle spread to 3 dimensions

我想了解如何为 3D 粒子系统实现角度扩展,以实现类似于喷泉的效果。我可以让它为 2D 系统工作,但不能为 3D 系统工作。我真的很感激任何帮助,因为我已经尝试了几乎所有的方法。

这是我正在做的事情:

计算一个介于 -180 到 +180 之间的初始随机角度。 spreadAmount 是一个从 0.0 到 1.0 的浮点数,用于控制传播程度。

float velangrnd = spreadAmount * ((((double)(rand() % RAND_MAX) / (RAND_MAX)) - 0.5) * 360.0 * 3.14159265359 / 180.0);

计算角度:

float vsin_anglex_dir = -SIN(velangrnd);
float vcos_anglex_dir = -COS(velangrnd);

最后,计算角度扩展。 Vel是速度从0-1:

// XY Spread
float px0 = (vel * vsin_anglex_dir);
float py0 = (vel * vcos_anglex_dir);
float pz0 = 0;

之后,我简单地计算屏幕位置。 x, y, z 是发射器坐标:

px0 = x + px0 * time;
py0 = y + py0 * time;
pz0 = z + pz0 * time;

当 spreadAmount 为 1.0 时,这会在 XY 轴上创建一个完美的粒子圆。换句话说,粒子将根据速度 (vel) 以 360 度射出。在较低的值下,它将创建一个 2D 喷泉效果。

但是,无论我怎么尝试,我都无法将其扩展到第二个轴。我正在尝试创建一个 3D 喷泉。因此,一个轴向外射出粒子,另一个轴在一个方向上增加随机扩散角,第三个轴在另一个方向上增加随机扩散角。因此,一个喷泉。

在我拔掉剩余的头发之前,是否有关于如何执行此操作的任何建议?

谢谢!

在 3D 中你需要主方向单位向量 t 和你的角度 a 从中你创建 2 个基本向量 u,v 垂直于每个和 t然后你构建你的随机方向......像这样(如果我没有犯任何愚蠢的错误):

float a=?,b,r;
vec3 u,v,t=vec3(?,?,?),d;

// create u,v from t
u=vec3(1,0,0); // u is any non zero vector
if (fabs(dot(u,t))>0.75) u=vec3(0,1,0); // but not (anti)parallel to t
u=normalize(cross(u,t)); // make it perpendicular and unit
v=normalize(cross(u,t)); // make it perpendicular and unit

// compute random direction d
b=2.0*M_PI*Random();   // random angle
r=tan(0.5*a)*Random(); // random radius
d=normalize((r*u*cos(b))+(r*v*sin(b))+t);

所以基本上它的随机向量在圆锥内,其中 t 是中心轴,圆锥角是 a。我使用了我的 GLSL_math.h 但是你可以使用任何向量数学,比如 GLM ...

[Edit1] 无库和组件智能版本

#include <math.h>
void normalize(float &x,float &y,float &z)
    {
    float l=sqrt((x*x)+(y*y)+(z*z));
    if (l>1e-6) l=1.0/l; else l=0.0;
    x*=l; y*=l; z*=l;
    }
void cross(float &x,float &y,float &z, float ax,float ay,float az, float bx,float by,float bz)
    {
    x=(ay*bz)-(az*by);
    y=(az*bx)-(ax*bz);
    z=(ax*by)-(ay*bx);
    }
void cone(float &dx,float &dy,float &dz, float tx,float ty,float tz, float a)
    {
    // (dx,dy,dz) <- random direction inside cone
    // (tx,ty,tz) -> cone axis direction
    // a          -> cone angle in [rad]
    float b,c,s,r;
    float ux,uy,uz;
    float vx,vy,vz;
    // create u,v from t
    ux=1.0; uy=0.0; uz=0.0;                 // u is any non zero vector
    if (fabs((ux*tx)+(uy*ty)+(uz*tz))>0.75) // but not (anti)parallel to t
        { ux=0.0; uy=1.0; uz=0.0; }
    cross(ux,uy,uz, ux,uy,uz, tx,ty,tz);    // make it perpendicular
    normalize(ux,uy,uz);                    // make it unit
    cross(vx,vy,vz, ux,uy,uz, tx,ty,tz);    // make it perpendicular
    normalize(vx,vy,vz);                    // make it unit
    // compute random direction d
    b=2.0*M_PI*Random();                    // random angle
    r=tan(0.5*a)*Random();                  // random radius
    c=r*cos(b); s=r*sin(b);
    dx=(ux*c)+(vx*s)+tx;                    // random direction inside cone
    dy=(uy*c)+(vy*s)+ty;
    dz=(uz*c)+(vz*s)+tz;
    normalize(dx,dy,dz);                    // make it unit
    }

如果您对矢量数学方程式(甚至数组版本的实现)感兴趣,请点击此处(底部附近的 Edit2):

使用数组甚至 类 更方便(与以前的版本相比,数量更少,代码更清晰)。

[Edit2] 更好的版本使用球坐标允许传播 <0 , 2*PI> [rad] 具有“均匀”分布

我正在考虑将我的圆锥方法与球坐标系合并以避免矩形圆锥帽......结果如下:

vec3 rnd_dir(vec3 t,float spread)
    {
    vec3 b,n;
    float a,r,x,y,z;
    static const float  pih=0.5*M_PI;
    static const float _pih=2.0/M_PI;
    // (x,y,z) = unit spread where +x is main direction (central axis)
    x=Random();                 // random angle within spread scaled to <0,1>
    a=x*spread*0.5;             // random angle within spread [rad]
    if (a>pih)                  // linearize the point distribution (avoid high density on poles)
        {
        a=M_PI-a;
        a=sqrt(a*_pih)*pih;     // sqrt looks good probably because surface area is scaled with ^2
        a=M_PI-a;
        }
    else{
        a=sqrt(a*_pih)*pih;     // sqrt looks good probably because surface area is scaled with ^2
        }
    x=cos(a);                   // convert angle to main axis coordinate
    r=sqrt(1.0-(x*x));          // max radius of cone cap still inside unit sphere
    a=Random()*2.0*M_PI;        // random polar angle inside the cone cap [rad]
    y=r*cos(a);                 
    z=r*sin(a);
    // create normal n, binormal b from tangent t ... TBN matrix
//  t=normalize(t);             // t should be unit if it is not uncomment this line
    n=vec3(1,0,0);              // n is any non zero vector
    if (fabs(dot(n,t))>0.75) n=vec3(0,1,0); // but not (anti)parallel to t
    n=normalize(cross(n,t));    // make it perpendicular and unit
    b=normalize(cross(n,t));    // make it perpendicular and unit
    // convert (x,y,z) so t is main direction
    return (t*x)+(b*y)+(n*z);
    }

它再次使用 vec3(我懒得在 x,y,z 中编写代码)但是轴对齐展开生成本身根本不使用向量,因此您可以直接使用它并使用转换到 t,b,n 从以前的版本 (t,u,v) 相同...

此处预览所有步长为 5 度的价差(1000 点):

在 OpenGL 中渲染如下:

void gl_draw()
    {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    float aspect=float(xs)/float(ys);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0/aspect,aspect,0.1,100.0);
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0,0.0,-5.5);
    static float a=0.0; a+=5.5; if (a>360.0) a-=360.0;
    a=-75.0;
    glRotatef(a,0.0,1.0,0.0);

    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);

    int i;
    vec3 d,t=normalize(vec3(1.0,0.5,-0.2));

    Randomize();
    RandSeed=0x1234567;
    glPointSize(1);

    glBegin(GL_POINTS);
    glColor3f(0.1,0.8,1.0);
    for (i=0;i<1000;i++)
        {
        d=rnd_dir(t,spread);
        glVertex3fv(d.dat);
        }
    glEnd();

    glPointSize(1);

    glFlush();
    SwapBuffers(hdc);
    }

其中float spread=M_PI;是用鼠标滚轮改变的全局变量...