How to create a nova shader effect

nova shader effect

This nova shader effect could be used as an attack in a top down video game on it’s own, or perhaps many of them could be used as a rain drop effect!

The effect was created using the GLSL shader programming language.

Full code:

#version 330 core

// 0.0f to 1.0f range
uniform float progress = 0.0f;

// Border smoothness
uniform float feather = 0.014f;

// Texture Unit
uniform sampler2D textureUnit;

// Texture coordinate
in vec2 texCoord;

// Final colour
out vec4 fragColour;

// Colours
const vec4 v0 = vec4(0.25, 0.5, 0.8, 1.0);
const vec4 v1 = vec4(0.8, 1.0, 1.0, 1.0);
const vec4 v2 = vec4(0.6, 0.9, 0.95, 1.0);
const vec4 v3 = vec4(0.12, 0.34, 0.8, 1.0);
const vec4 v4 = vec4(0.09, 0.15, 0.6, 1.0);
const vec4 v5 = vec4(0.06, 0.03, 0.2, 1.0);

float circle(vec2 pos, float radius)
{
float edge0 = radius-(radius*feather);
float edge1 = radius+(radius*feather);
float x = dot(pos, pos)*4.0f;
return 1.0 - smoothstep(edge0, edge1, x);
}

float donut(vec2 pos, float radius1, float radius2)
{
float c1 = circle(pos, radius1);
float c2 = circle(pos, radius2);
return c1 - c2;
}

void main()
{
// Calculate the shapes
vec2 pos = texCoord - 0.5f;
float p = progress;
float c1 = circle(pos, p);
float d1 = donut(pos, p, p * 0.95f);
float d2 = donut(pos, p * 0.95f, p * 0.9f);
float d3 = donut(pos, p * 0.9f, p * 0.6f);
float d4 = donut(pos, p * 0.6f, p * 0.55f);
float d5 = donut(pos, p * 0.55f, p * 0.5f);

// Get texture
vec4 tex = texture(textureUnit, texCoord);

// Combine the shapes
fragColour = v0 * c1;
fragColour += v1 * d1 * tex;
fragColour += v2 * d2 * tex;
fragColour += v3 * d3 * tex;
fragColour += v4 * d4 * tex;
fragColour += v5 * d5 * tex;

// Handle alpha
fragColour.a *= 1.0 - progress;
}

The nova effect is made up of 1 circle and 5 donut shapes, where each shape has it’s own colour defined by the constants:

// Colours
const vec4 v0 = vec4(0.25, 0.5, 0.8, 1.0);
const vec4 v1 = vec4(0.8, 1.0, 1.0, 1.0);
const vec4 v2 = vec4(0.6, 0.9, 0.95, 1.0);
const vec4 v3 = vec4(0.12, 0.34, 0.8, 1.0);
const vec4 v4 = vec4(0.09, 0.15, 0.6, 1.0);
const vec4 v5 = vec4(0.06, 0.03, 0.2, 1.0);

The shader allows you to modify 2 uniforms.

The progress uniform should be a value between 0 and 1. The progress value controls the size of the circle and donut shapes. Size is smallest at the start (0), and largest at the end (1).

The feather uniform lets you control how smooth the edge of the circle is, and how smooth the inner and outer edges of the donut are.

A texture can be used to provide more detail to the shapes. If you remove the texture, then each shape will be a solid colour.

The effect is created from the centre by offsetting the texture coordinates. This transforms the coordinates from the 0 to 1 range, to the -0.5 to 0.5 range.

vec2 pos = texCoord - 0.5f;

The circle formula comes from the book of shaders, which has some excellent tutorials:

float circle(vec2 pos, float radius)
{
float edge0 = radius-(radius*feather);
float edge1 = radius+(radius*feather);
float x = dot(pos, pos)*4.0f;
return 1.0 - smoothstep(edge0, edge1, x);
}

The circle function calculates the SDF (signed distance field) of a circle. A donut shape can be made by subtracting a smaller circle from a larger circle:

float donut(vec2 pos, float radius1, float radius2)
{
float c1 = circle(pos, radius1);
float c2 = circle(pos, radius2);
return c1 - c2;
}

The shapes are then combined together using addition. Multiplication is used to colourise and texture the shapes. The shapes shouldn’t get to bright since they are created a set distance apart from each other.

// Combine shapes
fragColour = v0 * c1;
fragColour += v1 * d1 * tex;
fragColour += v2 * d2 * tex;
fragColour += v3 * d3 * tex;
fragColour += v4 * d4 * tex;
fragColour += v5 * d5 * tex;

Finally the alpha is set so that the effect starts opaque and then becomes transparent over time.

// Handle alpha
fragColour.a *= 1.0 - progress;

Thank you for reading this tutorial. Let me know in the comments section if you enjoyed it, or have any questions!

Leave a comment