Sky-rendering techniques

Started by
137 comments, last by Filip 20 years, 2 months ago
1. And here we come again to the beginning of the discussion: that's exactly what I do, I keep the texture in sysmem and transfer it to the card. But that way, you can't use the hardware to generate the Perlin noise, as you suggested...

quote:A 1024x1024x1 texture is one meg. One byte per texel should give enough precision for the clouds

That's only for opacity. Don't forget the shading.

2. It all depends on your *texture* resolution, perspective projection, and scale. If I take out only one octave, I lose considerable detail. Look: let's start with a 16*16 texture as primary octave. 8 octaves would give you a 2048² texture, where every pixel has been 'fractalized'. That's more or less the resolution I use. If you cut down to 5 octaves, the maximum you will get out of your fractal function is a 256² texture. That is clearly not enough.

3. You can't combine one octave per colour channel. But you can preprocess 3 to 4 octaves into the alpha channel, by pre-adding them. The more ocvtaves you preprocess, the larger the texture will have to be. 3 octaves would be a 64² texture, that is acceptable to update every frame. It could even be created in realtime by using render-to-texture feedback.

quote:
Anyway, we're getting into very complex tricks here. Getting this to work and to look good will probably take a LONG time

I implemented it yesterday evening. It works, with the exception of a slight flaw in the exponentiation pass that required an additional texture shader (you can't address a texture on per-fragment level using the RGB result from a regcom, I didn't thought of that).

quote:
"Hugo writes: 'The secret behind Terragen's beauty: rather than going for all out brute force and mathematical accuracy, Matt just writes algorithms that produce good results, rather than trying to exactly model the physics. The clouds are essentially a beautiful bodge.'" Do you have any idea which algorithms they're talking about? I tried to find more details, but couldn't

Well, hmm, yes and no. Some time ago, and friend and I had a discussion with Matt about a realtime version of TerraGen, and he went briefly over his algorithms. But I guess details are his private 'professional secret'. In a nutshell, he means that the clouds look like 3D, but are really just a 2D plane. The trick is to make them look like 3D through the shading.

/ Yann

[edited by - Yann L on May 22, 2002 10:47:36 AM]
Advertisement
Instead of implementing the shading equation, I simply used perlin noise again to shade the clouds. I specified two colors and interpolated them for every texel based on the value of The Great Noise Function. Although when I shoot for the realistic look the clouds look ugly, the world of warcraft type clouds are done very easily. I''ll play around with the values some more to get the realistic feel.

Hopefully in a couple of days I''ll post the screenshots... I want to get the sun to work well and find really good color/gradient/frequency values before I let the whole wide world see my creation

Yann, can you give a little bit more detail about your skybox trick to reduce the texture resolution? I''m not sure how projecting the plane on a skybox will require a smaller texture to look good...
A little off topic.

This thread needs to go into the Resources section of GameDev. I have learnt more in this thread about sky rendering than I have in just about all the other threads combined.

Great links Yann, Impressive sky! Keep posting dude, I''m listening.

/me adds thread to watch list for future reference

D.V.

Carpe Diem
D.V.Carpe Diem
quote:
Hopefully in a couple of days I'll post the screenshots... I want to get the sun to work well and find really good color/gradient/frequency values before I let the whole wide world see my creation

Cool. It's always interesting to see shots from advanced sky engine implementations.

quote:
Yann, can you give a little bit more detail about your skybox trick to reduce the texture resolution? I'm not sure how projecting the plane on a skybox will require a smaller texture to look good...

Hmm, I was actually thinking of getting rid of it. It just takes too much processing power. And now that I have the clouds running in 100% HW on the GPU, there isn't really much need for it anymore (we're targeting GF3+ for the game).

