Terrain lighting seams?

Started by
12 comments, last by Carandiru 10 years, 7 months ago

I'm drawing my terrain in chunks and there is a seam between the chunks, even though i'm including neighbour chunk vertices when calculating edge normals. I was thinking maybe it's because my normals are being interpolated when sampling the chunk normal map in the pixel shader?

2vt.png

"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Advertisement

non-seamless normal map perhaps? or pehaps some out-of-phase type error ? notice how it looks like the shadows would line up better if they were shifted a bit along the seam.

it would seem to me that all types of maps (texture, normal, specular, bump, bakelite, etc) would need to be "seamless" at chunk edges. if all laid out together they'd make one huge seamless map. then everything should match up, whether you're drawing things straight out or sampling across seams.

i use chunks that are 300x300 in size. chunks are generated on the fly as needed. the ground mesh for a chunk is generated from a procedurally heightmapped 10x10 quad, one mesh per ground texture used (currently 4 tiles). but all normals are pointing straight up. i adjust the y values of the 4 corners of a quad, translate it to its proper location in the chunk, and add it to the ground mesh vb and ib. but i dont recompute the normals. i suppose i ought to do that eh?

[sharedmedia=gallery:images:4018]

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


I was thinking maybe it's because my normals are being interpolated when sampling the chunk normal map in the pixel shader?
Sounds reasonable. If you're using REPEAT texture access you're set up for surprises. Real thing is, you don't assemble chunks to make a big terrain. We slice big terrains into chunks instead. All bounduary samples must use inter-chunk fetch. Seamless chunks hide the problem at an extreme authoring cost and limitation, don't do that.

Previously "Krohm"

Is this seam there if you just go by normals, no normal maps?

What do your texture coordinates look like?

(Your normal map isn't in a texture atlas, is it?)

If you are using bezier patches, make sure you are joining your patches with at least C1 continuity.


I was thinking maybe it's because my normals are being interpolated when sampling the chunk normal map in the pixel shader?
Sounds reasonable. If you're using REPEAT texture access you're set up for surprises. Real thing is, you don't assemble chunks to make a big terrain. We slice big terrains into chunks instead. All bounduary samples must use inter-chunk fetch. Seamless chunks hide the problem at an extreme authoring cost and limitation, don't do that.

I already use neighbour chunk vertices for border cases if that's what you mean

Is this seam there if you just go by normals, no normal maps?

What do your texture coordinates look like?

(Your normal map isn't in a texture atlas, is it?)

Yes, i just tested and the normals look fine when passed with the vertex. I guess it is a problem with the texture sampling... Any idea how to fix it? Or rather, why not just pass the normals instead of using normal maps? Does bump mapping even work when the texture resolution is the same as the vertex resolution?

And no i'm not using an atlas; a separate texture per chunk

If you are using bezier patches, make sure you are joining your patches with at least C1 continuity.

I don't even know what that means tongue.png

"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "
Does bump mapping even work when the texture resolution is the same as the vertex resolution?

Normal mapping has nothing at all to do with how many vertices there are. Normal mapping is a per-pixel operation, the best normal map resolution is the one that most closely matches how many pixels the model takes up on the screen. If your screen size is 1024x1024 and your model takes up 1/4 of the screen, then you should use a normal map that is 512x512. If the normal map is heavily blurred then you can use a smaller resolution with out much noticeable decrease in apparent detail.

The idea of normal mapping is to fill in the space between vertices, with the illusion of detail. This makes a low-poly model look like a high-poly model. This illusion breaks down at the silhouette of a model and also at steep angles.

If you use very small textures then those textures will be sampled up in size for models that take up a lot of screen space, this will result in blocky artifacts.

To clear up those seam edges you will likely have to make the textures seamless.

I Googled -> how to make seamless textures and chose a couple to get you started.

http://www.obsidiandawn.com/creating-seamless-textures-in-photoshop-tutorial

http://www.vickiwenderlich.com/2011/06/tutorial-how-to-create-a-seamless-texture-in-gimp/

Also, I suppose I should also mention. The PhotoShop tutorial and the GIMP tutorial are using two very different methods to achieve seamless-ness.

The GIMP method is more of an optical illusion created by a plugin. However, the method used on the PhotoShop page will work in GIMP as well, both have the offset function. It's just that GIMP also has a quick and dirty plugin trick that may work for many circumstances.

Consider it pure joy, my brothers and sisters, whenever you face trials of many kinds, 3 because you know that the testing of your faith produces perseverance. 4 Let perseverance finish its work so that you may be mature and complete, not lacking anything.

I'm having a similar problem. Wish I knew how to fix it. I figured my over coordinates were off but they are correct. The chunks are also perfectly aligned. Nonetheless there is a visible seam on the x and zaxis where chunks meet. They also have exactly the same amount of vertices no lod here just bruteforce 64x64 terrain quads. I've basically moved onto other things can't figure it out even tried skirts didn't help sigh

I'm having a similar problem. Wish I knew how to fix it. I figured my over coordinates were off but they are correct. The chunks are also perfectly aligned. Nonetheless there is a visible seam on the x and zaxis where chunks meet. They also have exactly the same amount of vertices no lod here just bruteforce 64x64 terrain quads. I've basically moved onto other things can't figure it out even tried skirts didn't help sigh

Are you using neighbour vertices to calculate the edge normals? Because if you are, if you send the normals with the vertex the problem goes away. Even if you don't and choose to sample them from a texture, the seams are not noticeable in the slightest when you texture the terrain.

"Spending your life waiting for the messiah to come save the world is like waiting around for the straight piece to come in Tetris...even if it comes, by that time you've accumulated a mountain of shit so high that you're fucked no matter what you do. "

Well I had a crack at using neighbour patches to calculate the edge normals and here is my result:

scs-20130816.png

Which is worse than before with no neighbour patch sampling:

scs-20130816-2.png

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 );
}

This topic is closed to new replies.

Advertisement