0

I've got a very simple particle system which display particles on the screen and move them around the X, Y and Z axis. The problem is that particles are not always oriented to the camera.

glRotatef(g_fX, 1.0f, 1.0f, 1.0f);                                      
glEnable(GL_BLEND);                                                          
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, texCircle);

glColor3f (1.0f, 0.0f, 0.0f);

for (x = -1; x <= 1; x += 0.25)
{
    for (y = -1; y <= 1; y += 0.25)
    {
        for (z = -1; z <= 1; z += 0.25)
        {
            glBegin(GL_TRIANGLE_STRIP);                                    
            glTexCoord2d(1,1); glVertex3f(x+0.5f,y+0.5f,z);
            glTexCoord2d(0,1); glVertex3f(x-0.5f,y+0.5f,z);
            glTexCoord2d(1,0); glVertex3f(x+0.5f,y-0.5f,z);
            glTexCoord2d(0,0); glVertex3f(x-0.5f,y-0.5f,z);
            glEnd();
        }
    }
}      

glEnable(GL_DEPTH_TEST);  
glDisable(GL_BLEND);  

What should I change in the code?

0

1 Answer 1

3

You have to draw the particles in a plane that is orthogonal to the viewport. This plane is given by the X-Axis and Y-Axis of the current inverse modelview matrix.
The corner points of the rectangle should be calculated like in the following pseudo code:

X = X-Axis of the inverse modelview matrix
Y = Y-Axis of the inverse modelview matrix

TL = (x, y, z) - 0.5 * X + 0.5 * Y
TR = (x, y, z) + 0.5 * X + 0.5 * Y
BL = (x, y, z) - 0.5 * X - 0.5 * Y
BR = (x, y, z) + 0.5 * X - 0.5 * Y

Note a transformation matrix usually looks like this:

( X-axis.x, X-axis.y, X-axis.z, 0 )
( Y-axis.x, Y-axis.y, Y-axis.z, 0 )
( Z-axis.x, Z-axis.y, Z-axis.z, 0 )
( trans.x,  trans.y,  trans.z,  1 )

You should use a libray like glm to calculate the matirces, but if you have not used so far, you can also read the current modelview matrix with glGetFloatv(GL_MODELVIEW_MATRIX, ptr) (see How do you get the modelview and projection matrices in OpenGL?).
To draw in a plane that is orthogonal to the viewport you have to draw, the rectangle aligned to the axis of the inverse view matrix (see Inverse matrix). I use the glm::inverse for calculating the inverse view matrix, but any other library would be appropriate too. See also the answers to Stackoverflow question inverting a 4x4 matrix.

void InverseMat44( const GLfloat m[], GLfloat im[] )
{
    // here you can put in a function, of any other library , which calculates a inverse 4*4 matrix.
    glm::mat4 glm_m = glm::make_mat4(m);
    glm::mat4 glm_im = glm::inverse(glm_m);
    memcpy( im, glm::value_ptr( glm_im ), 16 * sizeof( GLfloat ) );
}

GLfloat vm[16], ivm[16];
glGetFloatv(GL_MODELVIEW_MATRIX, vm );
InverseMat44( vm, ivm );

If the view matrix is scaled, then you have to normalize the axis vectors, or you have to take in account the scale factor in the side length of the quads:

GLfloat scaleX = sqrt( ivm[0]*ivm[0] + ivm[1]*ivm[1] + ivm[2]*ivm[2] );
GLfloat scaleY = sqrt( ivm[4]*ivm[4] + ivm[5]*ivm[5] + ivm[6]*ivm[6] );

GLfloat lenX = 0.5f, lenY = 0.5f;
lenX = lenX / scaleX;
lenY = lenY / scaleY;

The drawing of the rectangle should look somehow like this:

GLfloat xx = ivm[0] * lenX, xy = ivm[1] * lenX, xz = ivm[2] * lenX;
GLfloat yx = ivm[4] * lenY, yy = ivm[5] * lenY, yz = ivm[6] * lenY;

glBegin(GL_TRIANGLE_STRIP);                                    

glTexCoord2d(1,1); glVertex3f( x + xx + yx, y + xy + yy, z + xz + yz );
glTexCoord2d(0,1); glVertex3f( x - xx + yx, y - xy + yy, z - xz + yz );
glTexCoord2d(1,0); glVertex3f( x + xx - yx, y + xy - yy, z + xz - yz );
glTexCoord2d(0,0); glVertex3f( x - xx - yx, y - xy - yy, z - xz - yz );

glEnd();


See also the answers to the following questions:

Solution by using the model view matrix

But the much more elegant solution would be to use the model view matrix to achieve the billboard effect. To do this you first have to apply the translation to the model matrix and not to the mesh, and second you have to apply the inverse view rotation to the model matrix:

GLfloat scaleX = sqrt( ivm[0]*ivm[0] + ivm[1]*ivm[1] + ivm[2]*ivm[2] );
GLfloat scaleY = sqrt( ivm[4]*ivm[4] + ivm[5]*ivm[5] + ivm[6]*ivm[6] );
GLfloat scaleZ = sqrt( ivm[8]*ivm[8] + ivm[9]*ivm[9] + ivm[10]*ivm[10] );

