您的位置:首页 > 理论基础

计算机图形学三角形基元填充算法即三角形光栅化重心双线性插值算法

2016-04-03 18:30 1486 查看
计算机图形学三角形基元填充算法即三角形光栅化重心双线性插值算法

 我们学过数学知识的人,都知道直线的隐函数:

f01(x,y)=(y0-y1)*x+(x1-x0)*y+x0*y1-x1*y0

这个函数的好处在于计算机计算时无需进行除法操作指令,我们学过汇编指令的都知道计算机在除法指令运行很多个周期才能计算出一个高度精确的结果。所以,在这里巧妙地回避了除法运算。

那么,三角形的光删化步骤如下所示:

实现三角形光栅化,寻找三顶点的包围矩形,只对该矩形内进行候选像素执行循环,寻找三角形的重心,利用重心,进行三角形光栅化。

     通过以下式子确定直线

     f01(x,y)=(y0-y1)*x+(x1-x0)*y+x0*y1-x1*y0

f12(x,y)=(y1-y2)*x+(x2-x1)*y+x1*y2-x2*y1

        f20(x,y)=(y2-y0)*x+(x0-x2)*y+x2*y0-x0*y2

                    因此重心坐标(a ,b,c),即a+b+c=1

    根据的三角形的3个顶点,分别XMIN,XMAX,YMIN,YMAX,得到了一个包含此三角形的最小的矩形区域,然后对此矩形区域的点进行逐一扫描。

此时令a=f12(x,y)/fa

             b=f20(x,y)/fb

             c=f01(x,y)/fc

同时判断条件 如果a、b、c同时都大于0,那么就把这个点绘制出来,颜色为    V=a*R+b*G+c*B,否则就不绘制,直到遍历完整个矩形区域,三角形的光栅化也就完成了。

至于光删化的代码就相当简单了,读者可以根据以上介绍的原理自己用win32编程实现,我在这里写出部分代码以作参考使用

////////////////绘制填充三角形(利用三角形重心光栅化算法实现) 2016年1月19记
// 三角形的三个顶点v1,v2,v3
void FillTriangle(const M3DVector3f v1, const M3DVector3f v2, const M3DVector3f v3, const FLOATCOLORRGBA clr1,
const FLOATCOLORRGBA clr2, const FLOATCOLORRGBA clr3, CColorBuffer* pCB, CZBuffer* pZB)
{
// 三边的斜率k[3]数组
M3DVector3f k;
// 计算直线斜率
k[0]=( (v1[1]-v2[1]) * v3[0] + (v2[0] - v1[0]) * v3[1] + v1[0] * v2[1] - v2[0] * v1[1] );
k[1]=( (v2[1]-v3[1]) * v1[0] + (v3[0] - v2[0]) * v1[1] + v2[0] * v3[1] - v3[0] * v2[1] );
k[2]=( (v3[1]-v1[1]) * v2[0] + (v1[0] - v3[0]) * v2[1] + v3[0] * v1[1] - v1[0] * v3[1] );
// 不绘制计算结果k为0的三角形 (处理不符合条件的三角形)
if ( ABS(k[0]) < FLOAT_ERROR_RANGE )return;
if ( ABS(k[1]) < FLOAT_ERROR_RANGE )return;
if ( ABS(k[2]) < FLOAT_ERROR_RANGE )return;
// 计算直线斜率的倒数
k[0] = 1 / k[0];
k[1] = 1 / k[1];
k[2] = 1 / k[2];
// 第一步找出XMAX YMAX XMIN YMIN找出最小矩形
int xMIN,xMAX,yMIN,yMAX;
xMIN = ROUND( (v1[0] < v2[0] ? v1[0] : v2[0]) < v3[0] ? (v1[0] < v2[0] ? v1[0] : v2[0]) : v3[0] );
xMAX = ROUND( (v1[0] > v2[0] ? v1[0] : v2[0]) > v3[0] ? (v1[0] > v2[0] ? v1[0] : v2[0]) : v3[0] );
yMIN = ROUND( (v1[1] < v2[1] ? v1[1] : v2[1]) < v3[1] ? (v1[1] < v2[1] ? v1[1] : v2[1]) : v3[1] );
yMAX = ROUND( (v1[1] > v2[1] ? v1[1] : v2[1]) > v3[1] ? (v1[1] > v2[1] ? v1[1] : v2[1]) : v3[1] );
// 顶点颜色临时存储变量
FLOATCOLORRGBA clrTemp;
// 存储顶点位置以及深度信息临时变量
M3DVector3f pTemp;
// zInBuffer缓冲区中的深度 zNowBuffer当前计算深度值
float zInBuffer, zNowBuffer;
// 三顶点对颜色的贡献值,a,b,c
// 在这里初始化对第一个顶点颜色的贡献值默认为三者的平均值0.33
float a = 0.333f, b = 0.333f,c = 0.333f;

// 循环变量
int i,j;
for( i = xMIN; i <= xMAX; ++i ){
for(j = yMIN; j <= yMAX; ++j ){
pTemp[0] = (float)(i);
pTemp[1] = (float)(j);
zInBuffer = pZB->GetDepth(pTemp);
zNowBuffer = a * v1[2] + b * v2[2] + c * v3[2];
if ( zNowBuffer < zInBuffer){
if(CalcWeight(i, j, v1, v2, v3, k, a, b, c)){
// 三顶点对颜色的贡献值进行融合(调色板)
clrTemp.red = a * clr1.red + b * clr2.red + c * clr3.red;
clrTemp.green = a * clr1.green + b * clr2.green + c * clr3.green;
clrTemp.blue = a * clr1.blue + b * clr2.blue + c * clr3.blue;
clrTemp.alpha = a * clr1.alpha + b * clr2.alpha + c * clr3.alpha;
// 深度值重置
pTemp[2] = zNowBuffer;
// 点亮该点
pCB->SetPixel(pTemp, clrTemp.red, clrTemp.green, clrTemp.blue, clrTemp.alpha);
// 重置该点的ZBuffer值
pZB->SetDepth(pTemp);
}
}// if ( zNowBuffer < zInBuffer)end

}// for end

}// for end

}// FillTriangle end

// 计算权重 CalculateWeight
BOOL CalcWeight(int i, int j, const M3DVector3f v1, const M3DVector3f v2, const M3DVector3f v3,
const M3DVector3f k, float &a, float &b, float &c)
{
// 计算权重
a = ((v2[1] - v3[1]) * i + (v3[0] - v2[0]) * j + v2[0] * v3[1] - v3[0] * v2[1]) * k[1];
b = ((v3[1] - v1[1]) * i + (v1[0] - v3[0]) * j + v3[0] * v1[1] -v1[0] * v3[1]) * k[2];
// 由于重心a+b+c=1 所以在这里计算c=1-a-b来优化算法
// c=((v1[1]-v2[1])*i+(v2[0]-v1[0])*j+v1[0]*v2[1]-v2[0]*v1[1])*k[0];
c= 1 - a - b;
// 判断是否在三角形内 连同边上的点进行处理
if ( a >= 0 && b >= 0 && c >= 0 ){
return TRUE;
}else{
return FALSE;
}
}

效果截图:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: