Jump to content

  • Log In with Google      Sign In   
  • Create Account


Terrain lighting seams?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 Waaayoff   Members   -  Reputation: 774

Like
0Likes
Like

Posted 22 June 2013 - 03:47 AM

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. "

Sponsor:

#2 Norman Barrows   Crossbones+   -  Reputation: 1849

Like
0Likes
Like

Posted 22 June 2013 - 08:30 AM

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?

 

Drawing things the "new way"

 


Norm Barrows

Rockland Software Productions

"Building PC games since 1988"

 

rocklandsoftware.net

 


#3 Krohm   Crossbones+   -  Reputation: 2980

Like
1Likes
Like

Posted 27 June 2013 - 06:11 AM


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.

#4 phil_t   Crossbones+   -  Reputation: 3211

Like
1Likes
Like

Posted 27 June 2013 - 12:10 PM

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?)



#5 metsfan   Members   -  Reputation: 653

Like
0Likes
Like

Posted 27 June 2013 - 04:45 PM

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



#6 Waaayoff   Members   -  Reputation: 774

Like
1Likes
Like

Posted 28 June 2013 - 02:19 AM

 


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


Edited by Waaayoff, 28 June 2013 - 02:23 AM.

"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. "

#7 marcClintDion   Members   -  Reputation: 431

Like
0Likes
Like

Posted 28 June 2013 - 03:31 PM

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, because you know that the testing of your faith produces perseverance. Let perseverance finish its work so that you may be mature and complete, not lacking anything.


#8 Carandiru   Members   -  Reputation: 212

Like
0Likes
Like

Posted 15 August 2013 - 05:51 PM

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

#9 Waaayoff   Members   -  Reputation: 774

Like
0Likes
Like

Posted 16 August 2013 - 03:25 AM

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. "

#10 Carandiru   Members   -  Reputation: 212

Like
1Likes
Like

Posted 16 August 2013 - 10:20 PM

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


#11 Carandiru   Members   -  Reputation: 212

Like
0Likes
Like

Posted 16 August 2013 - 10:24 PM

Oh I should also mention all textures I use are made seamless.



#12 Waaayoff   Members   -  Reputation: 774

Like
0Likes
Like

Posted 17 August 2013 - 03:14 AM

If your terrain is flat, there shouldn't be any seams even if you aren't using neighbour patches. Are you sure you're calculating your normals correctly? (i have no experience with the sobel operator). Try visualizing the normals with the pixel or geometry shader.

 

Also, i think you're overcomplicating your calculations. The way i do it is to simply store the neighbour vertices with the patches. So if my chunk is 65x65, i store a border around it so that it's 67x67 and use the extra vertices to calculate the normals. This way you won't have to consider each case (top, left, etc..) explicitly. Here's how the final code turns out:

for (int y = 1; y <= mChunkSamples; y++)
{
	for (int x = 1; x <= mChunkSamples; x++)
	{
		float sx = pHeights[(x + 1) + mHeightSamples*y] - pHeights[(x - 1) + mHeightSamples*y];
		float sy = pHeights[x + mHeightSamples*(y+1)] - pHeights[x + mHeightSamples*(y-1)];

		nor.x = -sx * mVerticalScale;
		nor.y = 2 * pChunk->mScale;
		nor.z = -sy * mVerticalScale;
		D3DXVec3Normalize(&nor, &nor);

		// ... store
	}


Edit: I just realized that you are hardcoding your normal to (0, 1, 0). This only leaves the texture... Did you try rendering with lighting only? 


Edited by Waaayoff, 17 August 2013 - 03:25 AM.

"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. "

#13 Carandiru   Members   -  Reputation: 212

Like
0Likes
Like

Posted 19 August 2013 - 02:58 AM

Haha a border so simple. Will implement and get back to you. Also will see if I can verify the normals. I added sobel calculation as it gives a noticeable enhancement with normal mapping. It is based purely off the height map. The tangent and binomial are calculated differently with the y axis now factored in with is important for surfaces that are not flat. The normal from this final calculation is then averaged with the sobel calc to give a good enough, visually, result.

#14 Carandiru   Members   -  Reputation: 212

Like
0Likes
Like

Posted 25 August 2013 - 12:40 AM

Well I ran into some problems implementing the border vertices for sampling normals actually became quite complex. So I reverted it and now I'm back to where I was before. I'll have to tackle this bug again. I have to move onto some important features and mechanics. 


Edited by Carandiru, 25 August 2013 - 12:58 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS