Tutorial 3: Render Event - tiagodinis/GParticles GitHub Wiki

This tutorial will cover how we can draw our particles in different ways with the render event.

As always, we make a copy of the previous tutorial folder, changing every name reference to "Tutorial_3". Opening "Tutorial_3.xml", we expand the render tag, remove its prefab attribute and add our file tags. Here, however, GParticles requires we use specific tags for the file type we are referencing, instead of simply using file like before. We'll keep writing small functions most of the time, but, on the render event, these functions will belong to a vertex, geometry or fragment shader, having an impact on certain aspects, such as variable names. If you have no idea what shaders are and how they are used in the OpenGL pipeline, consider checking one of the following resources before continuing this tutorial:

Aight. Since we'll need a vertex and fragment shader, we add the vertfile and fragfile tags.

<render>
    <vertfile path="projects/Tutorial_3/point.vert" />
    <fragfile path="projects/Tutorial_3/color.frag"/>
</render>

Then we create the corresponding files holding our functions. In the fragment shader we declare a vec4 variable (fragColor), so its value, representing the pixel color, is drawn to our screen. Yet, there is no obligation to draw anything. Instead, we could just pass data to one of our resource buffers or into a framebuffer we had defined.

// point.vert
void main()
{
    @colorsV = @colors;
    gl_Position = projection * view * model * @positions;
}

-----------------------------------------------------------------------------------
// point.frag
out vec4 fragColor;

void main()
{
    fragColor = @colorsV;
}

So far, we just defined the functionality hidden by the prefab attribute, so there's nothing particularly amazing going on if we run the project. And so, it is time for another tag to make its debut.

The rendertype tag indicates the particle system render type, impacting how its particles are represented on screen. The render type can be "point", "billboard" and "model", defaulting to "point" if it is not explicitly defined. Also, the "billboard" and "model" render types require an asset, an image and a 3D primitive, respectively, that is referenced by a file path.

We'll start with the "model" render type and draw our particles as spheres.

<render>
    <rendertype type="model" path="assets/models/sphere/sphere.obj"/>

    <vertfile path="projects/Tutorial_3/model.vert" />
    <fragfile path="projects/Tutorial_3/color.frag" />
</render>

Because we are using this render type, after the header generation process, our shader gains access to 3 new resources relative to every model vertex: vertexPosition, vertexNormal and texCoord. The names are pretty self-explanatory. If we want to draw a model for every instance of a particle we'll need to take into account every model vertex vertexPosition and offset it with the particle's own position, like so:

void main()
{
    @colorsV = @colors;
    vec4 surfacePointPos = vec4(vertexPosition,1) + @positions;
    gl_Position = projection * view * model * surfacePointPos;
}

Let's also change the maxParticles uniform override to 2, so we can see what's going on and have two nice balls moving around 😄

<override type="uniform" name="maxParticles" value=2 />

What a sight.

Now let's try the billboard render type. Billboards are 2D elements incrusted in a 3D world and one of the most common ways to render particles in applications, since a large amount of them can be drawn in real time with any modern GPU, as they are nothing but simple polygons (usually quads) capable of transparency. In GParticles, we use the particle position information to create our billboard quad, with the help of a geometry shader, able to process and generate 3D primitives. In "Tutorial_3.xml" we use the following setup:

...

<override type="uniform" name="maxParticles" value=500 />

...

<render>
    <rendertype type="billboard" path="assets/images/snow.png" />

    <vertfile path="projects/Tutorial_3/billboard.vert" />
    <geomfile path="modules/utilities.glsl" />
    <geomfile path="modules/billboard.glsl" />
    <geomfile path="projects/Tutorial_3/billboard.geom" />
    <geomfile path="templates/billboardMain.glsl" />
    <fragfile path="projects/Tutorial_3/billboardColor.frag" />
</render>

As you can see, we are using two modules ("billboard.glsl" has "utilities.glsl" dependencies) and a billboard template. This template is very specific to billboard generation, receiving as input an array with four vec4 elements, from a user defined transform function. Each array element holds the camera space position coordinates of a billboard quad vertex. The template code then projects those quad coordinates into screen space and finally assembles our new primitive. This requires that vertfile only transforms particle positions to camera space. So, we define the "billboard.vert" file as:

void main()
{
    @colorsV = @colors;
    gl_Position = view * model * @positions;
}

We'll draw our billboards as they usually appear in 3D applications, facing the camera at all times. To do so, we define the previously mentioned transform function, calling the "billboard.glsl" module getQuad function. Particle color data is passed through so that it reaches the fragment shader.

vec4[4] transform()
{
    @colorsG = @colorsV[0];

    return  getQuad(0.3, 0.3);
}

We could easily change modify our quads with the "billboard.glsl" module we included. It provides us with plenty of functions that manipulate billboards (for example, changing their orientation or stretching them taking into account their velocity vector).

It should also be noted that geometry shaders in GParticles are not limited to this small use case; you can use them as you wish, create your own templates and wacky effects.

All that is left for us to do is changing the fragfile so that it maps the image we indicated with the billboard vertices. We'll also change the final image color.

uniform sampler2D particleTexture;

out vec4 fragColor;

void main()
{
    fragColor = texture(particleTexture, @texCoordsG);
    fragColor.xyz = @colorsG.xyz;
}

Et voilàt, a billboard particle system. If you've been keeping up to this point, congratulations! With a bit of creativity, you are now able to design a lot of different particle systems. It ain't all over though, see the next tutorial to learn how you can make particles interact with the world!


Exercises:

  • Starting from our model render type example, create a particle system where both spheres move in circles around the origin, changing colors after a full circle. Also, try to improve the particle looks by implementing the Phong shading model.
  • Find a way to make the billboard particle system rotate around itself. Experiment with the "billboard.glsl" functions and see if you can make them look better in regards to the particle system movement.
  • How about choosing one particle to have a different color and propagate that color to other particles in close proximity?
⚠️ **GitHub.com Fallback** ⚠️