Godot Version
4.2.1
Question
I am trying to make a 3D game set in space, where I want to have a WorldEnivornment Sky with a rich starfield, as well as visible sun.
Ideally I would like to procedurally generate the starfield, so that I can vary the appearance procedurally for each game cycle, and I want to experiment with creating a “band” of stars representing a “milky way”-like nearest galaxy.
I have been experimenting with a sky shader to accomplish this, but I am new to shaders.
My current shader implementation:
shader_type sky;
group_uniforms sun; // First DirectionalLight3D will be the sun
uniform vec3 sun_color : source_color = vec3( 10.0, 8.0, 1.0 );
uniform vec3 sun_sunset_color : source_color = vec3( 10.0, 0.0, 0.0 );
uniform float sun_size : hint_range( 0.01, 1.0 ) = 0.012;
uniform float sun_blur : hint_range( 0.01, 20.0 ) = 0.01;
uniform lowp vec2 starpos_array[1000];
void sky() {
float color = 0.0;
COLOR = vec3(color);
float _sun_distance = 0.0;
if( LIGHT0_ENABLED )
{
_sun_distance = distance( EYEDIR, LIGHT0_DIRECTION );
// Bigger sun near the horizon
float _sun_size = sun_size + cos( LIGHT0_DIRECTION.y * PI ) * sun_size * 0.25;
// Finding sun disc and edge blur
float _sun_amount = clamp(( 1.0 - _sun_distance / _sun_size ) / sun_blur, 0.0, 1.0 );
if( _sun_amount > 0.0 )
{
// Changing color of the sun during sunset
vec3 _sun_color = sun_color;
// Leveling the "glow" in color
if( _sun_color.r > 1.0 || _sun_color.g > 1.0 || _sun_color.b > 1.0 )
_sun_color *= _sun_amount;
COLOR = mix( COLOR, _sun_color, _sun_amount );
}
}
float _star_distance = 0.0;
for (int i = 0; i < starpos_array.length(); i++)
{
_star_distance = distance( SKY_COORDS, starpos_array[i] );
if (_star_distance<0.0001)
{
COLOR = vec3(1.0,1.0,1.0);
}
}
}
The star x-y positions are given randomly by this simple loop:
static class RandomStarfield
{
public static Godot.Vector2[] Generate(int Nstars)
{
Random random = new Random(123);
List<Godot.Vector2> list = new List<Godot.Vector2>(Nstars);
for (int i = 0; i < Nstars; i++)
{
float x = (float)(random.NextDouble() - 0.5) * 2;
float y = (float)(random.NextDouble() - 0.5) * 2;
list.Add(new Godot.Vector2(x, y));
}
return list.ToArray();
}
}
This creates a sun and 1000 stars, where I feed in the star positions using a uniform array. This works in the sense that stars have fixed positions, but performance is very poor, probably because for each pixel the shader has to run through a for loop of size 1000 and do 1000 distance calculations.
Already at 1000 stars, this halves the FPS of my game from 60 to 30, and ideally I would want way more stars than this, and more detail, maybe adding stars of different sizes etc.
Ideas I have had to improve the shader performance is to sort the input for for instance the x-coordinate, and maybe implement some sort of search algorithm like binary search, and repalcing the for-loop with a do/while of some sort. Fundamentally you will still have to do an awful lot of distance calculations, so I was wondering if anyone has any better suggestions, because I feel like I am maybe approaching this issue in the wrong way, seeing as the number of stars will be massive.
(I have looket at godotshaders.com, but have not found anything that solves this issue. )