光栅化渲染器(六)着色准备和深度缓存实现

发表于2018-07-05
评论0 1.8k浏览
前面介绍完了坐标系,下面就来介绍下如何去更改一下顶点着色,让这个渲染器更漂亮。

首先是数据结构

更改数据结构
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);
}

最后上图

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引