# 2D Procedural Generated 2D Starfield & Planet

## Recommended Posts

Hi,

Not sure to post at the right place, if not, please forgive me...

For a game project I am working on, I would like to implement a 2D starfield as a background.

I do not want to deal with static tiles, since I plan to slowly animate the starfield. So, I am trying to figure out how to generate a random starfield for the entire map.

I feel that using a uniform distribution for the stars will not do the trick. Instead I would like something similar to the screenshot below, taken from the game Star Wars: Empire At War (all credits to Lucasfilm, Disney, and so on...).

Is there someone who could have an idea of a distribution which could result in such a starfield?

Any insight would be appreciated

Edited by yahiko00
Changed topic title and added tags

##### Share on other sites

Hi there,

I thought I should start with the obvious suggestion;  have you tried simply a random distribution of points?  Remember that stars do not all have uniform brightnesses so 2d random positions plus a random brightness factor would probably give you a nice effect I would have thought (and pretty simple to do).

Also, what do you mean by 'slowly animate'?  I don't know the game in question, but do you mean as you move around the x-y plane and you see the stars in the background move with some sort of parallax effect?  Because if that's the case, my random suggestion above still works but in that case, the brighter the star, the closer it is so the movement parallax will be bigger and pretty easy to generate I think.  Or maybe I'm thinking of something completely different to you :-)

##### Share on other sites

Sorry, I just realised you did say the word 'random', missed that one!  :-) .  But actually my point about the brightnesses still stands.  A 'random' distribution of points does not actually make a 'uniform' distribution of points.  It follows a Poisson distribution with clumps and voids naturally forming.  Adding the brightness factor I think will accentuate this and it will be a pretty decent effect I feel.

Of course, if that was not good enough, you could do some other things, like create some density function across the sky.  Let's say it was sinusoidal (yes, that's crap but just for example).  Then you can use something called Monte-Carlo rejection sampling.  You get some (on average) underlying density field plus an element of randomness on top.  But I think you should try the simple random x/y/brightness approach first.  I think you'll find it is better than you might first think.

##### Share on other sites

Thanks for your kinds replies @sergamer1!

It makes me think that years ago, I have developped a small PRNG visualizer: http://yahiko.co.nf/RNGVisualizer/ (see second row)

And that is quite true that the result is better than I expected when I opened this thread this afternoon.

Playing with the brightness would certainly improve the result too.

When I said "slowly animate", I am not still sure of what to get, but it could be nice if the starfield is not just a static picture in the background. A two-layer starfield with a parallax effect could be interesting indeed.

Also, you mentioned in your second post "density function accross the sky". Could you please explain this?

Edited by yahiko00

##### Share on other sites
50 minutes ago, yahiko00 said:

When I said "slowly animate", I am not still sure of what to get, but it could be nice if the starfield is not just a static picture in the background. A two-layer starfield with a parallax effect could be interesting indeed.

Yes, I was thinking more a 3 or 4 layer parallax starfield, but that's your preference of course.  The key to making it work is the further the starfield, the slower it moves in parallax, the more stars it has and the dimmer those stars are.  This should in principle be quite easy to do I think.

52 minutes ago, yahiko00 said:

Also, you mentioned in your second post "density function accross the sky". Could you please explain this?

If you have a uniform (on average) density of stars, then the simple random sampling works.  But let's say you want some structure, like the spiral arms of the galaxy, or perhaps some dense star clusters spread around?  Then you can either tabulate this or make a function of this density field of stars.  And then you perform the Monte-Carlo rejection sampling over the density field, and abracadabra, you have a more interesting density field :-) .  Other possibilities include perhaps Perlin noise or some other combination of simple density modes.  However, like I said, I think you'll find the uniform random (with different brightness parallax layers) to be more than adequate!  ;-)

##### Share on other sites

I've been quite busy this week, so I haven't been able to really work on this project.

However, I've succeeded to implement a first experiment of a 2-layer starfield as suggested. I've used TypeScript and Phaser.

For information, I've implemented a cache where I draw on a hidden canvas before displaying it.

As a begining, I find this result quite promising. A uniform distribution is not an issue after all since stars seem quite scattered with some voids and clusters.

I will try to improve this demonstration a little bit by using HSL colors instead of RGB, and chosing colors according to a gaussian distribution and not a uniform distribution, which is not realistic in such a case.

As a roadmap, I would like to implement some special objects like comets and supernovae. Also, I would be interested to have some hints to produce some gas clouds as we can seen below:

Thanks!

##### Share on other sites
16 hours ago, yahiko00 said:

As a begining, I find this result quite promising. A uniform distribution is not an issue after all since stars seem quite scattered with some voids and clusters.

Yes, I was pretty confident the simple prescription would still look pretty decent and not too uniform so glad to see I was not talking rubbish!  ;-)   Happy you're making progress on it anyway.

16 hours ago, yahiko00 said:

I will try to improve this demonstration a little bit by using HSL colors instead of RGB, and chosing colors according to a gaussian distribution and not a uniform distribution, which is not realistic in such a case.

Sure, I had a couple of things to say about the colours.  Although some are fine, some are not very 'star-like' should we say :-)  Stars typically go through a range of colours like brown-red-orange-yellow-green(ish)-blue in order of increasing mass (i.e. brown dwarfs are the smallest that are visible and blue supergiants are the largest ones you can see before they go supernova anyway)   In principle, other colours are there too but these are the typical ones.  The other thing is they seem to fade in-and-out, almost like twinkling in the night's sky.  Except star's don't twinkle in space :-)  (because it's an effect you get only with an atmosphere).  Of course, I am just saying what is realistic whereas maybe you just want what looks best ;-)

16 hours ago, yahiko00 said:

As a roadmap, I would like to implement some special objects like comets and supernovae. Also, I would be interested to have some hints to produce some gas clouds as we can seen below:

