Draw Refactoring Notes
The main goal is the correct drawing of transparent objects. This is also a nice chance to get rid of the fixed function matrix stack operations.
Transparent objects include:
- ship thrusters
- light billboards
- lasers
- shield effects
- glass cockpits
- planet rings
- star halos
Theory:
- Do a collection pass and collect all objects to "bins" or "buckets"
- Depth write on, depth testing on. Sort and draw opaque objects, preferably front to back
- Depth write off, depth testing on. Sort and draw transparent objects, back to front
Step 2 will not work, since bodies are currently drawn back to front. After drawing distant planets, or when shaders are off, the depth buffer is cleared (question: why not turn off depth write instead?). If this is still necessary to do, object drawing needs to be separated into two phases (after Background is drawn):
- Draw far objects: planets and their rings, stars. Depth will be cleared during this.
- Draw closer objects: cities, ships, lasers
Pass camera to all renderable objects:
void Body::Render(Camera* camera, ...) { //instead of immediate drawing, add Graphic objects to be drawn later //can transformations be calculated here? camera->opaqueBucket.Add(Graphic(...)) camera->transBucket.Add(Graphic(...)) }
Notes for optimization
A smarter structure will also allow for some optimizations - someday.
State sorting
Objects can be sorted by material or material properties. When everything uses Materials the Renderer will avoid some state switches by comparing material pointers. Example: thrusters all use the same material.
Early Z
In theory, depth testing can be done as soon as a vertex shader finishes. This can avoid running costly fragment shaders for invisible fragments. However Pioneer cannot benefit from this since all fragment shaders calculate a custom depth - the shader needs to be fully run before the final depth is known. It's not too bad, since we don't have that much overdraw - mostly the player is seeing black space.
In any case, the problem can be worked around with a Z prepass - render all objects using a shader that only does the depth calculation, then turn depth write off and draw the objects again with the usual fragment shader (but without gl_FragDepth).
We might see the benefit in cities with many, complex buildings.
Batching
To reduce the number of glDraw* calls, simple objects (thrusters, effect quads) can be copied into temporary vertex buffers (check identical materials again), and then drawn at once. Note thrusters, lasers are additively blended and do not have to be individually sorted.