TinyRenderer学习笔记--Lesson 5

Lesson 5 移动相机

和第四节相似,这一节也会用到一些矩阵向量的计算,可以想象一下,我们在围绕一个物体进行环绕观察,物体在

世界坐标系中的位置是不会变化的,变化的是相机的位置。此时要正确的把图形渲染出来,需要重新计算物体和相

机之间的距离,重新进行投影计算,比较麻烦。所以相对的,我们把物体进行反操作,也能达到相同的效果。比如

相机围绕物体的y轴旋转,和物体绕着y轴反方向旋转,最后渲染出来的图形是一样的。

image-20220831143038700

image-20220831143048946

逆变换就对应着逆矩阵。

准备工作,在摄像机坐标系下,我们要引入up向量,它始终指向摄像机的上方,注意,这里的up是摄像机坐标系下的up。即下图中的Up direction向量。

image-20220831151807750

下面,需要把摄像机进行旋转,让摄像机的上方向指向世界坐标系的Y方向,摄像机的目标方向 (direction) 指向世界坐标系的 -Z 方向

-direction方向比较好求,直接用摄像机的坐标(x,y,z) - (0,0,0)就能得到,这个就是新的坐标

系的Z轴。前面我们定义了up向量,up × Z = X,这样就得到了X轴,继而X × Z = Y,这样就得到了新的X,Y,Z轴。

矩阵表示为image-20220831161033427

这个就是把摄像机旋转到 ”摄像机的上方向指向世界坐标系的Y方向,摄像机的目标方向 (direction) 指向世界坐

**标系的 -Z 方向“**状态的矩阵。此时,摄像机依然看向物体,不过物体仍然在center位置,不在原点。

然后下一步,我们需要把看向的位置移动到世界坐标系的原点,看向的位置可以理解成物体的位置,因为我们最终

得到的场景是摄像机位于Z轴负方向的位置,看向原点,所以要把看向的位置移到原点,看向的位置我们定义成

center点。这个操作使用平移矩阵表示为image-20220831154149175

这就是摄像机的操作,先旋转,再平移,就能达到我们理想的状态。但是前面说过,我们要求得是摄像机不动,物体动,那就要求这两个矩阵的逆image-20220831161700596

现在,把这两个矩阵应用到物体上,就达到了摄像机移动的视觉效果。

这里和普通的属兔变换要注意区别,普通的视图变换是作用在摄像机上的,这里是作用在物体上的。games101里

面的视图变换是把摄像机移动到原点并看向-Z轴方向,这里是把物体移动到原点,摄像机在Z轴方向看向原点,之

后我会改代码,改成games101的那种,这个实在是不好理解,看了好半天!!!

Matrix lookat(Vec3f eye, Vec3f center, Vec3f up) {
    Vec3f z = (eye - center).normalize();
    Vec3f x = (up ^ z).normalize();
    Vec3f y = (z ^ x).normalize();
    Matrix res = Matrix::identity(4);
    for (int i = 0; i < 3; i++) {
        //旋转子矩阵
        res[0][i] = x[i];
        res[1][i] = y[i];
        res[2][i] = z[i];
        //平移子矩阵
        res[i][3] = -center[i];
    }
    return res;
}

效果图:image-20220831174418879

这里图片更加平滑了,因为加入了高洛德着色法,原来我们使用的是平面着色,就是整个三角形平面使用一个方向

的法线,计算得到的光强是一样的,这样得到的效果就是整个三角形都是一种颜色,导致渲染出来的图片有明显的

条痕。高洛德着色是将三角形三个顶点的法向量都算出来,然后进行插值,这样三角形内部的颜色就是渐变的。

这里插值使用的线性插值,所以三角形的栅格化就是用的扫线法。

void triangle(Vec3i t0, Vec3i t1, Vec3i t2, float ity0, float ity1, float ity2, Vec2i uv0, Vec2i uv1, Vec2i uv2, float dis0, float dis1, float dis2, TGAImage& image, int* zbuffer) {
    //按照y分割为两个三角形
    if (t0.y == t1.y && t0.y == t2.y) return;
    if (t0.y > t1.y) { std::swap(t0, t1); std::swap(ity0, ity1); std::swap(uv0, uv1); }
    if (t0.y > t2.y) { std::swap(t0, t2); std::swap(ity0, ity2); std::swap(uv0, uv2); }
    if (t1.y > t2.y) { std::swap(t1, t2); std::swap(ity1, ity2); std::swap(uv1, uv2); }
    int total_height = t2.y - t0.y;
    for (int i = 0; i < total_height; i++) {
        bool second_half = i > t1.y - t0.y || t1.y == t0.y;
        int segment_height = second_half ? t2.y - t1.y : t1.y - t0.y;
        float alpha = (float)i / total_height;
        float beta = (float)(i - (second_half ? t1.y - t0.y : 0)) / segment_height;
        //计算A,B两点的坐标
        Vec3i A = t0 + Vec3f(t2 - t0) * alpha;
        Vec3i B = second_half ? t1 + Vec3f(t2 - t1) * beta : t0 + Vec3f(t1 - t0) * beta;
        //计算A,B两点的光照强度
        float ityA = ity0 + (ity2 - ity0) * alpha;
        float ityB = second_half ? ity1 + (ity2 - ity1) * beta : ity0 + (ity1 - ity0) * beta;
        //计算UV
        Vec2i uvA = uv0 + (uv2 - uv0) * alpha;
        Vec2i uvB = second_half ? uv1 + (uv2 - uv1) * beta : uv0 + (uv1 - uv0) * beta;
        //计算距离
        float disA = dis0 + (dis2 - dis0) * alpha;
        float disB = second_half ? dis1 + (dis2 - dis1) * beta : dis0 + (dis1 - dis0) * beta;
        if (A.x > B.x) { std::swap(A, B); std::swap(ityA, ityB); }
        //x坐标作为循环控制
        for (int j = A.x; j <= B.x; j++) {
            float phi = B.x == A.x ? 1. : (float)(j - A.x) / (B.x - A.x);
            //计算当前需要绘制点P的坐标,光照强度
            Vec3i    P = Vec3f(A) + Vec3f(B - A) * phi;
            float ityP = ityA + (ityB - ityA) * phi;
            ityP = std::min(1.f, std::abs(ityP) + 0.01f);
            Vec2i uvP = uvA + (uvB - uvA) * phi;
            float disP = disA + (disB - disA) * phi;
            int idx = P.x + P.y * width;
            //边界限制
            if (P.x >= width || P.y >= height || P.x < 0 || P.y < 0) continue;
            if (zbuffer[idx] < P.z) {
                zbuffer[idx] = P.z;
                TGAColor color = model->diffuse(uvP);
                image.set(P.x, P.y, TGAColor(color.bgra[2], color.bgra[1], color.bgra[0]) * ityP * (20.f / std::pow(disP, 2.f)));
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值