接下来更改一下顶点着色,让这个渲染器更漂亮
首先是数据结构
更改数据结构
typedef struct { point_t pos; color_t color; } vertex_t;
更改顶点输入
vertex_t mesh[8] = {
{ { 1, -1, 1, 1 },{ 1.0f, 0.2f, 0.2f } },
{ { -1, -1, 1, 1 },{ 0.2f, 1.0f, 0.2f } },
{ { -1, 1, 1, 1 },{ 0.2f, 0.2f, 1.0f }},
{ { 1, 1, 1, 1 },{ 1.0f, 0.2f, 1.0f } },
{ { 1, -1, -1, 1 },{ 1.0f, 1.0f, 0.2f } },
{ { -1, -1, -1, 1 },{ 0.2f, 1.0f, 1.0f } },
{ { -1, 1, -1, 1 },{ 1.0f, 0.3f, 0.3f }},
{ { 1, 1, -1, 1 },{ 0.2f, 1.0f, 0.3f } },
};
更改画三角形算法
void DrawTriangle(vertex_t ve1, vertex_t ve2, vertex_t ve3)
{
point_t p1 = ve1.pos;
point_t p2 = ve2.pos;
point_t p3 = ve3.pos;
point_t v1, v2, v3, c1, c2, c3;
// 按照 Transform 变化
transform_apply(&Transform, &c1, &p1);
transform_apply(&Transform, &c2, &p2);
transform_apply(&Transform, &c3, &p3);
// 归一化
transform_homogenize(&Transform, &v1, &c1);
transform_homogenize(&Transform, &v2, &c2);
transform_homogenize(&Transform, &v3, &c3);
if (v1.x == v2.x&&v1.x == v3.x) return;
if (v1.y == v2.y&&v1.y == v3.y) return;
//DrawLine(v1, v2,c);
//DrawLine(v2, v3,c);
//DrawLine(v3, v1,c);
vector<float> PointY{ v1.y,v2.y,v3.y };
sort(PointY.begin(), PointY.end());
float midY = PointY[1];
float minY = PointY[0];
float maxY = PointY[2];
vertex_t MaxYPoint;
vertex_t MidYPoint;
vertex_t MinYPoint;
if (midY != minY && midY != maxY)
{
if (midY == v1.y)
{
MidYPoint.pos = v1;
MidYPoint.color = ve1.color;
if (maxY == v2.y)
{
MaxYPoint.pos= v2;
MaxYPoint.color = ve2.color;
MinYPoint.pos= v3;
MinYPoint.color = ve3.color;
}
if (maxY == v3.y)
{
MaxYPoint.pos= v3;
MaxYPoint.color = ve3.color;
MinYPoint.pos= v2;
MinYPoint.color = ve2.color;
}
}
else if (midY == v2.y)
{
MidYPoint.pos = v2;
MidYPoint.color = ve2.color;
if (maxY == v1.y)
{
MaxYPoint.pos = v1;
MaxYPoint.color = ve1.color;
MinYPoint.pos = v3;
MinYPoint.color = ve3.color;
}
if (maxY == v3.y)
{
MaxYPoint.pos = v3;
MaxYPoint.color = ve3.color;
MinYPoint.pos = v1;
MinYPoint.color = ve1.color;
}
}
else if (midY == v3.y)
{
MidYPoint.pos = v3;
MidYPoint.color = ve3.color;
if (maxY == v1.y)
{
MaxYPoint.pos = v1;
MaxYPoint.color = ve1.color;
MinYPoint.pos = v2;
MinYPoint.color = ve2.color;
}
if (maxY == v2.y)
{
MaxYPoint.pos = v2;
MaxYPoint.color = ve2.color;
MinYPoint.pos = v1;
MinYPoint.color = ve1.color;
}
}
vertex_t newV;
float t = (midY - maxY) / (minY - maxY);
newV.pos.x = interp(MaxYPoint.pos.x, MinYPoint.pos.x, t);
newV.pos.y = midY;
color_interp(newV.color,MaxYPoint.color, MinYPoint.color, t);
DrawTriangle_ScanConversion(MaxYPoint, MidYPoint, newV);
DrawTriangle_ScanConversion(MinYPoint, MidYPoint, newV);
}
else
{
if (v1.y == v2.y) DrawTriangle_ScanConversion(ve3, ve1, ve2);
if (v1.y == v3.y) DrawTriangle_ScanConversion(ve2, ve1, ve3);
if (v3.y == v2.y) DrawTriangle_ScanConversion(ve1, ve3, ve2);
}
}
接下来更改画线算法,由于代码太多,就不一一贴出
加入画点函数
//画点
void DrawPoint(int x,int y,color_t c)
{
glColor4f(c.r, c.g, c.b, c.a);
//glColor4f(1, 1, 0,0.5);
glVertex2i((int)x, (int)y);
}
发现了几个问题
1.立方体的某些本应被遮挡住的面被绘制在了这个立方体其他面之上,学过opengl,Direct的应该知道些,我们还缺少z缓冲也就是深度信息,
2.如果一直按f会出现视角中物体无法改变的现象
增加视锥裁剪,裁剪不在摄像头视锥内的物体
// 检查齐次坐标同 cvv 的边界用于视锥裁剪
int transform_check_cvv(const vector_t *v) {
float w = v->w;
int check = 0;
if (v->z < 0.0f) check |= 1;
if (v->z > w) check |= 2;
if (v->x < -w) check |= 4;
if (v->x > w) check |= 8;
if (v->y < -w) check |= 16;
if (v->y > w) check |= 32;
return check;
}
void DrawTriangle(vertex_t ve1, vertex_t ve2, vertex_t ve3)
{
point_t p1 = ve1.pos;
point_t p2 = ve2.pos;
point_t p3 = ve3.pos;
point_t v1, v2, v3, c1, c2, c3;
// 按照 Transform 变化
transform_apply(&Transform, &c1, &p1);
transform_apply(&Transform, &c2, &p2);
transform_apply(&Transform, &c3, &p3);
// 裁剪,注意此处可以完善为具体判断几个点在 cvv内以及同cvv相交平面的坐标比例
// 进行进一步精细裁剪,将一个分解为几个完全处在 cvv内的三角形
if (transform_check_cvv(&c1) != 0) return;
if (transform_check_cvv(&c2) != 0) return;
if (transform_check_cvv(&c3) != 0) return;
// 归一化
transform_homogenize(&Transform, &v1, &c1);
transform_homogenize(&Transform, &v2, &c2);
transform_homogenize(&Transform, &v3, &c3);
裁剪成功
增加深度缓存
博主选择了一种可能非常高耗的方法,就是用vector存储深度信息和颜色信息
//缓存
typedef struct { color_t c = {0 ,0,0,1 }; float depth=INT_MAX; }Buffer_t;
vector<vector<Buffer_t>> _buffer;
void BufferInit()
{
Buffer_t _b;
vector<Buffer_t> t(SCREEN_WIDTH * 2,_b);
_buffer = vector<vector<Buffer_t>>(SCREEN_HEIGHT * 2, t);
}
void BufferUpdate()
{
for (int i = 0; i <= SCREEN_HEIGHT; i++)
{
for (int j = 0; j <= SCREEN_WIDTH; j++)
{
_buffer[i][j].depth = INT_MAX;
}
}
}
缓存初始化
//<<<<<<<<<<<<<<<<<<<<<<<< main >>>>>>>>>>>>>>>>>>>>>>
void main(int argc, char **argv)
{
BufferInit();
接下来在重绘函数中加入缓存更新函数
// 重绘函数
void myDisplay(void)
{
BufferUpdate();
接下来更改画三角形的算法,需要z,w值传给画线算法以及最终的画点函数
void DrawTrigngleCH(vertex_t &M,point_t v1,point_t c1,vertex_t ve1)
{
M.pos = v1;
M.pos.z = v1.z;
M.pos.w = c1.w;
M.color = ve1.color;
}
void DrawTriangle(vertex_t ve1, vertex_t ve2, vertex_t ve3)
{
point_t p1 = ve1.pos;
point_t p2 = ve2.pos;
point_t p3 = ve3.pos;
point_t v1, v2, v3, c1, c2, c3;
// 按照 Transform 变化
transform_apply(&Transform, &c1, &p1);
transform_apply(&Transform, &c2, &p2);
transform_apply(&Transform, &c3, &p3);
// 裁剪,注意此处可以完善为具体判断几个点在 cvv内以及同cvv相交平面的坐标比例
// 进行进一步精细裁剪,将一个分解为几个完全处在 cvv内的三角形
if (transform_check_cvv(&c1) != 0) return;
if (transform_check_cvv(&c2) != 0) return;
if (transform_check_cvv(&c3) != 0) return;
// 归一化
transform_homogenize(&Transform, &v1, &c1);
transform_homogenize(&Transform, &v2, &c2);
transform_homogenize(&Transform, &v3, &c3);
if (v1.x == v2.x&&v1.x == v3.x) return;
if (v1.y == v2.y&&v1.y == v3.y) return;
//DrawLine(v1, v2,c);
//DrawLine(v2, v3,c);
//DrawLine(v3, v1,c);
vector<float> PointY{ v1.y,v2.y,v3.y };
sort(PointY.begin(), PointY.end());
float midY = PointY[1];
float minY = PointY[0];
float maxY = PointY[2];
vertex_t MaxYPoint;
vertex_t MidYPoint;
vertex_t MinYPoint;
if (midY != minY && midY != maxY)
{
if (midY == v1.y)
{
/* MidYPoint.pos = v1;
MidYPoint.pos.z = c1.z;
MidYPoint.color = ve1.color;*/
DrawTrigngleCH(MidYPoint,v1,c1, ve1);
if (maxY == v2.y)
{
DrawTrigngleCH(MaxYPoint, v2, c2, ve2);
DrawTrigngleCH(MinYPoint, v3, c3, ve3);
}
if (maxY == v3.y)
{
DrawTrigngleCH(MaxYPoint, v3, c3, ve3);
DrawTrigngleCH(MinYPoint, v2, c2, ve2);
}
}
else if (midY == v2.y)
{
DrawTrigngleCH(MidYPoint, v2, c2, ve2);
if (maxY == v1.y)
{
DrawTrigngleCH(MaxYPoint, v1, c1, ve1);
DrawTrigngleCH(MinYPoint, v3, c3, ve3);
}
if (maxY == v3.y)
{
DrawTrigngleCH(MaxYPoint, v3, c3, ve3);
DrawTrigngleCH(MinYPoint, v1, c1, ve1);
}
}
else if (midY == v3.y)
{
DrawTrigngleCH(MidYPoint, v3, c3, ve3);
if (maxY == v1.y)
{
DrawTrigngleCH(MaxYPoint, v1, c1, ve1);
DrawTrigngleCH(MinYPoint, v2, c2, ve2);
}
if (maxY == v2.y)
{
DrawTrigngleCH(MaxYPoint, v2, c2, ve2);
DrawTrigngleCH(MinYPoint, v1, c1, ve1);
}
}
vertex_t newV;
float t = (midY - maxY) / (minY - maxY);
newV.pos.x = interp(MaxYPoint.pos.x, MinYPoint.pos.x, t);
newV.pos.z= interp(MaxYPoint.pos.z, MinYPoint.pos.z, t);
newV.pos.y = midY;
newV.pos.w = interp(MaxYPoint.pos.w, MinYPoint.pos.w, t);
color_interp(newV.color, MaxYPoint.color, MinYPoint.color, t);
DrawTriangle_ScanConversion(MaxYPoint, MidYPoint, newV);
DrawTriangle_ScanConversion(MinYPoint, MidYPoint, newV);
}
else
{
ve1.pos.z = v1.z;
ve2.pos.z = v2.z;
ve3.pos.z = v3.z;
ve1.pos.w= c1.w;
ve2.pos.w= c2.w;
ve3.pos.w= c3.w;
if (v1.y == v2.y) DrawTriangle_ScanConversion(ve3, ve1, ve2);
if (v1.y == v3.y) DrawTriangle_ScanConversion(ve2, ve1, ve3);
if (v3.y == v2.y) DrawTriangle_ScanConversion(ve1, ve3, ve2);
}
}
画线里也要更新z,w值并传给画点函数由于代码太多就不一一贴出
,接下来是画点函数
还记得视锥裁剪算法吗
// 检查齐次坐标同 cvv 的边界用于视锥裁剪
int transform_check_cvv(const vector_t *v) {
float w = v->w;
int check = 0;
if (v->z < 0.0f) check |= 1;
if (v->z > w) check |= 2;
if (v->x < -w) check |= 4;
if (v->x > w) check |= 8;
if (v->y < -w) check |= 16;
if (v->y > w) check |= 32;
return check;
}
所谓的深度就是w-z
//画点
void DrawPoint(int x,int y,color_t c,float z,float w)
{
float d = w - z*w;
//glColor4f(1, 1, 0,0.5);
if (_buffer[x][y].depth <d)
{
c = _buffer[x][y].c;
return;
}
else
{
_buffer[x][y].depth =d;
_buffer[x][y].c = c;
}
glColor4f(c.r, c.g, c.b, c.a);
glVertex2i(x, y);
}
最后上图
暂时想不到别的方法,但先做出来再说,如果大家有什么好的方法,希望能在评论区留言