www.xbdev.net
xbdev - software development
Sunday May 11, 2025
Home | Contact | Support | The Maths of 3D You can't have 3D without a little maths...
>>
     
 

The Maths of 3D

You can't have 3D without a little maths...

 

Rasterization : Gouraud Solid Triangle

by bkenwright@xbdev.net



This is the most popular method of rendering triangles in the world of 3D....99% of all triangle rendering is done using Gouraud shading, so I think we should have a looksy....and believe me it looks cool :)


Gouraud Solid Triangle.
Gouraud Solid Triangle.


So how does it work? Well its very similar to how you would render a sold triangle using interpolation of the coordinates....but as well as interpolating the x and y....we also interpolate the red, green and blue. Now looking at the code is only going to scare you unless you understand the basics of how to interpolate from one point to the next, so if you've not gone over the previous tutorial on rendering a solid triangle I would recomend doing it first...the code is more or less identical...but this one has loads of colour interpolation stuff added in.

DownloadSourceCode

Test out the demo program, it should work without any problems - as you move the cursor around your triangle will resize. Again error checking has been cut down to almost nothing... but it seems okay and has no serious problems. Hopefully the underlying principles of how it works can be seen in this code.

Code Sample


void Draw_Gouraud_Triangle(unsigned intpBitsint wint hint pitch,
                        
int x0int y0float r0float g0float b0,
                              
int x1int y1float r1float g1float b1,
                              
int x2int y2float r2float g2float b2)
{
      
// Sort our points into order of y
      // 0 top
      // 2 middle
      // 1 bottom
      
if( y1 y0 )
      {
            
SWAP(y1y0);
            
SWAP(x1x0);
            
SWAP(r1r0);  SWAP(g1g0);  SWAP(b1b0);
      }
      if( 
y2 y0 )
      {
            
SWAP(y2y0);
            
SWAP(x2x0);
            
SWAP(r2r0);  SWAP(g2g0);  SWAP(b2b0);
      }
      if( 
y1 y2 )
      {
            
SWAP(y2y1);
            
SWAP(x2x1);
            
SWAP(r2r1);  SWAP(g2g1);  SWAP(b2b1);
      }
 
 
      
float xl_edge = (float)x0;  // left edge
      
float xr_edge = (float)x0;  // right edge
 
 
      
float dxldy;
      
float dxrdy;
 
 
      
float dxdy1 = (float)(x2-x0)/(y2-y0);
      
float dxdy2 = (float)(x1-x0)/(y1-y0);
 
      
float dr1 = (float)(r2-r0)/(y2-y0);
      
float dg1 = (float)(g2-g0)/(y2-y0);
      
float db1 = (float)(b2-b0)/(y2-y0);
 
      
float dr2 = (float)(r1-r0)/(y1-y0);
      
float dg2 = (float)(g1-g0)/(y1-y0);
      
float db2 = (float)(b1-b0)/(y1-y0);
 
      
float drldydgldydbldy;
      
float drrdydgrdydbrdy;
 
      if( 
dxdy1 dxdy2 )
      {
            
dxldy dxdy1;
            
dxrdy dxdy2;
 
            
drldy  dr1;     dgldy  dg1;     dbldy  db1// left  (r,g,b)
            
drrdy  dr2;     dgrdy  dg2;     dbrdy  db2// right (r,g,b)
 
      
}
      else
      {
            
dxldy dxdy2;
            
dxrdy dxdy1;
 
            
drldy  dr2;     dgldy  dg2;     dbldy  db2// left  (r,g,b)
            
drrdy  dr1;     dgrdy  dg1;     dbrdy  db1// right (r,g,b)
      
}
 
      
float r_left  r0;
      
float r_right r0;
      
float g_left  g0;
      
float g_right g0;
      
float b_left  b0;
      
float b_right b0;
 
      
// Top of the triangle
      
for(int y=y0y<y2y++)
      {
 
            
float dr = (r_right r_left)/(xr_edge xl_edge);
            
float dg = (g_right g_left)/(xr_edge xl_edge);
            
float db = (b_right b_left)/(xr_edge xl_edge);
 
            
float pr r_left;
            
float pg g_left;
            
float pb b_left;
 
            for(
int x=xl_edgex<xr_edgex++)
            {
                  
pr pr dr;
                  
pg pg dg;
                  
pb pb db;
 
                  
setpixel(pBitswhpitch,
                             
xyTO_RGB(pr,pg,pb) );
            }
//end for loop x
 
            
xl_edge xl_edge dxldy;
            
xr_edge xr_edge dxrdy;
 
            
r_left  += drldy;
            
r_right += drrdy;
            
g_left  += dgldy;
            
g_right += dgrdy;
            
b_left  += dbldy;
            
b_right += dbrdy;
 
      }
// end for loop y
 
 
      // Bottom half of the triangle
 
      
if( dxdy1 dxdy2 )
      {
            
dxldy = (float)(x2-x1)/(y2-y1);
 
            
drldy  = (r2-r1)/(y2-y1);
            
dgldy  = (g2-g1)/(y2-y1);
            
dbldy  = (b2-b1)/(y2-y1);
      }
      else
      {
            
dxrdy = (float)(x2-x1)/(y2-y1);
 
            
drrdy  = (r2-r1)/(y2-y1);
            
dgrdy  = (g2-g1)/(y2-y1);
            
dbrdy  = (b2-b1)/(y2-y1);
      }
 
      for(
int y=y2y<y1y++)
      {
 
            
float dr = (r_right r_left)/(xr_edge xl_edge);
            
float dg = (g_right g_left)/(xr_edge xl_edge);
            
float db = (b_right b_left)/(xr_edge xl_edge);
 
            
float pr r_left;
            
float pg g_left;
            
float pb b_left;
 
            for(
int x=xl_edgex<xr_edgex++)
            {
                  
pr pr dr;
                  
pg pg dg;
                  
pb pb db;
 
                  
setpixel(pBitswhpitch,
                             
xyTO_RGB(pr,pg,pb) );
            }
//end for loop x
 
            
xl_edge xl_edge dxldy;
            
xr_edge xr_edge dxrdy;
 
            
r_left  += drldy;
            
r_right += drrdy;
            
g_left  += dgldy;
            
g_right += dgrdy;
            
b_left  += dbldy;
            
b_right += dbrdy;
 
      }
// end for loop y
 
      /*
      // Debug Info - puts the y pos into the window title
      extern HWND hWnd;
      char buf[200];
      sprintf(buf, "y0: %d, y2: %d, y1:%d", y0, y2, y1);
      SetWindowText(hWnd, buf);
      */
}// End of Draw_Gouraud_Triangle(..)
 
