Terrain Lighting Per Vertex - Looks Terrible

Started by
38 comments, last by Geared 13 years, 3 months ago
maybe I made an error in reply. What i really meant was:
glColor3f ( <your ter data>/255.0f, <your ter data>/255.0f, <your ter data>/255.0f ) );


What method do you use to create terrain may I ask?

The actual colouring of my terrain looks like this:
glColor3f ( ter [ x ] [ z ] [ 1 ]/255.0f, ter [ x ] [ z ] [ 1 ]/255.0f, ter [ x ] [ z ] [ 1 ]/255.0f ) );



If what you say is true, we might actually be creating our terrains in the same way, using y vertices to first draw flat plane, consisting of x, y and z vertices;
so the [ 1 ] above is the y index that changes it's heihgt based on heightmap, and ofcourse that would be in for statements that account for each and every vertex created by terrain x and z size.

so indeed, it is glColor3f ( <your y data here>/255.0f, <your y data here>/255.0f, <your y data here>/255.0f ) sort of; but in truth involves all axis, but in reality using height axis ( y ) as index into array of terrain data.


Maybe I'm not advanced openGL wise enough to correct you. I am but merely a 2 month openGL programmer after all, and probably 3 good weeks out of that went towards learning openGL.

http://img440.images...gamedevsig.jpg/

I'm not the typical programmer.
Advertisement
I see what your getting at, but the problem I'm having doesn't necessarily have to do with coloring. I can turn color material off and the problem persists. This has to do with the calculation of the normals
Quote:Original post by Geared
Quote:Original post by swiftcoder
The simplest way to fix this, is to derive your normal directly from the heightmap (via central distance, or similar), rather than from the triangles.
When I was searching I read something about calculating normals directly from the height map, but I don't remember where that was and I didn't think much of it at the time. I can't really think (thought I haven't given it a lot of thought yet) how exactly you could calculate normals from a height field. It seems like you still need to create surfaces and calculate their normals.
I don't know if your math background includes calculus, but if it does, recall that to find the line normal to a curve, you take the first derivative of that curve. For a given point on the surface of a heightmap, we can regard it as the intersection of a curve in the x direction, and a curve in the z direction (assuming y is the vertical axis). One can approximate the derivative of a curve using finite differences (i.e. the difference between two nearby samples), and from this you can derive the normal.

That actually isn't as hard as it sounds, but we can also cheat a little. We can treat any point on the surface as belonging to a triangle of arbitrary size, and compute its normal by finding the vectors for two of its sides, and computing the cross product. Since our heightmap forms a grid, right triangles make the most sense, leading to something like this:

h00 = height(x, y)h10 = height(x + 1, y)h01 = height(x, y + 1)normal = vec3(1, h10 - h00, 0) cross vec3(0, h01 - h00, 1)normal.normalise()

Where 1 represents the distance to the next pixel.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I think this is just a problem with using quads to represent curved spaces.

If the two tris making the quad aren't planar (and they're usually not for stuff like fractal ground) then you will see different results for when the edge is connected to the other two vertices instead, which proves a quad doesn't cut it. Even tools like 3DS Max have an "edge turn" facility for manually fixing cases like you highlight.

It's better to use 4 tris per quad with a centroid vertex (letter envelope) but this only minimises it. You'll still get problems at the shared outer edges of your quad with the next one along, so you need to split the edges as well and you end up with millions of triangles. Think of this as an aliasing problem.

A hexagonal arrangement with 6 tris per hex might work out more efficient for solving this, but I've never tried it. It sure feels like it should work though.
------------------------------Great Little War Game
As Rubicon said, I'm not sure you can really fix the problem by changing the way normals are calculated.

Personally I'd suggest the best method of "fixing" it would be to add a nice LOD system to your terrain renderer, so that the number of triangles near the viewer and on the more sharply angled terrain is a lot higher. This should minimise any visible artifacts.
Quote:Original post by sprite_hound
As Rubicon said, I'm not sure you can really fix the problem by changing the way normals are calculated.
Wait... what? Of course you can. Do you think all lighting looked like this before normal mapping was invented?