OK, this was the idea behind it: If you texture your skyplane with a cloud texture (without tiling, since you don't want clouds repeating themselves), then this texture will have a highly degenerated texture space, due to the extreme perspective range it covers. It will be strongly magnified in the middle of the skyplane, just above your head. That's not good, since it will render the clouds just above the player very blurry. On the other side, towards the horizon, the cloud texture will be highly compressed. It will look OK, if you use mipmapping and anisotropic filtering, but you'll actually waste over 80% of your highres cloud texture, since the highres parts will never be displayed.

It would be possible to encode a kind of adaptive resolution texture, and adjust texcoords to reduce the effect. But it takes considerable performance to realtime encode such a texture and you will get weird looking artifacts when clouds are moving. You'd also need to tesselate your skyplane fairly well, to get a good texcoord approximation of the distortion function used.

Now take a skycube: the sampling distribution is almost constant over it's faces. That way, texture resolution is used to the maximal possible amount, and you don't waste texture space. But the CPU has to project the procedural cloud plane (that has 'infinite' resolution, due to it's procedural nature) onto the cloudbox using supersampling. I use hand optimized ASM for that, but it still swallows very much performance. As usual, it's a quality - performance tradeoff.

DeltaVee: thanks. Photorealistic rendering of natural effects is actually one of my favorite subjects, so expect some more threads of this type in the future

[Edit] made a few things clearer. Damnit, my spelling sucks...

/ Yann



[edited by - Yann L on May 23, 2002 4:15:01 PM]
OK, for all pixelshader freaks out there: I just came up with an interesting idea on how to overcome the slight exponentiation flaw I mentioned yesterday. That way, you could get photorealistic, full quality, 100% dynamic realtime clouds, that are fully computed by the 3D card. There would be almost 0% CPU involvement.

I haven''t tested the stuff yet, I''ll do that tonight. For those who are interested in implementing something similar, I''ll just describe my idea. If someone wants to try it out, it would be nice if we could discuss and compare the results of our implementations ! The implementation idea is given in OpenGL, but can be easily adapted to D3D.

So, the idea is a two pass approach (work on GF3+ only): first pass creates the perlin noise (up to 16 octaves are possible, without performance penalty !). The second pass performs the exponentiation, shading and final cloud plane rendering.

First pass: Setup a pbuffer of the resolution of your screen. Now load 2 to 4 pre-added noise textures into the texture units, as described earlier. Each noise texture should be signed and contain 3 or 4 pre-added octaves of perlin noise. Setup a vertex shader, that adjust the s,t coordinates of each used texture unit, so that the noisetextures get added at the right frequency, by adjsuting the tiling.

Eg: you have 2 preadded textures of 64² each, containing 3 noise octaves each (a total of 6 octaves). Then st0 = 1*base_frequency and st1=8*base_frequency.

Create a regcom setup, that multiplies each texture with a amplitude factor and adds everything together. For our 6 octave example, that would be: c = tex0_alpha * constant0_alpha + tex1_alpha * constant1_alpha. Very simple, gets a bit more complex if more octaves are used. Constant0_alpha and Constant1_alpha give the amplitude of each noise texture.

In an additional regcom stage (or in the final combiner), subtract the cloud_cover value. We do that at this point, because we want to use the maximum precision available. The clamping after the subtraction will reduce dynamic range, so it''s good to do that before writing the result to the 8 bit framebuffer.

Finally, render your skyplane to the pbuffer, with the vertex/pixelshader setup as described. The result should be a full perlin noise layer, that will already have clear and covered cloud parts. But it is not yet exponentiated.

Second pass: bind the pbuffer as texture in unit 0. Since we now have the result of the perlin noise generator as a texture, we can directly use it in a texture shader stage (unlike my first idea, where it was impossible to use a texture addressing operation with a regcom result). Load a 256x1 intensity texture map into texture unit 1, containing a pow-lookup table for all values from 0 to 255. Note, that you can''t use a 1D texture because of the texture shader we''ll use, but a 256x1 will just do fine.

Set up a texture shader in stage 0: GL_DEPENDENT_AR_TEXTURE_2D_NV. This will convert the alpha and red value of our perlin noise texture to a (s,t) pair. t will be ignored. s will contain a value between 0 and 255. Use it as index into our lookup table texture: cfinal = Pow_table. Now our perlin noise is realtime exponentiated by the GPU on a per fragment level !<br><br>You can use texture unit 2 to apply a shading pass. This one will have to be precalc''ed on the CPU (since we can''t do multiple scattering computations on the GPU yet <img src="wink.gif" width=15 height=15 align=middle>), but it can happen at a much lower resolution than the real cloud opacity. Combine alpha and shading values into an RGBA value using a regcom. Render your cloud layer into the framebuffer using standard alpha blending. Done: perfect quality, full dynamic clouds, calculated 100% on the GPU !<br><br>You can use a similar approach to HW accelerate the shafts of light coming from the sun.<br><br>OK, I''ll implement that now. If everything works, I''ll try to post some screenshots tomorrow, if anyone is interested.<br><br>/ Yann
quote:Original post by DeltaVee
This thread needs to go into the Resources section of GameDev. I have learnt more in this thread about sky rendering than I have in just about all the other threads combined.
Yann, you should really, really write an article (or articles) about this. It''d be perfect for the Hardcore column. I know you''re probably a bit time-limited, but considering how much you''ve written here , and the scarcity of articles covering doing these things in real time, you should definitely think about it.
Could someone explain the term "exponentiation" to me, and why it''s used ( I know exponentials - just not how it''s used in this context... ).

Cheers

Death of one is a tragedy, death of a million is just a statistic.
If at first you don't succeed, redefine success.
quote:
Could someone explain the term "exponentiation" to me, and why it's used ( I know exponentials - just not how it's used in this context... ).


Well, 'exponentiation' simply means raising a quantity to a power, but I guess you already know that It is a vital part of realistic cloud generation. The trick is to take a sharpness factor to the power of your original perlin noise. If the numeric ranges are well chosen, then this will apply a range mapping to the cloud noise. This mapping makes the clouds look more detailed and volumetric.

Here are some pics to show the effect.

This is the noise you normally get from a perlin noise generator:

Fig.1

It's distribution is very uniform, not really clouds yet. Or perhaps a very overcast day...

We need some clear parts in our sky, so we can just subtract an offset from the perlin noise, and clamp the result to a positive range:

Fig.2

The equation used is simply result = clamp_to_0( perlin_noise - cover_offset ) . It's already better, we have some cloudy and some clear parts. But it's still very fuzzy, more a kind of 'spiderweb' than actual clouds. Many sky engines stop at this step, and use this noise directly. This leads to very fuzzy and undefined clouds.

The problem is that in reality, clouds are 3D volumes. They have different thickness, and from a certain thickness on, a cloud becomes totally solid to the eye, no more transparency at all. This is not the case with our noise: it is always transparent to a certain degree.

We can change that by applying a range mapping. We are looking for a range mapping function that fully preserves our precision, since we only have 8 bit, and don't want to waste dynamic range.

The perfect candidate function is an exponential. Consider f(x) = nx . This will raise the constant factor n to the power x. This exponential function has an interesting property: if we can guarantee that n is always between 0 and 1, then the result itself will as well be between 0 and 1, for *all* exponents between 0 and infinity. Our exponent will be the clamped noise from Fig2. It is between 0 and 255. Let's assume a sharpness factor n of 0.96.

The two extremes will be:

f(0) = 0.960 = 1
f(255) = 0.96255 = 0.00003 (almost 0)


Great, we are between 0 and 1, as predicted. Now, let's map it back to the 0 to 255 range (and re-invert it, since the exponentiation inverted it in the first place):

final_clouds = 255 - ( 255 * 0.96x )

We haven't lost a single bit of precision, and we get a much nicer result, the clouds look more 3D now:


The hard part with exponentiation (as you noticed in the previous posts) is to have the 3D hardware do it on a perpixel level.

quote:
Yann, you should really, really write an article (or articles) about this. It'd be perfect for the Hardcore column. I know you're probably a bit time-limited, but considering how much you've written here , and the scarcity of articles covering doing these things in real time, you should definitely think about it.

Heh, yeah, I already wrote half of an article in this thread...

But you are right. It's very hard to find good public resources on those things. I already considered the idea of writing an article about it, perhaps a small series about photorealistic realtime rendering of various natural effects: terrain, sky, water, etc.

Besides the sky, I would love to do one about water, there is so much you can get out of a modern 3D card, people wouldn't believe it. Since most of the effects I wrote are used in our game, I'll have to check some IP/NDA issues with our company first. But besides that: yes I would be interested. I'll get back to you by mail for some more details and timing.

/ Yann


[edited by - Yann L on June 7, 2002 8:10:15 PM]
Yann, wait ''till I''m done with clouds I don''t particularily like the terrain topic, especially considering that a lot has been said about it in "Texturing and Modelling: The Procedural Approach", but we''ll definetly have another thread discussing water
Thanks Yann - you really cleared that up for me

As for water - look at the deep ocean rendering article on gamasutra.

Death of one is a tragedy, death of a million is just a statistic.
If at first you don't succeed, redefine success.

This topic is closed to new replies.

Advertisement