Well I had a crack at using neighbour patches to calculate the edge normals and here is my result:
Which is worse than before with no neighbour patch sampling:
I purposely made it flat to debug the terrain. I also disabled normal map sampling and parallax mapping in the shader to make sure its not that.
I'm positing the code here.
GenerateTerrainPatch
bool const cTerrainRender::GenerateTerrainPatch( cTerrainPatch* const& pSourcePatch,
LPTERRAINRENDERACTIVEPATCH const& pActivePatch )
{
__m128 fRegOne, fRegTwo, fRegXZScale, fRegHeightMapRadius, fRegUVScale;
D3DX_ALIGN16 unsigned int vertexiDx[4];
D3DX_ALIGN16 float fXCoord[4],
fZCoord,
fUCoord[4],
fVCoord;
XMFLOAT3A const vStartNormal(0.0f, 1.0f, 0.0f);
D3DX_ALIGN16 float fX(0.0f), fZ(0.0);
D3DX_ALIGN16 unsigned int vertexiDz(0);
// Resolve Unique Vertex Arrays //
D3DX_ALIGN16 float const* const pHeightMap( pSourcePatch->getHeightMapRaw() );
D3DX_ALIGN16 TERRAINVERTEX* const pTerrainSrcPatchBuffer(pActivePatch->getUniqueVertexArray());
fRegHeightMapRadius = XMM128::LoadAll( m_fLocalSpaceHeightMapRadius );
fRegXZScale = XMM128::LoadAll( m_fLocalSpaceXZScale );
fRegUVScale = XMM128::LoadAll( m_fLocalSpaceUVScale );
// Build Terrain Patch from Heightmap Data //
for ( unsigned int iDz = 0; iDz < m_uiNumVerticesXZ ; ++iDz, fZ += 1.0f )
{
fX = 0.0f;
vertexiDz = ( iDz * m_uiNumVerticesXZ );
fRegOne = XMM128::LoadOne( fZ );
fRegTwo = _mm_mul_ss( fRegOne, fRegUVScale );
XMM128::StoreOne( &fVCoord, fRegTwo );
fRegOne = _mm_mul_ss( fRegOne, fRegXZScale );
fRegOne = _mm_sub_ss( fRegOne, fRegHeightMapRadius );
XMM128::StoreOne( &fZCoord, fRegOne );
for ( unsigned int iDx = 0; iDx < m_uiNumVerticesXZ ; iDx += 4, fX += 4.0f )
{
// to map the 2D array coordaintes into a 1D array
vertexiDx[0] = vertexiDz + iDx;
vertexiDx[1] = vertexiDz + iDx + 1;
vertexiDx[2] = vertexiDz + iDx + 2;
vertexiDx[3] = vertexiDz + iDx + 3;
fRegOne = XMM128::Load( fX, fX + 1.0f, fX + 2.0f, fX + 3.0f);
fRegTwo = _mm_mul_ps( fRegOne, fRegUVScale );
XMM128::Store( fUCoord, fRegTwo );
fRegOne = _mm_mul_ps( fRegOne, fRegXZScale );
fRegOne = _mm_sub_ps( fRegOne, fRegHeightMapRadius );
XMM128::Store( fXCoord, fRegOne );
pTerrainSrcPatchBuffer[vertexiDx[0]].p = XMFLOAT3A(fXCoord[0], pHeightMap[vertexiDx[0]], fZCoord);
pTerrainSrcPatchBuffer[vertexiDx[1]].p = XMFLOAT3A(fXCoord[1], pHeightMap[vertexiDx[1]], fZCoord);
pTerrainSrcPatchBuffer[vertexiDx[2]].p = XMFLOAT3A(fXCoord[2], pHeightMap[vertexiDx[2]], fZCoord);
pTerrainSrcPatchBuffer[vertexiDx[3]].p = XMFLOAT3A(fXCoord[3], pHeightMap[vertexiDx[3]], fZCoord);
// Temporary Tangent & Binormal & Normal //
//pTerrainSrcPatchBuffer[vertexiDx[0]].t = pTerrainSrcPatchBuffer[vertexiDx[0]].b = pTerrainSrcPatchBuffer[vertexiDx[0]].n = vStartNormal;
//pTerrainSrcPatchBuffer[vertexiDx[1]].t = pTerrainSrcPatchBuffer[vertexiDx[1]].b = pTerrainSrcPatchBuffer[vertexiDx[1]].n = vStartNormal;
//pTerrainSrcPatchBuffer[vertexiDx[2]].t = pTerrainSrcPatchBuffer[vertexiDx[2]].b = pTerrainSrcPatchBuffer[vertexiDx[2]].n = vStartNormal;
//pTerrainSrcPatchBuffer[vertexiDx[3]].t = pTerrainSrcPatchBuffer[vertexiDx[3]].b = pTerrainSrcPatchBuffer[vertexiDx[3]].n = vStartNormal;
// Set the u,v values so one texture covers the entire terrain
pTerrainSrcPatchBuffer[vertexiDx[0]].tu = fUCoord[0];
pTerrainSrcPatchBuffer[vertexiDx[1]].tu = fUCoord[1];
pTerrainSrcPatchBuffer[vertexiDx[2]].tu = fUCoord[2];
pTerrainSrcPatchBuffer[vertexiDx[3]].tu = fUCoord[3];
pTerrainSrcPatchBuffer[vertexiDx[0]].tv = fVCoord;
pTerrainSrcPatchBuffer[vertexiDx[1]].tv = fVCoord;
pTerrainSrcPatchBuffer[vertexiDx[2]].tv = fVCoord;
pTerrainSrcPatchBuffer[vertexiDx[3]].tv = fVCoord;
}
}
// Remap Vertices for Vertex Cache Optimization //
// Copy the vertex data into a temp buffer.
TERRAINVERTEX *OldVertices = new TERRAINVERTEX[ m_uiNumVertices ];
memcpy( OldVertices, pTerrainSrcPatchBuffer, sizeof(TERRAINVERTEX) * m_uiNumVertices );
// Perform the remapping
const DWORD *CurrentRemap = m_pVertexCacheIndexRemap;
const TERRAINVERTEX *OldVertex = OldVertices;
for( DWORD i = 0; i < m_uiNumVertices; ++i )
{
pTerrainSrcPatchBuffer[ *( CurrentRemap++ ) ] = *( OldVertex++ );
}
delete[] OldVertices;
return(true);
}
GenerateTangentBinormalNormal
bool const cTerrainRender::GenerateTangentBinormalNormal( TERRAINVERTEX* const& pTerrainSrcPatchBuffer,
float const* const pHeightMap,
ZoneSphere const* const* const pAdjacentZoneSpheres ) const
{
D3DX_ALIGN16 unsigned int vertexiDx(0),
iDx(0), iDz(0),
iDzMin(0),
iDzMax(0),
iDxMin(0),
iDxMax(0);
D3DX_ALIGN16 DWORD const* const CurrentRemap(m_pVertexCacheIndexRemap);
/*D3DX_ALIGN16 cTerrainPatch const* pTerrainPatchAdj(NULL);
D3DX_ALIGN16 float const* pHeightMapAdj(NULL);
D3DX_ALIGN16 TERRAINVERTEX const* pTerrainSrcPatchBufferAdj(NULL);
// Check to see if we have adjacent terrain patches we need //
if ( !pAdjacentZoneSpheres[ZONE_LEFT] || !pAdjacentZoneSpheres[ZONE_LEFT]->getTerrainPatch()->getOwnedActivePatch() )
return(false);
if ( !pAdjacentZoneSpheres[ZONE_RIGHT] || !pAdjacentZoneSpheres[ZONE_RIGHT]->getTerrainPatch()->getOwnedActivePatch() )
return(false);
if ( !pAdjacentZoneSpheres[ZONE_TOP] || !pAdjacentZoneSpheres[ZONE_TOP]->getTerrainPatch()->getOwnedActivePatch() )
return(false);
if ( !pAdjacentZoneSpheres[ZONE_BOTTOM] || !pAdjacentZoneSpheres[ZONE_BOTTOM]->getTerrainPatch()->getOwnedActivePatch() )
return(false);
// Now we have todo the edge vertices on z and axis' by sampling adjacent terrain patches that have already
// been generated. They have already been generated as where this function is called from //
// Top of this patch, bottom of adjacent patch
pTerrainPatchAdj = pAdjacentZoneSpheres[ZONE_BOTTOM]->getTerrainPatch();
CalculateNormalsTOP(m_uiNumVerticesXZ-1,
pTerrainSrcPatchBuffer, pTerrainPatchAdj->getOwnedActivePatch()->getUniqueVertexArray(),
pHeightMap, pTerrainPatchAdj->getHeightMapRaw(),
CurrentRemap);
// Bottom of this patch, top of adjacent patch
pTerrainPatchAdj = pAdjacentZoneSpheres[ZONE_TOP]->getTerrainPatch();
CalculateNormalsBOTTOM(0,
pTerrainSrcPatchBuffer, pTerrainPatchAdj->getOwnedActivePatch()->getUniqueVertexArray(),
pHeightMap, pTerrainPatchAdj->getHeightMapRaw(),
CurrentRemap);
// Left of this patch, right of adjacent patch
pTerrainPatchAdj = pAdjacentZoneSpheres[ZONE_RIGHT]->getTerrainPatch();
CalculateNormalsLEFT(m_uiNumVerticesXZ-1,
pTerrainSrcPatchBuffer, pTerrainPatchAdj->getOwnedActivePatch()->getUniqueVertexArray(),
pHeightMap, pTerrainPatchAdj->getHeightMapRaw(),
CurrentRemap);
// Right of this patch, left of adjacent patch
pTerrainPatchAdj = pAdjacentZoneSpheres[ZONE_LEFT]->getTerrainPatch();
CalculateNormalsRIGHT(0,
pTerrainSrcPatchBuffer, pTerrainPatchAdj->getOwnedActivePatch()->getUniqueVertexArray(),
pHeightMap, pTerrainPatchAdj->getHeightMapRaw(),
CurrentRemap);*/
// ZONE_NUM_ADJACENT
// Now the rest of the inside of the patch //
tbb::parallel_for( tbb::blocked_range<unsigned int>(0, m_uiNumVerticesXZ),
[=](const tbb::blocked_range<unsigned int>& r) {
for( unsigned int iDz = r.begin(); iDz != r.end(); ++iDz )
{
unsigned int vertexiDx(0),
iDzMin(0),
iDzMax(0),
iDxMin(0),
iDxMax(0);
iDzMin = iDz > 0 ? iDz-1 : iDz; iDzMax = iDz < m_uiNumVerticesXZ - 1 ? iDz+1 : iDz;
// iDz -1 : iDz +1
//iDzMin = iDz-1;
//iDzMax = iDz+1;
for ( unsigned int iDx = 0 ; iDx < m_uiNumVerticesXZ ; ++iDx )
{
iDxMin = iDx > 0 ? iDx-1 : iDx; iDxMax = iDx < m_uiNumVerticesXZ - 1 ? iDx+1 : iDx;
// iDx -1 : iDx +1
//iDxMin = iDx-1;
//iDxMax = iDx+1;
// to map the 2D array coordaintes into a 1D array
vertexiDx = CurrentRemap[( iDz * m_uiNumVerticesXZ ) + iDx];
CalculateNormal(iDx, iDz, iDxMin, iDxMax, iDzMin, iDzMax, vertexiDx,
pTerrainSrcPatchBuffer, pHeightMap, CurrentRemap);
}
}
});
return(true);
}
CalculateNormal
void cTerrainRender::CalculateNormal( unsigned int const& iDx, unsigned int const& iDz,
unsigned int const& iDxMin, unsigned int const& iDxMax,
unsigned int const& iDzMin, unsigned int const& iDzMax,
unsigned int const& vertexiDx,
TERRAINVERTEX* const& pTerrainSrcPatchBuffer,
float const* const& pHeightMap,
DWORD const* const& CurrentRemap ) const
{
XMVECTOR xmX, xmZ, xmN;
D3DX_ALIGN16 float tl,
l,
bl,
t,
b,
tr,
r,
br,
dX, dY;
D3DX_ALIGN16 float const normalStrength(4.0f);
tl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMin ];
l = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDx ];
bl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMax ];
t = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMin ];
b = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMax ];
tr = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMin ];
r = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDx ];
br = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMax ];
// Compute dx using Sobel:
// -1 0 1
// -2 0 2
// -1 0 1
dX = tr + 2*r + br -tl - 2*l - bl;
// Compute dy using Sobel:
// -1 -2 -1
// 0 0 0
// 1 2 1
dY = bl + 2*b + br -tl - 2*t - tr;
// Build the DETAIL normal
xmN = XMLoadFloat3A( &XMFLOAT3A(dX, normalStrength, dY) );
// Use the adjoing vertices along both axis to compute the correct normal
//
// Tangent Vector : t = (1, 0, heightmap[x+1, y] - heightmap[x-1, y])
// Binormal Vector : ß = (0, 1, heightmap[x, y+1] - heightmap[x, y-1])
xmX = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMax] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMin] ].p) );
xmZ = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMax * m_uiNumVerticesXZ) + iDx] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMin * m_uiNumVerticesXZ) + iDx] ].p) );
// Average DETAIL normal from sobel calculation of heightmap with normal computed from vertices
xmN = XMVectorAdd(XMVector3Normalize(xmN), XMVector3Normalize(XMVector3Cross(xmZ, xmX)));
xmN = XMVectorMultiply(xmN, XMM128::LoadAll(0.5f));
xmN = XMVector3Normalize( xmN );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].n, xmN);
// Assign Correct Tangent //
xmX = XMVector3Normalize(xmX);
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].t, xmX);
// Calculate BiNormal From CrossProduct of Tangent and the Final Normal //
xmZ = XMVector3Normalize( XMVector3Cross(xmN, xmX) );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].b, xmZ);
}
CalculateNormalsTop
void cTerrainRender::CalculateNormalsTOP( unsigned int const iDzADJ,
TERRAINVERTEX* const& pTerrainSrcPatchBuffer, TERRAINVERTEX const* const& pTerrainSrcPatchBufferAdj,
float const* const& pHeightMap, float const* const& pHeightMapAdj,
DWORD const* const& CurrentRemap ) const
{
XMVECTOR xmX, xmZ, xmN;
D3DX_ALIGN16 float tl,
l,
bl,
t,
b,
tr,
r,
br,
dX, dY;
D3DX_ALIGN16 float const normalStrength(4.0f);
// iDz -1 : iDz +1
// must sample adjacent iDzMin = iDz-1;
D3DX_ALIGN16 unsigned int const iDz(0),
iDzMax(iDz+1);
D3DX_ALIGN16 unsigned int vertexiDx(0),
iDxMin(0), iDxMax(0);
for ( unsigned int iDx = 1 ; iDx < m_uiNumVerticesXZ - 1 ; ++iDx ) // except sides on xaxis & corners
{
// to map the 2D array coordaintes into a 1D array
vertexiDx = CurrentRemap[( iDz * m_uiNumVerticesXZ ) + iDx];
// iDx -1 : iDx +1
iDxMin = iDx-1;
iDxMax = iDx+1;
tl = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDxMin ];
l = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDx ];
bl = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDxMax ];
t = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMin ];
b = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMax ];
tr = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMin ];
r = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDx ];
br = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMax ];
// Compute dx using Sobel:
// -1 0 1
// -2 0 2
// -1 0 1
dX = tr + 2*r + br -tl - 2*l - bl;
// Compute dy using Sobel:
// -1 -2 -1
// 0 0 0
// 1 2 1
dY = bl + 2*b + br -tl - 2*t - tr;
// Build the DETAIL normal
xmN = XMLoadFloat3A( &XMFLOAT3A(dX, normalStrength, dY) );
// Use the adjoing vertices along both axis to compute the correct normal
//
// Tangent Vector : t = (1, 0, heightmap[x+1, y] - heightmap[x-1, y])
// Binormal Vector : ß = (0, 1, heightmap[x, y+1] - heightmap[x, y-1])
xmX = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMax] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMin] ].p) );
xmZ = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMax * m_uiNumVerticesXZ) + iDx] ].p),
XMLoadFloat3(&pTerrainSrcPatchBufferAdj[ CurrentRemap[( iDzADJ * m_uiNumVerticesXZ) + iDx] ].p) );
// Average DETAIL normal from sobel calculation of heightmap with normal computed from vertices
xmN = XMVectorAdd(XMVector3Normalize(xmN), XMVector3Normalize(XMVector3Cross(xmZ, xmX)));
xmN = XMVectorMultiply(xmN, XMM128::LoadAll(0.5f));
xmN = XMVector3Normalize( xmN );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].n, xmN);
// Assign Correct Tangent //
xmX = XMVector3Normalize(xmX);
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].t, xmX);
// Calculate BiNormal From CrossProduct of Tangent and the Final Normal //
xmZ = XMVector3Normalize( XMVector3Cross(xmN, xmX) );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].b, xmZ);
}
}
CalculateNormalsBOTTOM
void cTerrainRender::CalculateNormalsBOTTOM( unsigned int const iDzADJ,
TERRAINVERTEX* const& pTerrainSrcPatchBuffer, TERRAINVERTEX const* const& pTerrainSrcPatchBufferAdj,
float const* const& pHeightMap, float const* const& pHeightMapAdj,
DWORD const* const& CurrentRemap ) const
{
XMVECTOR xmX, xmZ, xmN;
D3DX_ALIGN16 float tl,
l,
bl,
t,
b,
tr,
r,
br,
dX, dY;
D3DX_ALIGN16 float const normalStrength(4.0f);
// iDz -1 : iDz +1
// must sample adjacent iDzMax = iDz+1;
D3DX_ALIGN16 unsigned int const iDz(m_uiNumVerticesXZ - 1),
iDzMin(iDz-1);
D3DX_ALIGN16 unsigned int vertexiDx(0),
iDxMin(0), iDxMax(0);
for ( unsigned int iDx = 1 ; iDx < m_uiNumVerticesXZ - 1 ; ++iDx ) // except sides on xaxis & corners
{
// to map the 2D array coordaintes into a 1D array
vertexiDx = CurrentRemap[( iDz * m_uiNumVerticesXZ ) + iDx];
// iDx -1 : iDx +1
iDxMin = iDx-1;
iDxMax = iDx+1;
tl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMin ];
l = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDx ];
bl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMax ];
t = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMin ];
b = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMax ];
tr = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDxMin ];
r = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDx ];
br = pHeightMapAdj[ ( iDzADJ * m_uiNumVerticesXZ) + iDxMax ];
// Compute dx using Sobel:
// -1 0 1
// -2 0 2
// -1 0 1
dX = tr + 2*r + br -tl - 2*l - bl;
// Compute dy using Sobel:
// -1 -2 -1
// 0 0 0
// 1 2 1
dY = bl + 2*b + br -tl - 2*t - tr;
// Build the DETAIL normal
xmN = XMLoadFloat3A( &XMFLOAT3A(dX, normalStrength, dY) );
// Use the adjoing vertices along both axis to compute the correct normal
//
// Tangent Vector : t = (1, 0, heightmap[x+1, y] - heightmap[x-1, y])
// Binormal Vector : ß = (0, 1, heightmap[x, y+1] - heightmap[x, y-1])
xmX = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMax] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMin] ].p) );
xmZ = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBufferAdj[ CurrentRemap[( iDzADJ * m_uiNumVerticesXZ) + iDx] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMin * m_uiNumVerticesXZ) + iDx] ].p) );
// Average DETAIL normal from sobel calculation of heightmap with normal computed from vertices
xmN = XMVectorAdd(XMVector3Normalize(xmN), XMVector3Normalize(XMVector3Cross(xmZ, xmX)));
xmN = XMVectorMultiply(xmN, XMM128::LoadAll(0.5f));
xmN = XMVector3Normalize( xmN );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].n, xmN);
// Assign Correct Tangent //
xmX = XMVector3Normalize(xmX);
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].t, xmX);
// Calculate BiNormal From CrossProduct of Tangent and the Final Normal //
xmZ = XMVector3Normalize( XMVector3Cross(xmN, xmX) );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].b, xmZ);
}
}
CalculateNormalsLEFT
void cTerrainRender::CalculateNormalsLEFT( unsigned int const iDxADJ,
TERRAINVERTEX* const& pTerrainSrcPatchBuffer, TERRAINVERTEX const* const& pTerrainSrcPatchBufferAdj,
float const* const& pHeightMap, float const* const& pHeightMapAdj,
DWORD const* const& CurrentRemap ) const
{
XMVECTOR xmX, xmZ, xmN;
D3DX_ALIGN16 float tl,
l,
bl,
t,
b,
tr,
r,
br,
dX, dY;
D3DX_ALIGN16 float const normalStrength(4.0f);
// iDx -1 : iDx +1
// must sample adjacent iDxMin = iDx-1;
D3DX_ALIGN16 unsigned int const iDx(0),
iDxMax(iDx+1);
D3DX_ALIGN16 unsigned int vertexiDx(0),
iDzMin(0), iDzMax(0);
for ( unsigned int iDz = 1 ; iDz < m_uiNumVerticesXZ - 1 ; ++iDz ) // except sides on zaxis & corners
{
// to map the 2D array coordaintes into a 1D array
vertexiDx = CurrentRemap[( iDz * m_uiNumVerticesXZ ) + iDx];
// iDx -1 : iDx +1
iDzMin = iDz-1;
iDzMax = iDz+1;
tl = pHeightMapAdj[ ( iDzMin * m_uiNumVerticesXZ) + iDxADJ ];
l = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDx ];
bl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMax ];
t = pHeightMapAdj[ ( iDz * m_uiNumVerticesXZ) + iDxADJ ];
b = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMax ];
tr = pHeightMapAdj[ ( iDzMax * m_uiNumVerticesXZ) + iDxADJ ];
r = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDx ];
br = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMax ];
// Compute dx using Sobel:
// -1 0 1
// -2 0 2
// -1 0 1
dX = tr + 2*r + br -tl - 2*l - bl;
// Compute dy using Sobel:
// -1 -2 -1
// 0 0 0
// 1 2 1
dY = bl + 2*b + br -tl - 2*t - tr;
// Build the DETAIL normal
xmN = XMLoadFloat3A( &XMFLOAT3A(dX, normalStrength, dY) );
// Use the adjoing vertices along both axis to compute the correct normal
//
// Tangent Vector : t = (1, 0, heightmap[x+1, y] - heightmap[x-1, y])
// Binormal Vector : ß = (0, 1, heightmap[x, y+1] - heightmap[x, y-1])
xmX = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMax] ].p),
XMLoadFloat3(&pTerrainSrcPatchBufferAdj[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxADJ] ].p) );
xmZ = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMax * m_uiNumVerticesXZ) + iDx] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMin * m_uiNumVerticesXZ) + iDx] ].p) );
// Average DETAIL normal from sobel calculation of heightmap with normal computed from vertices
xmN = XMVectorAdd(XMVector3Normalize(xmN), XMVector3Normalize(XMVector3Cross(xmZ, xmX)));
xmN = XMVectorMultiply(xmN, XMM128::LoadAll(0.5f));
xmN = XMVector3Normalize( xmN );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].n, xmN);
// Assign Correct Tangent //
xmX = XMVector3Normalize(xmX);
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].t, xmX);
// Calculate BiNormal From CrossProduct of Tangent and the Final Normal //
xmZ = XMVector3Normalize( XMVector3Cross(xmN, xmX) );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].b, xmZ);
}
}
CalculateNormalsRIGHT
void cTerrainRender::CalculateNormalsRIGHT( unsigned int const iDxADJ,
TERRAINVERTEX* const& pTerrainSrcPatchBuffer, TERRAINVERTEX const* const& pTerrainSrcPatchBufferAdj,
float const* const& pHeightMap, float const* const& pHeightMapAdj,
DWORD const* const& CurrentRemap ) const
{
XMVECTOR xmX, xmZ, xmN;
D3DX_ALIGN16 float tl,
l,
bl,
t,
b,
tr,
r,
br,
dX, dY;
D3DX_ALIGN16 float const normalStrength(4.0f);
// iDx -1 : iDx +1
// must sample adjacent iDxMax = iDx+1;
D3DX_ALIGN16 unsigned int const iDx(m_uiNumVerticesXZ - 1),
iDxMin(iDx-1);
D3DX_ALIGN16 unsigned int vertexiDx(0),
iDzMin(0), iDzMax(0);
for ( unsigned int iDz = 1 ; iDz < m_uiNumVerticesXZ - 1 ; ++iDz ) // except sides on zaxis & corners
{
// to map the 2D array coordaintes into a 1D array
vertexiDx = CurrentRemap[( iDz * m_uiNumVerticesXZ ) + iDx];
// iDx -1 : iDx +1
iDzMin = iDz-1;
iDzMax = iDz+1;
tl = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDxMin ];
l = pHeightMap[ ( iDzMin * m_uiNumVerticesXZ) + iDx ];
bl = pHeightMapAdj[ ( iDzMin * m_uiNumVerticesXZ) + iDxADJ ];
t = pHeightMap[ ( iDz * m_uiNumVerticesXZ) + iDxMin ];
b = pHeightMapAdj[ ( iDz * m_uiNumVerticesXZ) + iDxADJ ];
tr = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDxMin ];
r = pHeightMap[ ( iDzMax * m_uiNumVerticesXZ) + iDx ];
br = pHeightMapAdj[ ( iDzMax * m_uiNumVerticesXZ) + iDxADJ ];
// Compute dx using Sobel:
// -1 0 1
// -2 0 2
// -1 0 1
dX = tr + 2*r + br -tl - 2*l - bl;
// Compute dy using Sobel:
// -1 -2 -1
// 0 0 0
// 1 2 1
dY = bl + 2*b + br -tl - 2*t - tr;
// Build the DETAIL normal
xmN = XMLoadFloat3A( &XMFLOAT3A(dX, normalStrength, dY) );
// Use the adjoing vertices along both axis to compute the correct normal
//
// Tangent Vector : t = (1, 0, heightmap[x+1, y] - heightmap[x-1, y])
// Binormal Vector : ß = (0, 1, heightmap[x, y+1] - heightmap[x, y-1])
xmX = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBufferAdj[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxADJ] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDz * m_uiNumVerticesXZ) + iDxMin] ].p) );
xmZ = XMVectorSubtract( XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMax * m_uiNumVerticesXZ) + iDx] ].p),
XMLoadFloat3(&pTerrainSrcPatchBuffer[ CurrentRemap[( iDzMin * m_uiNumVerticesXZ) + iDx] ].p) );
// Average DETAIL normal from sobel calculation of heightmap with normal computed from vertices
xmN = XMVectorAdd(XMVector3Normalize(xmN), XMVector3Normalize(XMVector3Cross(xmZ, xmX)));
xmN = XMVectorMultiply(xmN, XMM128::LoadAll(0.5f));
xmN = XMVector3Normalize( xmN );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].n, xmN);
// Assign Correct Tangent //
xmX = XMVector3Normalize(xmX);
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].t, xmX);
// Calculate BiNormal From CrossProduct of Tangent and the Final Normal //
xmZ = XMVector3Normalize( XMVector3Cross(xmN, xmX) );
XMStoreFloat3(&pTerrainSrcPatchBuffer[vertexiDx].b, xmZ);
}
}
And the shader:
VS_OUTPUT vsTerrainParallax( VS_INPUT i )
{
VS_OUTPUT o;
o.vPosition = mul(i.vPosition,uModelViewProj);
o.vTex0 = i.vTex0;
float3x3 TBN = {i.vTangent,i.vBinormal,i.vNormal};
TBN = transpose(mul(TBN,uModel));
float3 WPos = mul(i.vPosition,uModel);
// Rotate Light & View Vector Into Tangent-Space
o.vLightDir = mul(uLightVec - WPos,TBN);
o.vViewDir = mul(uViewVec - WPos,TBN);
o.vHalfAngleDir = o.vLightDir + o.vViewDir;
o.vShadowTex = mul(i.vPosition, uTexBiasModelLightViewProj);
return(o);
}
void psTerrainParallax( PS_INPUT i,
out float4 oColor0 : COLOR0 )
{
float3 vViewDirNormalized = normalize(i.vViewDir);
// Sample Height GrayScale Portion of Parallax Map and Calculate New UV Coordinates //
//float2 vNewUv = ParallaxMappedUV(tParallaxMap, i.vTex0, vViewDirNormalized.xy);
// Sample Main Diffuse Texture //
float3 vDiffuseTex = tex2D(tDiffuseMap,i.vTex0);
/*this is how you uncompress a normal map*/
//float3 vNormalMap = tex2D(tParallaxMap,vNewUv) * 2.0f - 1.0f;
float3 vNormalMap = float3(0.0f,1.0f,0.0f);//tex2D(tParallaxMap,vNewUv) * 2.0f - 1.0f;
oColor0 = LitDiffuseSpecular( vDiffuseTex, vNormalMap, normalize(i.vLightDir),
vViewDirNormalized, normalize(i.vHalfAngleDir),
i.vShadowTex );
}