Procedural Terrain Rendering How-To

Hello all again! So, after two days of hard recoding of system - i get it!

No borders.

As i know normal map strength in calculation must be multiplied by special value depending on lod level.
So the formula is pretty simple for me.

LODMod = ((1 << LODLevel + 2) * (this.Planetoid.PlanetRadius / (LODLevel + 2)) - ((this.Planetoid.PlanetRadius / (LODLevel + 2)) / 2)) / this.Planetoid.PlanetRadius;

How i can fix that? (I mean broken normals :frowning: )


All normal maps have the same direction.
On hight lod level have the same…

Normal calculation alg. is:

float l = patchPreOutput[(id.x + 0) + (id.y + 1) * nVertsPerSideWithBorder].noise * LODMod;
float r = patchPreOutput[(id.x + 2) + (id.y + 1) * nVertsPerSideWithBorder].noise * LODMod;
float u = patchPreOutput[(id.x + 1) + (id.y + 0) * nVertsPerSideWithBorder].noise * LODMod;
float d = patchPreOutput[(id.x + 1) + (id.y + 2) * nVertsPerSideWithBorder].noise * LODMod;
float xdelta = ((l - r) + 1) * 0.5;
float ydelta = ((u - d) + 1) * 0.5;
float3 normal = float3(xdelta, ydelta, 1); //Normal color/
float slope = 0.5 / max(dot(normal, float3(0, 1, 0)), 0.001); //Slope calculation from normal.
float4 normalColor = float4(normal, slope); //Combine.
Normal[id.yx] = normalColor; //Output

Surface shader code is default:

fixed4 terrainNormalTexture = tex2D(_NormalTexture, IN.uv_NormalTexture);
…
o.Normal = UnpackNormal(terrainNormalTexture);`

Another thing… When i Getting data from cumpute shader via buffer.GetData(array); it takes about 60-100ms!!!

GetData has to wait for the GPU to actually carry out the work you’ve dispatched, so calling GetData immediately after Dispatch like you’re doing there is going to mean you’re stuck waiting for the GPU’s work queue to clear, and for it to carry out the kernel, and for the results to be copied back to system memory.

Try doing other work after Dispatch(), so that the GPU has a chance to get things done - you could even yield for a frame and retrieve the results later that way.

Profiler Screenshot

Any solutions?

Thx to @NavyFish for help and helpful information!

Is there any asynchronous method available for GetData?

If you have asynchronous method for GetData then you shouldn’t notice any hiccup on the rendering pipeline, but you’ll still have to wait 60-100ms for the data to return. That being said, you shouldn’t have to worry about the 60-100ms if you write your code properly with async in mind. You could first render the terrain without the CPU knowing anything about the terrain, and once you get the data back you can start building your collision mesh and whatever you need on the CPU side.

Maybe i don’t understand something… I simpy call Dispatch method on Quad class, which contains next:

  1. Create buffers.
  2. Fill data to kernel 1.
  3. Dispatch kernel 1.
  4. Fill data to kernel 2.
  5. Dispatch kernel 2.
  6. GetData and set it in to the surface shader. (Here is stall in about 50-100ms)
  7. Release and dispatch all buffers.

A little tease, I should have the video up soon…

/edit
Video is up.
https://youtu.be/ZlrTWy5jdUY

2 Likes

Wow, looks really good! Would you be willing to share some technical details on the engine? E.g. how you’re seeding the initial values for the mid-point displacement algorithm, it must be a lot faster than using 3D Perlin or Simplex noise. I’m also curious on how you’re computing the normal map using a mid-point displacement function. I assume it’s the same as calling a 3D noise function? And then you could use octaves on top of that?

It’s pretty simple, each corner of the cube gets a height value, that is the initial seed, then the displacement value also gets its own seed based on position. As for the normal map, the compute shader returns face/patch heights, normal map is generated with those heights.

I mentioned this issue much earlier in the thread before you guys joined the conversation - right now there’s no asynch getdata supported by unity.

My next step in working with Unity to try and build a native plugin that will provide such capability. No idea if this is doable, but it’s worth a shot. There are some discussions about this on the unity forums.

So @zameran you’re not doing anything wrong, it’s just not possible in Unity right now.

Or… replicate the noise functions on the CPU with threading :wink: I’ve ported the Ashima Simplex noise version to JavaScript, should be fairly easy to convert to other languages.

Runs OK on the CPU. Check out the demos: http://rarara.github.io/javascript-noise/demo/

Definitely an option. But seems like an expensive one esp if you’re doing that in JS. Still, might be good enough if you do a low resolution sampling.

Thx for your reply.Ya i know… Any chance to do some sort of “wait for” getData? Couritine?
Cuz 128 mesh calculation kernel and 512 texture takes soooo long…
So, i done hi-resolution normal maps and heightmaps. Now i know “the math”!
But problem with border still alive :frowning:

Just use Coroutines, spawn one for each face or patch or whatever…

http://docs.unity3d.com/Manual/Coroutines.html

I mean GetData method on separate thread or something.
To render quad only after GetData method complection.

Unity itself doesn’t support threading, but this might work, you might be limited in what functions you can call though.

http://answers.unity3d.com/questions/357033/unity3d-and-c-coroutines-vs-threading.html

Another option is to generate a geometry map. Usually people have around 33x33 vertices per patch. A 33x33 geometry map should be fairly quick to fetch? Just throwing this idea out, since I’ve seen people do this before.

2 Likes

Regarding doing getData on a separate thread, short answer is no. That must take place on the graphics driver thread, which is where rendering takes place. Calling getData will stall all rendering unless you use getDataAsync which Unity doesn’t expose. Hence the need for a new plugin

And for proper physics, you probably want a ‘full resolution’ height map

Thx for information. But all of these was founded in google before reading your reply :slightly_smiling:
So, @NavyFish, what about uv?
How do you calculate it?

Just in case you regularly update Unity you may be warned I have the impression they changed something with Unity 5.3.2 with regards to light.

My scene suddenly looks pale, overexposed and less detailed since I updated to that version. Would love to know of those who tried that version noticed something similar like me.

So. I have the same bugs - rolled down to prev. version…
Joerg, how do you apply normal maps for quads in surface shader?

They switched from Phong to GGX for their brdf. Thats probably what causes your lighting to look different.

Hi Jan, thanks for the hint. Could indeed be an issue related to that, yes. I went down to 5.3.1 where that issue doesnt appear anymore. Interesting So it seems not completely GGX which AFAIK they invented in 5.3.x, but caused by something that they specifically changed in 5.3.2. Probably something due to them doing GGX implementation bugfixing? Anyway I think I’ll skip the latest version and try out something like 5.4.x again.

With regards to my Unitys planetengine FPS drop (and CPU ms/frame increase) at lower altitude I’ve been moving certain parts to a separate thread I did in the render thread each frame previously. Especially parsing the quadtrees. Now the only thing remaining in the main render thread is
1. Dispatch to the GPU if there is something new to be calulcated
2. Relcaculate the bounds of each terrain plane (necessary each frame unless I create separate prototype meshes per quad).
3. Do the draw-calls.

But the framerate still drops at lower altitude. As this happens even when the camera does not move, the issue cannot be the dispatching to the compute buffer (1.). As I think setting the bounds (2.) should be straightforward and not CPU expensive, I suspect that the draw calls (3.) take too much CPU resources, increasing when getting close to ground.

Is there a certain rule of a number of draw calls you should not overcome without batching? In my case the FPS seems to drop sigtnificantly starting at ~600-1000 batches according to the Unity stats.

Guess the best thing I can do in Unity right now is to dynamically change and decrease the radius of the LOD Spheres while getting closer to the ground. While at high or medium altitude you notice farer details pretty well, at low ground its probably not necessary to keep them at higher distance (a few kilometers) - when I look out of the window right now, everything vanishes pretty quick after ~10-20 kilometers. I was guessing that the horizon culling solves the LOD at low grounds, but I think at large 1:1 scale the effect is not good enough to keep the number of planes and LOD (and so draw calls) low enough.

I am thinking of considering the distance of camera to ground compared to the planets radius to calulcate a certain decrease factor. Has anyone of you wo has used LODSpheres already tried dynamic radiuses?

@NavyFish: As far as I can see, you have worked on how to caluclate the best initial settings but do not change them based on camera position, right?