mercredi 7 août 2013

Nitro::Terrain Frustum Culling

I have progressed a bit on my terrain, I have brought back full dynamic lighting and frustum culling !
The lighting equations were from Shader X7 originally, and they model the Cook-Torrance lighting model which suits all my shading needs.

I had some concerns regarding the blending of different terrain materials (I'm doing a simple slope based material splatting), especially regarding the normals. I read this blog post about blending normals, and it got me thinking about how I should blend the normals of the different terrain materials.
I decided to test out some normal blending functions but none of them gave the expected results... the only thing that worked was when I added the two normals together !
float3 BlendNormals(float3 n1, float3 n2, float factor)
{
    return (normalize(float3(lerp(n1.xy, n2.xy, factor), n1.z*n2.z)));
}
I settled on something like the whiteout blend, except I needed to select how much of which normal to choose so I added the lerp; I'm not sure how that affects the accuracy of the result but it gives a decent enough normal for lighting.

Finally frustum culling makes a big comeback, I hadn't touched it since the last version of the terrain that used my home-cooked-ready-to-blow math functions. Well I say that but hey, it worked, so it wasn't broken and I shouldn't have touched it. My fault. But now that I've converted to XNA::Math let's keep rolling with it.
Turns out I was getting weird culling with just ComputeFrustumFromProjection (XNA::Math built-in), and true enough it was getting me a View Space frustum, so I had to convert it to a World Space frustum (transform it by the inverse of the view matrix). That's it, now frustum culling works !

That's an important step for the algorithms I plan on adding soon, which will require the terrain to be rendered multiple times from different perspectives (yes, shadow maps !).
Below you can find the code that computes the correct World frustum using XNA::Math (credit to this guy):

void CDLODTerrain::Prepare(Camera const &camera)
{
    _curtSelection = CDLODQuadTreeTerrain::LODSelection();
    XMFLOAT3 camPos;
    XMStoreFloat3(&camPos, camera.Position());
    XNA::Frustum frustum;
    XNA::Frustum worldFrustum;
    XMMATRIX projMat = camera.ProjectionMatrix();
    ComputeFrustumFromProjection(&frustum, &projMat);
    XMVECTOR scale, rotation, translation, detView;

    // The frustum is in view space, so we need to get the inverse view matrix
    // to transform it to world space.
    XMMATRIX invView = XMMatrixInverse(&detView, camera.ViewMatrix());

    // Decompose the inverse view matrix and transform the frustum with the components.
    XMMatrixDecompose(&scale, &rotation, &translation, invView);
    TransformFrustum(&worldFrustum, &frustum, XMVectorGetX(scale), rotation, translation);

    _curtViewingRange = camera.FarClip();
    _quadTree.LODSelect(camPos, worldFrustum, _curtViewingRange, _curtSelection);
}

The code is not optimized at all but it's correct and that's all that matters.
And below a video of the result:


mardi 6 août 2013

Continuous Level Of Detail terrain

I have recently re-started to work on my terrain engine from 2 years ago. This time it's DX11 only, and I've managed to finally get the geometry just right.

The problems:
I had been battling with the vertices for so long, not exactly sure what was causing all the artifacts I was having: stripes, background appearing in front of foreground objects, vertex being culled for (apparent) no reason and finally holes in the geometry (patch).

The first one (the stripes) and second one were due to not setting depth writes (duh, I was tired), I finally caught that after plowing through dozens of rasterizer states. My objects (terrain tiles) were actually sorted from front to back so the bug was somewhat hard to track.

It was certainly not helped by the wrong vertex winding order (I should have read the doc more carefully): I always wind my vertices in counter-clockwise fashion (maybe an old OpenGL habit). But in DirectX 11 it's not the default, so I turned "counter clockwise is front" to true in the rasterizer.

That was not the end of the road, as the code to draw the terrain patches was very old and back then I was very much debuting my programming career; so not so many comments, lots of "magic" values and a questionable sense of object orientation. Which means now that my patches were drawing I had one triangle missing in each quad. Turns out I had a similar problem on an OpenGL game I developed for Epitech recently (the R-Type) and it had to do with the index size.

Short version is: check that the number of vertices you store in those vertex buffers are < 65536 if you are going to use DXGI_FORMAT_R16_UINT, and don't hard code that enum, but make it dynamic so that if you have less than those vertices you can enjoy a smaller buffer, while when you go over that you switch seamlessly to R32_UINT.

The implementation:
I chose MJP's framework to rebase my algorithm, so that I have a solid base to start off. The algorithm is based on Philip Strugar's paper on CDLOD terrain and I've adapted a few things to make it simpler to implement (at least for me), as I was trying to get it working before trying to optimize it.

So what's the result ? Here's a video showing what I have so far:



Next I'll be working on the shading of the terrain, and making it larger. I'm deciding between two routes for the texturing technique: one massive texture (think 16kx16k +) that is broken down in mip-maps that are paged in when required (a la Rage but not quite) or a procedural texture splatting technique which is a classic in this domain.
Personally, the procedural texture is going to be easier to implement at first, and it would be a nice complement to a procedural terrain. Then I should be able to add a mega texture on top for all those unique features an artist would want to put in.