GLfloat len = 0.5f;
for (x = -1; x <= 1; x += 0.25)
{
    for (y = -1; y <= 1; y += 0.25)
    {
        for (z = -1; z <= 1; z += 0.25)
        {
            GLfloat rm[16] = {
                ivm[0]/scaleX, ivm[1]/scaleX, ivm[2]/scaleX,  0.0f,
                ivm[4]/scaleY, ivm[5]/scaleY, ivm[6]/scaleY,  0.0f,
                ivm[8]/scaleZ, ivm[9]/scaleZ, ivm[10]/scaleZ, 0.0f,
                0.0f,          0.0f,          0.0f,           1.0f
            };

            glPushMatrix();
            glTranslated( x, y, z );
            glMultMatrixf( rm );

            glBegin(GL_TRIANGLE_STRIP);                                    
            glTexCoord2d(1,1); glVertex3f(  len,  len, 0.0f );
            glTexCoord2d(0,1); glVertex3f( -len,  len, 0.0f );
            glTexCoord2d(1,0); glVertex3f(  len, -len, 0.0f );
            glTexCoord2d(0,0); glVertex3f( -len, -len, 0.0f );
            glEnd();

            glPopMatrix();
        }
    }
}

Note, for any solution you have to know the current model view matrix and you have to calculate the inverse modelview matrix.

An alternativ function for calculating an inverse matrix would look like this (see inverting a 4x4 matrix):

bool InverseMat44( const GLfloat m[16], GLfloat invOut[16] )
{
    float inv[16], det;
    int i;

    inv[0]  =  m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
    inv[4]  = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];
    inv[8]  =  m[4] * m[9]  * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];
    inv[12] = -m[4] * m[9]  * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];
    inv[1]  = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];
    inv[5]  =  m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];
    inv[9]  = -m[0] * m[9]  * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];
    inv[13] =  m[0] * m[9]  * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];
    inv[2]  =  m[1] * m[6]  * m[15] - m[1] * m[7]  * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7]  - m[13] * m[3] * m[6];
    inv[6]  = -m[0] * m[6]  * m[15] + m[0] * m[7]  * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7]  + m[12] * m[3] * m[6];
    inv[10] =  m[0] * m[5]  * m[15] - m[0] * m[7]  * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7]  - m[12] * m[3] * m[5];
    inv[14] = -m[0] * m[5]  * m[14] + m[0] * m[6]  * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6]  + m[12] * m[2] * m[5];
    inv[3]  = -m[1] * m[6]  * m[11] + m[1] * m[7]  * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9]  * m[2] * m[7]  + m[9]  * m[3] * m[6];
    inv[7]  =  m[0] * m[6]  * m[11] - m[0] * m[7]  * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8]  * m[2] * m[7]  - m[8]  * m[3] * m[6];
    inv[11] = -m[0] * m[5]  * m[11] + m[0] * m[7]  * m[9]  + m[4] * m[1] * m[11] - m[4] * m[3] * m[9]  - m[8]  * m[1] * m[7]  + m[8]  * m[3] * m[5];
    inv[15] =  m[0] * m[5]  * m[10] - m[0] * m[6]  * m[9]  - m[4] * m[1] * m[10] + m[4] * m[2] * m[9]  + m[8]  * m[1] * m[6]  - m[8]  * m[2] * m[5];

    det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
    if (det == 0) return false;
    det = 1.0 / det;

    for (i = 0; i < 16; i++)
        invOut[i] = inv[i] * det;

    return true;
}
6
  • I changed the code, but error occures during compilation: error C2109: subscript requires array or pointer type according to lines: GLfloat xx = vm[0][0] * 0.5f, xy = vm[0][1] * 0.5f, xz = vm[0][2] * 0.5f; GLfloat yx = vm[1][0] * 0.5f, yy = vm[1][1] * 0.5f, yz = vm[1][2] * 0.5f;
    – slowbro
    Commented Aug 21, 2017 at 21:27
  • I changed code in the loop, but unfortunatelly particles still are not oriented to the camera. Is there something that I need to change in the code above?
    – slowbro
    Commented Aug 22, 2017 at 21:08
  • glGetFloatv(GL_MODELVIEW_MATRIX, vm) is inside the loop. Here are detalis pastebin.com/8u3sS9vb
    – slowbro
    Commented Aug 22, 2017 at 21:58
  • Thank you for your input. You are OpenGL master:) Unfortunatelly GLM does not work with Visual C++ 2010 Express which I use for coding. I am forced to look for other solution.
    – slowbro
    Commented Aug 28, 2017 at 20:52
  • Raddid76: Thank you for clarifications. I changed the code. Unfortunatelly partices are not shown on the screen. Here is code attached pastebin.com/nMPbjRGX
    – slowbro
    Commented Sep 1, 2017 at 22:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.