// This is where we call our Triangle Draw function from, and is responsible for capturing the mouse
// and altering one of the triangles coordinates values.
void Render(unsigned intpBitsint wint hint pitch)
{
      
// Clear our screen to black
      
cls(pBitswhpitch);
 
      
int x0=100,  y0=100;
      static 
int x1=90;
      static 
int y1=100;
      
int x2=260,  y2=350;
 
      
extern HWND hWnd;
      
POINT mousePos;
      
GetCursorPos(&mousePos);
      
ScreenToClient(hWnd, &mousePos);   
 
      
SetCursor(LoadCursor (NULLIDC_CROSS)) ;
 
      
// This just makes sure our cursor is inside our window.
      
if( (mousePos.h) && (mousePos.x>0) && (mousePos.x<w) && (mousePos.y>0) )
      {
            
char buf[200];
            
sprintf(buf"x: %d, y: %d"mousePos.xmousePos.y);
            
//SetWindowText(hWnd, buf);
 
            
x1 mousePos.x;
            
y1 mousePos.y;
      }
// End of if mousePos...etc
 
      
Draw_Gouraud_Triangle(pBitswhpitch,
                        
x0y00xff0x000x00// x,y (point) - red, greed blue (colour)
                              
x1y10x000xff0x00,
                              
x2y20x000x000xff );
 
}
// end of Render(..)


It looks like a lot, and it is... the best thing to do, is to break it up! Don't let that code intimidate you....as I had to do, I printed it out and spent a while with a pencil doodling some thinking and testing it out on the pc. But once you understand how it works....the only other thing worth doing is? is? what is it? Well its optimisation.... all of those float to int conversions taking place might need a bit of fixing.... which is what we'll do next.


Things to Try


• Using Fixed Point Math For Optimisation





















 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2025 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.