Re: [Algorithms] Terrain Normals
Brought to you by:
vexxed72
|
From: Peter D. <pd...@mm...> - 2000-08-15 10:05:39
|
> Also, could you briefly explain the reasoning of the normal-generating
> algorithm you suggested as follows? A high-level, general idea is fine.
:-)
Brief explanation:
The surface partial derivatives are approximated with central differences;
the normal is computed by normalizing the cross product of the tangential
vectors.
More elaborate explanation:
Assume that the height field represents a regularly sampled smooth surface y
= F(x, z). To compute the tangential vectors to this surface we need to
approximate the partial derivatives dF/dx and dF/dz. I have chosen to use
central differences
dF/dx ~= ( F(x+a) - F(x-a) ) / (2 a)
instead of the more widely used forward differences
dF/dx ~= ( F(x+a) - F(x) ) / a
because the central difference is exact for degree 2 polynomials
(ax^2+bx+c), while the forward/backward version is exact for linear
functions (ax+b), and heightfields (including bumpmaps) are much better
approximated by the former.
So, we compute
nx = height(x-1, z) - height(x+1, z)
nz = height(x, z-1) - height(x, z+1)
(note that the signs are reversed)
and
dF/dx ~= - nx / 2a
dF/dz ~= - nz / 2a
where a is the distance in world units between two adjacent vertices of the
grid, i.e. the grid spacing.
The tangential vectors are
vx = (1, - nx / 2a, 0)
vz = (0, - nz / 2a, 1)
and their cross product is
v = (nx / 2a, 1, nz / 2a).
The only thing left is to normalize v. Note that to avoid the two divisions
we may compute v' = 2av = (nx, 2a, nz) instead, because it's going to be
normalized anyway.
Hope this helps.
--
Peter Dimov
Multi Media Ltd.
(code left for reference)
> > void VertexNormal(
> > Vector3& normal,
> > long col,
> > long row,
> > float gridSpacing)
> > {
> > float nx, nz, denom;
> >
> > if (col > 0 && col < m_fieldSize - 1)
> > {
> > nx = GetElev(col - 1, row) - (col + 1, row);
> > }
> > else if (col > 0)
> > {
> > nx = 2.0f * (GetElev(col - 1, row) - GetElev(col, row));
> > }
> > else nx = 2.0f * (GetElev(col, row) - GetElev(col + 1, row));
> >
> > if (row > 0 && row < m_fieldSize - 1)
> > {
> > nz = GetElev(col, row - 1) - GetElev(col, row + 1);
> > }
> > else if (row > 0)
> > {
> > nz = 2.0f * (GetElev(col, row - 1) - GetElev(col, row));
> > }
> > else nz = 2.0f * (GetElev(col, row) - GetElev(col, row + 1));
> >
> > gridSpacing *= 2.0f;
> >
> > denom = 1.0f / sqrt(nx * nx + gridSpacing * gridSpacing + nz * nz);
> >
> > normal.x = nx * denom;
> > normal.y = gridSpacing * denom;
> > normal.z = nz * denom;
> > }
|