Okay, gas clouds are a whole other ball-game!  Their form is highly irregular and not described by some simple equation (unfortunately).  When astrophysicists try to generate such density fields, they usually try and produce some kind of fractal density field (i.e. with fractal dimension less than 3 so it has holes and other structures).   Maybe you should consider something like Perlin noise (https://en.wikipedia.org/wiki/Perlin_noise) since you'll presumably only be generating in 2d.  Many people generate irregular shaped profiles, such as mountain ranges or islands, using Perlin noise, and I think this the route to take.  I've seen other people try stuff like this (i.e. interstellar gas clouds) in the past, although I'm not sure what method they used.  If I find something that's useful, I'll post it here!  :-)

##### Share on other sites

Hello,

I'm back with some news about my project. I've brought some improvements to the star field generation.

Colors now use HSL instead of RGB. Also, I choose them over a Gaussian distribution giving the star field a more realistic aspect. All stars of a given layer now share a same "tone".

I've encountered some issues with Phaser which seemed to leak into memory above 700 stars --- and drew me lines instead of circles.

So, I changed to Pixi.js which is hopefully quite close to Phaser and seems more reliable since there is no more glitches for a large number of stars.

To give an overall better aspect, I've applied a blur filter to the whole scene. At first, I've tried to blur stars one by one but this is really inefficient...

You can see below a static overview. The animated GIF was too big to upload

From a performance point of view, instead of drawing and redrawing stars each frame, I turned them into sprites. This improved dramatically my FPS.

I've also added a quick and dirty GUI on the right side in order to be able to play along with various parameters. I've uploaded an online version here: http://yahiko.developpez.com/apps/Starfield/

For the time being, I hadn't got the time to implement comets, supernovae or gas clouds, but this is still in the roadmap. I'll let you know.

##### Share on other sites

I think you are approaching the star coloring the wrong way. In space (i.e., not from the atmosphere), all stars are white. You only see faint color tinges at the edges of the stars as they glow, see this hubble image:

Since you said you’re using sprites, consider pre-rendering, say 256 stars that look like the ones in the picture varying along a heat gradient

instead of just using round colored dots. The heat gradient is not gaussian, the true distribution is something astrophysicists have been measuring for god knows how long, but i’d be willing to bet that linear would look more realistic than gaussian.

Then rendering the stars is as easy as picking a random star from your 256 stars.

P.S., you’re confusing star distance with red shift. Closer stars aren’t any more prone to being yellow/red than more distant stars. Red shift comes from star velocity relative to the viewer; if anything more distant stars are slightly redder than nearer stars from universe expansion.

Edited by taylorswift

##### Share on other sites

Thanks for your contributions. I know all of these, but this starfield has more aesthetics goals than being accurate astrophysically speaking

##### Share on other sites

You haven't said much about your platform (and I don't know very much about HTML5 platforms in general) but if you have access to shader technology, you can often accomplish this kind of thing with quite decent performance. Just search at ShaderToy for star fields, and you can see stuff like this: https://www.shadertoy.com/view/XlfGRj

Granted, deciphering what's going on in some shaders is kinda tough, but you ought to be able to at least pull out some things you can use to your own benefit.

##### Share on other sites

I've used a fragment shader to blur each of my circles but it seemed quite inefficient.

Right now I am working on a simplex noise 2D fragment shader to generate gas clouds, but I am not an expert of shaders in general to be honest.

I've clicked on your link, and this is really, really impressive. I will definitely investigate the really short code of this visually astounding shader

Edited by yahiko00

##### Share on other sites
21 hours ago, yahiko00 said:

You can see below a static overview. The animated GIF was too big to upload

Yes, that seems like really nice progress :-) .  I have a few comments that maybe will help improve.  A comment above about stars appearing more white in space is partly true yes in the sense they have a broad spectrum so the 'redness' or 'blueness' is not so apparent.  So making them more white helps with the 'realism' (at least with the nearest stars; see my final comment below).

Second, it feels like there are too many stars in the nearest plane and you can see they all move at the same speed a little too clearly which breaks the illusion slightly.  I would perhaps reduce the density of stars here (although the distant ones seem fine for this).

9 hours ago, taylorswift said:

P.S., you’re confusing star distance with red shift. Closer stars aren’t any more prone to being yellow/red than more distant stars. Red shift comes from star velocity relative to the viewer; if anything more distant stars are slightly redder than nearer stars from universe expansion.

Actually, I wanted to say something about this also.  This is incorrect but for the wrong reasons.  It's true that there is redshift due to the expansion of the Universe but we can't actually see stars in other galaxies with the naked eye so all visible stars are in our own Galaxy.  However, there is an effect known as interstellar reddening where gas/dust in the interstellar medium (ISM) absorbs preferentially blue light from distant stars making them appear redder.  And since more distant stars have more ISM between us and it, they appear redder.  This is a quite famous example

where you can see an obvious gas cloud making stars redder.  This is obviously a very discrete blob of gas, but this happens in many directions on large-scales so I would make the near stars white/yellow/blue (depending on mass) and the distant stars more and more red if you wish to mimic this kind of Galactic-fog effect :-)

##### Share on other sites

As a recall, I have implemented a GUI to let anyone play with parameters with the online demonstration of the star field generator.

Feel free to play along with these parameters and tell me which ones produce good visual results. It is not possible at the moment to save and export parameters, but you can take screenshot you can share.

Thanks to that, it is already surely possible to choose more "realistic" colors for stars, or their numbers in each layer. Although any comments can be interesting... as long as it is said in polite form.

Also, a very first version of comets has been implemented.

It uses a particle system with 500 particles, which seems a good compromise between resolution and performance.

Now gas clouds...

##### Share on other sites
48 minutes ago, yahiko00 said:

Feel free to play along with these parameters and tell me which ones produce good visual results. It is not possible at the moment to save and export parameters, but you can take screenshot you can share.

Thanks to that, it is already surely possible to choose more "realistic" colors for stars, or their numbers in each layer. Although any comments can be interesting... as long as it is said in polite form.

Yes, I just had a quick play around.  Not saying this is so incredibly realistic but should be a little more so :-)  Actually, it's perhaps a little 'boring' looking in a way since it's dominated by white, yellow and red stars but is closer to how it problably looks.  The massive blue stars are usually located in young stars clusters and normally they die quickly before they enter the Galactic background (either fizzling out or exploding as Supernova).  If you want to add other features like gas clouds and star clusters, then that is one thing to think about.  Anyway, you seem to be making good progress so look forward to seeing some gas clouds soon ;-)

##### Share on other sites

Just curious: on which hardware have you taken this screenshot? I'm wondering so because of the FPS which is quite low.

##### Share on other sites
6 minutes ago, yahiko00 said:

Just curious: on which hardware have you taken this screenshot? I'm wondering so because of the FPS which is quite low.

Just on my not-particularly powerful laptop (a cheapish Lenovo Thinkpad).  I also have more stars than you (800-odd) which might slow it down a little more I guess?

##### Share on other sites

Since I got similar FPS on one of my GPU-less laptop too, I guess your Thinkpad has no GPU too.

Any FPS feedback on other devices would interest me as well

##### Share on other sites
9 hours ago, sergamer1 said:

Actually, I wanted to say something about this also.  This is incorrect but for the wrong reasons.  It's true that there is redshift due to the expansion of the Universe but we can't actually see stars in other galaxies with the naked eye so all visible stars are in our own Galaxy.  However, there is an effect known as interstellar reddening where gas/dust in the interstellar medium (ISM) absorbs preferentially blue light from distant stars making them appear redder.  And since more distant stars have more ISM between us and it, they appear redder.  This is a quite famous example

