项目请见Github:tinyrenderer
C++前置知识
头文件简介(C++语法):
C/C++内存对齐详解
struct/class/union内存对齐原则有四个:
1).数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小或者成员的子成员大小(只要该成员有子成员,比如说是数组,结构体等)的整数倍开始(比如int在32位机为4字节, 则要从4的整数倍地址开始存储),基本类型不包括struct/class/uinon。
2).结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部"最宽基本类型成员"的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)。
3).收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的"最宽基本类型成员"的整数倍.不足的要补齐.(基本类型不包括struct/class/uinon)。
4).sizeof(union),以结构里面size最大元素为union的size,因为在某一时刻,union只有一个成员真正存储于该地址。
一言以蔽之:节约内存
在 C++ 中,当用户定义一个类的时候,如果没有给类定义任何构造函数,那么编译器会生成一个默认构造函数,使得在定义该类的对象的时候,调用了自动生成的默认构造函数为成员变量执行默认初始化。如果用户在定义一个类的时候,只要定义了任何一个构造函数,编译器则不会再为该类生成默认的构造函数,如果我们还需要一个默认构造函数,则只能自己定义。而在 C++11 新标准中,可以通过 =default 关键来声明构造函数,告诉编译器为该类生成一个默认的版本,由编译器自己生成的默认构造函数,性能上一般会比用户自己定义的更好,而且也更有标志性,便于代码的阅读。如下例:
#include <string> class Person { public: Person() = default; Person(std::string n, std::string addr) : name(n), address(addr) {} private: std::string name; std::string address; }; int main() { Person p1; //如果不包括 Person() = default 这行代码将报错,因为没有默认构造函数 Person p2("Bob", "Roma Street"); return 0; }
Static_cast用法详解
https://blog.csdn.net/weixin_41036447/article/details/115984885
Test_01
第一课的目标是渲染线网。要做到这一点,我们要学会如何画线段。我们可以简单的看一下Bresenham的线算法是什么,但是还是自己写代码吧。在(x0,y0)和(x1,y1)点之间画一条线段的最简单的代码是什么样子的?显然,是这样的:
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { for (float t=0.; t<1.; t+=.01) { int x = x0 + (x1-x0)*t; int y = y0 + (y1-y0)*t; image.set(x, y, color); } } 我们可以也可以通过下面代码实现 void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { for (int x=x0; x<=x1; x++) { float t = (x-x0)/(float)(x1-x0); int y = y0*(1.-t) + y1*t; image.set(x, y, color); } }
PS:代码中的第一个错误来源是整数除法,比如(x-x0)/(x1-x0)。
Test_02
line(13, 20, 80, 40, image, white);
line(20, 13, 40, 80, image, red);
line(80, 40, 13, 20, image, red);
PS:在上述代码中的1,3相互反向绘制
输出结果如下: