4
\$\begingroup\$

I am working on a particle simulation where we have a lot of spheres which can have different radii. Using this tutorial http://mmmovania.blogspot.de/2011/01/point-sprites-as-spheres-in-opengl33.html (see also code below) I was able to create a sphere from a point, but they all have the size from glPointSize(). Is it possible to extend this with a radius?

#version 330
out vec4 vFragColor;

uniform vec3 Color;
uniform vec3 lightDir;

void main(void)
{
    // calculate normal from texture coordinates
    vec3 N;
    N.xy = gl_PointCoord* 2.0 - vec2(1.0);    
    float mag = dot(N.xy, N.xy);
    if (mag > 1.0) discard;   // kill pixels outside circle
    N.z = sqrt(1.0-mag);

    // calculate lighting
    float diffuse = max(0.0, dot(lightDir, N));

    vFragColor = vec4(Color,1) * diffuse;
}

Maybe of interest: I am using #version 120 for my shaders. Or, is there a better way to do this?

So, combining the two answers I now have:

void
Draw::setGeometry(float* geometry, float* velocity, float* radius, GLuint size){
    this->_size = size;

    glGenBuffers(1, &_vertexData);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexData);
    glBufferData(GL_ARRAY_BUFFER, _size * 3 * sizeof(float), geometry, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0); 

    glGenBuffers(1, &_velocityData);                                                                                                               
    glBindBuffer(GL_ARRAY_BUFFER, _velocityData);
    glBufferData(GL_ARRAY_BUFFER, size * 3 * sizeof(float), velocity, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glGenBuffers(1, &_radiusData);  
    glBindBuffer(GL_ARRAY_BUFFER, _radiusData);
    glBufferData(GL_ARRAY_BUFFER, size * sizeof(float), radius, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

}

And the drawing method:

void
Draw::paint(GLenum mode){
    glBindBuffer(GL_ARRAY_BUFFER, _vertexData);
     glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, 0);

    glDrawArrays(mode, 0, _size * 3 * sizeof(float));
    glDisableVertexAttribArray(0);
}

And the Vertex Shader:

#version 130

attribute vec3 position;
attribute vec3 velocity;
attribute float size;

uniform mat4 MVP;
uniform mat4 MV;

void main(){
   gl_PointSize = size;                                                                                                                     

   gl_Position = MVP * vec4(position, 1.0);
}       

The problem is that when gl_PointSize = 15 is set, they are drawn well. When I make it equal with size, then it looks like this: http://picpaste.com/Big_particles-daIBnJCG.png

Works better now. Particles still getting bigger as they move along.

\$\endgroup\$
4
  • \$\begingroup\$ Geometry shaders are no option? \$\endgroup\$
    – Mario
    Jun 25, 2014 at 9:15
  • \$\begingroup\$ They might be, if I find a good enough solution. The particles also have to be colored depending on the radius, but I also don't know how to start on that one. \$\endgroup\$
    – Daniel
    Jun 25, 2014 at 9:25
  • \$\begingroup\$ Coloring based on radius... You can do that in the vertex shader or in your particle engine code. My answer already has a particle color property. In my particle engine I pass the color and opacity of the particle to the fragment shader. But I do my sizing using 2 tris and calculate it in the engine code and not in the shader. \$\endgroup\$
    – badweasel
    Jun 25, 2014 at 9:43
  • \$\begingroup\$ Is it working now? Maybe you just needed to scale the size as you set it or better yet scale it when you set the attribute. It was likely then just a unit scale issue. Don't forget to either post your own answer or checkmark an answer below. Stack Exchange is a points based system and people do the work of answering in hopes of winning those points. \$\endgroup\$
    – badweasel
    Jun 26, 2014 at 22:04

3 Answers 3

4
\$\begingroup\$

Yes.

I would send the radius as an attribute. Which means you'll have to add it in your vertex shader, then pass it to the fragment shader as a varying.

I don't know how you're storing your attributes for the particles. But I usually do this in an array of structs. That way the data is interleaved per particle. So let's say you have something like:

typedef struct {
    vec3 vertex;
    vec2 texture;
    vec4 color;
} TexturedColoredVertex;

You'd add to it at the end:

    float radius;

Then when you're setting up the attribute data you're going to have something like:

glVertexAttribPointer(a_vertexPosition, 3, GL_FLOAT, GL_FALSE, sizeof(TexturedColoredVertex), 0);
glVertexAttribPointer(a_vertexColor, 4, GL_FLOAT, GL_FALSE, sizeof(TexturedColoredVertex), (GLvoid*) offsetof(TexturedColoredVertex, color));
glVertexAttribPointer(a_vertexTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedColoredVertex), (GLvoid*) offsetof(TexturedColoredVertex, texture));

and you'll add:

glVertexAttribPointer(a_vertexRadius, 1, GL_FLOAT, GL_FALSE, sizeof(TexturedColoredVertex), (GLvoid*) offsetof(TexturedColoredVertex, radius));