where you can see an obvious gas cloud making stars redder.  This is obviously a very discrete blob of gas, but this happens in many directions on large-scales so I would make the near stars white/yellow/blue (depending on mass) and the distant stars more and more red if you wish to mimic this kind of Galactic-fog effect :-)

That’s interesting i was not aware of this!

##### Share on other sites
On 7/3/2017 at 1:46 PM, sergamer1 said:

Okay, gas clouds are a whole other ball-game!  Their form is highly irregular and not described by some simple equation (unfortunately).  When astrophysicists try to generate such density fields, they usually try and produce some kind of fractal density field (i.e. with fractal dimension less than 3 so it has holes and other structures).   Maybe you should consider something like Perlin noise (https://en.wikipedia.org/wiki/Perlin_noise) since you'll presumably only be generating in 2d.  Many people generate irregular shaped profiles, such as mountain ranges or islands, using Perlin noise, and I think this the route to take.  I've seen other people try stuff like this (i.e. interstellar gas clouds) in the past, although I'm not sure what method they used.  If I find something that's useful, I'll post it here!  :-)

I wanted to add you can get very nice results by combining gradient noise with cell noise which gives you a lot more well-defined structure than gradient noise alone. The horizon zero dawn cloud thread has a lot of useful info on this technique. It tends to be heavy to compute but you might have luck prerendering it since the camera is only moving in one direction and gas clouds in space don’t change much in time.

##### Share on other sites

Some progresses have been made since the last time.

I've replaced the blur filter which cause an undesirable loss of brightness by a bloom filter which on the contrary, enhance stars' brightness, which is what I wanted.

More importantly, I have implemented a first attempt of a gas cloud based on the simplex noise shader --- inspired by the many examples available on shadertoy.com (thanks again @JTippetts).

If find the results quite convincing although there is room for improvement. This is not really possible for the moment to control the shape of the gas cloud, and I can hardly define the main color of it. I guess I need to dig more into the math of this shader...

Feel free to try it onlinehttp://yahiko.developpez.com/apps/Starfield/

To make it works, with the gas cloud and the bloom effect, your browser and your GPU (or integrated Intel Graphics) need to support WebGL.

Any feedback and suggestion are welcome to improve the overall aspect.

Cheers!

Edited by yahiko00

##### Share on other sites
On 7/13/2017 at 11:16 AM, yahiko00 said:

Some progresses have been made since the last time.

I've replaced the blur filter which cause an undesirable loss of brightness by a bloom filter which on the contrary, enhance stars' brightness, which is what I wanted.

More importantly, I have implemented a first attempt of a gas cloud based on the simplex noise shader --- inspired by the many examples available on shadertoy.com (thanks again @JTippetts).

If find the results quite convincing although there is room for improvement. This is not really possible for the moment to control the shape of the gas cloud, and I can hardly define the main color of it. I guess I need to dig more into the math of this shader...

Feel free to try it onlinehttp://yahiko.developpez.com/apps/Starfield/

To make it works, with the gas cloud and the bloom effect, your browser and your GPU (or integrated Intel Graphics) need to support WebGL.

Any feedback and suggestion are welcome to improve the overall aspect.

Cheers!

That looks so much nicer congratulations! If I had any suggestions I would add some more high frequency detail to the gas clouds and fix the star color selection because the big yellow stars in the front and the faint blue stars in the back look very weird to me. You have basically no red stars and way, way too many blue stars. Blue stars are actually less common compared to white, yellow, orange, and red stars.

##### Share on other sites

Thanks. Since I'm brand new in the world of shaders, my gas clouds are far from perfect, and I'm not sure to understand what you mean @taylorswift by "high frequencies" nor how to implement these. My background is not really CG, unfortunately.

Here is the fragment shader I use to generate my nebulae. Anyone who has suggestions to improve this shader both in terms of rendering aspect and/or performance is welcome.

// nebulae.frag.glsl

precision mediump float;

uniform vec2  iResolution;
uniform float iGlobalTime;
uniform float redPow;
uniform float greenPow;
uniform float bluePow;
uniform float noiseColor;

#define PI 3.141592653589793

// Simplex Noise by IQ
vec2 hash(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)));

return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}

float noise(in vec2 p) {
const float K1 = 0.366025404; // (sqrt(3) - 1) / 2;
const float K2 = 0.211324865; // (3 - sqrt(3)) / 6;

vec2 i = floor(p + K1 * (p.x + p.y));

vec2 a = p - i + K2 * (i.x + i.y);
vec2 o = (a.x > a.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); // vec2 of = 0.5 + 0.5 * vec2(sign(a.x - a.y), sign(a.y - a.x));
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0 * K2;

vec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0);

vec3 n = h * h * h * h * vec3(dot(a, hash(i + 0.0)), dot(b, hash(i + o)), dot(c, hash(i + 1.0)));

return dot(n, vec3(70.0));
}

const mat2 m = mat2(0.80, 0.60, -0.60, 0.80);

float fbm4(in vec2 p) {
float f = 0.0;
f += 0.5000 * noise(p); p = m * p * 2.02;
f += 0.2500 * noise(p); p = m * p * 2.03;
f += 0.1250 * noise(p); p = m * p * 2.01;
f += 0.0625 * noise(p);
return f;
}

float marble(in vec2 p) {
return cos(p.x + fbm4(p));
}

float dowarp (in vec2 q, out vec2 a, out vec2 b) {
float ang = 0.;
ang = 1.2345 * sin(33.33); // 0.015 * iGlobalTime;
mat2 m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
ang = 0.2345 * sin(66.66); // 0.021 * iGlobalTime;
mat2 m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

a = vec2(marble(m1 * q), marble(m2 * q + vec2(1.12, 0.654)));

ang = 0.543 * cos(13.33); // 0.011 * iGlobalTime;
m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
ang = 1.128 * cos(53.33); // 0.018 * iGlobalTime;
m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

b = vec2(marble(m2 * (q + a)), marble(m1 * (q + a)));

return marble(q + b + vec2(0.32, 1.654));
}

void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;

vec2 q = 2. * uv - 1.;
q.y *= iResolution.y / iResolution.x;

// camera
vec3 rd = normalize(vec3(q.x, q.y, 1.));

// Nebulae Background
q.x = 0.5 + atan(rd.z, rd.x) / (2. * PI);
q.y = 0.5 - asin(rd.y) / PI + 0.512 + 0.0001 * iGlobalTime;
q *= 2.34;

