Archived

This topic is now archived and is closed to further replies.

Roaders

Gourad shading problem

Recommended Posts

Roaders    122
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

Share this post


Link to post
Share on other sites
Roaders    122

  
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]

Share this post


Link to post
Share on other sites
Alimonster    185
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.

Share this post


Link to post
Share on other sites
felonius    122
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.

Share this post


Link to post
Share on other sites
Roaders    122
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.

Share this post


Link to post
Share on other sites
felonius    122
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.

Share this post


Link to post
Share on other sites
felonius    122
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)

Share this post


Link to post
Share on other sites
Roaders    122
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

Share this post


Link to post
Share on other sites
felonius    122
>> is this what you meant by clipping or have I got the wrong end of the stick?

No this is not what I meant.

The issue here is clipping. Lets say you a screen that goes from (0,0) to (100,100) and you want to draw a triangle: (90,20,10) (130,50,110), (90, 80, 10). This extends out of the screen.

At y=100 this triangle would have a z-value about 35 (according to my head math).

In your code you simply change the triangle to (90,20,10) (100,50,110), (90, 80, 10), i.e. you clamp the x-value to fit inside the screen. Now a y=100 the z value is 110. You have in other words changed the value of z of those parts of the triangle within the screen. And even worse the triangle has another shape - it should be drawn as a quad when a corner is cut of.

The easy way to get around this problem is to apply scissoring (I think this is what the hardware does). To do this, change your code for PutPixel so it only draws to screen if the given coordinates are within screen coordinates (a simple check). You can optionally speed this up since there is a space coherency in what pixels need to be scissored and which do not. And then remove all your clamping of corners - the corners of each triangle should not be changed.

Do you follow me?

Share this post


Link to post
Share on other sites
Roaders    122
Yes I do. There is a problem with triangles not being drawn if they are clipped at the top of the screen. I haven''t really looked into it yet but it sounds like this could be what''s causing it. Thanks for letting me know.


Giles

Share this post


Link to post
Share on other sites
EgonOlsen    145
The upper and the left edge of the screen are usually the parts where you have to take care of Z, color and u/v-information when clipping (as long as you don't scanconvert your polygons right-to-left...:-)). So if you are having problems at the top or the left, faulty clipping of any of these may be your problem. I haven't had a look into your code...sorry, but it's simply too much..:-) Do you still use z for interpolation? I would still say that 1/z would be much better, but you ignored this in the "other thread" too :-).




[edited by - EgonOlsen on June 7, 2002 7:18:50 PM]

Share this post


Link to post
Share on other sites
Roaders    122
I didn''t mean to ignore it. The thing is that perspective is working fine at the moment so I see no reason to change it. If you can tell me more about the reason why I should be using 1/z then I''ll use that instead.

Thanks for the suggestion anyway.

Giles

Share this post


Link to post
Share on other sites
EgonOlsen    145
Because z is not linear in screenspace, but 1/z is. And because you are linear interpolating, 1/z is the better choice IMHO. If you are using z, you''ll get errors (like you get when you are doing affine texture mapping). These errors are most visible on large polygons. Try how it looks in your engine if two large polygons intersect when you use z for interpolation. The errors should be noticable. If they are not...well, then be happy..until they are...:-)

Share this post


Link to post
Share on other sites