Gourad shading problem

Started by
13 comments, last by Roaders 21 years, 10 months ago
Hi guys. I am trying to gourad shade objects but I am getting light lines where two polygons meet that should not be there. Take a look here. Ithink that the colour is being calculated correctly at each vertex so I am sure that there is a problem in my triangle drawing functions. Here is the code that does it: void renderSystem::drawHLine(float x1,float x2, float z1, float z2, int y,colour c1,colour c2, bool boolZbuffer){ //adapted from code written by Simon Brown. // Check line is visible //cout << "x1: " << x1 << "\tx2: " << x2 << endl; int intX1 = x1; intX1++; float cur_z = 0; float dx_z = 0; colour cur_col; colour dx_col; if (y > -1 && y < intScreenHeight) { // Make sure left point is on screen if ( intX1 < 0 ) intX1 = 0; // Make sure right point is on screen if ( x2 > intScreenWidth - 1 ) x2 = intScreenWidth - 1; cur_z = z1; dx_z = (x1 != x2) ? (float)(z1-z2) / (x1-x2) : 0; cur_z = cur_z + (dx_z*((float)intX1 - x1)); cur_col = c1; dx_col = (x1 != x2) ? (c1-c2) / (x1-x2) : dx_col; cur_col = cur_col + (dx_col*((float)intX1 - x1)); //cout << "y: " << y << "\tdx_col: " << dx_col.fltGetBlue() << endl; // Draw the line pixel by pixel for ( int i = intX1; i <= x2; i++ ) { //cout << i << " "; if(!boolZbuffer){ putPixel(i, y, cur_col.intGetColourBits()); } else { fillBuffer(i,y,cur_z,&cur_col); } cur_z += dx_z; cur_col = cur_col + dx_col; } } } Gourad shading is renderMode 3. Can anyone see where I am going wrong? Thanks Giles
Giles Roadnightgiles.roadnight.name
Advertisement

  void renderSystem::drawTriangle (triangle drawTriangle,bool boolZbuffer,int renderMode){	//adapted from code written by Simon Brown.	vertex* vertTemp;	vertexNormal vertNormalTemp;	triangle temp = drawTriangle;	vertexNormal tempVertA = *drawTriangle.getVertNormA();	vertexNormal tempVertB = *drawTriangle.getVertNormB();	vertexNormal tempVertC = *drawTriangle.getVertNormC();	temp.setVertNormA(&tempVertA);	temp.setVertNormB(&tempVertB);	temp.setVertNormC(&tempVertC);	float intNewX;	float intNewZ;	colour colNewCol;	vertexNormal vertNormal;	long int faceColour = drawTriangle.colGetReflectedLight()->intGetColourBits();	// Sort screen co-ordinates in ascending y order	// Make sure point B is below (on screen) point A	if ( temp.getB()->dblGetY() < temp.getA()->dblGetY() )	{		vertTemp = temp.getA();		temp.setA(temp.getB());		temp.setB(vertTemp);		vertNormalTemp = *temp.getVertNormA();		*temp.getVertNormA() = *temp.getVertNormB();		*temp.getVertNormB() = vertNormalTemp;	}	// Make sure point C is below (on screen) point A	if ( temp.getC()->dblGetY() < temp.getA()->dblGetY() )	{		vertTemp = temp.getC();		temp.setC(temp.getA());		temp.setA(vertTemp);		vertNormalTemp = *temp.getVertNormC();		*temp.getVertNormC() = *temp.getVertNormA();		*temp.getVertNormA() = vertNormalTemp;	}	// Make sure point C is below (on screen) point B	if ( temp.getC()->dblGetY() < temp.getB()->dblGetY() )	{		vertTemp = temp.getC();		temp.setC(temp.getB());		temp.setB(vertTemp);		vertNormalTemp = *temp.getVertNormC();		*temp.getVertNormC() = *temp.getVertNormB();		*temp.getVertNormB() = vertNormalTemp;	}			// Calculate top height divided by total height	float ratio = float ( temp.getB()->dblGetY() - temp.getA()->dblGetY() ) 		/ float ( temp.getC()->dblGetY() - temp.getA()->dblGetY() );	// Calculate fourth x point	intNewX = ( (temp.getC()->dblGetX() - temp.getA()->dblGetX()) * ratio ) + temp.getA()->dblGetX();	intNewZ = ( (temp.getC()->dblGetZ() - temp.getA()->dblGetZ()) * ratio ) + temp.getA()->dblGetZ();	if (renderMode == 3){		colNewCol.setAll(			(temp.getVertNormC()->colGetReflectedLight()->fltGetBlue() - temp.getVertNormA()->colGetReflectedLight()->fltGetBlue()) 				* ratio + temp.getVertNormA()->colGetReflectedLight()->fltGetBlue(),			(temp.getVertNormC()->colGetReflectedLight()->fltGetGreen() - temp.getVertNormA()->colGetReflectedLight()->fltGetGreen()) 				* ratio + temp.getVertNormA()->colGetReflectedLight()->fltGetGreen(),			(temp.getVertNormC()->colGetReflectedLight()->fltGetRed() - temp.getVertNormA()->colGetReflectedLight()->fltGetRed()) 				* ratio + temp.getVertNormA()->colGetReflectedLight()->fltGetRed()				);		vertNormal.setReflectedLight(colNewCol);		vertNormalTemp = *temp.getVertNormC();		*temp.getVertNormC() = vertNormal;	}	vertex vertD(intNewX,temp.getB()->dblGetY(),intNewZ);		vertTemp = temp.getC();	temp.setC(&vertD);		if (temp.getA()->dblGetY() != temp.getC()->dblGetY()){		drawFBTriangle (temp, faceColour, boolZbuffer, renderMode);	}		temp.setA(temp.getC());	temp.setC(vertTemp);	if (renderMode == 3){		temp.getVertNormA()->setReflectedLight(*temp.getVertNormC()->colGetReflectedLight());		temp.getVertNormC()->setReflectedLight(*vertNormalTemp.colGetReflectedLight());	}	if (temp.getA()->dblGetY() != temp.getC()->dblGetY()){		drawFTTriangle (temp, faceColour, boolZbuffer, renderMode);	}}void renderSystem::drawFBTriangle (triangle drawTriangle,long int faceColour, bool boolZbuffer, int renderMode){	//adapted from code written by Simon Brown.	float dx_left        = 0;  // Change in x value of left sloping line per scaline (per y)	float dx_right       = 0;  //                      right	float dx_left_z		= 0;	float dx_right_z	= 0;	float cur_left       = 0;  // Current left end of line	float cur_right      = 0;  //         right	float cur_left_z	= 0;	float cur_right_z	= 0;	colour dx_left_col;	colour dx_right_col;	colour cur_left_col;	colour cur_right_col;	colour tmpSwapCol;	float tmpSwap;	int y_counter;	float y_start = drawTriangle.getA()->dblGetY();	float y_end = drawTriangle.getC()->dblGetY();	//cout << "y_end:\t" << drawTriangle.getC()->dblGetY() << " = " << y_end << endl << endl;	float height = drawTriangle.getB()->dblGetY() - drawTriangle.getA()->dblGetY();  // height of triangle, positive only	//if ( height < 0.5 && height > -0.5 ) return;  // do not draw a zero height triangle	dx_left  = ( drawTriangle.getC()->dblGetX() - drawTriangle.getA()->dblGetX()) / height;	dx_right = ( drawTriangle.getB()->dblGetX() - drawTriangle.getA()->dblGetX()) / height;	if(boolZbuffer){		dx_left_z  = ( drawTriangle.getC()->dblGetZ() - drawTriangle.getA()->dblGetZ()) / height;		dx_right_z = ( drawTriangle.getB()->dblGetZ() - drawTriangle.getA()->dblGetZ()) / height;	}	if(renderMode==3){		dx_left_col = (*drawTriangle.getVertNormC()->colGetReflectedLight() - *drawTriangle.getVertNormA()->colGetReflectedLight())/height;		dx_right_col = (*drawTriangle.getVertNormB()->colGetReflectedLight() - *drawTriangle.getVertNormA()->colGetReflectedLight())/height;	}	//swap values so that line is drawn the right way	if (drawTriangle.getC()->dblGetX() > drawTriangle.getB()->dblGetX()){		tmpSwap = dx_left;		dx_left = dx_right;		dx_right = tmpSwap;		if(boolZbuffer){			tmpSwap = dx_left_z;			dx_left_z = dx_right_z;			dx_right_z = tmpSwap;		}		if(renderMode==3){			tmpSwapCol = dx_left_col;			dx_left_col = dx_right_col;			dx_right_col = tmpSwapCol;		}	}			y_counter = y_start+1;	float y_remainder = y_counter-y_start;		cur_left = drawTriangle.getA()->dblGetX() + (y_remainder*dx_left);	cur_right = drawTriangle.getA()->dblGetX() + (y_remainder*dx_right);	if(boolZbuffer){		cur_left_z = drawTriangle.getA()->dblGetZ() + (y_remainder*dx_left_z);		cur_right_z = drawTriangle.getA()->dblGetZ() + (y_remainder*dx_right_z);	}	if(renderMode==3){		cur_left_col = *drawTriangle.getVertNormA()->colGetReflectedLight() + (dx_left_col*y_remainder);		cur_right_col = *drawTriangle.getVertNormA()->colGetReflectedLight() + (dx_right_col*y_remainder);	}	for (y_counter; y_counter <= y_end; y_counter++ )  // go from top to bottom	{		if ( y_start > -1 && y_start < intScreenHeight ){			if(renderMode==2){				drawHLine ( cur_left, cur_right, cur_left_z, cur_right_z, y_counter, faceColour, boolZbuffer);			} else {				drawHLine ( cur_left, cur_right, cur_left_z, cur_right_z, y_counter, cur_left_col, cur_right_col, boolZbuffer);			}		}		cur_left    += dx_left;  		cur_right   += dx_right;		if(boolZbuffer){			cur_left_z	+= dx_left_z;			cur_right_z	+= dx_right_z;		}		if(renderMode==3){			cur_left_col = cur_left_col + dx_left_col;			cur_right_col = cur_right_col + dx_right_col;		}	}}void renderSystem::drawFTTriangle (triangle drawTriangle,long int faceColour, bool boolZbuffer, int renderMode){	//adapted from code written by Simon Brown.	float dx_left        = 0;  // Change in x value of left sloping line per scaline (per y)	float dx_right       = 0;  //                      right	float dx_left_z		= 0;	float dx_right_z	= 0;	float cur_left       = 0;  // Current left end of line	float cur_right      = 0;  //         right	float cur_left_z	= 0;	float cur_right_z	= 0;	colour dx_left_col;	colour dx_right_col;	colour cur_left_col;	colour cur_right_col;	colour tmpSwapCol;	float tmpSwap;	int y_counter;	float y_start = drawTriangle.getA()->dblGetY();	float y_end = drawTriangle.getC()->dblGetY();	float left_start = drawTriangle.getA()->dblGetX();	float right_start = drawTriangle.getB()->dblGetX();	float left_start_z = drawTriangle.getA()->dblGetZ();	float right_start_z = drawTriangle.getB()->dblGetZ();	colour left_start_col = *drawTriangle.getVertNormA()->colGetReflectedLight();	colour right_start_col = *drawTriangle.getVertNormB()->colGetReflectedLight();	float height = drawTriangle.getC()->dblGetY() - drawTriangle.getA()->dblGetY();  // height of triangle, positive only	dx_left  = ( drawTriangle.getC()->dblGetX() - drawTriangle.getA()->dblGetX() ) / height;	dx_right = ( drawTriangle.getC()->dblGetX() - drawTriangle.getB()->dblGetX() ) / height;	if(boolZbuffer){		dx_left_z  = ( drawTriangle.getC()->dblGetZ() - drawTriangle.getA()->dblGetZ() ) / height;		dx_right_z = ( drawTriangle.getC()->dblGetZ() - drawTriangle.getB()->dblGetZ() ) / height;	}	if(renderMode==3){		dx_left_col = (*drawTriangle.getVertNormC()->colGetReflectedLight() - *drawTriangle.getVertNormA()->colGetReflectedLight())/height;		dx_right_col = (*drawTriangle.getVertNormC()->colGetReflectedLight() - *drawTriangle.getVertNormB()->colGetReflectedLight())/height;	}	//swap values so that line is drawn the right way	if (drawTriangle.getA()->dblGetX() > drawTriangle.getB()->dblGetX()){		tmpSwap = dx_left;		dx_left = dx_right;		dx_right = tmpSwap;		tmpSwap = left_start;		left_start = right_start;		right_start = tmpSwap;		if(boolZbuffer){			tmpSwap = dx_left_z;			dx_left_z = dx_right_z;			dx_right_z = tmpSwap;			tmpSwap = left_start_z;			left_start_z = right_start_z;			right_start_z = tmpSwap;		}		if(renderMode == 3){			tmpSwapCol = dx_left_col;			dx_left_col = dx_right_col;			dx_right_col = tmpSwapCol;			tmpSwapCol = left_start_col;			left_start_col = right_start_col;			right_start_col = tmpSwapCol;		}	}	y_counter = y_start + 1;	float y_remainder = y_counter-y_start;		cur_left = left_start + (y_remainder*dx_left);	cur_right = right_start + (y_remainder*dx_right);	if(boolZbuffer){		cur_left_z = left_start_z + (y_remainder*dx_left_z);		cur_right_z = right_start_z + (y_remainder*dx_right_z);	}	if(renderMode==3){		cur_left_col = left_start_col + (dx_left_col*y_remainder);		cur_right_col = right_start_col + (dx_right_col*y_remainder);	}	for (y_counter; y_counter <= y_end; y_counter++ )  // go from bottom to top	{				if ( y_counter > -1 && y_counter < intScreenHeight ){			if(renderMode==2){				drawHLine ( cur_left, cur_right, cur_left_z, cur_right_z, y_counter, faceColour, boolZbuffer);			} else {				drawHLine ( cur_left, cur_right, cur_left_z, cur_right_z, y_counter, cur_left_col, cur_right_col, boolZbuffer);			}		}		cur_left    += dx_left; 		cur_right   += dx_right;		if(boolZbuffer){			cur_left_z	+= dx_left_z;			cur_right_z	+= dx_right_z;		}		if(renderMode==3){			cur_left_col = cur_left_col + dx_left_col;			cur_right_col = cur_right_col + dx_right_col;		}	}}void renderSystem::drawHLine(float x1,float x2, float z1, float z2, int y,colour c1,colour c2, bool boolZbuffer){	//adapted from code written by Simon Brown.	// Check line is visible	//cout << "x1: " << x1 << "\tx2: " << x2 << endl;	int intX1 = x1;	intX1++;	float cur_z = 0;	float dx_z = 0;	colour cur_col;	colour dx_col;	if (y > -1 && y < intScreenHeight)	{			// Make sure left point is on screen		if ( intX1 < 0 )			intX1 = 0;									// Make sure right point is on screen		if ( x2 > intScreenWidth - 1 )			x2 = intScreenWidth - 1;		cur_z = z1;		dx_z = (x1 != x2) ? (float)(z1-z2) / (x1-x2) : 0;		cur_z = cur_z + (dx_z*((float)intX1 - x1));		cur_col = c1;		dx_col = (x1 != x2) ? (c1-c2) / (x1-x2) : dx_col;		cur_col = cur_col + (dx_col*((float)intX1 - x1));		// Draw the line pixel by pixel		for ( int i = intX1; i <= x2; i++ )		{			//cout << i << " ";			if(!boolZbuffer){				putPixel(i, y, cur_col.intGetColourBits());			} else {				fillBuffer(i,y,cur_z,&cur_col);			}			cur_z += dx_z;			cur_col = cur_col + dx_col;		}	}}  


[Edit: the source tag is your friend...]

[edited by - Yann L on June 6, 2002 6:49:42 PM]
Giles Roadnightgiles.roadnight.name
Not all of the code appeared in the first post so I re-posted it.
Giles Roadnightgiles.roadnight.name
It''s probably Mach Banding. This is an aberration in the way the eye interprets changes of intensity. It sees a light ''band'' at the edge that doesn''t exist. You''re pretty much stuck with this unless you implement Phong shading (which interpolates the normals so removes the sudden change).

That may not be the problem here as I haven''t peeked at your source code. For future reference, use [ source][ /source] tags (without the extra spaces after the "[") to get a white syntax highighting box.
Hi Roaders, (I feeling better)

Although I did come across a single problem in your code,

in drawHLine you write:
dx_col = (x1 != x2) ? (c1-c2) / (x1-x2) : dx_col;
I think the final dx_col should be changed to c1.

I think that Alimonster might be right. It is just Mach banding and that you can do nothing about when using Gouraud shading of polyogonal objects. If you look closely it is not bright lines but just a change in intensity and through and optical illusion this seems like lines.
Jacob Marner, M.Sc.Console Programmer, Deadline Games
Thanks felonius

I think that the line you mention is OK. If X1 != X2 it means that the line has a length and the dx_col is set by dividing by x1-x2. if x1 == x2 then x1-x2 = 0 and anything divided by 0 produces an error. In this case dx_col is set to dx_col - in other words nothing. If the line has no length then the change doesn''t really matter.

Thanks for taking a look both of you. It makes me feel better that it might not be my code and might be an optical illusion. When I put all the colours in Excel and looked at the numbers I must say that I couldn''t see any problems there.

Giles.
Giles Roadnightgiles.roadnight.name
you''re welcome.

>> In this case dx_col is set to dx_col - in other words nothing.

but dx_col is not initialized at this point, so you are setting it to a undefined value. In the rare case that x1 and x2 is the same and on integer bounderies the loop will run a single time and dx_col will be set to a undefined value (or to some constant color depending on your default constructor for colour). Instead you should set it to c1 since that is the colour you want in that rare case.
Jacob Marner, M.Sc.Console Programmer, Deadline Games
But the colour class is always set to 0 when it is created in the constructor.
Giles Roadnightgiles.roadnight.name
but you don''t want it to be zero since you in the rare case where x1 and x2 are identical and both whole numbers are drawing a pixel in your code. And that pixel should not be place but should be the color at that line.

Anyway, now that I think about it the very fact that you draw anything in the case mentioned above will cause pixel overdrawing if many such triangles are placed next to each other. What you should do is to make sure x1!=x2 before entering the calculations needed for the loop. If you do that x1-x2 will never be zero and you won''t need the check for zero there. I.e. before the line curz = z insert
if(x1!=x2)
{
rest of rendering code here
}

Also, I just noted that your clipping code is wrong. You just clamp x1Int and x2 to the screen without adjusting the z and colour values accordingly. This will give you an error in the z-values over those triangles and it will give shading errors. (But only on partially clipped triangles)
Jacob Marner, M.Sc.Console Programmer, Deadline Games
Thanks again for your comments

I have taken out the first line (dx_col = (x1 != x2) ? (c1-c2) / (x1-x2) : dx_col and replaced it with dx_col = (c1-c2) / (x1-x2); which works fine. I think that the reason I did the if was because in testing I got an error because of a 0 length line. Running the program normally this will not happen.

About the second point you make do yo mean that if the X1 value is not an integer I just cnahge it to an integer and don''t update the z values and shade? Well, I do do that but it is a little bit further down the function inside the if (y > -1 && y < intScreenHeight) bit:

cur_z = cur_z + (dx_z*((float)intX1 - x1));
cur_col = cur_col + (dx_col*((float)intX1 - x1));

is this what you meant by clipping or have I got the wrong end of the stick?

Thanks

Giles
Giles Roadnightgiles.roadnight.name

This topic is closed to new replies.

Advertisement