给定(x0,y0) (x1,y1)两个点,画出一条直线。 首先通过交换x,y轴使得变化较快的那个轴变为x轴,再交换x0,x1使得x1永远大于x0。令dx=x1-x0;dy=y1-y0;
从(x0,y0)开始,x轴每增加一个像素,因为y轴变化较慢所以增加的值dy/dx小于1, 把增加的值累积起来用变量error记录,每当error>0.5就令y的值增加1,同时error的值减去1. 但是这样做的缺点是需要使用0.5和dy/dx两个浮点数,所以直接两者同时乘以2dx。
void line(int x0, int y0,int x1, int y1, TGAImage &image,TGAColor color){
bool steep=false;
if(abs(y1-y0)>abs(x1-x0)){
steep=true;
std::swap(x0,y0);
std::swap(x1,y1);
}
if(x1dx){
y+=dy>0?1:-1;
error-=2*dx;
}
if(steep){
image.set(y,x, color);
}else{
image.set(x,y,color);
}
}
}
-
模型导入
.obj文件的每一行要么是点,要么表示一个面v 0.296 -0.070 0.953 v -0.319 -0.065 0.946
f 1193/1240/1193 1180/1227/1180 1179/1226/1179
第一行表示一个点,使用归一化坐标,第二行表示一由第1193,1180和1179个点组成的面。
思路,求出三角形的bbox后,需要判断其中的每一个点是否在三角形内。 这里用到了重心坐标的概念,即如果P在三角形ABC内,则存在三个分别放在ABC点上的质量,使得P成为三角形的重心。
即P点满足
u
P
A
→
+
v
P
B
→
+
(
1
−
u
−
v
)
P
C
→
=
0
u overrightarrow{P A}+v overrightarrow{P B}+(1-u-v) overrightarrow{PC}=0
uPA
+vPB
+(1−u−v)PC
=0
现在给定P点需要求出u,v,(1-u-v),如果其中一个值小于0则说明P不在三角形内。
整理得
{
[
u
v
1
]
[
A
B
→
x
A
C
→
x
P
A
→
x
]
=
0
[
u
v
1
]
[
A
B
→
y
A
C
→
y
P
A
→
y
]
=
0
left{begin{array}{l} {left[begin{array}{lll} u & v & 1 end{array}right]} & {left[begin{array}{c} overrightarrow{A B}_{x} \ overrightarrow{A C}_{x} \ overrightarrow{P A}_{x} end{array}right]=0} \ {left[begin{array}{lll} u & v & 1 end{array}right]} & {left[begin{array}{c} overrightarrow{A B}_{y} \ overrightarrow{A C}_{y} \ overrightarrow{P A}_{y} end{array}right]=0} end{array}right.
⎩⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎧[uv1][uv1]⎣⎢⎢⎡AB
xAC
xPA
x⎦⎥⎥⎤=0⎣⎢⎢⎡AB
yAC
yPA
y⎦⎥⎥⎤=0
可以看出(u,v,1)和两个向量(ABx,ACx,PAx) and (ABy,ACy,PAy)正交,所以可以先求出两个向量的叉乘,然后除以z轴的值,得到(u,v,1),注意这里叉乘结果的z值可能为0,这种情况可以直接归为P不在三角形内。
Vec3f barycentric(Vec2i* pts,Vec2i P){
Vec3i v1(pts[1].x-pts[0].x,pts[2].x-pts[0].x,pts[0].x-P.x);
Vec3i v2(pts[1].y-pts[0].y,pts[2].y-pts[0].y,pts[0].y-P.y);
auto u=v1.cross(v2);
if(u.z==0){
return Vec3f(-1,1,1);
}else{
Vec3f v=u;
return Vec3f(v.x/v.z,v.y/v.z,1.0f-(v.x+v.y)/v.z);
}
}
void triangle(Vec2i* pts, TGAImage& image,TGAColor color){
Vec2i boxmax(0,0);
Vec2i boxmin(image.get_width()-1,image.get_height()-1);
for(int i=0;i<3;i++){
for(int j=0;j<2;j++){
if(pts[i].raw[j]>boxmax.raw[j]){
boxmax.raw[j]=pts[i].raw[j];
}
if(pts[i].raw[j]=0 && b.y>=0&& b.z>=0){
image.set(x,y,color);
}
}
}
}
Zbuffer
上面的实现的triangle没有考虑深度的问题,即使三角形A的z值比三角形B大,但是渲染时还是可能被B给覆盖,解决方法是利用一个zbuffer[image_width*imag_height],来缓存渲染过程中,三角形上每个点的z值,这样渲染结束后,zbuffer中每一个z值都是所有对应点中最大的。 三角形上某点z值的计算过程是以该点的重心坐标为权重,乘以三角形三个顶点的z坐标。
void triangle(Vec3i* pts, float* z_buffer, TGAImage& image,TGAColor color){
Vec2i boxmax(0,0);
Vec2i boxmin(image.get_width()-1,image.get_height()-1);
for(int i=0;i<3;i++){
for(int j=0;j<2;j++){
if(pts[i].raw[j]>boxmax.raw[j]){
boxmax.raw[j]=pts[i].raw[j];
}
if(pts[i].raw[j]=0 && b.y>=0&& b.z>=0){
float z=0;
for(int i=0;i<3;i++){
z+=b.raw[i]*pts[i].z;
}
int index=P.y*image.get_width()+P.x;
if(z_buffer[index] 


