Lighting artifacts

Started by
11 comments, last by widmowyfox 8 years, 2 months ago

Hello, i found problem with lighting in my terrain generator. Where is the problem? Any ideas?

Where is the problem?
Where is the problem?

24xo37b.jpg

Advertisement

Looks like you haven't calculated smoothed normals for your terrain. The normal at each vertex should be an average of all faces/triangles to which it's connected. To me it looks like the lighting is being done according to the triangle normal.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

But i calculated normals:


float3 tangent = normalize(float3(2.0f*gWorldCellSpace, rightY - leftY, 0.0f));
	float3 bitan   = normalize(float3(0.0f, bottomY - topY, -2.0f*gWorldCellSpace)); 
	float3 normalW = cross(tangent, bitan);

And its probably ok.

That code looks like it's calculating the normal of the triangle as I said above. If you have the same normal used across the entire triangle then your terrain is going to appear as a series of connected triangles rather than a smooth surface.

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

ok, so tesselation should help ? or how can i improve normals? I detect something strange, problem exist only in high terrains like moutains ;/

Normals calculate:


float2 leftTex   = pin.Tex + float2(-gTexelCellSpaceU, 0.0f);
	float2 rightTex  = pin.Tex + float2(gTexelCellSpaceU, 0.0f);
	float2 bottomTex = pin.Tex + float2(0.0f, gTexelCellSpaceV);
	float2 topTex    = pin.Tex + float2(0.0f, -gTexelCellSpaceV);
	
	float leftY   = gHeightMap.SampleLevel( samHeightmap, leftTex, 0 ).r;
	float rightY  = gHeightMap.SampleLevel( samHeightmap, rightTex, 0 ).r;
	float bottomY = gHeightMap.SampleLevel( samHeightmap, bottomTex, 0 ).r;
	float topY    = gHeightMap.SampleLevel( samHeightmap, topTex, 0 ).r;
	
	float3 tangent = normalize(float3(2.0f*gWorldCellSpace, rightY - leftY, 0.0f));
	float3 bitan   = normalize(float3(0.0f, bottomY - topY, -2.0f*gWorldCellSpace)); 
	float3 normalW = cross(tangent, bitan);

