Although Osiris started out as a project focussed on generating and rendering just a single procedural planet, I've found recently the temptation to expand the scope of the project to encompass an entire procedural universe increasingly difficult to resist and to this end have been reworking many of the internal data storage mechanisms and rendering code to handle this.
Rendering a single planet presents many numerical precision challenges for computation and rendering but an entire universe makes it even more so, fortunately there is a natural hierarchy available to help so within my universe I have galaxies, within my galaxies I have solar systems and within my solar systems I have planets and stars - I'll talk about some of the challenges of navigation in such vast spaces another time but it's stars I want to focus on here.
Rendering stars is a little bit unusual in that much of the time computer graphics are trying to make their subjects look as realistic as possible but with a star that breaks down somewhat - if I were fortunate enough to possess a display capable of truly recreating all possible lighting conditions and smart enough to write code that could accurately render a star I have a feeling that the consequences for anyone and everything in the vicinity of said display would be somewhat undesirable!
Fortunately of course that's not really an issue but the point is that for games as for films and books the problem here is more to create a convincing reproduction of what people imagine a star would look like close up rather than trying to achieve a physically accurate rendition as is desirable for many other real world phenomenons.
Here's what I have come up with so far anyway:
I've focussed on creating something familiar based upon our own Sun (A G2 star according to the Harvard Spectral Classification system) and I'm quite happy with it so far. It would be interesting to try some other parameters to recreate different classifications of stars - I'll post some screenshots if I get round to that but for now though here's how my glowey yellow ball is made up:
Unlike the planets I've been working with so far, for my purposes stars can be conveniently represented as pure spheres which made me think that using conventional geometry may not be the best way to go about rendering them. You can certainly get a good approximation of a sphere with triangles but as the body gets larger on screen you inevitably end up with faceted edges unless a truly massive number of triangles are used. You can also treat the sphere as a 2D disc saving a whole heap of triangles across the surface as only the edges are really significant but you then need some maths to work out the 3D points for the pixels covered by the disc. Both these systems also suffer from being difficult to incorporate into the corona effect I wanted to achieve so I decided instead to embrace a technology that has a long and distinguished relationship with spheres - namely ray tracing.
By rendering a single screen aligned quad that covers the area of the star on screen, calculating a ray in the pixel shader that passes through the given pixel and then intersecting that ray with the sphere of the star you get a perfect circular edge from any distance and a true 3D point for shading and texturing purposes :
Not the most convincing star perhaps but it shows the ray tracing is working. Next I wanted to give my star a nice swirly yellow and red pattern but as it's a sphere mapping a 2D texture onto it is a bit troublesome. Three planar maps could be used and blended together (as I do for the terrain texture) but this tends to suffer from obvious blending artifacts as points move between planes and as the star is a perfect sphere this would account for quite a lot of the area. A cube map could also be used and generated to avoid seams at the edges (as the skybox does) but as I wanted to animate the effect I was worried about getting enough variation this way.
What I went with in the end was using a 3D volume texture that stored various types and frequencies of noise in it's four RGBA channels. Although volume textures can be memory hungry, I found that one that was only 64x64x64 in size produced enough variation for what I needed plus it also feels like a really handy texture to have around for future effects. It's possible to generate noise in a pixel shader using ALU instructions which would save the memory cost but I suspect that my single volume texture read is likely as fast if not faster - although I don't have empirical evidence to back that up at the moment.
One difficulty with using a texture based noise function is that it's essential that the noise tiles smoothly to avoid visual seams where the texture wraps and temporal snapping when the noise is used for animation. There are a few ways to ensure this but I went with a weighted trilinear blend of eight noise samples for each point in my volume to make sure it blended smoothly from one extreme of the volume to the other. This also has the benefit of working for any underlying signal generator allowing me to experiment with different noise functions without having to make each of them tile individually.
Using the 3D sphere intersection point to look up into the noise texture, summing and scaling the four different frequencies found there and then mapping the resultant noise value onto a red-yellow-white colour spline produces this effect:
The effect really comes to life though once animation is applied by modulating the co-ordinates used to look up the noise texture over time, see the video below to see this in action.
It's already looking like a big ball of hotness but it's not quite there yet. To make it look even hotter I add a glow effect by intersecting each pixels ray with not just the inner sphere representing the star's surface, but also with an outer sphere representing the outer edge of the star's corona. Where the star surface is not hit but the corona is a fresnel term is calculated which is used to control the brightness of that pixel within the corona. Fresnel is used here so the brightness varies with the angle between the ray from the camera to the intersection point on the corona sphere and the normal of the corona sphere at that point - essentially producing a low brightness where the ray from the camera is nearly tangental to the corona sphere's surface and a high brightness as the ray from the camera become more aligned with the sphere's normal. In other words the more the ray "looks at" the star the brighter it is which achieves a nice glow around the star's surface:
With a little tweak this technique also works as the view point sinks into the corona filling the screen with progressively brighter colours as you near the surface of the star as one might expect. To make it more interesting a random jitter is also applied to the radius of the corona sphere to make the effect a bit more lively at runtime.
Another effect which can help make objects look hotter is a touch of screen space distortion. This is an effect commonly used in games and films where the screen above or around a hot object is given a shimmery effect to simulate the refractive shimmering witnessed in the atmosphere by currents of air with differing temperatures - look into the distance along a road surface on a hot day for example to see this in action.
Where this effect is being applied as a post-process on top of other rendering it can be achieved by taking the frame rendered so far (or in some cases the previous frame for speed) and using that as a source texture in conjunction with perturbed texture co-ordinates to re-render the affected area. This can be expensive however and doesn't exactly do what I want in this case as I would like to produce some wibble on the surface of the star without affecting the corona.
For this I again dusted off my old ray tracing toolbox and found a suitable technique to achieve my goal. Rather than render the star's surface once then perturb it afterwards I instead perturb the ray itself using the screen space pixel co-ordinates to index my trusty noise texture prior to testing for an intersection with the star's surface. This produces a nice heat haze style wobble for the edges and interior of the star's surface but leaves the corona (which is still calculated with the un-perturbed ray) unaffected:
The effect is more obvious the closer you get to the surface so here's a shot from closer up:
and again it's more obvious in motion so see the video below for the full effect.
Finally to produce that desired "OMG My eyes!" look I additively blend on a halo sprite which maxes out some bright areas and produces a nice lens flare effect. This halos sprite also ensures that the star is still visible when viewed from large distances where rendering the actual surface sphere would cover only a pixel or two if that and produces the sun glow effect when within the atmosphere of a planet.
The size of the halo sprite is also randomly jittered at runtime to make the effect more interesting - nothing scientific about that, it just looks better!
The size of the halo sprite is also randomly jittered at runtime to make the effect more interesting - nothing scientific about that, it just looks better!
So there you have it, my very own star. As mentioned above I think it looks best in motion so here's a video I captured to demonstrate just that:
Comments as always are welcome!
This is a very good looking star. Have you considered creating bursts of flames coming out of the star's profile? We all imagine the surface of stars as if they were on fire. It will be mesmerizing to watch. You could use the same 3d noise, may come free as you evaluate this noise anyway even for fragments outside the profile. But really nice, I will steal this star!
ReplyDeletefantastic! great work!
ReplyDeleteVery nice! I would love to see your attempts on solar variations and events, such as flares and blackspots.
ReplyDelete