An infinite procedural starfield pattern
(featuring: parallax scrolling)
The basic recipe is:
- Seed a random generator (Same seed every frame)
Generate N random points (X,Y) and a "Z" value (say 0.5 ... 2 for every point)
Translate every point's position: (X,Y) += scrollXY * Z
That's the tricky part. Every point has a different Z value, so they scroll with different speeds. This is called "parallax scrolling" and makes an amazing effect.
Wrap the points (modulo the screen size).
Using the same seed every frame gives you a random starfield pattern, but persistent every frame (only altered by the scroll value).
Fun fact
This algorithm can be easily modified so that you never have to actually store all N positions in memory. You only need a generator that gives you random base (X,Y) coordinates (and depth) and is seeded by a constant value plus some star number.
Pseudocode:
for (int i=0; i<1000; ++i) {
RandomGenerator rnd;
rnd.seed(SomeConstantSeed + i*10293);
// determine i-th star's position
var basePosition = vec2(rnd.next(800), rnd.next(600));
var depth = rnd.next(0.5, 2);
// parallax scrolling and wrapping
var realPosition = basePosition + scrollXY * depth;
var wrappedPosition = realPosition modulo vec2(800, 600);
// these are our coordinates! let's draw
renderStar(wrappedPosition);
}
Here, every star is generated on-demand and we don't have to keep its value in memory between frames. It's sufficient for the random generator seed to be constant for this to generate the same (but animated) starfield every frame.
You can use the same technique to assign a different random (but persistent) colour to each star.
How to implement efficiently using OpenGL?
The simplest and most effective way is to generate the positions on the fly in your vertex shader.
Prepare your star geometry in a VBO. It can be whatever you want: one vertex (for a point -sprite), a textured quad, anything.
Render it using glDrawArraysInstanced
, where primcount
is your desired number of stars
In your vertex shader, implement the body of the loop from the pseudocode above, substituting gl_InstanceID
for i
. Then alter each vertex' input XY screen position (an attribute variable in your VS) by the scrolling value (passed as an uniform vec2
variable) like described above.
Some footnotes:
- You're going to need a random generator in your vertex shader. Implement it using a 1D noise texture or have a look here.
- If your stars have more geometry than one point (i.e. you use quads, not point sprites), you need to be more careful with screen wrapping to avoid the situation when some vertices of a star wrap and the rest don't. This is a bit more tricky, but doable. Using point sprites is simpler.