vec2 wa = vec2(0.);
vec2 wb = vec2(0.);
float f = dowarp(q, wa, wb);
f = 0.5 + 0.5 * f;

// Colorization
vec3 col = vec3(f);
float wc = f;
col = vec3(wc, wc * wc, wc * wc * wc);             // increase: R, G, B
wc = abs(wa.x);
col -= vec3(wc * wc, wc, wc * wc * wc);            // decrease: G, R, B
wc = abs(wb.x);
col += vec3(wc * wc * wc, wc * wc, wc);            // increase: B, G, R
col *= 0.7;                                        // decrease all RGB components: more black, less white
col.r = pow(col.r, redPow);                        // high pass filter for red
col.g = pow(col.g, greenPow);                      // high pass filter for green
col.b = pow(col.b, bluePow);                       // high pass filter for blue
col = smoothstep(0., 1., col);                     // Smoothen color gradients
//col = 0.5 - (1.4 * col - 0.7) * (1.4 * col - 0.7); // color translation
col = 0.75 * sqrt(col);                            // increase all RGB components: less black, more white
col *= 1. - noiseColor * fbm4(8. * q);             // add noise
col = clamp(col, 0., 1.);

// Vignetting
// vec2 r = -1.0 + 2.0 * uv;
// float vb = max(abs(r.x), abs(r.y));
// col *= (0.15 + 0.85 * (1.0 - exp(-(1.0 - vb) * 30.0)));

gl_FragColor = vec4(col, 1.0);
}

About star color selection, this is a parameter I can easily change through the GUI of my generator. But I agree, colors shown above are not realistic.

By now, I am working on (fast) planet generation. If anyone has some hints or ressources about this, I am obviously interested.

Cheers!

##### Share on other sites
On 7/15/2017 at 0:58 PM, yahiko00 said:

Thanks. Since I'm brand new in the world of shaders, my gas clouds are far from perfect, and I'm not sure to understand what you mean @taylorswift by "high frequencies" nor how to implement these. My background is not really CG, unfortunately.

Here is the fragment shader I use to generate my nebulae. Anyone who has suggestions to improve this shader both in terms of rendering aspect and/or performance is welcome.


// nebulae.frag.glsl

precision mediump float;

uniform vec2  iResolution;
uniform float iGlobalTime;
uniform float redPow;
uniform float greenPow;
uniform float bluePow;
uniform float noiseColor;

#define PI 3.141592653589793

// Simplex Noise by IQ
vec2 hash(vec2 p) {
p = vec2(dot(p, vec2(127.1, 311.7)),
dot(p, vec2(269.5, 183.3)));

return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
}

float noise(in vec2 p) {
const float K1 = 0.366025404; // (sqrt(3) - 1) / 2;
const float K2 = 0.211324865; // (3 - sqrt(3)) / 6;

vec2 i = floor(p + K1 * (p.x + p.y));

vec2 a = p - i + K2 * (i.x + i.y);
vec2 o = (a.x > a.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); // vec2 of = 0.5 + 0.5 * vec2(sign(a.x - a.y), sign(a.y - a.x));
vec2 b = a - o + K2;
vec2 c = a - 1.0 + 2.0 * K2;

vec3 h = max(0.5 - vec3(dot(a, a), dot(b, b), dot(c, c)), 0.0);

vec3 n = h * h * h * h * vec3(dot(a, hash(i + 0.0)), dot(b, hash(i + o)), dot(c, hash(i + 1.0)));

return dot(n, vec3(70.0));
}

const mat2 m = mat2(0.80, 0.60, -0.60, 0.80);

float fbm4(in vec2 p) {
float f = 0.0;
f += 0.5000 * noise(p); p = m * p * 2.02;
f += 0.2500 * noise(p); p = m * p * 2.03;
f += 0.1250 * noise(p); p = m * p * 2.01;
f += 0.0625 * noise(p);
return f;
}

float marble(in vec2 p) {
return cos(p.x + fbm4(p));
}

float dowarp (in vec2 q, out vec2 a, out vec2 b) {
float ang = 0.;
ang = 1.2345 * sin(33.33); // 0.015 * iGlobalTime;
mat2 m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
ang = 0.2345 * sin(66.66); // 0.021 * iGlobalTime;
mat2 m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

a = vec2(marble(m1 * q), marble(m2 * q + vec2(1.12, 0.654)));

ang = 0.543 * cos(13.33); // 0.011 * iGlobalTime;
m1 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));
ang = 1.128 * cos(53.33); // 0.018 * iGlobalTime;
m2 = mat2(cos(ang), -sin(ang), sin(ang), cos(ang));

b = vec2(marble(m2 * (q + a)), marble(m1 * (q + a)));

return marble(q + b + vec2(0.32, 1.654));
}

void main() {
vec2 uv = gl_FragCoord.xy / iResolution.xy;

vec2 q = 2. * uv - 1.;
q.y *= iResolution.y / iResolution.x;

// camera
vec3 rd = normalize(vec3(q.x, q.y, 1.));

// Nebulae Background
q.x = 0.5 + atan(rd.z, rd.x) / (2. * PI);
q.y = 0.5 - asin(rd.y) / PI + 0.512 + 0.0001 * iGlobalTime;
q *= 2.34;

vec2 wa = vec2(0.);
vec2 wb = vec2(0.);
float f = dowarp(q, wa, wb);
f = 0.5 + 0.5 * f;

// Colorization
vec3 col = vec3(f);
float wc = f;
col = vec3(wc, wc * wc, wc * wc * wc);             // increase: R, G, B
wc = abs(wa.x);
col -= vec3(wc * wc, wc, wc * wc * wc);            // decrease: G, R, B
wc = abs(wb.x);
col += vec3(wc * wc * wc, wc * wc, wc);            // increase: B, G, R
col *= 0.7;                                        // decrease all RGB components: more black, less white
col.r = pow(col.r, redPow);                        // high pass filter for red
col.g = pow(col.g, greenPow);                      // high pass filter for green
col.b = pow(col.b, bluePow);                       // high pass filter for blue
col = smoothstep(0., 1., col);                     // Smoothen color gradients
//col = 0.5 - (1.4 * col - 0.7) * (1.4 * col - 0.7); // color translation
col = 0.75 * sqrt(col);                            // increase all RGB components: less black, more white
col *= 1. - noiseColor * fbm4(8. * q);             // add noise
col = clamp(col, 0., 1.);

// Vignetting
// vec2 r = -1.0 + 2.0 * uv;
// float vb = max(abs(r.x), abs(r.y));
// col *= (0.15 + 0.85 * (1.0 - exp(-(1.0 - vb) * 30.0)));

gl_FragColor = vec4(col, 1.0);
}