There are of course other things like getting the location of that attribute along with all your others. But hopefully you get the idea.

Then in your particle emitter code just set the radius of each particle and it can even change over time or whatever happens with your particles.

Then in the vertex shader you'll have access to the radius of each particle via:

attribute vec3 a_vertexPosition;
attribute lowp vec2 a_vertexTexCoord0;
attribute lowp vec3 a_vertexColor;
attribute float a_vertexRadius;

Set up a varying and copy the radius to it. This gets it to the fragment shader. Just like you do with the vertex position.

I'm still on OpenGL 2.0, so I'm not using in/out yet. But it's all the same. Maybe in your version instead of claiming a varying float v_radius in both shaders you do out float v_radius in one and in float v_radius in the other.

\$\endgroup\$
4
  • \$\begingroup\$ I'm quite sure you wouldn't declare a_vertexRadius as a vec3, would you? \$\endgroup\$
    – Mario
    Jun 25, 2014 at 9:35
  • \$\begingroup\$ Fixed. That was a typo. Everywhere else I had it as a float. \$\endgroup\$
    – badweasel
    Jun 25, 2014 at 9:40
  • \$\begingroup\$ It looks good. I'm trying it out right now \$\endgroup\$
    – Daniel
    Jun 25, 2014 at 9:56
  • \$\begingroup\$ I edited my question with the current status, having built on your answer. Still not working, maybe because I am not using the struct and my structures are a bit different. Could you take a look, maybe you can spot something wrong. \$\endgroup\$
    – Daniel
    Jun 25, 2014 at 12:30
2
\$\begingroup\$

Call glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); You can then write to the built in variable gl_PointSize in your vertex shader. This affects the size of the quad that will be created for the point. If you have a vertex attribute to specify the size of each point you can simply set gl_PointSize from that. The point size is specified in device pixels.

\$\endgroup\$
2
  • \$\begingroup\$ I've tried this and just added in my shader a static gl_PointSize=15.0 and I am getting this: link ; any ideas? \$\endgroup\$
    – Daniel
    Jun 25, 2014 at 10:19
  • \$\begingroup\$ Ignore that, it was an artifact \$\endgroup\$
    – Daniel
    Jun 25, 2014 at 10:20
0
\$\begingroup\$

After some 12 hours spent on the problem, the solution is a combination of the two before-mentioned solutions:

Vertex Shader

Thanks to GuyRT's answer, I added the gl_PointSize to the Vertex Shader, which sets the size of the points. Look at his answer for more details.

uniform mat4 MVP;          
uniform mat4 MV;

attribute vec3 position;   
attribute float velocity;  
attribute float radius;

varying float speed;

void main(){
   gl_PointSize = radius;
   speed = velocity;

   gl_Position = MVP * vec4(position, 1.0);
}

Fragment Shader

As pointed by badweasel in his answer, transferring from one kernel to another is fairly simple, no matter what version of GLSL you use. I did not color my particles by size in the end, but by velocity, but moving the data from one shader to another should be similar.

#version 130                           

varying float speed;         

void main(){
    gl_FragColor = vec4(speed, speed, speed, 1.0);
}

All the fancy sprite stuff from above can be then copied here.

Moving and updating the particles

This was the actual problem where I got stuck in the end. After looking at https://code.google.com/p/opengl-tutorial-org/source/browse/tutorial04_colored_cube/tutorial04.cpp I came up with the following:

    //Somewhere in the class
    GLuint vboHandles[3];

Init Method

    //Declaring the buffers
    glBindAttribLocation(this->getProgram(), 0, "position");
    glBindAttribLocation(this->getProgram(), 1, "velocity");
    glBindAttribLocation(this->getProgram(), 2, "radius");

    glGenBuffers(3, vboHandles);    

    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[0]);
    glBufferData(GL_ARRAY_BUFFER, _memSize, geometry, GL_STATIC_DRAW);

    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[1]);
    glBufferData(GL_ARRAY_BUFFER, _memSize / 3, velocity, GL_STATIC_DRAW); 

    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[2]);
    glBufferData(GL_ARRAY_BUFFER, _memSize / 3, radius, GL_STATIC_DRAW);

Update method

    //Updating positions
    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[0]);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLubyte*) NULL);
    glBufferData(GL_ARRAY_BUFFER, _memSize, geometry, GL_STATIC_DRAW);

Paint method

    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[1]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ARRAY_BUFFER, vboHandles[2]);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, 0);

    glDrawArrays(mode, 0, _size);  //Mode is GL_POINTS                                                                                                                 
    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2); 

So again, the answer is based on the two good given answers. For this specific problem, it was a bit harder to implement and combine. Improvements to the code are welcome.

\$\endgroup\$
1
  • \$\begingroup\$ When are you calling glBindAttribLocation? Unless you call it before linking the program it will have no effect (it might just be working by chance now, but will likely not work with a different GPU) \$\endgroup\$
    – GuyRT
    Jun 26, 2014 at 11:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .