2

For a game I am working on I would like to implement a scrolling starfield. All the game so far is being drawn from OpenGL primitives and I would like to continue this and gl_points seems appropriate.

I am using pyglet (python) and know I could achieve this storing the positions of a whole bunch or points updating them and moving them manually but I was hoping there was a neater alternative.

EDIT: In answer to Ian Mallett I guess what I am trying to ask is if I generate a bunch of points, is there some way I can blit these onto some kind of surface or buffer and scroll this in the background.

Also as for what kind of star-field all I am trying to generate at this stage all I am trying for a simple single layer for a top down game, pretty much how you would have in asteroids

madth3
  • 7,275
  • 12
  • 50
  • 74
Hugoagogo
  • 1,598
  • 16
  • 34

2 Answers2

3

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.
Community
  • 1
  • 1
Kos
  • 70,399
  • 25
  • 169
  • 233
  • This is a very good and detailed answer I marked up Ian Mallett's answer because I ended up finding out how to blit to a texture and then wrap that. This is a better answer though and I hope to be able to use this method when my open gl skills improve a bit. – Hugoagogo Oct 19 '12 at 13:00
1

Can you clarify what sort of starfield? 2D scrolling (for a side or top scrolling game, maybe with different layers) or 3D (like really flying through a starfield in an impossibly fast spaceship)?

In the former, a texture (or layers of textures blended additively) is probably the cleanest and fastest approach. [EDIT: Textures are by far the best approach, but if you really don't want to use textures, you can do the following, which is the next best thing:

  • Make a static VBO or display list of points that is maybe six times as large as it needs to be in the direction of scroll (e.g. if you're running 800x600 screen and you're scrolling horizontally, generate points on a 4800x600 grid).
  • Draw these points twice, offset by the width and the scrolling variable. E.g., let x be your scrolling variable (it starts at 0, then is incremented until it reaches 4800 (the width of your points), and then wraps back around and restarts at 0). Each frame, draw your points with a glTranslatef(x,0,0). Then draw them again with a glTranslatef(x+4800,0,0).
  • In this way your points will scroll past seemingly continuously. The constant (I chose six, above) doesn't really matter to the algorithm. Larger values will have fewer repeats but slower performance.
  • You can also try doing all of the above several times with different scrolling constants to give the illusion of depth (with multiple layers).

]

In the latter, there are a bunch of clever algorithms I can think of, but I'd suggest just using the raw points (point sprites if you're feeling fancy); GPUs can handle this if you put them in a static VBO or display list. If your points are small, you can probably throw a few thousand up at a time without any noticeable performance hit.

geometrian
  • 14,775
  • 10
  • 56
  • 132
  • I guess what I am trying to ask is if I generate a bunch of points, is there some way I can blit these onto some kind of surface or buffer and scroll this in the background. Also as for what kind of star-field all I am trying to generate at this stage all I am trying for a simple single layer for a top down game, pretty much how you would have in asteroids. I would like to stay away from using textures unless I absolutely must. – Hugoagogo Sep 18 '12 at 06:24
  • Sorry by stay way from textures I mean stay away from loading images – Hugoagogo Sep 18 '12 at 10:06
  • You don't need to load images to have textures. Create a FBO and use it to render your points to a texture. Then draw that texture in the background. A texture *is* a buffer, surface, etc. This might be too advanced for you (FBOs can be tricky), so see the above algorithm in my edit. – geometrian Sep 18 '12 at 15:48