About star color selection, this is a parameter I can easily change through the GUI of my generator. But I agree, colors shown above are not realistic.

High frequency just means add more octaves of noise to your FBM. However I’d recommend storing your noise in a texture; generating it in the shader might be okay for one octave of perlin noise but when you get more complex its faster to prebake it.

By the way, I would steer clear of simplex-lattice additive gradient noise; it’s okay for two dimensions but when you scale it up to three or more dimensions the implementation gets very hairy. Take it from someone who maintains the Swift procedural noise library. Plus, a lot of popular noise recipes assume you’re working with a 3D classical gradient noise generator (3D cubic-lattice interpolated gradient noise). Contrary to the claims of CS theoreticians, classical gradient noise is much faster than simplex noise.

Quote

By now, I am working on (fast) planet generation. If anyone has some hints or ressources about this, I am obviously interested.

Cheers!

Welcome to my hell. Procedural planet generation is no small task, it’s one of the more challenging feats in the procedural generation world, and there are a lot of poor-quality tutorials that give ugly results. How much detail do you need? If you only need marble-sized renders, it’s relatively easy to get something that looks good. If you need to fly over them, get ready to spend a few months getting it right.

## Create an account

Register a new account

• 11
• 11
• 9
• 12
• 10
• ### Similar Content

• By EddieK
Hello. I'm trying to make an android game and I have come across a problem. I want to draw different map layers at different Z depths so that some of the tiles are drawn above the player while others are drawn under him. But there's an issue where the pixels with alpha drawn above the player. This is the code i'm using:
int setup(){ GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glEnable(GL10.GL_ALPHA_TEST); GLES20.glEnable(GLES20.GL_TEXTURE_2D); } int render(){ GLES20.glClearColor(0, 0, 0, 0); GLES20.glClear(GLES20.GL_ALPHA_BITS); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); GLES20.glBlendFunc(GLES20.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); // do the binding of textures and drawing vertices } My vertex shader:
uniform mat4 MVPMatrix; // model-view-projection matrix uniform mat4 projectionMatrix; attribute vec4 position; attribute vec2 textureCoords; attribute vec4 color; attribute vec3 normal; varying vec4 outColor; varying vec2 outTexCoords; varying vec3 outNormal; void main() { outNormal = normal; outTexCoords = textureCoords; outColor = color; gl_Position = MVPMatrix * position; } My fragment shader:
precision highp float; uniform sampler2D texture; varying vec4 outColor; varying vec2 outTexCoords; varying vec3 outNormal; void main() { vec4 color = texture2D(texture, outTexCoords) * outColor; gl_FragColor = vec4(color.r,color.g,color.b,color.a);//color.a); } I have attached a picture of how it looks. You can see the black squares near the tree. These squares should be transparent as they are in the png image:

Its strange that in this picture instead of alpha or just black color it displays the grass texture beneath the player and the tree:

Any ideas on how to fix this?

• This article uses material originally posted on Diligent Graphics web site.
Introduction
Graphics APIs have come a long way from small set of basic commands allowing limited control of configurable stages of early 3D accelerators to very low-level programming interfaces exposing almost every aspect of the underlying graphics hardware. Next-generation APIs, Direct3D12 by Microsoft and Vulkan by Khronos are relatively new and have only started getting widespread adoption and support from hardware vendors, while Direct3D11 and OpenGL are still considered industry standard. New APIs can provide substantial performance and functional improvements, but may not be supported by older hardware. An application targeting wide range of platforms needs to support Direct3D11 and OpenGL. New APIs will not give any advantage when used with old paradigms. It is totally possible to add Direct3D12 support to an existing renderer by implementing Direct3D11 interface through Direct3D12, but this will give zero benefits. Instead, new approaches and rendering architectures that leverage flexibility provided by the next-generation APIs are expected to be developed.
There are at least four APIs (Direct3D11, Direct3D12, OpenGL/GLES, Vulkan, plus Apple's Metal for iOS and osX platforms) that a cross-platform 3D application may need to support. Writing separate code paths for all APIs is clearly not an option for any real-world application and the need for a cross-platform graphics abstraction layer is evident. The following is the list of requirements that I believe such layer needs to satisfy:
Lightweight abstractions: the API should be as close to the underlying native APIs as possible to allow an application leverage all available low-level functionality. In many cases this requirement is difficult to achieve because specific features exposed by different APIs may vary considerably. Low performance overhead: the abstraction layer needs to be efficient from performance point of view. If it introduces considerable amount of overhead, there is no point in using it. Convenience: the API needs to be convenient to use. It needs to assist developers in achieving their goals not limiting their control of the graphics hardware. Multithreading: ability to efficiently parallelize work is in the core of Direct3D12 and Vulkan and one of the main selling points of the new APIs. Support for multithreading in a cross-platform layer is a must. Extensibility: no matter how well the API is designed, it still introduces some level of abstraction. In some cases the most efficient way to implement certain functionality is to directly use native API. The abstraction layer needs to provide seamless interoperability with the underlying native APIs to provide a way for the app to add features that may be missing. Diligent Engine is designed to solve these problems. Its main goal is to take advantages of the next-generation APIs such as Direct3D12 and Vulkan, but at the same time provide support for older platforms via Direct3D11, OpenGL and OpenGLES. Diligent Engine exposes common C++ front-end for all supported platforms and provides interoperability with underlying native APIs. It also supports integration with Unity and is designed to be used as graphics subsystem in a standalone game engine, Unity native plugin or any other 3D application. Full source code is available for download at GitHub and is free to use.
Overview
Diligent Engine API takes some features from Direct3D11 and Direct3D12 as well as introduces new concepts to hide certain platform-specific details and make the system easy to use. It contains the following main components:
Render device (IRenderDevice  interface) is responsible for creating all other objects (textures, buffers, shaders, pipeline states, etc.).
Device context (IDeviceContext interface) is the main interface for recording rendering commands. Similar to Direct3D11, there are immediate context and deferred contexts (which in Direct3D11 implementation map directly to the corresponding context types). Immediate context combines command queue and command list recording functionality. It records commands and submits the command list for execution when it contains sufficient number of commands. Deferred contexts are designed to only record command lists that can be submitted for execution through the immediate context.
An alternative way to design the API would be to expose command queue and command lists directly. This approach however does not map well to Direct3D11 and OpenGL. Besides, some functionality (such as dynamic descriptor allocation) can be much more efficiently implemented when it is known that a command list is recorded by a certain deferred context from some thread.
The approach taken in the engine does not limit scalability as the application is expected to create one deferred context per thread, and internally every deferred context records a command list in lock-free fashion. At the same time this approach maps well to older APIs.
In current implementation, only one immediate context that uses default graphics command queue is created. To support multiple GPUs or multiple command queue types (compute, copy, etc.), it is natural to have one immediate contexts per queue. Cross-context synchronization utilities will be necessary.
Swap Chain (ISwapChain interface). Swap chain interface represents a chain of back buffers and is responsible for showing the final rendered image on the screen.
Render device, device contexts and swap chain are created during the engine initialization.
Resources (ITexture and IBuffer interfaces). There are two types of resources - textures and buffers. There are many different texture types (2D textures, 3D textures, texture array, cubmepas, etc.) that can all be represented by ITexture interface.
Resources Views (ITextureView and IBufferView interfaces). While textures and buffers are mere data containers, texture views and buffer views describe how the data should be interpreted. For instance, a 2D texture can be used as a render target for rendering commands or as a shader resource.
Pipeline State (IPipelineState interface). GPU pipeline contains many configurable stages (depth-stencil, rasterizer and blend states, different shader stage, etc.). Direct3D11 uses coarse-grain objects to set all stage parameters at once (for instance, a rasterizer object encompasses all rasterizer attributes), while OpenGL contains myriad functions to fine-grain control every individual attribute of every stage. Both methods do not map very well to modern graphics hardware that combines all states into one monolithic state under the hood. Direct3D12 directly exposes pipeline state object in the API, and Diligent Engine uses the same approach.
Shader Resource Binding (IShaderResourceBinding interface). Shaders are programs that run on the GPU. Shaders may access various resources (textures and buffers), and setting correspondence between shader variables and actual resources is called resource binding. Resource binding implementation varies considerably between different API. Diligent Engine introduces a new object called shader resource binding that encompasses all resources needed by all shaders in a certain pipeline state.
API Basics
Creating Resources
Device resources are created by the render device. The two main resource types are buffers, which represent linear memory, and textures, which use memory layouts optimized for fast filtering. Graphics APIs usually have a native object that represents linear buffer. Diligent Engine uses IBuffer interface as an abstraction for a native buffer. To create a buffer, one needs to populate BufferDesc structure and call IRenderDevice::CreateBuffer() method as in the following example:
BufferDesc BuffDesc; BufferDesc.Name = "Uniform buffer"; BuffDesc.BindFlags = BIND_UNIFORM_BUFFER; BuffDesc.Usage = USAGE_DYNAMIC; BuffDesc.uiSizeInBytes = sizeof(ShaderConstants); BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; m_pDevice->CreateBuffer( BuffDesc, BufferData(), &m_pConstantBuffer ); While there is usually just one buffer object, different APIs use very different approaches to represent textures. For instance, in Direct3D11, there are ID3D11Texture1D, ID3D11Texture2D, and ID3D11Texture3D objects. In OpenGL, there is individual object for every texture dimension (1D, 2D, 3D, Cube), which may be a texture array, which may also be multisampled (i.e. GL_TEXTURE_2D_MULTISAMPLE_ARRAY). As a result there are nine different GL texture types that Diligent Engine may create under the hood. In Direct3D12, there is only one resource interface. Diligent Engine hides all these details in ITexture interface. There is only one  IRenderDevice::CreateTexture() method that is capable of creating all texture types. Dimension, format, array size and all other parameters are specified by the members of the TextureDesc structure:
TextureDesc TexDesc; TexDesc.Name = "My texture 2D"; TexDesc.Type = TEXTURE_TYPE_2D; TexDesc.Width = 1024; TexDesc.Height = 1024; TexDesc.Format = TEX_FORMAT_RGBA8_UNORM; TexDesc.Usage = USAGE_DEFAULT; TexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET | BIND_UNORDERED_ACCESS; TexDesc.Name = "Sample 2D Texture"; m_pRenderDevice->CreateTexture( TexDesc, TextureData(), &m_pTestTex ); If native API supports multithreaded resource creation, textures and buffers can be created by multiple threads simultaneously.
Interoperability with native API provides access to the native buffer/texture objects and also allows creating Diligent Engine objects from native handles. It allows applications seamlessly integrate native API-specific code with Diligent Engine.
Next-generation APIs allow fine level-control over how resources are allocated. Diligent Engine does not currently expose this functionality, but it can be added by implementing IResourceAllocator interface that encapsulates specifics of resource allocation and providing this interface to CreateBuffer() or CreateTexture() methods. If null is provided, default allocator should be used.
Initializing the Pipeline State
As it was mentioned earlier, Diligent Engine follows next-gen APIs to configure the graphics/compute pipeline. One big Pipelines State Object (PSO) encompasses all required states (all shader stages, input layout description, depth stencil, rasterizer and blend state descriptions etc.). This approach maps directly to Direct3D12/Vulkan, but is also beneficial for older APIs as it eliminates pipeline misconfiguration errors. With many individual calls tweaking various GPU pipeline settings it is very easy to forget to set one of the states or assume the stage is already properly configured when in fact it is not. Using pipeline state object helps avoid these problems as all stages are configured at once.
While in earlier APIs shaders were bound separately, in the next-generation APIs as well as in Diligent Engine shaders are part of the pipeline state object. The biggest challenge when authoring shaders is that Direct3D and OpenGL/Vulkan use different shader languages (while Apple uses yet another language in their Metal API). Maintaining two versions of every shader is not an option for real applications and Diligent Engine implements shader source code converter that allows shaders authored in HLSL to be translated to GLSL. To create a shader, one needs to populate ShaderCreationAttribs structure. SourceLanguage member of this structure tells the system which language the shader is authored in:
When sampling a texture in a shader, the texture sampler was traditionally specified as separate object that was bound to the pipeline at run time or set as part of the texture object itself. However, in most cases it is known beforehand what kind of sampler will be used in the shader. Next-generation APIs expose new type of sampler called static sampler that can be initialized directly in the pipeline state. Diligent Engine exposes this functionality: when creating a shader, textures can be assigned static samplers. If static sampler is assigned, it will always be used instead of the one initialized in the texture shader resource view. To initialize static samplers, prepare an array of StaticSamplerDesc structures and initialize StaticSamplers and NumStaticSamplers members. Static samplers are more efficient and it is highly recommended to use them whenever possible. On older APIs, static samplers are emulated via generic sampler objects.
The following is an example of shader initialization:
Creating the Pipeline State Object
After all required shaders are created, the rest of the fields of the PipelineStateDesc structure provide depth-stencil, rasterizer, and blend state descriptions, the number and format of render targets, input layout format, etc. For instance, rasterizer state can be described as follows:
PipelineStateDesc PSODesc; RasterizerStateDesc &RasterizerDesc = PSODesc.GraphicsPipeline.RasterizerDesc; RasterizerDesc.FillMode = FILL_MODE_SOLID; RasterizerDesc.CullMode = CULL_MODE_NONE; RasterizerDesc.FrontCounterClockwise = True; RasterizerDesc.ScissorEnable = True; RasterizerDesc.AntialiasedLineEnable = False; Depth-stencil and blend states are defined in a similar fashion.
Another important thing that pipeline state object encompasses is the input layout description that defines how inputs to the vertex shader, which is the very first shader stage, should be read from the memory. Input layout may define several vertex streams that contain values of different formats and sizes:
// Define input layout InputLayoutDesc &Layout = PSODesc.GraphicsPipeline.InputLayout; LayoutElement TextLayoutElems[] = {     LayoutElement( 0, 0, 3, VT_FLOAT32, False ),     LayoutElement( 1, 0, 4, VT_UINT8, True ),     LayoutElement( 2, 0, 2, VT_FLOAT32, False ), }; Layout.LayoutElements = TextLayoutElems; Layout.NumElements = _countof( TextLayoutElems ); Finally, pipeline state defines primitive topology type. When all required members are initialized, a pipeline state object can be created by IRenderDevice::CreatePipelineState() method:
// Define shader and primitive topology PSODesc.GraphicsPipeline.PrimitiveTopologyType = PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; PSODesc.GraphicsPipeline.pVS = pVertexShader; PSODesc.GraphicsPipeline.pPS = pPixelShader; PSODesc.Name = "My pipeline state"; m_pDev->CreatePipelineState(PSODesc, &m_pPSO); When PSO object is bound to the pipeline, the engine invokes all API-specific commands to set all states specified by the object. In case of Direct3D12 this maps directly to setting the D3D12 PSO object. In case of Direct3D11, this involves setting individual state objects (such as rasterizer and blend states), shaders, input layout etc. In case of OpenGL, this requires a number of fine-grain state tweaking calls. Diligent Engine keeps track of currently bound states and only calls functions to update these states that have actually changed.
Direct3D11 and OpenGL utilize fine-grain resource binding models, where an application binds individual buffers and textures to certain shader or program resource binding slots. Direct3D12 uses a very different approach, where resource descriptors are grouped into tables, and an application can bind all resources in the table at once by setting the table in the command list. Resource binding model in Diligent Engine is designed to leverage this new method. It introduces a new object called shader resource binding that encapsulates all resource bindings required for all shaders in a certain pipeline state. It also introduces the classification of shader variables based on the frequency of expected change that helps the engine group them into tables under the hood:
Static variables (SHADER_VARIABLE_TYPE_STATIC) are variables that are expected to be set only once. They may not be changed once a resource is bound to the variable. Such variables are intended to hold global constants such as camera attributes or global light attributes constant buffers. Mutable variables (SHADER_VARIABLE_TYPE_MUTABLE) define resources that are expected to change on a per-material frequency. Examples may include diffuse textures, normal maps etc. Dynamic variables (SHADER_VARIABLE_TYPE_DYNAMIC) are expected to change frequently and randomly. Shader variable type must be specified during shader creation by populating an array of ShaderVariableDesc structures and initializing ShaderCreationAttribs::Desc::VariableDesc and ShaderCreationAttribs::Desc::NumVariables members (see example of shader creation above).
Static variables cannot be changed once a resource is bound to the variable. They are bound directly to the shader object. For instance, a shadow map texture is not expected to change after it is created, so it can be bound directly to the shader:
m_pPSO->CreateShaderResourceBinding(&m_pSRB); Note that an SRB is only compatible with the pipeline state it was created from. SRB object inherits all static bindings from shaders in the pipeline, but is not allowed to change them.
Mutable resources can only be set once for every instance of a shader resource binding. Such resources are intended to define specific material properties. For instance, a diffuse texture for a specific material is not expected to change once the material is defined and can be set right after the SRB object has been created:
m_pSRB->GetVariable(SHADER_TYPE_PIXEL, "tex2DDiffuse")->Set(pDiffuseTexSRV); In some cases it is necessary to bind a new resource to a variable every time a draw command is invoked. Such variables should be labeled as dynamic, which will allow setting them multiple times through the same SRB object:
m_pSRB->GetVariable(SHADER_TYPE_VERTEX, "cbRandomAttribs")->Set(pRandomAttrsCB); Under the hood, the engine pre-allocates descriptor tables for static and mutable resources when an SRB objcet is created. Space for dynamic resources is dynamically allocated at run time. Static and mutable resources are thus more efficient and should be used whenever possible.
As you can see, Diligent Engine does not expose low-level details of how resources are bound to shader variables. One reason for this is that these details are very different for various APIs. The other reason is that using low-level binding methods is extremely error-prone: it is very easy to forget to bind some resource, or bind incorrect resource such as bind a buffer to the variable that is in fact a texture, especially during shader development when everything changes fast. Diligent Engine instead relies on shader reflection system to automatically query the list of all shader variables. Grouping variables based on three types mentioned above allows the engine to create optimized layout and take heavy lifting of matching resources to API-specific resource location, register or descriptor in the table.
This post gives more details about the resource binding model in Diligent Engine.
Setting the Pipeline State and Committing Shader Resources
Before any draw or compute command can be invoked, the pipeline state needs to be bound to the context:
m_pContext->SetPipelineState(m_pPSO); Under the hood, the engine sets the internal PSO object in the command list or calls all the required native API functions to properly configure all pipeline stages.
The next step is to bind all required shader resources to the GPU pipeline, which is accomplished by IDeviceContext::CommitShaderResources() method:
m_pContext->CommitShaderResources(m_pSRB, COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES); The method takes a pointer to the shader resource binding object and makes all resources the object holds available for the shaders. In the case of D3D12, this only requires setting appropriate descriptor tables in the command list. For older APIs, this typically requires setting all resources individually.
Next-generation APIs require the application to track the state of every resource and explicitly inform the system about all state transitions. For instance, if a texture was used as render target before, while the next draw command is going to use it as shader resource, a transition barrier needs to be executed. Diligent Engine does the heavy lifting of state tracking.  When CommitShaderResources() method is called with COMMIT_SHADER_RESOURCES_FLAG_TRANSITION_RESOURCES flag, the engine commits and transitions resources to correct states at the same time. Note that transitioning resources does introduce some overhead. The engine tracks state of every resource and it will not issue the barrier if the state is already correct. But checking resource state is an overhead that can sometimes be avoided. The engine provides IDeviceContext::TransitionShaderResources() method that only transitions resources:
m_pContext->TransitionShaderResources(m_pPSO, m_pSRB); In some scenarios it is more efficient to transition resources once and then only commit them.
Invoking Draw Command
The final step is to set states that are not part of the PSO, such as render targets, vertex and index buffers. Diligent Engine uses Direct3D11-syle API that is translated to other native API calls under the hood:
ITextureView *pRTVs[] = {m_pRTV}; m_pContext->SetRenderTargets(_countof( pRTVs ), pRTVs, m_pDSV); // Clear render target and depth buffer const float zero[4] = {0, 0, 0, 0}; m_pContext->ClearRenderTarget(nullptr, zero); m_pContext->ClearDepthStencil(nullptr, CLEAR_DEPTH_FLAG, 1.f); // Set vertex and index buffers IBuffer *buffer[] = {m_pVertexBuffer}; Uint32 offsets[] = {0}; Uint32 strides[] = {sizeof(MyVertex)}; m_pContext->SetVertexBuffers(0, 1, buffer, strides, offsets, SET_VERTEX_BUFFERS_FLAG_RESET); m_pContext->SetIndexBuffer(m_pIndexBuffer, 0); Different native APIs use various set of function to execute draw commands depending on command details (if the command is indexed, instanced or both, what offsets in the source buffers are used etc.). For instance, there are 5 draw commands in Direct3D11 and more than 9 commands in OpenGL with something like glDrawElementsInstancedBaseVertexBaseInstance not uncommon. Diligent Engine hides all details with single IDeviceContext::Draw() method that takes takes DrawAttribs structure as an argument. The structure members define all attributes required to perform the command (primitive topology, number of vertices or indices, if draw call is indexed or not, if draw call is instanced or not, if draw call is indirect or not, etc.). For example:
DrawAttribs attrs; attrs.IsIndexed = true; attrs.IndexType = VT_UINT16; attrs.NumIndices = 36; attrs.Topology = PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; pContext->Draw(attrs); For compute commands, there is IDeviceContext::DispatchCompute() method that takes DispatchComputeAttribs structure that defines compute grid dimension.
Source Code
Full engine source code is available on GitHub and is free to use. The repository contains two samples, asteroids performance benchmark and example Unity project that uses Diligent Engine in native plugin.
AntTweakBar sample is Diligent Engine’s “Hello World” example.

Atmospheric scattering sample is a more advanced example. It demonstrates how Diligent Engine can be used to implement various rendering tasks: loading textures from files, using complex shaders, rendering to multiple render targets, using compute shaders and unordered access views, etc.

Asteroids performance benchmark is based on this demo developed by Intel. It renders 50,000 unique textured asteroids and allows comparing performance of Direct3D11 and Direct3D12 implementations. Every asteroid is a combination of one of 1000 unique meshes and one of 10 unique textures.

Finally, there is an example project that shows how Diligent Engine can be integrated with Unity.

Future Work
The engine is under active development. It currently supports Windows desktop, Universal Windows and Android platforms. Direct3D11, Direct3D12, OpenGL/GLES backends are now feature complete. Vulkan backend is coming next, and support for more platforms is planned.
• By Snaked
Im working in this project for 1 year .... mostly i develop a tool and databases for make the different maps and now i'm doing the client for play the game
Tell me if you like it......
this is a capture of how is viewing atm

https://youtu.be/9251v4wDTQ0

• Another one of our new UI for #screenshotsaturday. This is the inventory screen for showing what animal fossils you have collected so far. #gamedev #indiedev #sama

• Hi everyone. I need some help with my project. It's a 2D-graphics-heavy WPF front-end app written in C#, which talks to two Access 2000 databases (yes I know, it's all I've got). It will be distributed freely on the internet, and so will be being used by Windows users of various installations/versions of Windows, Office, etc.
One of the two databases (let's call it A), is intended to be read-only, and will be distributed with the app. It has half a dozen relational tables which I as the developer have populated, and is connected to in the app via OleDB Jet 4 with SQL querying the data now and then as the user uses the front-end. The database will be replaced whenever I release an update to the app.
Database B is read/write, and contains end-user preferences, for example when they favourite something in my front-end, a Favourites table in here gets appended to. This database is not distributed with my app, and should not be overwritten, as it will lose user prefs, etc. and annoy my users.
Whenever my app is run by a user, during initialisation database A will suck in the user data from database B (using simple SQL SELECT * INTO...), so that all the tables can be joined together by the SQL in database A (to include user prefs/favourites in SQL queries), and whenever the user favourites something, a record is created both in A (for the short-term session) and B (permanently). Database B isn't just about holding favourites, there is other user data in here as well, so there are 3 or 4 tables in B.
So far, this is all working fine and I'm happy...
Unfortunately my app is currently 32-bit, and it now needs to break the 32-bit memory barrier what with the size and volume of the graphics I'm pulling in (using the HDD is not really an option, as different graphics are needed kind of instantly and the hard disc would be being hosed and the app dog-slow otherwise, I suspect even off an SSD).
I'm using VS2015, and switching to 64-bit will probably fix the memory problem, but it breaks Jet 4.0. I'm sure this is old news to most of you.
To try to keep with 32-bit (and Jet4) but get the memory I need I've tried the -largeaddressaware toggle, and I've tried the editbin suggestion, but I just can't get these solutions to work in VS2015 no matter how hard I try. Are these definitely 100% solutions to 2gb memory limit in 32-bit applications? Should they always work? Am I dumb in being unable to get this to work?
So otherwise I'm resigned to migrating to 64-bit, and having to get around the database issue, not the memory issue.
My users will be using a variety of Windows versions (probably 7 and 10), and I'm sure various versions of Office, and so my solution for querying my two Access databases needs to be pretty open if possible.
Googling has suggested I switch from JET4 to ACE12, but this is apparently requiring me to uninstall Office 2000 and install a 64-bit version (which I don't have), so I can't use it, and I suspect any users who also have an old version of Office installed won't be able to use it either?
Googling has also suggested I use MS SQL Server. This sounds fine if there's such a thing as a "lite" local version which can manage database access, but I still need to somehow get the data from the databases (A.mdb and B.mdb) into the SQL Server each time the users fire up my app.
The only solution I can think of at the minute, is to export all the tables from database A into CSVs every time I update the data in there, and have the app import them in a lame way, and also convert database B into some crappy text file which gets written to whenever the user changes a preference. I'd much rather use SQL to do all this if possible, as when the user browses around the app, queries involving joining several tables in A are regularly created and executed to adjust the user's experience/return search results/etc.
So to summarise my misery, is there either an easy reliable way for me to keep with 32-bit/Jet4 and be able to address >2gb. Or is there instead an easy reliable way for me to switch to 64-bit and successfully query two Access databases without requiring all my users to have 64-bit Office installed?
Thanks for reading and I hope someone can help.