tinyrenderer-zBuffer隐藏面剔除

绘制物体的深度远近会影响最终投射的效果
画家算法:是z值排序所有物体,从远往近全部绘制,近处物体覆盖远处物体。效率低,且无法处理物体相互穿插的情况

维护一个zBuffer,记录每个像素点的最近zBuffer,根据每个像素点的zBuffer决定 绘制哪个物体的颜色

关键点在已知求三角形三个顶点的情况下,求内部任一点的z值。通过重心坐标求解

求重心坐标系数

std::pair<float, float> getBarycentricCoordinates(Vec3f* pts, float x, float y) {
    float beta = ((pts[0].y - pts[1].y) * x + (pts[1].x - pts[0].x) * y + pts[0].x * pts[1].y - pts[1].x * pts[0].y)
        / ((pts[0].y - pts[1].y) * pts[2].x + (pts[1].x - pts[0].x) * pts[2].y + pts[0].x * pts[1].y - pts[1].x * pts[0].y);
    float gamma = ((pts[0].y - pts[2].y) * x + (pts[2].x - pts[0].x) * y + pts[0].x * pts[2].y - pts[2].x * pts[0].y)
        / ((pts[0].y - pts[2].y) * pts[1].x + (pts[2].x - pts[0].x) * pts[1].y + pts[0].x * pts[2].y - pts[2].x * pts[0].y);
    return { beta,gamma };
}

记录zBuffer

std::vector<float> zBuffer(Width * Height, -std::numeric_limits<float>::infinity());
void triangle(Vec3f* pts, TGAImage& image, TGAColor color) {
    float minX = image.get_width() - 1;
    float minY = image.get_height() - 1;
    float maxX = 0;
    float maxY = 0;
    for (int i = 0; i < 3; i++) {
        minX = std::min(minX, pts[i].x);
        maxX = std::max(maxX, pts[i].x);
        minY = std::min(minY, pts[i].y);
        maxY = std::max(maxY, pts[i].y);
    }
    minX = std::max(0.f, minX);
    maxX = std::min(maxX, (float)image.get_width() - 1);
    minY = std::max(0.f, minY);
    maxY = std::min(maxY, (float)image.get_height() - 1);

    for (float x = minX; x <= maxX; x++) {
        for (float y = minY; y <= maxY; y++) {
            auto data = getBarycentricCoordinates(pts, x, y);
            float alpha = 1 - data.first - data.second;
            float beta = data.first;
            float gamma = data.second;
            if (alpha >= 0 && beta >= 0 && gamma >= 0) {
                auto z = alpha * pts[0].z + beta * pts[1].z + gamma * pts[2].z;
                int idx = int(x) + int(y) * Width;
                if (zBuffer[idx] < z) {
                    zBuffer[idx] = z;
                    image.set(x, y, color);
                }
            }
        }
    }
}

这里传入的顶点数据vp,x轴y轴是经过坐标变换的,z没有变换,因为z在这里的用处是对比大小的,虽然没有换算,但相对大小是不变的

最终效果
在这里插入图片描述
在这里插入图片描述
上图中有一些黑点,查代码最终发现是重心坐标记录点在三角形内时有浮动误差,调整了下代码

if (alpha < -0.001 || beta < -0.001 || gamma < -0.001) continue;

在这里插入图片描述
附加纹理着色,读取了obj文件的vt(纹理坐标),以及f数组的第二个值(纹理坐标的索引)

else if (!line.compare(0, 2, "vt")) {
     iss >> trash >> trash;
     Vec3f vt;
     for (int i = 0; i < 3; i++) {
         iss >> vt.raw[i];
         
     }
     tverts_.push_back(vt);
 }
else if (!line.compare(0, 2, "f ")) {
      std::vector<int> f;
      std::vector<int> tf;
      int itrash, idx, tidx;
      iss >> trash;
      while (iss >> idx >> trash >> tidx >> trash >> itrash) {
          idx--; // in wavefront obj all indices start at 1, not zero
          f.push_back(idx);
          tidx--;
          tf.push_back(tidx);
      }
      faces_.push_back(f);
      tfaces_.push_back(tf);
  }

由于没有进行投影变化,所以屏幕三角形坐标求的重心坐标和实际空间中的坐标是一样的,可以直接用来求纹理插值的坐标

auto uv = ts[0] * alpha + ts[1] * beta + ts[2] * gamma;
auto c = tex.get(uv.x * tex.get_width(), uv.y * tex.get_height());

其中tex是读取的纹理,ts是三角形三个顶点的纹理坐标

Vec3f v1 = model->tverts(tface[j]);
vt[j] = v1;
//........
TGAImage tex = TGAImage();
 tex.read_tga_file("obj/african_head_diffuse.tga");
 tex.flip_vertically();

最后用纹理插值来涂色

image.set(x, y, TGAColor(-I * c.r, -I * c.g, -I * c.b));

在这里插入图片描述

项目跟随练习代码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值