Where is this code located? It looks like you're calculating the normal for a single cell. If you're calculating that per cell, then passing that normal to all 4 vertices in the cell, then the entire cell will appear flat (like you're screenshot). As Adam pointed out, you need to calculate the average normal between cells, for for every vertex you need to average it with the neighboring cells.


11--12--13--14
| A | B | C |
21--22--23--24
| D | E | F |
31--32--33--34
| G | H | I |
41--33--43--44

I assume that currently you have each cell contain it's own 4 vertices (so A has 11/12/21/22), and each vertex has the normal of that cell. To smooth out the normals 22 needs to average the normals from cells A/B/D/E since it's shared with all 4 cells. Even if you separate all cells into their own 4 unique vertices, to smooth out the normals you still need to treat them all as shared by the neighbors.

ok, but why this problem exist only in hight terrain generation? If it is as you say it should be everywhere. Can you give me a sample how i should solve it?

Howe to solve it really depends on the answer to my previous question. Where are you using the code that you posted? Is it in a geometry shader where you're generating the vertex for each cell, is it in a vertex shader, is it in a pixel shader, other? The simple answer is exactly what we've said, you need to smooth out your normals per vertex by averaging them with all neighboring cells.


Vertex22.normal = (FaceA.normal + FaceB.normal + FaceD.normal + FaceE.Normal) / 4

Why you only see it on height terrain generation also requires an answer. If you're generating the cells one at a time, 4 vertex per cell, all in a geometry shader, then each cell will be independent and have it's own flat normal. The code you posted, regardless of how/where you're using it, only generates a single normal for an entire cell, that means that entire cell will only face in a single direction and won't be smooth.

Check your index buffer for vertex index order. It looks like the two triangles, do not form correct face, thus causing the wrong values to be interpolated from vertex to pixel shader. Why I think this is a case i,s because even your UVs appear to be off.

Is it possible that when the area is folded it reduces the number of triangles to the area and this causes high accuracy normal ?

Below complete shader that explain where is previously written code.


#include "LightHelper.fx"
 
cbuffer cbPerFrame
{
	DirectionalLight gDirLights[3];
	float3 gEyePosW;

	float  gFogStart;
	float  gFogRange;
	float4 gFogColor;
	
	
	float gMinDist;
	float gMaxDist;

	
	float gMinTess;
	float gMaxTess;
	
	float gTexelCellSpaceU;
	float gTexelCellSpaceV;
	float gWorldCellSpace;
	float2 gTexScale = 500.0f;
	
	float4 gWorldFrustumPlanes[6];
};

cbuffer cbPerObject
{
	
	
	float4x4 gViewProj;
	Material gMaterial;
	float4x4 gShadowTransform; 

};


Texture2DArray gLayerMapArray;
Texture2D gBlendMap;
Texture2D gHeightMap;
Texture2D gShadowMap;

SamplerState samLinear
{
	Filter = MIN_MAG_MIP_LINEAR;

	AddressU = WRAP;
	AddressV = WRAP;
};

SamplerState samHeightmap
{
	Filter = MIN_MAG_LINEAR_MIP_POINT;

	AddressU = CLAMP;
	AddressV = CLAMP;
};

SamplerComparisonState samShadow
{
	Filter   = COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
	AddressU = BORDER;
	AddressV = BORDER;
	AddressW = BORDER;
	BorderColor = float4(0.0f, 0.0f, 0.0f, 0.0f);

    ComparisonFunc = LESS;
};


struct VertexIn
{
	float3 PosL     : POSITION;
	float2 Tex      : TEXCOORD0;
	float2 BoundsY  : TEXCOORD1;
};

struct VertexOut
{
	float3 PosW     : POSITION;
	float2 Tex      : TEXCOORD0;
	float2 BoundsY  : TEXCOORD1;
	

};

VertexOut VS(VertexIn vin)
{
	VertexOut vout;
	
	
	vout.PosW = vin.PosL;

	
	vout.PosW.y = gHeightMap.SampleLevel( samHeightmap, vin.Tex, 0 ).r;

	
	vout.Tex      = vin.Tex;
	vout.BoundsY  = vin.BoundsY;


	return vout;
}
 
float CalcTessFactor(float3 p)
{
	float d = distance(p, gEyePosW);

	
	//float d = max( abs(p.x-gEyePosW.x), abs(p.z-gEyePosW.z) );
	
	float s = saturate( (d - gMinDist) / (gMaxDist - gMinDist) );
	
	return pow(2, (lerp(gMaxTess, gMinTess, s)) );
}


bool AabbBehindPlaneTest(float3 center, float3 extents, float4 plane)
{
	float3 n = abs(plane.xyz);
	
	
	float r = dot(extents, n);
	
	
	float s = dot( float4(center, 1.0f), plane );
	
	
	return (s + r) < 0.0f;
}


bool AabbOutsideFrustumTest(float3 center, float3 extents, float4 frustumPlanes[6])
{
	for(int i = 0; i < 6; ++i)
	{
		
		if( AabbBehindPlaneTest(center, extents, frustumPlanes[i]) )
		{
			return true;
		}
	}
	
	return false;
}

struct PatchTess
{
	float EdgeTess[4]   : SV_TessFactor;
	float InsideTess[2] : SV_InsideTessFactor;
};

PatchTess ConstantHS(InputPatch<VertexOut, 4> patch, uint patchID : SV_PrimitiveID)
{
	PatchTess pt;
	
	
	
	float minY = patch[0].BoundsY.x;
	float maxY = patch[0].BoundsY.y;
	
	
	float3 vMin = float3(patch[2].PosW.x, minY, patch[2].PosW.z);
	float3 vMax = float3(patch[1].PosW.x, maxY, patch[1].PosW.z);
	
	float3 boxCenter  = 0.5f*(vMin + vMax);
	float3 boxExtents = 0.5f*(vMax - vMin);
	if( 1==0 )
	{
		pt.EdgeTess[0] = 0.0f;
		pt.EdgeTess[1] = 0.0f;
		pt.EdgeTess[2] = 0.0f;
		pt.EdgeTess[3] = 0.0f;
		
		pt.InsideTess[0] = 0.0f;
		pt.InsideTess[1] = 0.0f;
		
		return pt;
	}
	
	else 
	{
		
		
		
		float3 e0 = 0.5f*(patch[0].PosW + patch[2].PosW);
		float3 e1 = 0.5f*(patch[0].PosW + patch[1].PosW);
		float3 e2 = 0.5f*(patch[1].PosW + patch[3].PosW);
		float3 e3 = 0.5f*(patch[2].PosW + patch[3].PosW);
		float3  c = 0.25f*(patch[0].PosW + patch[1].PosW + patch[2].PosW + patch[3].PosW);
		
		pt.EdgeTess[0] = CalcTessFactor(e0);
		pt.EdgeTess[1] = CalcTessFactor(e1);
		pt.EdgeTess[2] = CalcTessFactor(e2);
		pt.EdgeTess[3] = CalcTessFactor(e3);
		
		pt.InsideTess[0] = CalcTessFactor(c);
		pt.InsideTess[1] = pt.InsideTess[0];
	
		return pt;
	}
}

struct HullOut
{
	float3 PosW     : POSITION;
	float2 Tex      : TEXCOORD0;


};

[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("ConstantHS")]
[maxtessfactor(64.0f)]
HullOut HS(InputPatch<VertexOut, 4> p, 
           uint i : SV_OutputControlPointID,
           uint patchId : SV_PrimitiveID)
{
	HullOut hout;
	
	// Shader "przelotowy".
	hout.PosW     = p[i].PosW;
	hout.Tex      = p[i].Tex;


	return hout;
}

struct DomainOut
{
	float4 PosH     : SV_POSITION;
    float3 PosW     : POSITION;
	float2 Tex      : TEXCOORD0;
	float2 TiledTex : TEXCOORD1;
	float4 ShadowPosH : TEXCOORD2;



};


[domain("quad")]
DomainOut DS(PatchTess patchTess, 
             float2 uv : SV_DomainLocation, 
             const OutputPatch<HullOut, 4> quad)
{
	DomainOut dout;
	
	
	dout.PosW = lerp(
		lerp(quad[0].PosW, quad[1].PosW, uv.x),
		lerp(quad[2].PosW, quad[3].PosW, uv.x),
		uv.y); 
	
	dout.Tex = lerp(
		lerp(quad[0].Tex, quad[1].Tex, uv.x),
		lerp(quad[2].Tex, quad[3].Tex, uv.x),
		uv.y); 
		
	
	dout.TiledTex = dout.Tex*gTexScale; 
	
	
	dout.PosW.y = gHeightMap.SampleLevel( samHeightmap, dout.Tex, 0 ).r;
	
	
	dout.PosH    = mul(float4(dout.PosW, 1.0f), gViewProj);
	
	dout.ShadowPosH = mul(float4(dout.PosW, 1.0f), gShadowTransform);

	
	return dout;
}

float4 PS(DomainOut pin, 
          uniform int gLightCount, 
		  uniform bool gFogEnabled) : SV_Target
{
	
	float2 leftTex   = pin.Tex + float2(-gTexelCellSpaceU, 0.0f);
	float2 rightTex  = pin.Tex + float2(gTexelCellSpaceU, 0.0f);
	float2 bottomTex = pin.Tex + float2(0.0f, gTexelCellSpaceV);
	float2 topTex    = pin.Tex + float2(0.0f, -gTexelCellSpaceV);
	
	float leftY   = gHeightMap.SampleLevel( samHeightmap, leftTex, 0 ).r;
	float rightY  = gHeightMap.SampleLevel( samHeightmap, rightTex, 0 ).r;
	float bottomY = gHeightMap.SampleLevel( samHeightmap, bottomTex, 0 ).r;
	float topY    = gHeightMap.SampleLevel( samHeightmap, topTex, 0 ).r;
	
	float3 tangent = normalize(float3(2.0f*gWorldCellSpace, rightY - leftY, 0.0f));
	float3 bitan   = normalize(float3(0.0f, bottomY - topY, -2.0f*gWorldCellSpace)); 
	float3 normalW = cross(tangent, bitan);


	
	float3 toEye = gEyePosW - pin.PosW;

	
	float distToEye = length(toEye);

	
	toEye /= distToEye;

	
	
	
	float4 c0 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 0.0f) );
	float4 c1 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 1.0f) );
	float4 c2 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 2.0f) );
	float4 c3 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 3.0f) );
	float4 c4 = gLayerMapArray.Sample( samLinear, float3(pin.TiledTex, 4.0f) ); 
	
	
	float4 t  = gBlendMap.Sample( samLinear, pin.Tex ); 
    
    
    float4 texColor = c0;
    texColor = lerp(texColor, c1, t.r);
    texColor = lerp(texColor, c2, t.g);
    texColor = lerp(texColor, c3, t.b);
    texColor = lerp(texColor, c4, t.a);
 
	

	float4 litColor = texColor;
	if( gLightCount > 0  )
	{  
		// Rozpocznij sumowanie od zera. 
		float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);
		float4 spec    = float4(0.0f, 0.0f, 0.0f, 0.0f);

		float3 shadow = float3(1.0f, 1.0f, 1.0f);
		shadow[0] = CalcShadowFactor(samShadow, gShadowMap, pin.ShadowPosH);

		
		[unroll]
		for(int i = 0; i < 1; ++i)
		{
			float4 A, D, S;
			ComputeDirectionalLight(gMaterial, gDirLights[i], normalW, toEye, 
				A, D, S);

			ambient += A;
			diffuse += shadow[i]*D;
			spec    += shadow[i]*S;

		}

		litColor = texColor*(ambient + diffuse) + spec;
	}
 
	

	if( gFogEnabled )
	{
		float fogLerp = saturate( (distToEye - gFogStart) / gFogRange ); 

		// Zmieszaj kolor mg?y i o?wietlony kolor.
		litColor = lerp(litColor, gFogColor, fogLerp);
	}

    return litColor;
}

This topic is closed to new replies.

Advertisement