The current issues are a form of aliasing, caused by taking a (fairly) continuous representation (the heightmap), and mapping it to a discrete representation (triangles), which is then used to calculate the normals. Calculating the normals directly from the continuous representation sidesteps the issue.

Even though two adjacent triangles are not generally planar, the shared edge is comprised of two vertices, both of which are shared between the two triangles. Since in each triangle it has the same end vertices, they will be interpolated identically, and the lighting will be consistent. This is true for all three edges of a triangle, which leads to continuous lighting across neighbouring faces.

***

In this day and age, you should honestly try to forgo vertex normals entirely, in favour of normal mapping with per-pixel lighting, at which point you can increase the detail immensely, at very little cost...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by swiftcoder
Quote:Original post by sprite_hound
As Rubicon said, I'm not sure you can really fix the problem by changing the way normals are calculated.
Wait... what? Of course you can. Do you think all lighting looked like this before normal mapping was invented?


No, I suppose I should have said "while using per vertex lighting". My point was more that while using per vertex lighting it doesn't matter how you calculate the normal and send it to the graphics card, it'll still be interpolated linearly between the vertices, which causes those artifacts.

I agree that using a detail normal map and per pixel lighting would be a good idea.

(I should probably also amend my LOD suggestion to be "if it's feasible for you / your terrain creation method and you really want to stick to per vertex lighting")
Quote:Original post by sprite_hound
My point was more that while using per vertex lighting it doesn't matter how you calculate the normal and send it to the graphics card, it'll still be interpolated linearly between the vertices, which causes those artifacts.
I don't buy it.

Yes, the normals are linearly interpolated, but provided that your vertex normals are of unit length (and they should be), the differences are very small. Certainly not enough to cause the bright/dark lines the OP is experiencing.

The OP's normal generation code is bjorked enough that I can't tell at a glance if it is correct or not, but I *can* tell that he is missing the final normalise - and this is likely the cause of the issue.

I also don't see much point in calculating vertex normals by summing triangle normals, given that your triangle normals are already created by central differencing - you might as well just use central differencing at each vertex.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Well I tried some things. I'm pretty convinced that this does not have to do with normal calculation.

I got better results when I (and this was before I read your new suggestions) changed the triangle pattern, changed normal calculation to look at the nearest 24 vertices, and used a higher resolution heightmap.

Now the seam effect on some flat slopes is gone, and it is mostly apparent at light-dark boundaries.

Essentially by making the triangle pattern bigger, any lighting errors are less noticeable because they do not repeat so frequently.

terrain

Top left: I first tried calculating normals by doing
n= a x b + b x c + c x d + d x e + e x f + f x g + g x h + h x a

Top right: Then I decided to take into account more vertices. I took the previous normal and added:
n += (a x b) * RATIO + (b x c) * RATIO ... and then normalized it.

Doing all this made surprisingly little difference. So I changed the pattern to the one seen at the bottom of the image. This helped a lot, but it still seems fuzzy at light-dark boundaries (because of a lack of triangles, I now realize)

Here's an example of what it looks like now. I think it's better, it just looks kinda fuzzy:
better

So with LOD you need to detect the curvature of of the terrain and use more triangles for greater curvature and less for less. right?
Since I already have a normal field for the map, calculating the curvature at any point isn't that difficult.

Maybe I can start with 2 tris per poly for low curvature, and if the curvature is higher then start splitting the tris in half (using some maths to predict new vertices.

I feel like that would be a pretty good solution, though I can already see issues with implementation... I haven't done any reading over LOD terrain, but this is how it works right?
Looks a helluva lot better now you've tesselated it better ;)

Whilst you're in the mood to dick about, why not try my hexagonal suggestion? If you add half a unit sideways to every second row of vertex positions you should be mostly there.

This is meant to look fuzzy - that just proves there are no sharp edges, which there shouldn't be. Adding a bit more ambient will make it look more natural.

------------------------------Great Little War Game

This topic is closed to new replies.

Advertisement