├── 01_从画点开始.md ├── 02_画线.md ├── 03_填充三角形.md ├── 04_z-buffer.md ├── 05_纹理.md ├── 06_数学知识.md ├── 07_平面着色.md ├── 08_Gouraud着色.md ├── 09_Phong光照模型.md ├── ReadMe.md ├── code ├── 00_prepare │ ├── main │ ├── main.cpp │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 01_points │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 02_wireframe │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 03_filledtriangle │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 04_barycentricfilled │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 05_simplelight │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 06_simplelightzbuffer │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 07_texture │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ ├── african_head.obj │ │ └── african_head_diffuse.tga │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 08_math_involved copy │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output2.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 08_math_involved │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ └── tgaimage.h ├── 09_more_math │ ├── geometry.cpp │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ └── african_head.obj │ ├── output.tga │ ├── tgaimage.cpp │ ├── tgaimage.h │ └── zbuffer.tga ├── 10_flat_shading │ ├── Makefile │ ├── geometry.cpp │ ├── geometry.h │ ├── geometry.o │ ├── main │ ├── main.cpp │ ├── main.o │ ├── model.cpp │ ├── model.h │ ├── model.o │ ├── obj │ │ └── african_head.obj │ ├── our_gl.cpp │ ├── our_gl.h │ ├── our_gl.o │ ├── output.tga │ ├── tgaimage.cpp │ ├── tgaimage.h │ ├── tgaimage.o │ └── zbuffer.tga ├── 11_gouraud_shading │ ├── Makefile │ ├── geometry.cpp │ ├── geometry.h │ ├── geometry.o │ ├── main │ ├── main.cpp │ ├── main.o │ ├── model.cpp │ ├── model.h │ ├── model.o │ ├── obj │ │ └── african_head.obj │ ├── our_gl.cpp │ ├── our_gl.h │ ├── our_gl.o │ ├── output.tga │ ├── tgaimage.cpp │ ├── tgaimage.h │ ├── tgaimage.o │ └── zbuffer.tga ├── 12_texture_again │ ├── Makefile │ ├── geometry.cpp │ ├── geometry.h │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ ├── african_head.obj │ │ └── african_head_diffuse.tga │ ├── our_gl.cpp │ ├── our_gl.h │ ├── tgaimage.cpp │ └── tgaimage.h ├── 13_normal │ ├── Makefile │ ├── geometry.cpp │ ├── geometry.h │ ├── main │ ├── main.cpp │ ├── model.cpp │ ├── model.h │ ├── obj │ │ ├── african_head.obj │ │ ├── african_head_diffuse.tga │ │ └── african_head_nm.tga │ ├── our_gl.cpp │ ├── our_gl.h │ ├── output.tga │ ├── tgaimage.cpp │ ├── tgaimage.h │ └── zbuffer.tga └── 14_phong_light │ ├── Makefile │ ├── geometry.cpp │ ├── geometry.h │ ├── geometry.o │ ├── main │ ├── main.cpp │ ├── main.o │ ├── model.cpp │ ├── model.h │ ├── model.o │ ├── obj │ ├── african_head.obj │ ├── african_head_diffuse.tga │ ├── african_head_nm.tga │ └── african_head_spec.tga │ ├── our_gl.cpp │ ├── our_gl.h │ ├── our_gl.o │ ├── output.tga │ ├── tgaimage.cpp │ ├── tgaimage.h │ ├── tgaimage.o │ └── zbuffer.tga ├── images ├── AB_cross_AC.png ├── Bresenham.png ├── Phong_light_model.png ├── affine.jpg ├── african_head_nm.png ├── african_head_nm_tangent.png ├── cos_alpha.png ├── directional_light.png ├── filledframe.png ├── filledrandom.png ├── flat_shading.png ├── gouraud_shading.png ├── gouraud_shading2.png ├── light_energy.png ├── light_scene01.png ├── light_scene02.png ├── math01.png ├── math_loc.png ├── normal_texture.png ├── output.png ├── output.tga ├── phong_light.png ├── points.png ├── prepare.png ├── r17-texture-mapping.png ├── simple_light.png ├── simple_light2.png ├── texture.png ├── texture02.png ├── texture02.tga ├── texture_uv.png ├── triangle_left_right.png ├── upper_down_triangle.png ├── uvwh.png ├── wireframe.png ├── z_buffer01.png └── zbufferhead.png └── index.html /01_从画点开始.md: -------------------------------------------------------------------------------- 1 | # 从画点开始 2 | 3 | ## TGAImage 4 | 5 | 生成图像我们使用TGAImage,这个使用起来很简单: 6 | 7 | ```C++ 8 | #include "tgaimage.h" 9 | 10 | const TGAColor white = TGAColor(255, 255, 255, 255); 11 | const TGAColor red = TGAColor(255, 0, 0, 255); 12 | 13 | int main(int argc, char** argv){ 14 | TGAImage image(100, 100, TGAImage::RGB); 15 | image.set(52, 41, red); 16 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 17 | image.write_tga_file("output.tga"); 18 | return 0; 19 | } 20 | ``` 21 | 22 | 生成的图像(注意中间哪一个小小的红色点): 23 | 24 | ![](images/prepare.png) 25 | 26 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/00_prepare) 27 | 28 | compile: 29 | 30 | ``` 31 | g++ main.cpp tgaimage.cpp -o main 32 | ``` 33 | 34 | 35 | 36 | ## wavefront obj 37 | 38 | 然后我们来学习一种3d格式文件,wavefront obj file: 39 | 40 | ``` 41 | # List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. 42 | v 0.123 0.234 0.345 1.0 43 | v ... 44 | ... 45 | # List of texture coordinates, in (u, [v ,w]) coordinates, these will vary between 0 and 1, v and w are optional and default to 0. 46 | vt 0.500 1 [0] 47 | vt ... 48 | ... 49 | # List of vertex normals in (x,y,z) form; normals might not be unit vectors. 50 | vn 0.707 0.000 0.707 51 | vn ... 52 | ... 53 | # Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement ( see below ) 54 | vp 0.310000 3.210000 2.100000 55 | vp ... 56 | ... 57 | # Polygonal face element (see below) 58 | f 1 2 3 59 | f 3/1 4/2 5/3 60 | f 6/4/1 3/5/3 7/6/5 61 | f 7//1 8//2 9//3 62 | f ... 63 | ... 64 | # Line element (see below) 65 | l 5 8 1 2 4 9 66 | ``` 67 | 68 | 我们现在只需要知道了解顶点是v,现在我们想把一个文件中的3d模型的顶点 v (x, y, z) 给画出来,(因为我们已经知道怎么在图上相应的位置放像素)这个文件所有的 x, y, z ∈ [-1, 1],所以我们 69 | 70 | - 需要把它们映射到合适范围。 71 | - 然后注意我们画的点 `image.set(52, 41, red);`, 这里的 52 和 41 是 int,映射之后需要转成int,因为我们总是画在一个一个像素点上。 72 | 73 | 写一个简单的parser读入文件建立模型,画之。 74 | 75 | 核心部分长这样: 76 | 77 | ```C++ 78 | for (int i = 0; i != model->nverts(); i++) { 79 | Vec3f v = model->vert(i); 80 | Vec2i p = world2screen(v); 81 | image.set(p.x, p.y, white); 82 | } 83 | ``` 84 | 85 | ![](images/points.png) 86 | 87 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/01_points) 88 | 89 | 90 | compile: 91 | 92 | ``` 93 | g++ main.cpp tgaimage.cpp model.cpp -o main 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /02_画线.md: -------------------------------------------------------------------------------- 1 | # 画线 2 | 3 | 画完了点,我们来开始画线。画线的同时我们依旧要记得,我们是画在一个一个整数的pixel上。 4 | 5 | 6 | ## 尝试一: 按照参数绘制直线 7 | 8 | 9 | ```C++ 10 | void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 11 | for (float t=0.; t<1.; t+=.01) { 12 | int x = x0 + (x1-x0)*t; 13 | int y = y0 + (y1-y0)*t; 14 | image.set(x, y, color); 15 | } 16 | } 17 | ``` 18 | 19 | 这里的问题有两个: 20 | 21 | - 效率低 22 | - t如何控制 23 | 24 | t取大了画出来的并不是线,而是一堆点。t取小了会浪费,有很多重复的x和y。 25 | 26 | ## 尝试二: 按x的增加画线 27 | 28 | ```C++ 29 | void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 30 | for (int x=x0; x<=x1; x++) { 31 | float t = (x-x0)/(float)(x1-x0); 32 | int y = y0 + (y1 - y0)*t; 33 | image.set(x, y, color); 34 | } 35 | } 36 | ``` 37 | 38 | 我们想着要节约,就每次 x 增加1,然后来画y。 39 | 40 | 这样画线是对的因为我们假设 $y = mx + b $, 直线斜率m, 截距b 41 | 42 | $$ 43 | \frac{y_1 - y_0}{x_1 - x_0} = m 44 | $$ 45 | 46 | $$ 47 | y_0 = mx_0 + b 48 | $$ 49 | 50 | $$ 51 | y = y_0 + \frac{y_1 - y_0}{x_1 - x_0}(x - x_0) 52 | $$ 53 | 54 | 所以 55 | 56 | $$ 57 | y = y_0 + mx - mx_0 = mx + (y_0 - mx_0) = mx + b 58 | $$ 59 | 60 | 61 | 同时它的问题是我们也已经指出: 62 | 63 | - 如果直线斜率太大,比如 m = 3, 那么x每增加1个像素,y增加3个像素,这样画出来就是分离的点。 64 | - 只能适用于 x0 < x1的状况 65 | 66 | ## 尝试三 67 | 68 | 所以想法是: 69 | 70 | - 如有必要交换 x0 和 x1,这样使得 x0 一定小于 x1 71 | - 如果斜率比较大,则交换 x 和 y 72 | 73 | 74 | 看代码: 75 | 76 | ```C++ 77 | void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 78 | bool steep = false; 79 | if (std::abs(x0-x1)x1) { // make it left−to−right 85 | std::swap(x0, x1); 86 | std::swap(y0, y1); 87 | } 88 | for (int x=x0; x<=x1; x++) { 89 | float t = (x-x0)/(float)(x1-x0); 90 | int y = y0 + (y1 - y0)*t; 91 | if (steep) { 92 | image.set(y, x, color); // if transposed, de−transpose 93 | } else { 94 | image.set(x, y, color); 95 | } 96 | } 97 | } 98 | ``` 99 | 100 | 这样就可以完善上述出现的问题来画线了。 101 | 102 | 103 | 这里其实还是有一些可以进步的空间,比如出现了浮点数t,同时也依旧我们之前说的我们只需要画在整数上。可以参见: 104 | 105 | [再谈绘制直线](https://zhuanlan.zhihu.com/p/64989645)中的优化部分。 106 | 107 | 不过我们画线就暂时停在这里。我们就用这个函数来画了,因为compiler的优化已经足够好了。 108 | 109 | ## wavefront obj 110 | 111 | 之前我们已经用过这个文件,上次我们认识了v 代表顶点(vertex),这次我们来多认识一个f 代表面(face),实际上是三角形面,在这个文件中我们的一行f有: 112 | 113 | ``` 114 | f 1193/1240/1193 1180/1227/1180 1179/1226/1179 115 | ``` 116 | 117 | 我们现在只需要知道每组的第一个数字: 1193,1180,1179 是代表vertex list中的三个顶点的索引(index),这三个顶点构成一个三角形。 118 | 119 | 所以进一步修改model parser,我们来用这个画出线框,核心代码长这样: 120 | 121 | ```C++ 122 | for (int i = 0; i < model->nfaces(); i++) { 123 | std::vector face = model->face(i); 124 | // face: i0,i1,i2 of triangle 125 | for (int j = 0; j < 3; j++) { 126 | Vec3f v0 = model->vert(face[j]); 127 | // this % used for when j = 2 to get i2, i0 128 | Vec3f v1 = model->vert(face[(j+1)%3]); 129 | int x0 = (v0.x+1.)*width/2.; 130 | int y0 = (v0.y+1.)*height/2.; 131 | int x1 = (v1.x+1.)*width/2.; 132 | int y1 = (v1.y+1.)*height/2.; 133 | line(x0, y0, x1, y1, image, white); 134 | } 135 | } 136 | ``` 137 | 138 | ![](images/wireframe.png) 139 | 140 | looks good. 141 | 142 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/02_wireframe) 143 | 144 | compile 145 | 146 | ``` 147 | g++ -std=c++11 main.cpp tgaimage.cpp model.cpp -o main 148 | ``` 149 | -------------------------------------------------------------------------------- /04_z-buffer.md: -------------------------------------------------------------------------------- 1 | # z-buffer 2 | 3 | ## 简单光 4 | 5 | 我们现在先复习一下,我们经过了画点、画线,填三角形之后已经能画出来一些东西了,现在我们有好几条路可以走,那就是 6 | 7 | - 光(上帝说“要有光”) 8 | - 纹理(不然就填白色和随机颜色么?) 9 | - 数学(之前做的所有事情就是简单的把x,y对应的画到图像上来) 10 | 11 | 12 | 这里我们做的事就是简单的给我们的模型一点‘方向光’,注意我这里说了一专有名词‘方向光’,所以还会有别光(暂且不表)。方向光就是类似太阳光一样的,我们只考虑它的方向: 13 | 14 | ![](images/directional_light.png) 15 | 16 | 对于一束光,我们到达物体表面的能量实际上是: 17 | 18 | ![](images/cos_alpha.png) 19 | 20 | 21 | 它的强度 Icosα, α是物体光与物体的法向量的夹角。 22 | 23 | 如果我们用$\overrightarrow{L}$表示光的方向,$\overrightarrow{N}$指向物体光照处'向内的'法向量,那么 24 | 25 | 26 | $$ 27 | cos\alpha = \frac{\overrightarrow{L} \cdot \overrightarrow{N}}{|\overrightarrow{L}| \cdot |\overrightarrow{N}|} ​ 28 | $$ 29 | 30 | 31 | 这里我们就必须要考虑一些数学问题了,物体我们放在这,然后有光的方向: 32 | 33 | ![](images/light_scene01.png) 34 | 35 | 36 | 那么'朝内的'法向量可以这样得到$\overrightarrow{AC} \times \overrightarrow{AB}$,然后正交化: 37 | 38 | ![](images/light_scene02.png) 39 | 40 | 41 | 这里我们先做很多简化操作: 42 | 43 | 44 | - 光的方向是 Vec3f light(0, 0, -1), 强度就是1 45 | - 假设每个三角形收到光照的强度相同,都是 Icosα 46 | - 三角形法向量$\overrightarrow{AC} \times \overrightarrow{AB}$ 47 | - 当然我们还要知道 cosα 大于0才有意义,我们不可能减去光o(╯□╰)o 48 | 49 | 50 | 核心代码: 51 | 52 | ```C++ 53 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 54 | norm.normalize(); 55 | float intensity = light*norm; 56 | if (intensity > 0) { 57 | triangle(screen_coords, image, TGAColor(intensity*255,intensity*255,intensity*255,255)); 58 | } 59 | ``` 60 | 61 | 62 | 看效果: 63 | 64 | ![](images/simple_light.png) 65 | 66 | 妈妈他是凸嘴。我们换一个光的方向。 67 | 68 | ![](images/simple_light2.png) 69 | 70 | 更吓人了。。。。他嘴巴怎么长后面了。。 71 | 72 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/05_simplelight) 73 | 74 | compile & run: 75 | 76 | ``` 77 | $ g++ -std=c++11 main.cpp tgaimage.cpp model.cpp -o main 78 | $ ./main 79 | ``` 80 | 81 | ## z-buffer 82 | 83 | 造成这个问题的原因很简单,我们就是一股脑的把三角形画出来了,没有考虑三角形的先后顺序,正如画画一样,我们应该先画远处的东西,如果近处有什么东西把它给覆盖了,我们就不会看到远处的东西,这里我们就是画三角形的时候没有考虑先后顺序。那么这个问题要怎么解决呢? 84 | 85 | 86 | 这里我们先继续回顾一下三角形重心坐标: 87 | 88 | $$ 89 | P = (1 - a/c - b/c)A + a/cB + b/cC, c \ne 0 90 | $$ 91 | 92 | 这里其实有一个很cool的点,就是我们把P表示成三角形三个顶点的线性组合,再回忆一下线性插值,其实对于P点的任何性质,我们都可以利用类似线性插值,把它变成三个顶点的组合: 93 | 94 | 95 | $$ 96 | P_z = (1 - a/c - b/c)A_z + a/cB_z + b/cC_z, c \ne 0 97 | $$ 98 | 99 | 100 | 所以这里就给了我们提示,对于任意一点P,我们算出它的z值,如果z值更靠近我们,那么我们就用它来替换已经画上的点,否则我们则不更新P点。 101 | 102 | 103 | 同样我们也只用考虑画布上的所有的点的P值,可以用一个二维的数组来表示,不过我们这里偷懒,就用一维的数组,因为画布上的(x,y)点可以写成(x + y *width),可以这样来转换: 104 | 105 | ```C++ 106 | int idx = x + y*width; 107 | ``` 108 | 109 | 110 | ```C++ 111 | int x = idx % width; 112 | int y = idx / width; 113 | ``` 114 | 115 | 同时注意我们在把物体坐标系做映射时,需要保留z值,所以一些计算我们最好就用float.同时我们也需要注意在转换坐标系的时候我们需要注意还是需要把 x 和 y 变成int,否则有些地方会因为浮点数的原因for loop不会覆盖所有的像素,会有黑色部分产生: 116 | 117 | ```C++ 118 | Vec3f world2screen(Vec3f v) { 119 | // 注意这里我们还是保留了int这个操作,因为我们的画像素的for loop要用到这个x和y 120 | // 如果都是浮点数,那么for loop有些可能无法顺利进行 121 | // 我们再加上0.5来四舍五入 122 | return Vec3f(int((v.x+1.)*width/2.+.5), int((v.y+1.)*height/2.+.5), v.z); 123 | } 124 | ``` 125 | 126 | 第二个需要注意的点是我们物体的位置和朝向,这里我们把z-buffer初始化为负无穷大,然后如果P.z更大意味更靠近我们。 127 | 128 | ``` 129 | void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color) { 130 | Vec2f bboxmin(std::numeric_limits::max(),std::numeric_limits::max()); 131 | Vec2f bboxmax(std::numeric_limits::min(),std::numeric_limits::min()); 132 | Vec2f clamp(image.get_width()-1, image.get_height()-1); 133 | for (int i = 0; i < 3; i++) { 134 | for (int j = 0; j < 2; j++) { 135 | bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j])); 136 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 137 | } 138 | } 139 | Vec3f P; 140 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 141 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 142 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 143 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 144 | P.z = 0; 145 | for (int i=0; i<3; i++) P.z += pts[i][2]*bc_screen[i]; 146 | if (zbuffer[int(P.x+P.y*width)] < P.z) { 147 | image.set(P.x, P.y, color); 148 | zbuffer[int(P.x+P.y*width)] = P.z; 149 | } 150 | } 151 | } 152 | } 153 | ``` 154 | 155 | 看结果: 156 | 157 | ![](images/z_buffer01.png) 158 | 159 | 160 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/06_simplelightzbuffer) 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /05_纹理.md: -------------------------------------------------------------------------------- 1 | # 纹理 2 | 3 | 4 | 之前我们说有三个方向可以走 - 光、纹理、数学,我们先轻松一下,看一下纹理。 5 | 6 | 纹理其实就是贴图,比如看这个正方体,如果我们想要它有木质效果。对于它的每一面,我们给它贴上图就ok. 7 | 8 | ![](images/r17-texture-mapping.png) 9 | 10 | 注意只用图的一部分也是完全ok的。 11 | 12 | 我们定义 u 和 v 13 | 14 | $$ 15 | 0 \le u \le 1 16 | $$ 17 | 18 | 19 | $$ 20 | 0 \le v \le 1 21 | $$ 22 | 23 | 24 | (u, v) 会对应到图片(宽w, 高h)上的 (u(w - 1), v(h - 1)),这样定义的的好处当然很多啦, 比如我们可以随意换纹理图,还可以随意换纹理图大小...等等等。 25 | 26 | ![](images/uvwh.png) 27 | 28 | 针对每个三角形的顶点我们有 (u, v), 同样用重心坐标系算出对于三角形的每一点的 (u, v),然后根据这个 (u, v) 来画图。 29 | 30 | ![](images/texture_uv.png) 31 | 32 | 33 | ## wavefront obj 34 | 35 | 对于我们的文件,其中有: 36 | 37 | ``` 38 | vt 0.532 0.923 0.000 39 | ``` 40 | 41 | 这个数据就是我们三角形对应的(u, v)。 42 | 43 | 而我们之前读f的时候,丢弃了一些数据,实际上: 44 | 45 | 46 | ``` 47 | f 1193/1240/1193 1180/1227/1180 1179/1226/1179 48 | ``` 49 | 50 | 实际上三个数据分别是: 顶点索引/顶点法向量索引/顶点纹理索引。 51 | 52 | 53 | 我们来修改model, 给出纹理文件:'african\_head\_diffuse.tga',读入纹理,然后贴上图看看,注意这里我们并没有给他加上光: 54 | 55 | ![](images/texture.png) 56 | 57 | looks good! 58 | 59 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/07_texture) 60 | 61 | 62 | compile & run: 63 | 64 | ``` 65 | $ g++ -std=c++11 main.cpp tgaimage.cpp model.cpp -o main 66 | $ ./main 67 | ``` 68 | -------------------------------------------------------------------------------- /06_数学知识.md: -------------------------------------------------------------------------------- 1 | # 数学知识 2 | 3 | 之前我们说有三个方向可以走 - 光、纹理、数学。然后我们简单看了一个光的例子来学习z-buffer和纹理,现在我们来看一下相关的数学知识。这些也很重要。 4 | 5 | 需要了解的概念包括: 6 | 7 | - 矩阵是如何变换向量的: V' = Matrix * V 8 | - 窗口变换、坐标变换: 同样可以用矩阵表示 9 | - 正交投影、透视投影:矩阵表示 10 | - 左手坐标系vs右手坐标系 11 | - ... 12 | 13 | 这部分可以参考我的一些文章 14 | 15 | - [[从零开始计算机图形学]之十四数学知识](https://zhuanlan.zhihu.com/p/63610995) 中的齐次坐标、变换矩阵部分 16 | - [矩阵的逆、坐标变换和窗口变换](https://zhuanlan.zhihu.com/p/66240124) 17 | - [LookAt、Viewport、Perspective矩阵](https://zhuanlan.zhihu.com/p/66384929) 18 | - [左手坐标系vs右手坐标系](https://zhuanlan.zhihu.com/p/64707259) 19 | 20 | 21 | 经过这些补充之后,我们需要知道的是为了变换物体位置,并且符合近大远小的原则我们最终画在屏幕上的点需要经过这些变换: 22 | 23 | 24 | ``` 25 | 顶点 → 世界坐标系 → 摄像头坐标系 → 投影 → 屏幕坐标系 26 | 27 | viewport * projection * view * model * vertex. 28 | ``` 29 | 30 | 31 | ## 加上数学 32 | 33 | 这里我们简单用一点点数学,我们不动物体,但是把眼睛/摄像头放到 z = 3 的位置,头像缩小一点点,再加上透视投影。 34 | 35 | ![](images/math_loc.png) 36 | 37 | 结果: 38 | 39 | ![](images/math01.png) 40 | 41 | 42 | 看起来不错,近大远小我们能看出来。 43 | 44 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/08_math_involved) 在geometry.h 中有较大的修改,引入了矩阵类及其运算。 45 | 46 | 47 | 说起数学我们还不能逃开一些问题,比如: 48 | 49 | - 我们在代码中没有考虑当我们变换物体的时候,它的法向量会怎样变化?是否可以用同样的矩阵来变换?→ [法向量变换](https://zhuanlan.zhihu.com/p/66669463) 50 | - 我们把物体映射到一个范围,但是不是物体的所有部分都在我们能看到的区域,所以这里还需要一个裁剪步骤 51 | - 有些三角形可以提前丢弃(比如处于视线背面的),我们可以尽早的丢掉它们,这样可以节省计算 → 参考[隐藏面消除]() 中的背面消除部分 52 | - 需要比较清楚什么时候选什么坐标系中的顶点,比如光:它应该是跟哪个坐标系或者变换在什么状态下的顶点互动? 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /07_平面着色.md: -------------------------------------------------------------------------------- 1 | # 平面着色 2 | 3 | ## 更多的数学 4 | 5 | 在上一次计算的代码中,我们把模型的坐标$v_x,v_y$映射投影到了一定的区间,其实对于$v_z$我们也可以这样来做。这样可以更多更好的运用我们的齐次坐标,为了方便,我们可以把$v_z$映射到[0,255]之间,这样当然我们的 viewport 矩阵要跟着改了, d = 255: 6 | 7 | $$ 8 | \begin{pmatrix} 9 | w/2 & 0 & 0 & x + w/2\\ 10 | 0 & h/2 & 0 & y + h/2 \\ 11 | 0 & 0 & d/2 & d/2 \\ 12 | 0 & 0 & 0 & 1 13 | \end{pmatrix} 14 | $$ 15 | 16 | 17 | 我们把$v_z$映射到0~255之间还有一个附加原因(好处)是因为TGAImage支持灰度图像,我们可以利用$v_z$生成一副灰度图像,这样更利于我们debug,同时我们也不需要再使用zbuffer数组了。 18 | 19 | 看 code 关键部分: 20 | 21 | ```C++ 22 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 23 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 24 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 25 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 26 | image.set(P.x, P.y, color); 27 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 28 | ``` 29 | 30 | 我们的zbuffer是一幅TGA图像,方便我们直接从中取值,同时z的计算我们变得更加方便了一些,我们也新增了一些几何计算的部分,比如 Vec3f - Vec3i 互相转换, Vec2f - Vec2i 互相转换。 31 | 32 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/09_more_math) 33 | 34 | 现在compile就需要: 35 | 36 | ``` 37 | $ g++ -std=c++11 main.cpp tgaimage.cpp model.cpp geometry.cpp -o main 38 | $ .\main 39 | ``` 40 | 41 | 生成的output跟之前一样,附加生成了一幅zbuffer的图像: 42 | 43 | ![](images/zbufferhead.png) 44 | 45 | 46 | ## 着色 47 | 48 | 之前写过为了生成这个output,我们加了一束方向光,并做了一些假设:假设每个三角形收到光照的强度相同,都是 Icosα,法向量是我们根据三角形的边叉乘算出来的。 49 | 50 | 这种给物体上色的方式就叫做平面着色(Flat Shading),三角形这个平面着一样的色。除了平面着色以外,我们还有别的着色方式: 高洛德着色(Gouraud shading) 和冯氏着色 (Phong shading). 51 | 52 | ### Gouraud shading 53 | 54 | 高洛德着色(Gouraud shading) 跟我们处理三角形的z值差不多,算出每个顶点的颜色,然后根据重心坐标,算出三角形内每个P点的颜色,给P点上色。 55 | 56 | 至于每个顶点的颜色怎么算,别忘了我们的 wavefront file 57 | ``` 58 | f 1193/1240/1193 1180/1227/1180 1179/1226/1179 59 | ``` 60 | 61 | 顶点索引/顶点法向量索引/顶点纹理索引 62 | 63 | 顶点法向量在文件中是 vn开头的数字: 64 | 65 | ``` 66 | vn 0.001 0.482 -0.876 67 | ``` 68 | 有了顶点法向量我们当然就可以算顶点颜色,进一步修改model文件读入更多的数据。 69 | 70 | 71 | ## Shader 72 | 73 | 不同的着色方式会有不同的代码,如果我们每次都要回去修改都太麻烦了,这里我们来整理一次代码,让它靠近OpenGL,把顶点变换和着色抽象出来,变成专门的shader部分,也就是所谓的 vertex shader(顶点着色器) 和 fragment shader(片段着色器)。 74 | 75 | 我们定义一个IShader基类,所有的找色方式都继承和实现里面的 virtual method. 76 | 77 | ``` 78 | struct IShader { 79 | virtual ~IShader(); 80 | virtual Vec4f vertex(int iface, int nthvert) = 0; 81 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 82 | }; 83 | ``` 84 | 85 | 顶点着色器主要做两件事: 86 | 87 | - 变换顶点 88 | - 准备数据给片段着色器用 89 | 90 | 片段着色器也主要做两件事: 91 | 92 | - 决定当前像素的颜色 93 | - 是否要丢弃当前像素 94 | 95 | 平面着色器: 96 | 97 | ```C++ 98 | struct FlatShader: public IShader{ 99 | mat<3,3,float> varying_tri; // 用来记录transform之后的三角形 100 | 101 | virtual Vec4f vertex(int iface, int nthvert){ 102 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); 103 | gl_Vertex = Projection*ModelView*gl_Vertex; 104 | varying_tri.set_col(nthvert, proj<3>(gl_Vertex/gl_Vertex[3])); //记录transform后的三角形,这个在我们片段着色器决定三角形的颜色的时候使用来使用 105 | gl_Vertex = ViewPort*gl_Vertex; 106 | return gl_Vertex; 107 | } 108 | 109 | virtual bool fragment(Vec3f bar, TGAColor &color){ 110 | Vec3f n = cross(varying_tri.col(1)- varying_tri.col(0),varying_tri.col(2)- 111 | varying_tri.col(0)).normalize(); // 计算法向量 112 | float intensity = CLAMP(n*light_dir); // 光强度clamp到0,1之间 113 | color = TGAColor(255,255,255)*intensity; // 计算颜色 114 | return false; 115 | } 116 | }; 117 | ``` 118 | 119 | 之所以我们叫这个为 varying_tri 是因为 **varying**是GLSL中的保留字,我们之后会再聊到它,改变一下光的方向、眼睛位置,看最终效果: 120 | 121 | ![](images/flat_shading.png) 122 | 123 | 124 | 代码: 125 | 126 | run: 127 | 128 | ``` 129 | $ g++ -std=c++11 main.cpp tgaimage.cpp model.cpp geometry.cpp our_gl.cpp -o main 130 | $ ./main 131 | ``` 132 | 133 | 鉴于我们的compile越来越复杂,之后我们会开始使用makefile. 134 | 135 | 136 | -------------------------------------------------------------------------------- /08_Gouraud着色.md: -------------------------------------------------------------------------------- 1 | # Gouraud 着色 2 | 3 | 我们前面已经写过如何Gouraud shading,算出三角形每个顶点的光照,然后根据重心坐标插值上色。 4 | 5 | 那么我们首先再复习一下我们的代码,非常美妙,抽象了很多部分出来,现在如果我们需要更改着色方式,我们只需要更改 main.cpp 中的 shader部分。所以我们来更改代码,变成Gouraud着色: 6 | 7 | ```C++ 8 | struct GouraudShader: public IShader{ 9 | Vec3f varying_intensity; // write by vertex shader, read by fragment shader 10 | 11 | virtual Vec4f vertex(int iface, int nthvert){ 12 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 13 | gl_Vertex = ViewPort*Projection*ModelView*gl_Vertex; 14 | varying_intensity[nthvert] = CLAMP(model->normal(iface, nthvert)*light_dir); // diffuse light intensity 15 | return gl_Vertex; 16 | } 17 | 18 | virtual bool fragment(Vec3f bar, TGAColor &color){ 19 | float intensity = varying_intensity * bar; //interpolate intensity for current Pixel 20 | color = TGAColor(255,255,255)*intensity; 21 | return false; // do not discard pixel 22 | } 23 | }; 24 | ``` 25 | 26 | 看一下这个跟triangle函数完美的配合: 27 | 28 | ```C++ 29 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 30 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 31 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 32 | for (int i = 0; i < 3; i++) { 33 | for (int j = 0; j < 2; j++) { 34 | // x/w y/w 35 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 36 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 37 | } 38 | } 39 | 40 | Vec2i P; 41 | TGAColor color; 42 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 43 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 44 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 45 | proj<2>(pts[2]/pts[2][3]), P); 46 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 47 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 48 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 49 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 50 | bool discard = shader.fragment(c, color); 51 | if (!discard) { 52 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 53 | image.set(P.x, P.y, color); 54 | } 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | 我们把重心坐标系传入 fragment,这个坐标系会根据三个顶点的光照来计算出这个点的光照,给出对应像素的颜色。 61 | 62 | 当然也少不了我们的main函数: 63 | 64 | ```C++ 65 | GouraudShader shader; 66 | for (int i = 0; i < model->nfaces(); i++) { 67 | std::vector face = model->face(i); 68 | Vec4f screen_coords[3]; 69 | for (int j = 0; j < 3; j++) { 70 | screen_coords[j] = shader.vertex(i, j); //处理每个三角形 71 | } 72 | triangle(screen_coords, shader, image, zbuffer); 73 | } 74 | ``` 75 | 76 | 77 | ![](images/gouraud_shading.png) 78 | 79 | looks good. 80 | 81 | [代码]() 82 | 83 | 之所以我们让fragment返回一个bool告诉我们是否需要保留pixel这个也可以用处待谈,我们也可以修改 fragment shader改变着色方式: 84 | 85 | 86 | ```C++ 87 | virtual bool fragment(Vec3f bar, TGAColor &color){ 88 | float intensity = varying_intensity * bar; //interpolate intensity for current Pixel 89 | if (intensity > .85) intensity = 1; 90 | else if (intensity > .60) intensity = .80; 91 | else if (intensity > .45) intensity = .60; 92 | else if (intensity > .30) intensity = .45; 93 | else if (intensity > .15) intensity = .30; 94 | else intensity = 0; 95 | color = TGAColor(255,255,255)*intensity; 96 | return false; // do not discard pixel 97 | } 98 | ``` 99 | 100 | 101 | ![](images/gouraud_shading2.png) 102 | 103 | 有点卡通效果。 104 | 105 | 106 | ## Phong着色 107 | 108 | Gouround 着色看起来不错,但是也存在问题,就是当物体距离‘点光源’很近的时候,这个可以参考[[从零开始计算机图形学]之十八Gouraud着色](https://zhuanlan.zhihu.com/p/64523601)的缺陷部分。 109 | 110 | 111 | - 平面着色: 每个三角形只计算一个光照 112 | - Gouraud着色: 每个三角形计算三个光照,同时针对三角形中每个点做线性插值 113 | - Phong着色: 我们把三角形的法向量和光照针对每个点P都做线性插值 114 | 115 | Phong着色会要求更多的计算,当然也会解决Gourand着色的缺陷 116 | 117 | -------------------------------------------------------------------------------- /09_Phong光照模型.md: -------------------------------------------------------------------------------- 1 | # Phong光照模型 2 | 3 | 先注意标题: Phong光照模型,光照模型并不是着色,不要把这个和Phong着色弄混。 4 | 5 | 6 | 7 | ## 再次纹理 8 | 9 | 我们再次来看纹理,之前我们添加纹理的时候没有加光照,没有做投影。现在我们把纹理加到我们现有的系统中: 10 | 11 | ```C++ 12 | struct Shader: public IShader{ 13 | Vec3f varying_intensity; // write by vertex shader, read by fragment shader 14 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 15 | 16 | virtual Vec4f vertex(int iface, int nthvert){ 17 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 18 | varying_intensity[nthvert] = CLAMP(model->normal(iface, nthvert)*light_dir); // diffuse light intensity 19 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 20 | return ViewPort*Projection*ModelView*gl_Vertex; 21 | } 22 | 23 | virtual bool fragment(Vec3f bar, TGAColor &color){ 24 | float intensity = varying_intensity * bar; //interpolate intensity for current Pixel 25 | Vec2f uv = varying_uv * bar; //interpolate uv for current Pixel 26 | color = model->diffuse(uv)*intensity; 27 | return false; // do not discard pixel 28 | } 29 | }; 30 | ``` 31 | 32 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/12_texture_again) 33 | 34 | 35 | 最终效果: 36 | 37 | ![](images/texture02.png) 38 | 39 | ## 法向量插值 40 | 41 | 有了这张纹理图,我们就可以根据插值法得到每一个像素的纹理。不禁让人思考,除了纹理,我们还可以把什么也存在图像中呢?答案是-很多:颜色、方向甚至温度。 42 | 43 | 这张图,如果我们把RGB值翻译成 xyz,那么这张图会给我们每个pixel的法向量值。这样就不仅仅是顶点的法向量了。 44 | 45 | ![](images/african_head_nm.png) 46 | 47 | 48 | 我们像加载法向量,修改shader,再次生成图像: 49 | 50 | ![](images/normal_texture.png) 51 | 52 | 看起来更加生动,看核心代码,我们读入了每个像素的法向量,根据每个像素做计算,这里也用到了之前写的法向量变换。 53 | 54 | 55 | ```C++ 56 | struct Shader: public IShader{ 57 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 58 | mat<4,4,float> uniform_M; //Projection*ModelView 59 | mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() 60 | 61 | virtual Vec4f vertex(int iface, int nthvert){ 62 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 63 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 64 | return ViewPort*Projection*ModelView*gl_Vertex; // transform to screen coords 65 | } 66 | 67 | virtual bool fragment(Vec3f bar, TGAColor &color){ 68 | Vec2f uv = varying_uv*bar; //interpolate uv for current Pixel 69 | Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); // transform normal vector 70 | Vec3f l = proj<3>(uniform_M *embed<4>(light_dir)).normalize(); // transfrom light direction 71 | float intensity = std::max(0.f, n*l); 72 | color = model->diffuse(uv)*intensity; //uv 73 | return false; // do not discard pixel 74 | } 75 | }; 76 | ``` 77 | 78 | 还需要注意的是从图像中颜色我们用的是TGAColor,它的顺序是bgra.具体的可以看一下在model中新增的normal函数. 79 | 80 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/13_normal) 81 | 82 | 83 | ## Phong 光照模型 84 | 85 | Phong提出我们可以把最终光的效果看为: 86 | 87 | 环境 + 漫反射 + 镜面 = Phong 88 | 89 | ![](images/phong_light_model.png) 90 | 91 | 其实光我们也可以分模型:环境光、方向光、点光源。 92 | 93 | specular light 具体的计算式子是: 94 | 95 | $$ 96 | I_s = I_L(\frac{\overrightarrow{N} \cdot \overrightarrow{V}}{|\overrightarrow{N}| |\overrightarrow{V}|})^s 97 | $$ 98 | 99 | - $I_L$: 光射入方向 100 | - $\overrightarrow{N}$: 法向量 101 | - $\overrightarrow{V}$: 摄像机方向 102 | - s:高光 103 | 104 | 所有的光加起来的公式是: 105 | 106 | $$ 107 | I = I_A + \sum I_D\frac{I_D \cdot \overrightarrow{N}}{|I_D||\overrightarrow{N}|} + \sum I_L(\frac{\overrightarrow{N} \cdot \overrightarrow{V}}{|\overrightarrow{N}| |\overrightarrow{V}|})^s 108 | $$ 109 | 110 | 完整的关于这个式子推理可见: 111 | 112 | [[从零开始计算机图形学]之二漫反射](https://zhuanlan.zhihu.com/p/63343562) 113 | 114 | [[从零开始计算机图形学]之三高光](https://zhuanlan.zhihu.com/p/63350881) 115 | 116 | 我们同样用一幅图来表示图中每个像素所在点的反射(镜面)系数。加载上镜面系数,看最终结果: 117 | 118 | ![](images/phong_light.png) 119 | 120 | 可以看到右侧脸,脖子还是有比较明显的‘镜面高光’效果。cool. 121 | 122 | 核心代码: 123 | 124 | ```C++ 125 | struct Shader: public IShader{ 126 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 127 | mat<4,4,float> uniform_M; //Projection*ModelView 128 | mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() 129 | 130 | virtual Vec4f vertex(int iface, int nthvert){ 131 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 132 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 133 | return ViewPort*Projection*ModelView*gl_Vertex; // transform to screen coords 134 | } 135 | 136 | virtual bool fragment(Vec3f bar, TGAColor &color){ 137 | Vec2f uv = varying_uv*bar; //interpolate uv for current Pixel 138 | Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); // transform normal vector 139 | Vec3f l = proj<3>(uniform_M *embed<4>(light_dir)).normalize(); // transfrom light direction 140 | Vec3f r = (n*(n*l*2.f) - l).normalize(); // reflected light 141 | float spec = pow(std::max(r.z, 0.0f), model->specular(uv)); // we're looking from z-axis, 142 | float diff = std::max(0.f, n*l); 143 | TGAColor c = model->diffuse(uv); 144 | color = c; 145 | for (int i = 0; i < 3; i++) color[i] = std::min(5+c[i]*(diff+.6*spec),255); 146 | return false; // do not discard pixel 147 | } 148 | }; 149 | ``` 150 | 151 | 这里的fragment shader中我们增加了r作为镜面反射光,然后镜面系数是从图像中读出,同样,我们也只会取大于0的部分。 152 | 153 | ```C++ 154 | for (int i = 0; i < 3; i++) color[i] = std::min(5+c[i]*(diff+.6*spec),255); \\5 是环境光, 155 | \\diff, spec*0.6这里有点随意分配的意思(一般来说我们所有光的强度加在一起最好不要超过1), 156 | \\然后最大值是255,毕竟颜色不能超过这里。 157 | ``` 158 | 159 | 160 | 161 | [代码](https://github.com/KrisYu/tinyrender/tree/master/code/14_phong_light) 162 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | # 500行代码学懂OpenGL 2 | 3 | 4 | 项目来自[tinyrenderer](https://github.com/ssloy/tinyrenderer),作者尝试用500行代码来写一个tiny render,让我们来明白OpenGL是怎样工作的,这个系列可以理解为我的读书笔记或者心得或翻译。 5 | 6 | 这500多行代码包含的内容太多,它模仿了OpenGL,从零开始构建了一个光栅化渲染器,而一切都是始于从在屏幕上画点开始。 7 | 8 | 为了生成图片,在屏幕上画点,我们使用的是 TGAImage,它用起来很简单: 9 | 10 | 11 | 12 | ```C++ 13 | #include "tgaimage.h" 14 | 15 | const TGAColor white = TGAColor(255, 255, 255, 255); 16 | const TGAColor red = TGAColor(255, 0, 0, 255); 17 | 18 | int main(int argc, char** argv){ 19 | TGAImage image(100, 100, TGAImage::RGB); 20 | image.set(52, 41, red); 21 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 22 | image.write_tga_file("output.tga"); 23 | return 0; 24 | } 25 | ``` 26 | 27 | 最终就凭着画点的思想我们一步一步构建,目前我们可以生成的图像是这样: 28 | 29 | ![](images/phong_light.png) 30 | 31 | 32 | Very cool! 33 | 34 | -------------------------------------------------------------------------------- /code/00_prepare/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/00_prepare/main -------------------------------------------------------------------------------- /code/00_prepare/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | 3 | const TGAColor white = TGAColor(255, 255, 255, 255); 4 | const TGAColor red = TGAColor(255, 0, 0, 255); 5 | 6 | int main(int argc, char** argv){ 7 | TGAImage image(100, 100, TGAImage::RGB); 8 | image.set(52, 41, red); 9 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 10 | image.write_tga_file("output.tga"); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /code/00_prepare/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/00_prepare/output.tga -------------------------------------------------------------------------------- /code/00_prepare/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/01_points/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | typedef vec<2, int> Vec2i; 46 | typedef vec<3, float> Vec3f; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /code/01_points/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/01_points/main -------------------------------------------------------------------------------- /code/01_points/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | using std::cout; using std::endl; 5 | 6 | 7 | Model *model = NULL; 8 | const int width = 400; 9 | const int height = 400; 10 | 11 | const TGAColor white = TGAColor(255, 255, 255, 255); 12 | const TGAColor red = TGAColor(255, 0, 0, 255); 13 | 14 | 15 | Vec2i world2screen(Vec3f v){ 16 | return Vec2i(int((v.x+1.)*width/2.), int((v.y+1.)*height/2.)); 17 | } 18 | 19 | int main(int argc, char** argv){ 20 | if (2 == argc) { 21 | model = new Model(argv[1]); 22 | } else { 23 | model = new Model("obj/african_head.obj"); 24 | } 25 | 26 | TGAImage image(width, height, TGAImage::RGB); 27 | 28 | 29 | for (int i = 0; i != model->nverts(); i++) { 30 | Vec3f v = model->vert(i); 31 | Vec2i p = world2screen(v); 32 | image.set(p.x, p.y, white); 33 | } 34 | 35 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 36 | image.write_tga_file("output.tga"); 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /code/01_points/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "model.h" 5 | 6 | Model::Model(const char *filename): verts_() 7 | { 8 | std::ifstream in(filename, std::ifstream::in); 9 | if (in.is_open()) { 10 | std::string line; 11 | while (std::getline(in,line)) 12 | { 13 | char trash; 14 | std::istringstream iss(line); 15 | if(line.substr(0,2) == "v "){ 16 | iss >> trash; 17 | Vec3f v; 18 | for (int i = 0; i < 3; i++) iss >> v[i]; 19 | verts_.push_back(v); 20 | } 21 | } 22 | 23 | in.close(); 24 | } 25 | 26 | std::cout << "# v#" << verts_.size() << std::endl; 27 | 28 | } 29 | 30 | Model::~Model(){} 31 | 32 | int Model::nverts(){ 33 | return (int)verts_.size(); 34 | } 35 | 36 | Vec3f Model::vert(int i){ 37 | return verts_[i]; 38 | } 39 | -------------------------------------------------------------------------------- /code/01_points/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | public: 10 | Model(const char *filename); 11 | ~Model(); 12 | Vec3f vert(int i); 13 | int nverts(); 14 | }; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /code/01_points/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/01_points/output.tga -------------------------------------------------------------------------------- /code/01_points/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/02_wireframe/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | typedef vec<2, int> Vec2i; 46 | typedef vec<3, float> Vec3f; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /code/02_wireframe/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/02_wireframe/main -------------------------------------------------------------------------------- /code/02_wireframe/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | using std::cout; using std::endl; 5 | 6 | 7 | Model *model = NULL; 8 | const int width = 400; 9 | const int height = 400; 10 | 11 | const TGAColor white = TGAColor(255, 255, 255, 255); 12 | const TGAColor red = TGAColor(255, 0, 0, 255); 13 | 14 | void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) { 15 | bool steep = false; 16 | if (std::abs(x0-x1)x1) { // make it left−to−right 22 | std::swap(x0, x1); 23 | std::swap(y0, y1); 24 | } 25 | for (int x=x0; x<=x1; x++) { 26 | float t = (x-x0)/(float)(x1-x0); 27 | int y = y0 + (y1 - y0)*t; 28 | if (steep) { 29 | image.set(y, x, color); // if transposed, de−transpose 30 | } else { 31 | image.set(x, y, color); 32 | } 33 | } 34 | } 35 | 36 | int main(int argc, char** argv){ 37 | if (2 == argc) { 38 | model = new Model(argv[1]); 39 | } else { 40 | model = new Model("obj/african_head.obj"); 41 | } 42 | 43 | TGAImage image(width, height, TGAImage::RGB); 44 | 45 | for (int i = 0; i < model->nfaces(); i++) { 46 | std::vector face = model->face(i); 47 | // face: i0,i1,i2 of triangle 48 | for (int j = 0; j < 3; j++) { 49 | Vec3f v0 = model->vert(face[j]); 50 | // this % used for when j = 2 to get i2, i0 51 | Vec3f v1 = model->vert(face[(j+1)%3]); 52 | int x0 = (v0.x+1.)*width/2.; 53 | int y0 = (v0.y+1.)*height/2.; 54 | int x1 = (v1.x+1.)*width/2.; 55 | int y1 = (v1.y+1.)*height/2.; 56 | line(x0, y0, x1, y1, image, white); 57 | } 58 | } 59 | 60 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 61 | image.write_tga_file("output.tga"); 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /code/02_wireframe/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/02_wireframe/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/02_wireframe/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/02_wireframe/output.tga -------------------------------------------------------------------------------- /code/02_wireframe/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/03_filledtriangle/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | template vec operator+(vec lhs, const vec& rhs){ 46 | for (size_t i = DIM; i--; lhs[i]+=rhs[i]); 47 | return lhs; 48 | } 49 | 50 | template vec operator-(vec lhs, const vec& rhs){ 51 | for (size_t i = DIM; i--; lhs[i]-=rhs[i]); 52 | return lhs; 53 | } 54 | 55 | template vec operator*(vec lhs, const U& rhs) { 56 | for (size_t i = DIM; i--; lhs[i]*=rhs); 57 | return lhs; 58 | } 59 | 60 | 61 | typedef vec<2, int> Vec2i; 62 | typedef vec<3, float> Vec3f; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /code/03_filledtriangle/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/03_filledtriangle/main -------------------------------------------------------------------------------- /code/03_filledtriangle/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | using std::cout; using std::endl; 7 | 8 | 9 | Model *model = NULL; 10 | const int width = 600; 11 | const int height = 600; 12 | 13 | const TGAColor white = TGAColor(255, 255, 255, 255); 14 | const TGAColor red = TGAColor(255, 0, 0, 255); 15 | 16 | void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color) { 17 | if (t0.y==t1.y && t0.y==t2.y) return; // I dont care about degenerate triangles 18 | // sort the vertices, t0, t1, t2 lower−to−upper (bubblesort yay!) 19 | if (t0.y>t1.y) std::swap(t0, t1); 20 | if (t0.y>t2.y) std::swap(t0, t2); 21 | if (t1.y>t2.y) std::swap(t1, t2); 22 | int total_height = t2.y-t0.y; 23 | for (int i=0; it1.y-t0.y || t1.y==t0.y; 25 | int segment_height = second_half ? t2.y-t1.y : t1.y-t0.y; 26 | float alpha = (float)i/total_height; 27 | float beta = (float)(i-(second_half ? t1.y-t0.y : 0))/segment_height; // be careful: with above conditions no division by zero here 28 | Vec2i A = t0 + (t2-t0)*alpha; 29 | Vec2i B = second_half ? t1 + (t2-t1)*beta : t0 + (t1-t0)*beta; 30 | if (A.x>B.x) std::swap(A, B); 31 | for (int j=A.x; j<=B.x; j++) { 32 | image.set(j, t0.y+i, color); // attention, due to int casts t0.y+i != A.y 33 | } 34 | } 35 | } 36 | 37 | Vec2i world2screen(Vec3f v){ 38 | return Vec2i(int((v.x+1.)*width/2.), int((v.y+1.)*height/2.)); 39 | } 40 | 41 | int main(int argc, char** argv){ 42 | if (2 == argc) { 43 | model = new Model(argv[1]); 44 | } else { 45 | model = new Model("obj/african_head.obj"); 46 | } 47 | 48 | TGAImage image(width, height, TGAImage::RGB); 49 | srand(time(NULL)); 50 | 51 | for (int i = 0; i < model->nfaces(); i++) { 52 | std::vector face = model->face(i); 53 | Vec3f world_coords[3]; 54 | Vec2i screen_coords[3]; 55 | for (int j = 0; j < 3; j++) { 56 | world_coords[j] = model->vert(face[j]); 57 | screen_coords[j] = world2screen(world_coords[j]); 58 | } 59 | 60 | int r = std::rand() % 255; 61 | int g = std::rand() % 255; 62 | int b = std::rand() % 255; 63 | 64 | triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(r, g, b, 255)); 65 | } 66 | 67 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 68 | image.write_tga_file("output.tga"); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /code/03_filledtriangle/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/03_filledtriangle/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/03_filledtriangle/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/03_filledtriangle/output.tga -------------------------------------------------------------------------------- /code/03_filledtriangle/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/04_barycentricfilled/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | template vec operator+(vec lhs, const vec& rhs){ 46 | for (size_t i = DIM; i--; lhs[i]+=rhs[i]); 47 | return lhs; 48 | } 49 | 50 | template vec operator-(vec lhs, const vec& rhs){ 51 | for (size_t i = DIM; i--; lhs[i]-=rhs[i]); 52 | return lhs; 53 | } 54 | 55 | template vec operator*(vec lhs, const U& rhs) { 56 | for (size_t i = DIM; i--; lhs[i]*=rhs); 57 | return lhs; 58 | } 59 | 60 | template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2){ 61 | return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); 62 | } 63 | 64 | 65 | typedef vec<2, int> Vec2i; 66 | typedef vec<3, float> Vec3f; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /code/04_barycentricfilled/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/04_barycentricfilled/main -------------------------------------------------------------------------------- /code/04_barycentricfilled/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | using std::cout; using std::endl; 7 | 8 | 9 | Model *model = NULL; 10 | const int width = 600; 11 | const int height = 600; 12 | 13 | const TGAColor white = TGAColor(255, 255, 255, 255); 14 | const TGAColor red = TGAColor(255, 0, 0, 255); 15 | 16 | Vec3f barycentric(Vec2i A, Vec2i B, Vec2i C, Vec2i P) { 17 | Vec3f s[2]; 18 | for (int i=2; i--; ) { 19 | s[i][0] = C[i]-A[i]; 20 | s[i][1] = B[i]-A[i]; 21 | s[i][2] = A[i]-P[i]; 22 | } 23 | Vec3f u = cross(s[0], s[1]); 24 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 25 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 26 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 27 | } 28 | 29 | void triangle(Vec2i *pts, TGAImage &image, TGAColor color) { 30 | Vec2i bboxmin(image.get_width()-1, image.get_height()-1); 31 | Vec2i bboxmax(0, 0); 32 | Vec2i clamp(image.get_width()-1, image.get_height()-1); 33 | for (int i = 0; i < 3; i++) { 34 | for (int j = 0; j < 2; j++) { 35 | bboxmin[j] = std::max(0, std::min(bboxmin[j], pts[i][j])); 36 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 37 | } 38 | } 39 | Vec2i P; 40 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 41 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 42 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 43 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 44 | image.set(P.x, P.y, color); 45 | } 46 | } 47 | 48 | } 49 | 50 | Vec2i world2screen(Vec3f v){ 51 | return Vec2i(int((v.x+1.)*width/2.), int((v.y+1.)*height/2.)); 52 | } 53 | 54 | int main(int argc, char** argv){ 55 | if (2 == argc) { 56 | model = new Model(argv[1]); 57 | } else { 58 | model = new Model("obj/african_head.obj"); 59 | } 60 | 61 | TGAImage image(width, height, TGAImage::RGB); 62 | srand(time(NULL)); 63 | 64 | for (int i = 0; i < model->nfaces(); i++) { 65 | std::vector face = model->face(i); 66 | Vec3f world_coords[3]; 67 | Vec2i screen_coords[3]; 68 | for (int j = 0; j < 3; j++) { 69 | world_coords[j] = model->vert(face[j]); 70 | screen_coords[j] = world2screen(world_coords[j]); 71 | } 72 | 73 | int r = std::rand() % 255; 74 | int g = std::rand() % 255; 75 | int b = std::rand() % 255; 76 | 77 | triangle(screen_coords, image, TGAColor(r,g,b,255)); 78 | } 79 | 80 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 81 | image.write_tga_file("output.tga"); 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /code/04_barycentricfilled/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/04_barycentricfilled/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/04_barycentricfilled/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/04_barycentricfilled/output.tga -------------------------------------------------------------------------------- /code/04_barycentricfilled/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/05_simplelight/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | template vec operator+(vec lhs, const vec& rhs){ 46 | for (size_t i = DIM; i--; lhs[i]+=rhs[i]); 47 | return lhs; 48 | } 49 | 50 | template vec operator-(vec lhs, const vec& rhs){ 51 | for (size_t i = DIM; i--; lhs[i]-=rhs[i]); 52 | return lhs; 53 | } 54 | 55 | template vec operator*(vec lhs, const U& rhs) { 56 | for (size_t i = DIM; i--; lhs[i]*=rhs); 57 | return lhs; 58 | } 59 | 60 | template T operator*(const vec& lhs,const vec& rhs) { 61 | T ret = T(); 62 | for (size_t i = DIM; i--; ret+=lhs[i]*rhs[i]); 63 | return ret; 64 | } 65 | 66 | template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2){ 67 | return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); 68 | } 69 | 70 | 71 | typedef vec<2, int> Vec2i; 72 | typedef vec<3, float> Vec3f; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /code/05_simplelight/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/05_simplelight/main -------------------------------------------------------------------------------- /code/05_simplelight/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | using std::cout; using std::endl; 7 | 8 | 9 | Model *model = NULL; 10 | const int width = 600; 11 | const int height = 600; 12 | 13 | const TGAColor white = TGAColor(255, 255, 255, 255); 14 | const TGAColor red = TGAColor(255, 0, 0, 255); 15 | 16 | Vec3f barycentric(Vec2i A, Vec2i B, Vec2i C, Vec2i P) { 17 | Vec3f s[2]; 18 | for (int i=2; i--; ) { 19 | s[i][0] = C[i]-A[i]; 20 | s[i][1] = B[i]-A[i]; 21 | s[i][2] = A[i]-P[i]; 22 | } 23 | Vec3f u = cross(s[0], s[1]); 24 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 25 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 26 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 27 | } 28 | 29 | void triangle(Vec2i *pts, TGAImage &image, TGAColor color) { 30 | Vec2i bboxmin(image.get_width()-1, image.get_height()-1); 31 | Vec2i bboxmax(0, 0); 32 | Vec2i clamp(image.get_width()-1, image.get_height()-1); 33 | for (int i = 0; i < 3; i++) { 34 | for (int j = 0; j < 2; j++) { 35 | bboxmin[j] = std::max(0, std::min(bboxmin[j], pts[i][j])); 36 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 37 | } 38 | } 39 | Vec2i P; 40 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 41 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 42 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 43 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 44 | image.set(P.x, P.y, color); 45 | } 46 | } 47 | 48 | } 49 | 50 | Vec2i world2screen(Vec3f v){ 51 | return Vec2i(int((v.x+1.)*width/2.), int((v.y+1.)*height/2.)); 52 | } 53 | 54 | int main(int argc, char** argv){ 55 | if (2 == argc) { 56 | model = new Model(argv[1]); 57 | } else { 58 | model = new Model("obj/african_head.obj"); 59 | } 60 | 61 | TGAImage image(width, height, TGAImage::RGB); 62 | Vec3f light(0, 0, -1); 63 | 64 | for (int i = 0; i < model->nfaces(); i++) { 65 | std::vector face = model->face(i); 66 | Vec3f world_coords[3]; 67 | Vec2i screen_coords[3]; 68 | for (int j = 0; j < 3; j++) { 69 | world_coords[j] = model->vert(face[j]); 70 | screen_coords[j] = world2screen(world_coords[j]); 71 | } 72 | 73 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 74 | norm.normalize(); 75 | float intensity = light*norm; 76 | if (intensity > 0) { 77 | triangle(screen_coords, image, TGAColor(intensity*255,intensity*255,intensity*255,255)); 78 | } 79 | } 80 | 81 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 82 | image.write_tga_file("output.tga"); 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /code/05_simplelight/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/05_simplelight/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/05_simplelight/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/05_simplelight/output.tga -------------------------------------------------------------------------------- /code/05_simplelight/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | template vec operator+(vec lhs, const vec& rhs){ 46 | for (size_t i = DIM; i--; lhs[i]+=rhs[i]); 47 | return lhs; 48 | } 49 | 50 | template vec operator-(vec lhs, const vec& rhs){ 51 | for (size_t i = DIM; i--; lhs[i]-=rhs[i]); 52 | return lhs; 53 | } 54 | 55 | template vec operator*(vec lhs, const U& rhs) { 56 | for (size_t i = DIM; i--; lhs[i]*=rhs); 57 | return lhs; 58 | } 59 | 60 | template T operator*(const vec& lhs,const vec& rhs) { 61 | T ret = T(); 62 | for (size_t i = DIM; i--; ret+=lhs[i]*rhs[i]); 63 | return ret; 64 | } 65 | 66 | template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2){ 67 | return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); 68 | } 69 | 70 | typedef vec<2, int> Vec2i; 71 | typedef vec<3, float> Vec3f; 72 | typedef vec<2, float> Vec2f; 73 | #endif 74 | -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/06_simplelightzbuffer/main -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using std::cout; using std::endl; 8 | 9 | 10 | Model *model = NULL; 11 | const int width = 600; 12 | const int height = 600; 13 | 14 | const TGAColor white = TGAColor(255, 255, 255, 255); 15 | const TGAColor red = TGAColor(255, 0, 0, 255); 16 | 17 | Vec3f barycentric(Vec3f A, Vec3f B, Vec3f C, Vec3f P) { 18 | Vec3f s[2]; 19 | for (int i=2; i--; ) { 20 | s[i][0] = C[i]-A[i]; 21 | s[i][1] = B[i]-A[i]; 22 | s[i][2] = A[i]-P[i]; 23 | } 24 | Vec3f u = cross(s[0], s[1]); 25 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 26 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 27 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 28 | } 29 | 30 | void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color) { 31 | Vec2f bboxmin(std::numeric_limits::max(),std::numeric_limits::max()); 32 | Vec2f bboxmax(std::numeric_limits::min(),std::numeric_limits::min()); 33 | Vec2f clamp(image.get_width()-1, image.get_height()-1); 34 | for (int i = 0; i < 3; i++) { 35 | for (int j = 0; j < 2; j++) { 36 | bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j])); 37 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 38 | } 39 | } 40 | 41 | Vec3f P; 42 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 43 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 44 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 45 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 46 | P.z = 0; 47 | for (int i=0; i<3; i++) P.z += pts[i][2]*bc_screen[i]; 48 | if (zbuffer[int(P.x+P.y*width)] <= P.z) { 49 | image.set(P.x, P.y, color); 50 | zbuffer[int(P.x+P.y*width)] = P.z; 51 | } 52 | } 53 | } 54 | } 55 | 56 | Vec3f world2screen(Vec3f v) { 57 | // attention we have to change x and y to int otherwise they maybe too close for the for loop 58 | // we add 0.5 to rounding off 59 | return Vec3f(int((v.x+1.)*width/2.+.5), int((v.y+1.)*height/2.+.5), v.z); 60 | } 61 | 62 | int main(int argc, char** argv){ 63 | if (2 == argc) { 64 | model = new Model(argv[1]); 65 | } else { 66 | model = new Model("obj/african_head.obj"); 67 | } 68 | 69 | float *zbuffer = new float[width*height]; 70 | for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits::max()); 71 | 72 | TGAImage image(width, height, TGAImage::RGB); 73 | Vec3f light(0, 0, -1); 74 | 75 | for (int i = 0; i < model->nfaces(); i++) { 76 | std::vector face = model->face(i); 77 | Vec3f world_coords[3]; 78 | Vec3f screen_coords[3]; 79 | for (int j = 0; j < 3; j++) { 80 | world_coords[j] = model->vert(face[j]); 81 | screen_coords[j] = world2screen(world_coords[j]); 82 | } 83 | 84 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 85 | norm.normalize(); 86 | float intensity = light*norm; 87 | if (intensity > 0) { 88 | triangle(screen_coords, zbuffer, image, TGAColor(intensity*255,intensity*255,intensity*255,255)); 89 | } 90 | } 91 | 92 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 93 | image.write_tga_file("output.tga"); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/06_simplelightzbuffer/output.tga -------------------------------------------------------------------------------- /code/06_simplelightzbuffer/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/07_texture/geometry.h: -------------------------------------------------------------------------------- 1 | #ifndef GEOMETRY_H 2 | #define GEOMETRY_H 3 | #include 4 | #include 5 | #include 6 | 7 | template struct vec { 8 | vec() {for (size_t i = DIM; i--; data_[i] = T());} 9 | T& operator[](const size_t i) {assert(i struct vec<2,T>{ 17 | vec() : x(T()), y(T()) {} 18 | vec(T X, T Y): x(X), y(Y) {} 19 | template vec<2,T>(const vec<2,U> &v); 20 | T& operator[](const size_t i) {assert(i<2); return i <= 0 ? x : y;} 21 | const T& operator[](const size_t i) const {assert(i<2); return i <= 0 ? x : y;} 22 | 23 | T x, y; 24 | }; 25 | 26 | template struct vec<3,T>{ 27 | vec() : x(T()), y(T()), z(T()) {} 28 | vec(T X, T Y, T Z): x(X), y(Y), z(Z) {} 29 | template vec<3,T>(const vec<3,U> &v); 30 | T& operator[](const size_t i) {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 31 | const T& operator[](const size_t i) const {assert(i<3); return i <= 0 ? x : (1 == i? y : z);} 32 | float norm() {return std::sqrt(x*x+y*y+z*z);} 33 | vec<3, T> & normalize(T l=1) {*this = (*this)*(1/norm()); return *this;} 34 | 35 | T x, y, z; 36 | }; 37 | 38 | template std::ostream& operator<<(std::ostream& out, const vec&v){ 39 | for (size_t i = 0; i < DIM; i++) { 40 | out << v[i] << " "; 41 | } 42 | return out; 43 | } 44 | 45 | template vec operator+(vec lhs, const vec& rhs){ 46 | for (size_t i = DIM; i--; lhs[i]+=rhs[i]); 47 | return lhs; 48 | } 49 | 50 | template vec operator-(vec lhs, const vec& rhs){ 51 | for (size_t i = DIM; i--; lhs[i]-=rhs[i]); 52 | return lhs; 53 | } 54 | 55 | template vec operator*(vec lhs, const U& rhs) { 56 | for (size_t i = DIM; i--; lhs[i]*=rhs); 57 | return lhs; 58 | } 59 | 60 | template T operator*(const vec& lhs,const vec& rhs) { 61 | T ret = T(); 62 | for (size_t i = DIM; i--; ret+=lhs[i]*rhs[i]); 63 | return ret; 64 | } 65 | 66 | template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2){ 67 | return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); 68 | } 69 | 70 | typedef vec<2, int> Vec2i; 71 | typedef vec<3, float> Vec3f; 72 | typedef vec<2, float> Vec2f; 73 | #endif 74 | -------------------------------------------------------------------------------- /code/07_texture/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/07_texture/main -------------------------------------------------------------------------------- /code/07_texture/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using std::cout; using std::endl; 8 | 9 | 10 | Model *model = NULL; 11 | const int width = 600; 12 | const int height = 600; 13 | 14 | const TGAColor white = TGAColor(255, 255, 255, 255); 15 | const TGAColor red = TGAColor(255, 0, 0, 255); 16 | 17 | Vec3f barycentric(Vec3f A, Vec3f B, Vec3f C, Vec3f P) { 18 | Vec3f s[2]; 19 | for (int i=2; i--; ) { 20 | s[i][0] = C[i]-A[i]; 21 | s[i][1] = B[i]-A[i]; 22 | s[i][2] = A[i]-P[i]; 23 | } 24 | Vec3f u = cross(s[0], s[1]); 25 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 26 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 27 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 28 | } 29 | 30 | void triangle(Vec3f *pts, Vec2f *texts, float *zbuffer, TGAImage &image) { 31 | Vec2f bboxmin(std::numeric_limits::max(),std::numeric_limits::max()); 32 | Vec2f bboxmax(std::numeric_limits::min(),std::numeric_limits::min()); 33 | Vec2f clamp(image.get_width()-1, image.get_height()-1); 34 | for (int i = 0; i < 3; i++) { 35 | for (int j = 0; j < 2; j++) { 36 | bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j])); 37 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 38 | } 39 | } 40 | Vec3f P; 41 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 42 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 43 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 44 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 45 | P.z = 0; 46 | Vec2f Ptext(0,0); 47 | for (int i=0; i<3; i++) P.z += pts[i][2]*bc_screen[i]; 48 | for (int i=0; i<3; i++) Ptext[0] += texts[i][0]*bc_screen[i]; 49 | for (int i=0; i<3; i++) Ptext[1] += texts[i][1]*bc_screen[i]; 50 | if (zbuffer[int(P.x+P.y*width)] <= P.z) { 51 | TGAColor color = model->diffuse(Ptext); 52 | image.set(P.x, P.y, color); 53 | zbuffer[int(P.x+P.y*width)] = P.z; 54 | } 55 | } 56 | } 57 | } 58 | 59 | Vec3f world2screen(Vec3f v) { 60 | // attention we have to change x and y to int otherwise they maybe too close for the for loop 61 | // we add 0.5 to rounding off 62 | return Vec3f(int((v.x+1.)*width/2.+.5), int((v.y+1.)*height/2.+.5), v.z); 63 | } 64 | 65 | int main(int argc, char** argv){ 66 | if (2 == argc) { 67 | model = new Model(argv[1]); 68 | } else { 69 | model = new Model("obj/african_head.obj"); 70 | } 71 | 72 | float *zbuffer = new float[width*height]; 73 | for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits::max()); 74 | 75 | TGAImage image(width, height, TGAImage::RGB); 76 | Vec3f light(0, 0, -1); 77 | 78 | for (int i = 0; i < model->nfaces(); i++) { 79 | std::vector face = model->face(i); 80 | Vec3f world_coords[3]; 81 | Vec3f screen_coords[3]; 82 | Vec2f texts[3]; 83 | for (int j = 0; j < 3; j++) { 84 | world_coords[j] = model->vert(face[2*j]); 85 | screen_coords[j] = world2screen(world_coords[j]); 86 | texts[j] = model->uv(face[2*j+1]); 87 | } 88 | triangle(screen_coords, texts, zbuffer, image); 89 | } 90 | 91 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 92 | image.write_tga_file("output.tga"); 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /code/07_texture/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), uvs_(), diffusemap_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if(line.substr(0,2) == "vt"){ 22 | iss >> trash >> trash; 23 | Vec2f uv; 24 | for (int i = 0; i < 2; i++) iss>> uv[i]; 25 | uvs_.push_back(uv); 26 | } else if (line.substr(0,2) == "f "){ 27 | iss >> trash; 28 | std::vector f; 29 | int idxt, idx,itrash; 30 | while (iss >> idx >> trash >> idxt >> trash >> itrash) { 31 | idx--; // in wavefront obj all indices start at 1, not zero 32 | idxt--; // in wavefront obj all indices start at 1, not zero 33 | f.push_back(idx); 34 | f.push_back(idxt); 35 | } 36 | // face is now (point0, texture0, point1, texture1, point2, texture2) 37 | faces_.push_back(f); 38 | } 39 | } 40 | 41 | load_texture(filename, "_diffuse.tga", diffusemap_); 42 | in.close(); 43 | } 44 | 45 | std::cout << "# v#" << verts_.size() << std::endl; 46 | 47 | } 48 | 49 | Model::~Model(){} 50 | 51 | int Model::nverts(){ 52 | return (int)verts_.size(); 53 | } 54 | 55 | Vec3f Model::vert(int i){ 56 | return verts_[i]; 57 | } 58 | 59 | void Model::load_texture(std::string filename, const char *suffix, TGAImage& img){ 60 | std::string textfile(filename); 61 | size_t dot = textfile.find_last_of("."); 62 | if (dot != std::string::npos) { 63 | textfile = textfile.substr(0, dot) + std::string(suffix); 64 | std::cout << "textfile file" << textfile << "loading " << 65 | (img.read_tga_file(textfile.c_str()) ? "ok": "failed") << std::endl; 66 | img.flip_vertically(); 67 | } 68 | } 69 | 70 | TGAColor Model::diffuse(Vec2f uv){ 71 | Vec2i uvwh(uv[0]*diffusemap_.get_width(), uv[1]*diffusemap_.get_height()); 72 | return diffusemap_.get(uvwh[0],uvwh[1]); 73 | } 74 | 75 | int Model::nfaces(){ 76 | return (int)faces_.size(); 77 | } 78 | 79 | std::vector Model::face(int idx){ 80 | return faces_[idx]; 81 | } 82 | 83 | Vec2f Model::uv(int i){ 84 | return uvs_[i]; 85 | } 86 | -------------------------------------------------------------------------------- /code/07_texture/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | #include "tgaimage.h" 7 | 8 | class Model { 9 | private: 10 | std::vector verts_; 11 | std::vector> faces_; 12 | std::vector uvs_; 13 | TGAImage diffusemap_; 14 | void load_texture(std::string filename, const char *suffix, TGAImage &img); 15 | public: 16 | Model(const char *filename); 17 | ~Model(); 18 | int nverts(); 19 | int nfaces(); 20 | Vec3f vert(int i); 21 | Vec2f uv(int i); 22 | std::vector face(int idx); 23 | TGAColor diffuse(Vec2f uv); 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /code/07_texture/obj/african_head_diffuse.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/07_texture/obj/african_head_diffuse.tga -------------------------------------------------------------------------------- /code/07_texture/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/07_texture/output.tga -------------------------------------------------------------------------------- /code/07_texture/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/08_math_involved copy/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/08_math_involved copy/main -------------------------------------------------------------------------------- /code/08_math_involved copy/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using std::cout; using std::endl; 8 | 9 | const TGAColor white = TGAColor(255, 255, 255, 255); 10 | const TGAColor red = TGAColor(255, 0, 0, 255); 11 | 12 | 13 | Model *model = NULL; 14 | const int width = 600; 15 | const int height = 600; 16 | 17 | Vec3f light_dir(0, 0, -1); 18 | Vec3f eye(0, 0, 3); 19 | Vec3f center(0, 0, 0); 20 | Vec3f up(0, 1, 0); 21 | 22 | Matrix ModelView; 23 | Matrix ViewPort; 24 | Matrix Projection; 25 | 26 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 27 | Vec3f z = (eye-center).normalize(); 28 | Vec3f x = cross(up,z).normalize(); 29 | Vec3f y = cross(z,x).normalize(); 30 | Matrix Minv = Matrix::identity(); 31 | Matrix Tr = Matrix::identity(); 32 | for (int i=0; i<3; i++) { 33 | Minv[0][i] = x[i]; 34 | Minv[1][i] = y[i]; 35 | Minv[2][i] = z[i]; 36 | Tr[i][3] = -center[i]; 37 | } 38 | ModelView = Minv*Tr; 39 | } 40 | 41 | void viewport(int x, int y, int w, int h){ 42 | ViewPort = Matrix::identity(); 43 | ViewPort[0][3] = x + w/2.f; 44 | ViewPort[1][3] = y + h/2.f; 45 | ViewPort[2][3] = 1.f; 46 | ViewPort[0][0] = w/2.f; 47 | ViewPort[1][1] = h/2.f; 48 | ViewPort[2][2] = 1.f; 49 | } 50 | 51 | void projection(float coeff) { 52 | Projection = Matrix::identity(); 53 | Projection[3][2] = coeff; 54 | } 55 | 56 | Vec3f barycentric(Vec3f A, Vec3f B, Vec3f C, Vec3f P) { 57 | Vec3f s[2]; 58 | for (int i=2; i--; ) { 59 | s[i][0] = C[i]-A[i]; 60 | s[i][1] = B[i]-A[i]; 61 | s[i][2] = A[i]-P[i]; 62 | } 63 | Vec3f u = cross(s[0], s[1]); 64 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 65 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 66 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 67 | } 68 | 69 | void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color) { 70 | Vec2f bboxmin(std::numeric_limits::max(),std::numeric_limits::max()); 71 | Vec2f bboxmax(std::numeric_limits::min(),std::numeric_limits::min()); 72 | Vec2f clamp(image.get_width()-1, image.get_height()-1); 73 | for (int i = 0; i < 3; i++) { 74 | for (int j = 0; j < 2; j++) { 75 | bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j])); 76 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 77 | } 78 | } 79 | 80 | Vec3f P; 81 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 82 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 83 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 84 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 85 | P.z = 0; 86 | for (int i=0; i<3; i++) P.z += pts[i][2]*bc_screen[i]; 87 | // cout << P.z << endl; 88 | cout << 1/P.z << endl; 89 | if (zbuffer[int(P.x+P.y*width)] > 1/P.z) { 90 | image.set(P.x, P.y, color); 91 | zbuffer[int(P.x+P.y*width)] = 1/P.z; 92 | } 93 | } 94 | } 95 | } 96 | 97 | Vec3f world2screen(Vec3f v) { 98 | Vec4f gl_vertex = embed<4>(v); // embed Vec3f to homogenius coordinates 99 | gl_vertex = ViewPort * Projection * ModelView * gl_vertex; // transform it to screen coordinates 100 | Vec3f v3 = proj<3>(gl_vertex/gl_vertex[3]); // transfromed vec3f vertex 101 | return Vec3f(int(v3.x+.5),int(v3.y+.5),v3.z); 102 | } 103 | 104 | int main(int argc, char** argv){ 105 | if (2 == argc) { 106 | model = new Model(argv[1]); 107 | } else { 108 | model = new Model("obj/african_head.obj"); 109 | } 110 | 111 | float *zbuffer = new float[width*height]; 112 | for (int i=width*height; i--; zbuffer[i] = std::numeric_limits::max()); 113 | 114 | lookat(eye, center, up); 115 | viewport(width/8, height/8, width*3/4, height*3/4); 116 | projection(-1.f/3); 117 | 118 | TGAImage image(width, height, TGAImage::RGB); 119 | 120 | for (int i = 0; i < model->nfaces(); i++) { 121 | std::vector face = model->face(i); 122 | Vec3f world_coords[3]; 123 | Vec3f screen_coords[3]; 124 | for (int j = 0; j < 3; j++) { 125 | world_coords[j] = model->vert(face[j]); 126 | screen_coords[j] = world2screen(world_coords[j]); 127 | } 128 | 129 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 130 | norm.normalize(); 131 | float intensity = light_dir*norm; 132 | if (intensity > 0) { 133 | triangle(screen_coords, zbuffer, image, TGAColor(intensity*255,intensity*255,intensity*255,255)); 134 | } 135 | } 136 | 137 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 138 | image.write_tga_file("output.tga"); 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /code/08_math_involved copy/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/08_math_involved copy/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/08_math_involved copy/output2.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/08_math_involved copy/output2.tga -------------------------------------------------------------------------------- /code/08_math_involved copy/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/08_math_involved/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/08_math_involved/main -------------------------------------------------------------------------------- /code/08_math_involved/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using std::cout; using std::endl; 8 | 9 | const TGAColor white = TGAColor(255, 255, 255, 255); 10 | const TGAColor red = TGAColor(255, 0, 0, 255); 11 | 12 | 13 | Model *model = NULL; 14 | const int width = 600; 15 | const int height = 600; 16 | 17 | Vec3f light_dir(0, 0, -1); 18 | Vec3f eye(0, 0, 3); 19 | Vec3f center(0, 0, 0); 20 | Vec3f up(0, 1, 0); 21 | 22 | Matrix ModelView; 23 | Matrix ViewPort; 24 | Matrix Projection; 25 | 26 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 27 | Vec3f z = (eye-center).normalize(); 28 | Vec3f x = cross(up,z).normalize(); 29 | Vec3f y = cross(z,x).normalize(); 30 | Matrix Minv = Matrix::identity(); 31 | Matrix Tr = Matrix::identity(); 32 | for (int i=0; i<3; i++) { 33 | Minv[0][i] = x[i]; 34 | Minv[1][i] = y[i]; 35 | Minv[2][i] = z[i]; 36 | Tr[i][3] = -center[i]; 37 | } 38 | ModelView = Minv*Tr; 39 | } 40 | 41 | void viewport(int x, int y, int w, int h){ 42 | ViewPort = Matrix::identity(); 43 | ViewPort[0][3] = x + w/2.f; 44 | ViewPort[1][3] = y + h/2.f; 45 | ViewPort[2][3] = 1.f; 46 | ViewPort[0][0] = w/2.f; 47 | ViewPort[1][1] = h/2.f; 48 | ViewPort[2][2] = 1.f; 49 | } 50 | 51 | void projection(float coeff) { 52 | Projection = Matrix::identity(); 53 | Projection[3][2] = coeff; 54 | } 55 | 56 | Vec3f barycentric(Vec3f A, Vec3f B, Vec3f C, Vec3f P) { 57 | Vec3f s[2]; 58 | for (int i=2; i--; ) { 59 | s[i][0] = C[i]-A[i]; 60 | s[i][1] = B[i]-A[i]; 61 | s[i][2] = A[i]-P[i]; 62 | } 63 | Vec3f u = cross(s[0], s[1]); 64 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 65 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 66 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 67 | } 68 | 69 | void triangle(Vec3f *pts, float *zbuffer, TGAImage &image, TGAColor color) { 70 | Vec2f bboxmin(std::numeric_limits::max(),std::numeric_limits::max()); 71 | Vec2f bboxmax(std::numeric_limits::min(),std::numeric_limits::min()); 72 | Vec2f clamp(image.get_width()-1, image.get_height()-1); 73 | for (int i = 0; i < 3; i++) { 74 | for (int j = 0; j < 2; j++) { 75 | bboxmin[j] = std::max(0.f, std::min(bboxmin[j], pts[i][j])); 76 | bboxmax[j] = std::min(clamp[j], std::max(bboxmax[j], pts[i][j])); 77 | } 78 | } 79 | 80 | Vec3f P; 81 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 82 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 83 | Vec3f bc_screen = barycentric(pts[0], pts[1], pts[2], P); 84 | if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0 ) continue; 85 | P.z = 0; 86 | for (int i=0; i<3; i++) P.z += pts[i][2]*bc_screen[i]; 87 | if (zbuffer[int(P.x+P.y*width)] <= P.z) { 88 | image.set(P.x, P.y, color); 89 | zbuffer[int(P.x+P.y*width)] = P.z; 90 | } 91 | } 92 | } 93 | } 94 | 95 | Vec3f world2screen(Vec3f v) { 96 | Vec4f gl_vertex = embed<4>(v); // embed Vec3f to homogenius coordinates 97 | gl_vertex = ViewPort * Projection * ModelView * gl_vertex; // transform it to screen coordinates 98 | Vec3f v3 = proj<3>(gl_vertex/gl_vertex[3]); // transfromed vec3f vertex 99 | return Vec3f(int(v3.x+.5),int(v3.y+.5),v3.z); 100 | } 101 | 102 | int main(int argc, char** argv){ 103 | if (2 == argc) { 104 | model = new Model(argv[1]); 105 | } else { 106 | model = new Model("obj/african_head.obj"); 107 | } 108 | 109 | float *zbuffer = new float[width*height]; 110 | for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits::max()); 111 | 112 | lookat(eye, center, up); 113 | viewport(width/8, height/8, width*3/4, height*3/4); 114 | projection(-1.f/3); 115 | 116 | TGAImage image(width, height, TGAImage::RGB); 117 | 118 | for (int i = 0; i < model->nfaces(); i++) { 119 | std::vector face = model->face(i); 120 | Vec3f world_coords[3]; 121 | Vec3f screen_coords[3]; 122 | for (int j = 0; j < 3; j++) { 123 | world_coords[j] = model->vert(face[j]); 124 | screen_coords[j] = world2screen(world_coords[j]); 125 | } 126 | 127 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 128 | norm.normalize(); 129 | float intensity = light_dir*norm; 130 | if (intensity > 0) { 131 | triangle(screen_coords, zbuffer, image, TGAColor(intensity*255,intensity*255,intensity*255,255)); 132 | } 133 | } 134 | 135 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 136 | image.write_tga_file("output.tga"); 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /code/08_math_involved/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/08_math_involved/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/08_math_involved/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/08_math_involved/output.tga -------------------------------------------------------------------------------- /code/08_math_involved/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/09_more_math/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/09_more_math/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/09_more_math/main -------------------------------------------------------------------------------- /code/09_more_math/main.cpp: -------------------------------------------------------------------------------- 1 | #include "tgaimage.h" 2 | #include "model.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | using std::cout; using std::endl; 8 | 9 | const TGAColor white = TGAColor(255, 255, 255, 255); 10 | const TGAColor red = TGAColor(255, 0, 0, 255); 11 | 12 | 13 | Model *model = NULL; 14 | const int width = 600; 15 | const int height = 600; 16 | 17 | Vec3f light_dir(0, 0, -1); 18 | Vec3f eye(0, 0, 3); 19 | Vec3f center(0, 0, 0); 20 | Vec3f up(0, 1, 0); 21 | 22 | Matrix ModelView; 23 | Matrix ViewPort; 24 | Matrix Projection; 25 | 26 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 27 | Vec3f z = (eye-center).normalize(); 28 | Vec3f x = cross(up,z).normalize(); 29 | Vec3f y = cross(z,x).normalize(); 30 | Matrix Minv = Matrix::identity(); 31 | Matrix Tr = Matrix::identity(); 32 | for (int i=0; i<3; i++) { 33 | Minv[0][i] = x[i]; 34 | Minv[1][i] = y[i]; 35 | Minv[2][i] = z[i]; 36 | Tr[i][3] = -center[i]; 37 | } 38 | ModelView = Minv*Tr; 39 | } 40 | 41 | void viewport(int x, int y, int w, int h){ 42 | ViewPort = Matrix::identity(); 43 | ViewPort[0][3] = x + w/2.f; 44 | ViewPort[1][3] = y + h/2.f; 45 | ViewPort[2][3] = 255/2.f; 46 | ViewPort[0][0] = w/2.f; 47 | ViewPort[1][1] = h/2.f; 48 | ViewPort[2][2] = 255/2.f; 49 | } 50 | 51 | void projection(float coeff) { 52 | Projection = Matrix::identity(); 53 | Projection[3][2] = coeff; 54 | } 55 | 56 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 57 | Vec3f s[2]; 58 | for (int i=2; i--; ) { 59 | s[i][0] = C[i]-A[i]; 60 | s[i][1] = B[i]-A[i]; 61 | s[i][2] = A[i]-P[i]; 62 | } 63 | Vec3f u = cross(s[0], s[1]); 64 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 65 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 66 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 67 | } 68 | 69 | void triangle(Vec4f *pts, TGAImage &image, TGAImage &zbuffer, TGAColor color) { 70 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 71 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 72 | for (int i = 0; i < 3; i++) { 73 | for (int j = 0; j < 2; j++) { 74 | // x/w y/w 75 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 76 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 77 | } 78 | } 79 | 80 | Vec2i P; 81 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 82 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 83 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 84 | proj<2>(pts[2]/pts[2][3]), P); 85 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 86 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 87 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 88 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 89 | image.set(P.x, P.y, color); 90 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 91 | } 92 | } 93 | } 94 | 95 | Vec4f world2screen(Vec3f v) { 96 | Vec4f gl_vertex = embed<4>(v); // embed Vec3f to homogenius coordinates 97 | gl_vertex = ViewPort * Projection * ModelView * gl_vertex; // transform it to screen coordinates 98 | return gl_vertex; 99 | } 100 | 101 | int main(int argc, char** argv){ 102 | if (2 == argc) { 103 | model = new Model(argv[1]); 104 | } else { 105 | model = new Model("obj/african_head.obj"); 106 | } 107 | 108 | lookat(eye, center, up); 109 | viewport(width/8, height/8, width*3/4, height*3/4); 110 | projection(-1.f/3); 111 | 112 | TGAImage image(width, height, TGAImage::RGB); 113 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 114 | 115 | for (int i = 0; i < model->nfaces(); i++) { 116 | std::vector face = model->face(i); 117 | Vec3f world_coords[3]; 118 | Vec4f screen_coords[3]; 119 | for (int j = 0; j < 3; j++) { 120 | world_coords[j] = model->vert(face[j]); 121 | screen_coords[j] = world2screen(world_coords[j]); 122 | } 123 | 124 | Vec3f norm = cross(world_coords[2] - world_coords[0], world_coords[1] - world_coords[0]); 125 | norm.normalize(); 126 | float intensity = light_dir*norm; 127 | if (intensity > 0) { 128 | triangle(screen_coords, image, zbuffer, TGAColor(intensity*255,intensity*255,intensity*255,255)); 129 | } 130 | } 131 | 132 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 133 | zbuffer.flip_vertically(); 134 | image.write_tga_file("output.tga"); 135 | zbuffer.write_tga_file("zbuffer.tga"); 136 | 137 | delete model; 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /code/09_more_math/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "f "){ 22 | iss >> trash; 23 | std::vector f; 24 | int idx,itrash; 25 | while (iss >> idx >> trash >> itrash >> trash >> itrash) { 26 | idx--; // in wavefront obj all indices start at 1, not zero 27 | f.push_back(idx); 28 | } 29 | faces_.push_back(f); 30 | } 31 | } 32 | 33 | in.close(); 34 | } 35 | 36 | std::cout << "# v#" << verts_.size() << std::endl; 37 | 38 | } 39 | 40 | Model::~Model(){} 41 | 42 | int Model::nverts(){ 43 | return (int)verts_.size(); 44 | } 45 | 46 | Vec3f Model::vert(int i){ 47 | return verts_[i]; 48 | } 49 | 50 | int Model::nfaces(){ 51 | return (int)faces_.size(); 52 | } 53 | 54 | std::vector Model::face(int idx){ 55 | return faces_[idx]; 56 | } 57 | -------------------------------------------------------------------------------- /code/09_more_math/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; 10 | public: 11 | Model(const char *filename); 12 | ~Model(); 13 | int nverts(); 14 | int nfaces(); 15 | Vec3f vert(int i); 16 | std::vector face(int idx); 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /code/09_more_math/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/09_more_math/output.tga -------------------------------------------------------------------------------- /code/09_more_math/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/09_more_math/zbuffer.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/09_more_math/zbuffer.tga -------------------------------------------------------------------------------- /code/10_flat_shading/Makefile: -------------------------------------------------------------------------------- 1 | SYSCONF_LINK = g++ -std=c++11 2 | CPPFLAGS = 3 | LDFLAGS = 4 | LIBS = -lm 5 | 6 | DESTDIR = ./ 7 | TARGET = main 8 | 9 | OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) 10 | 11 | all: $(DESTDIR)$(TARGET) 12 | 13 | $(DESTDIR)$(TARGET): $(OBJECTS) 14 | $(SYSCONF_LINK) -Wall $(LDFLAGS) -o $(DESTDIR)$(TARGET) $(OBJECTS) $(LIBS) 15 | 16 | $(OBJECTS): %.o: %.cpp 17 | $(SYSCONF_LINK) -Wall $(CPPFLAGS) -c $(CFLAGS) $< -o $@ 18 | 19 | clean: 20 | -rm -f $(OBJECTS) 21 | -rm -f $(TARGET) 22 | -rm -f *.tga 23 | 24 | -------------------------------------------------------------------------------- /code/10_flat_shading/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/10_flat_shading/geometry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/geometry.o -------------------------------------------------------------------------------- /code/10_flat_shading/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/main -------------------------------------------------------------------------------- /code/10_flat_shading/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tgaimage.h" 5 | #include "model.h" 6 | #include "our_gl.h" 7 | #include "geometry.h" 8 | 9 | #define CLAMP(t) ((t>1.f)?1.f:((t<0.f)?0.f:t)) 10 | 11 | Model *model = NULL; 12 | const int width = 600; 13 | const int height = 600; 14 | 15 | Vec3f light_dir(1, 1, 1); 16 | Vec3f eye(0, -1, 3); 17 | Vec3f center(0, 0, 0); 18 | Vec3f up(0, 1, 0); 19 | 20 | struct FlatShader: public IShader{ 21 | mat<3,3,float> varying_tri; // record the transformed triangle 22 | 23 | virtual Vec4f vertex(int iface, int nthvert){ 24 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); 25 | gl_Vertex = Projection*ModelView*gl_Vertex; 26 | varying_tri.set_col(nthvert, proj<3>(gl_Vertex/gl_Vertex[3])); // 27 | gl_Vertex = ViewPort*gl_Vertex; 28 | return gl_Vertex; 29 | } 30 | 31 | virtual bool fragment(Vec3f bar, TGAColor &color){ 32 | Vec3f n = cross(varying_tri.col(1)- varying_tri.col(0),varying_tri.col(2)- 33 | varying_tri.col(0)).normalize(); 34 | float intensity = CLAMP(n*light_dir); 35 | color = TGAColor(255,255,255)*intensity; 36 | return false; 37 | } 38 | }; 39 | 40 | int main(int argc, char** argv){ 41 | if (2 == argc) { 42 | model = new Model(argv[1]); 43 | } else { 44 | model = new Model("obj/african_head.obj"); 45 | } 46 | 47 | lookat(eye, center, up); 48 | viewport(width/8, height/8, width*3/4, height*3/4); 49 | projection(-1.f/(eye-center).norm()); 50 | light_dir.normalize(); 51 | 52 | TGAImage image(width, height, TGAImage::RGB); 53 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 54 | 55 | FlatShader shader; 56 | for (int i = 0; i < model->nfaces(); i++) { 57 | std::vector face = model->face(i); 58 | Vec4f screen_coords[3]; 59 | for (int j = 0; j < 3; j++) { 60 | screen_coords[j] = shader.vertex(i, j); 61 | } 62 | triangle(screen_coords, shader, image, zbuffer); 63 | } 64 | 65 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 66 | zbuffer.flip_vertically(); 67 | image.write_tga_file("output.tga"); 68 | zbuffer.write_tga_file("zbuffer.tga"); 69 | 70 | delete model; 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /code/10_flat_shading/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/main.o -------------------------------------------------------------------------------- /code/10_flat_shading/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), norms_(), uv_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "vn") { 22 | iss >> trash >> trash; 23 | Vec3f n; 24 | for (int i = 0; i < 3; i++) iss >> n[i]; 25 | norms_.push_back(n); 26 | }else if (line.substr(0,2)=="vt") { 27 | iss >> trash >> trash; 28 | Vec2f uv; 29 | for (int i = 0; i < 2; i++) iss >> uv[i]; 30 | uv_.push_back(uv); 31 | }else if (line.substr(0,2) == "f "){ 32 | std::vector f; 33 | Vec3i tmp; 34 | iss >> trash; 35 | while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) { 36 | for (int i = 0; i < 3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero 37 | f.push_back(tmp); 38 | } 39 | faces_.push_back(f); 40 | } 41 | } 42 | in.close(); 43 | } 44 | 45 | std::cerr << "# v#" << verts_.size() <<" f# " << faces_.size() <<" vt# "<< 46 | uv_.size() << " vn# " << norms_.size() << std::endl; 47 | } 48 | 49 | Model::~Model(){} 50 | 51 | int Model::nverts(){ 52 | return (int)verts_.size(); 53 | } 54 | 55 | int Model::nfaces(){ 56 | return (int)faces_.size(); 57 | } 58 | 59 | std::vector Model::face(int idx){ 60 | std::vector face; 61 | for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); 62 | return face; 63 | } 64 | 65 | Vec3f Model::vert(int iface, int nthvert){ 66 | return verts_[faces_[iface][nthvert][0]]; 67 | } 68 | 69 | Vec2f Model::uv(int iface, int nthvert){ 70 | return uv_[faces_[iface][nthvert][1]]; 71 | } 72 | 73 | Vec3f Model::normal(int iface, int nthvert){ 74 | int idx = faces_[iface][nthvert][2]; 75 | return norms_[idx].normalize(); 76 | } 77 | -------------------------------------------------------------------------------- /code/10_flat_shading/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; //Vec3i means vertex/uv/normal 10 | std::vector norms_; 11 | std::vector uv_; 12 | public: 13 | Model(const char *filename); 14 | ~Model(); 15 | int nverts(); 16 | int nfaces(); 17 | Vec2f uv(int iface, int nthvert); 18 | Vec3f vert(int iface, int nthvert); 19 | Vec3f normal(int iface, int nthvert); 20 | std::vector face(int idx); 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /code/10_flat_shading/model.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/model.o -------------------------------------------------------------------------------- /code/10_flat_shading/our_gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "our_gl.h" 5 | 6 | Matrix ModelView; 7 | Matrix ViewPort; 8 | Matrix Projection; 9 | 10 | IShader::~IShader() {} 11 | 12 | void viewport(int x, int y, int w, int h){ 13 | ViewPort = Matrix::identity(); 14 | ViewPort[0][3] = x + w/2.f; 15 | ViewPort[1][3] = y + h/2.f; 16 | ViewPort[2][3] = 255/2.f; 17 | ViewPort[0][0] = w/2.f; 18 | ViewPort[1][1] = h/2.f; 19 | ViewPort[2][2] = 255/2.f; 20 | } 21 | 22 | void projection(float coeff) { 23 | Projection = Matrix::identity(); 24 | Projection[3][2] = coeff; 25 | } 26 | 27 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 28 | Vec3f z = (eye-center).normalize(); 29 | Vec3f x = cross(up,z).normalize(); 30 | Vec3f y = cross(z,x).normalize(); 31 | Matrix Minv = Matrix::identity(); 32 | Matrix Tr = Matrix::identity(); 33 | for (int i=0; i<3; i++) { 34 | Minv[0][i] = x[i]; 35 | Minv[1][i] = y[i]; 36 | Minv[2][i] = z[i]; 37 | Tr[i][3] = -center[i]; 38 | } 39 | ModelView = Minv*Tr; 40 | } 41 | 42 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 43 | Vec3f s[2]; 44 | for (int i=2; i--; ) { 45 | s[i][0] = C[i]-A[i]; 46 | s[i][1] = B[i]-A[i]; 47 | s[i][2] = A[i]-P[i]; 48 | } 49 | Vec3f u = cross(s[0], s[1]); 50 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 51 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 52 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 53 | } 54 | 55 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 56 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 57 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 58 | for (int i = 0; i < 3; i++) { 59 | for (int j = 0; j < 2; j++) { 60 | // x/w y/w 61 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 62 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 63 | } 64 | } 65 | 66 | Vec2i P; 67 | TGAColor color; 68 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 69 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 70 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 71 | proj<2>(pts[2]/pts[2][3]), P); 72 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 73 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 74 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 75 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 76 | bool discard = shader.fragment(c, color); 77 | if (!discard) { 78 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 79 | image.set(P.x, P.y, color); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/10_flat_shading/our_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef OURGL_H 2 | #define OURGL_H 3 | #include "tgaimage.h" 4 | #include "geometry.h" 5 | 6 | extern Matrix ModelView; 7 | extern Matrix ViewPort; 8 | extern Matrix Projection; 9 | 10 | void viewport(int x, int y, int w, int h); 11 | void projection(float coeff =0.f); // coeff = -1/c 12 | void lookat(Vec3f eye, Vec3f center, Vec3f up); 13 | 14 | struct IShader { 15 | virtual ~IShader(); 16 | virtual Vec4f vertex(int iface, int nthvert) = 0; 17 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 18 | }; 19 | 20 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/10_flat_shading/our_gl.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/our_gl.o -------------------------------------------------------------------------------- /code/10_flat_shading/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/output.tga -------------------------------------------------------------------------------- /code/10_flat_shading/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/10_flat_shading/tgaimage.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/tgaimage.o -------------------------------------------------------------------------------- /code/10_flat_shading/zbuffer.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/10_flat_shading/zbuffer.tga -------------------------------------------------------------------------------- /code/11_gouraud_shading/Makefile: -------------------------------------------------------------------------------- 1 | SYSCONF_LINK = g++ -std=c++11 2 | CPPFLAGS = 3 | LDFLAGS = 4 | LIBS = -lm 5 | 6 | DESTDIR = ./ 7 | TARGET = main 8 | 9 | OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) 10 | 11 | all: $(DESTDIR)$(TARGET) 12 | 13 | $(DESTDIR)$(TARGET): $(OBJECTS) 14 | $(SYSCONF_LINK) -Wall $(LDFLAGS) -o $(DESTDIR)$(TARGET) $(OBJECTS) $(LIBS) 15 | 16 | $(OBJECTS): %.o: %.cpp 17 | $(SYSCONF_LINK) -Wall $(CPPFLAGS) -c $(CFLAGS) $< -o $@ 18 | 19 | clean: 20 | -rm -f $(OBJECTS) 21 | -rm -f $(TARGET) 22 | -rm -f *.tga 23 | 24 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/geometry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/geometry.o -------------------------------------------------------------------------------- /code/11_gouraud_shading/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/main -------------------------------------------------------------------------------- /code/11_gouraud_shading/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tgaimage.h" 5 | #include "model.h" 6 | #include "our_gl.h" 7 | #include "geometry.h" 8 | 9 | #define CLAMP(t) ((t>1.f)?1.f:((t<0.f)?0.f:t)) 10 | 11 | Model *model = NULL; 12 | const int width = 600; 13 | const int height = 600; 14 | 15 | Vec3f light_dir(1, 1, 1); 16 | Vec3f eye(0, -1, 3); 17 | Vec3f center(0, 0, 0); 18 | Vec3f up(0, 1, 0); 19 | 20 | struct GouraudShader: public IShader{ 21 | Vec3f varying_intensity; // write by vertex shader, read by fragment shader 22 | 23 | virtual Vec4f vertex(int iface, int nthvert){ 24 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 25 | gl_Vertex = ViewPort*Projection*ModelView*gl_Vertex; 26 | varying_intensity[nthvert] = CLAMP(model->normal(iface, nthvert)*light_dir); // diffuse light intensity 27 | return gl_Vertex; 28 | } 29 | 30 | virtual bool fragment(Vec3f bar, TGAColor &color){ 31 | float intensity = varying_intensity * bar; //interpolate intensity for current Pixel 32 | color = TGAColor(255,255,255)*intensity; 33 | return false; // do not discard pixel 34 | } 35 | }; 36 | 37 | int main(int argc, char** argv){ 38 | if (2 == argc) { 39 | model = new Model(argv[1]); 40 | } else { 41 | model = new Model("obj/african_head.obj"); 42 | } 43 | 44 | lookat(eye, center, up); 45 | viewport(width/8, height/8, width*3/4, height*3/4); 46 | projection(-1.f/(eye-center).norm()); 47 | light_dir.normalize(); 48 | 49 | TGAImage image(width, height, TGAImage::RGB); 50 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 51 | 52 | GouraudShader shader; 53 | for (int i = 0; i < model->nfaces(); i++) { 54 | std::vector face = model->face(i); 55 | Vec4f screen_coords[3]; 56 | for (int j = 0; j < 3; j++) { 57 | screen_coords[j] = shader.vertex(i, j); 58 | } 59 | triangle(screen_coords, shader, image, zbuffer); 60 | } 61 | 62 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 63 | zbuffer.flip_vertically(); 64 | image.write_tga_file("output.tga"); 65 | zbuffer.write_tga_file("zbuffer.tga"); 66 | 67 | delete model; 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/main.o -------------------------------------------------------------------------------- /code/11_gouraud_shading/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), norms_(), uv_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "vn") { 22 | iss >> trash >> trash; 23 | Vec3f n; 24 | for (int i = 0; i < 3; i++) iss >> n[i]; 25 | norms_.push_back(n); 26 | }else if (line.substr(0,2)=="vt") { 27 | iss >> trash >> trash; 28 | Vec2f uv; 29 | for (int i = 0; i < 2; i++) iss >> uv[i]; 30 | uv_.push_back(uv); 31 | }else if (line.substr(0,2) == "f "){ 32 | std::vector f; 33 | Vec3i tmp; 34 | iss >> trash; 35 | while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) { 36 | for (int i = 0; i < 3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero 37 | f.push_back(tmp); 38 | } 39 | faces_.push_back(f); 40 | } 41 | } 42 | in.close(); 43 | } 44 | 45 | std::cerr << "# v#" << verts_.size() <<" f# " << faces_.size() <<" vt# "<< 46 | uv_.size() << " vn# " << norms_.size() << std::endl; 47 | } 48 | 49 | Model::~Model(){} 50 | 51 | int Model::nverts(){ 52 | return (int)verts_.size(); 53 | } 54 | 55 | int Model::nfaces(){ 56 | return (int)faces_.size(); 57 | } 58 | 59 | std::vector Model::face(int idx){ 60 | std::vector face; 61 | for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); 62 | return face; 63 | } 64 | 65 | Vec3f Model::vert(int iface, int nthvert){ 66 | return verts_[faces_[iface][nthvert][0]]; 67 | } 68 | 69 | Vec2f Model::uv(int iface, int nthvert){ 70 | return uv_[faces_[iface][nthvert][1]]; 71 | } 72 | 73 | Vec3f Model::normal(int iface, int nthvert){ 74 | int idx = faces_[iface][nthvert][2]; 75 | return norms_[idx].normalize(); 76 | } 77 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | class Model { 7 | private: 8 | std::vector verts_; 9 | std::vector> faces_; //Vec3i means vertex/uv/normal 10 | std::vector norms_; 11 | std::vector uv_; 12 | public: 13 | Model(const char *filename); 14 | ~Model(); 15 | int nverts(); 16 | int nfaces(); 17 | Vec2f uv(int iface, int nthvert); 18 | Vec3f vert(int iface, int nthvert); 19 | Vec3f normal(int iface, int nthvert); 20 | std::vector face(int idx); 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/model.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/model.o -------------------------------------------------------------------------------- /code/11_gouraud_shading/our_gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "our_gl.h" 5 | 6 | Matrix ModelView; 7 | Matrix ViewPort; 8 | Matrix Projection; 9 | 10 | IShader::~IShader() {} 11 | 12 | void viewport(int x, int y, int w, int h){ 13 | ViewPort = Matrix::identity(); 14 | ViewPort[0][3] = x + w/2.f; 15 | ViewPort[1][3] = y + h/2.f; 16 | ViewPort[2][3] = 255/2.f; 17 | ViewPort[0][0] = w/2.f; 18 | ViewPort[1][1] = h/2.f; 19 | ViewPort[2][2] = 255/2.f; 20 | } 21 | 22 | void projection(float coeff) { 23 | Projection = Matrix::identity(); 24 | Projection[3][2] = coeff; 25 | } 26 | 27 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 28 | Vec3f z = (eye-center).normalize(); 29 | Vec3f x = cross(up,z).normalize(); 30 | Vec3f y = cross(z,x).normalize(); 31 | Matrix Minv = Matrix::identity(); 32 | Matrix Tr = Matrix::identity(); 33 | for (int i=0; i<3; i++) { 34 | Minv[0][i] = x[i]; 35 | Minv[1][i] = y[i]; 36 | Minv[2][i] = z[i]; 37 | Tr[i][3] = -center[i]; 38 | } 39 | ModelView = Minv*Tr; 40 | } 41 | 42 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 43 | Vec3f s[2]; 44 | for (int i=2; i--; ) { 45 | s[i][0] = C[i]-A[i]; 46 | s[i][1] = B[i]-A[i]; 47 | s[i][2] = A[i]-P[i]; 48 | } 49 | Vec3f u = cross(s[0], s[1]); 50 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 51 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 52 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 53 | } 54 | 55 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 56 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 57 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 58 | for (int i = 0; i < 3; i++) { 59 | for (int j = 0; j < 2; j++) { 60 | // x/w y/w 61 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 62 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 63 | } 64 | } 65 | 66 | Vec2i P; 67 | TGAColor color; 68 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 69 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 70 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 71 | proj<2>(pts[2]/pts[2][3]), P); 72 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 73 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 74 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 75 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 76 | bool discard = shader.fragment(c, color); 77 | if (!discard) { 78 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 79 | image.set(P.x, P.y, color); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/our_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef OURGL_H 2 | #define OURGL_H 3 | #include "tgaimage.h" 4 | #include "geometry.h" 5 | 6 | extern Matrix ModelView; 7 | extern Matrix ViewPort; 8 | extern Matrix Projection; 9 | 10 | void viewport(int x, int y, int w, int h); 11 | void projection(float coeff =0.f); // coeff = -1/c 12 | void lookat(Vec3f eye, Vec3f center, Vec3f up); 13 | 14 | struct IShader { 15 | virtual ~IShader(); 16 | virtual Vec4f vertex(int iface, int nthvert) = 0; 17 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 18 | }; 19 | 20 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/our_gl.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/our_gl.o -------------------------------------------------------------------------------- /code/11_gouraud_shading/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/output.tga -------------------------------------------------------------------------------- /code/11_gouraud_shading/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/11_gouraud_shading/tgaimage.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/tgaimage.o -------------------------------------------------------------------------------- /code/11_gouraud_shading/zbuffer.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/11_gouraud_shading/zbuffer.tga -------------------------------------------------------------------------------- /code/12_texture_again/Makefile: -------------------------------------------------------------------------------- 1 | SYSCONF_LINK = g++ -std=c++11 2 | CPPFLAGS = 3 | LDFLAGS = 4 | LIBS = -lm 5 | 6 | DESTDIR = ./ 7 | TARGET = main 8 | 9 | OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) 10 | 11 | all: $(DESTDIR)$(TARGET) 12 | 13 | $(DESTDIR)$(TARGET): $(OBJECTS) 14 | $(SYSCONF_LINK) -Wall $(LDFLAGS) -o $(DESTDIR)$(TARGET) $(OBJECTS) $(LIBS) 15 | 16 | $(OBJECTS): %.o: %.cpp 17 | $(SYSCONF_LINK) -Wall $(CPPFLAGS) -c $(CFLAGS) $< -o $@ 18 | 19 | clean: 20 | -rm -f $(OBJECTS) 21 | -rm -f $(TARGET) 22 | -rm -f *.tga 23 | 24 | -------------------------------------------------------------------------------- /code/12_texture_again/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/12_texture_again/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tgaimage.h" 5 | #include "model.h" 6 | #include "our_gl.h" 7 | #include "geometry.h" 8 | 9 | #define CLAMP(t) ((t>1.f)?1.f:((t<0.f)?0.f:t)) 10 | 11 | Model *model = NULL; 12 | const int width = 600; 13 | const int height = 600; 14 | 15 | Vec3f light_dir(1, 1, 1); 16 | Vec3f eye(0, -1, 3); 17 | Vec3f center(0, 0, 0); 18 | Vec3f up(0, 1, 0); 19 | 20 | struct Shader: public IShader{ 21 | Vec3f varying_intensity; // write by vertex shader, read by fragment shader 22 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 23 | 24 | virtual Vec4f vertex(int iface, int nthvert){ 25 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 26 | varying_intensity[nthvert] = CLAMP(model->normal(iface, nthvert)*light_dir); // diffuse light intensity 27 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 28 | return ViewPort*Projection*ModelView*gl_Vertex; 29 | } 30 | 31 | virtual bool fragment(Vec3f bar, TGAColor &color){ 32 | float intensity = varying_intensity * bar; //interpolate intensity for current Pixel 33 | Vec2f uv = varying_uv * bar; //interpolate uv for current Pixel 34 | color = model->diffuse(uv)*intensity; 35 | return false; // do not discard pixel 36 | } 37 | }; 38 | 39 | int main(int argc, char** argv){ 40 | if (2 == argc) { 41 | model = new Model(argv[1]); 42 | } else { 43 | model = new Model("obj/african_head.obj"); 44 | } 45 | 46 | lookat(eye, center, up); 47 | viewport(width/8, height/8, width*3/4, height*3/4); 48 | projection(-1.f/(eye-center).norm()); 49 | light_dir.normalize(); 50 | 51 | TGAImage image(width, height, TGAImage::RGB); 52 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 53 | 54 | Shader shader; 55 | for (int i = 0; i < model->nfaces(); i++) { 56 | std::vector face = model->face(i); 57 | Vec4f screen_coords[3]; 58 | for (int j = 0; j < 3; j++) { 59 | screen_coords[j] = shader.vertex(i, j); 60 | } 61 | triangle(screen_coords, shader, image, zbuffer); 62 | } 63 | 64 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 65 | zbuffer.flip_vertically(); 66 | image.write_tga_file("output.tga"); 67 | zbuffer.write_tga_file("zbuffer.tga"); 68 | 69 | delete model; 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /code/12_texture_again/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), norms_(), uv_(), diffusemap_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "vn") { 22 | iss >> trash >> trash; 23 | Vec3f n; 24 | for (int i = 0; i < 3; i++) iss >> n[i]; 25 | norms_.push_back(n); 26 | }else if (line.substr(0,2)=="vt") { 27 | iss >> trash >> trash; 28 | Vec2f uv; 29 | for (int i = 0; i < 2; i++) iss >> uv[i]; 30 | uv_.push_back(uv); 31 | }else if (line.substr(0,2) == "f "){ 32 | std::vector f; 33 | Vec3i tmp; 34 | iss >> trash; 35 | while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) { 36 | for (int i = 0; i < 3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero 37 | f.push_back(tmp); 38 | } 39 | faces_.push_back(f); 40 | } 41 | } 42 | in.close(); 43 | } 44 | 45 | std::cerr << "# v#" << verts_.size() <<" f# " << faces_.size() <<" vt# "<< 46 | uv_.size() << " vn# " << norms_.size() << std::endl; 47 | load_texture(filename, "_diffuse.tga", diffusemap_); 48 | } 49 | 50 | Model::~Model(){} 51 | 52 | int Model::nverts(){ 53 | return (int)verts_.size(); 54 | } 55 | 56 | int Model::nfaces(){ 57 | return (int)faces_.size(); 58 | } 59 | 60 | std::vector Model::face(int idx){ 61 | std::vector face; 62 | for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); 63 | return face; 64 | } 65 | 66 | void Model::load_texture(std::string filename, const char *suffix, TGAImage& img){ 67 | std::string textfile(filename); 68 | size_t dot = textfile.find_last_of("."); 69 | if (dot != std::string::npos) { 70 | textfile = textfile.substr(0, dot) + std::string(suffix); 71 | std::cout << "textfile file" << textfile << "loading " << 72 | (img.read_tga_file(textfile.c_str()) ? "ok": "failed") << std::endl; 73 | img.flip_vertically(); 74 | } 75 | } 76 | 77 | TGAColor Model::diffuse(Vec2f uvf){ 78 | Vec2i uv(uvf[0]*diffusemap_.get_width(), uvf[1]*diffusemap_.get_height()); 79 | return diffusemap_.get(uv[0],uv[1]); 80 | } 81 | 82 | Vec3f Model::vert(int iface, int nthvert){ 83 | return verts_[faces_[iface][nthvert][0]]; 84 | } 85 | 86 | Vec2f Model::uv(int iface, int nthvert){ 87 | return uv_[faces_[iface][nthvert][1]]; 88 | } 89 | 90 | Vec3f Model::normal(int iface, int nthvert){ 91 | int idx = faces_[iface][nthvert][2]; 92 | return norms_[idx].normalize(); 93 | } 94 | -------------------------------------------------------------------------------- /code/12_texture_again/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | #include "tgaimage.h" 7 | class Model { 8 | private: 9 | std::vector verts_; 10 | std::vector> faces_; //Vec3i means vertex/uv/normal 11 | std::vector norms_; 12 | std::vector uv_; 13 | TGAImage diffusemap_; 14 | void load_texture(std::string filename, const char *suffix, TGAImage& img); 15 | public: 16 | Model(const char *filename); 17 | ~Model(); 18 | int nverts(); 19 | int nfaces(); 20 | Vec2f uv(int iface, int nthvert); 21 | Vec3f vert(int iface, int nthvert); 22 | Vec3f normal(int iface, int nthvert); 23 | std::vector face(int idx); 24 | TGAColor diffuse(Vec2f uvf); 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /code/12_texture_again/obj/african_head_diffuse.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/12_texture_again/obj/african_head_diffuse.tga -------------------------------------------------------------------------------- /code/12_texture_again/our_gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "our_gl.h" 5 | 6 | Matrix ModelView; 7 | Matrix ViewPort; 8 | Matrix Projection; 9 | 10 | IShader::~IShader() {} 11 | 12 | void viewport(int x, int y, int w, int h){ 13 | ViewPort = Matrix::identity(); 14 | ViewPort[0][3] = x + w/2.f; 15 | ViewPort[1][3] = y + h/2.f; 16 | ViewPort[2][3] = 255/2.f; 17 | ViewPort[0][0] = w/2.f; 18 | ViewPort[1][1] = h/2.f; 19 | ViewPort[2][2] = 255/2.f; 20 | } 21 | 22 | void projection(float coeff) { 23 | Projection = Matrix::identity(); 24 | Projection[3][2] = coeff; 25 | } 26 | 27 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 28 | Vec3f z = (eye-center).normalize(); 29 | Vec3f x = cross(up,z).normalize(); 30 | Vec3f y = cross(z,x).normalize(); 31 | Matrix Minv = Matrix::identity(); 32 | Matrix Tr = Matrix::identity(); 33 | for (int i=0; i<3; i++) { 34 | Minv[0][i] = x[i]; 35 | Minv[1][i] = y[i]; 36 | Minv[2][i] = z[i]; 37 | Tr[i][3] = -center[i]; 38 | } 39 | ModelView = Minv*Tr; 40 | } 41 | 42 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 43 | Vec3f s[2]; 44 | for (int i=2; i--; ) { 45 | s[i][0] = C[i]-A[i]; 46 | s[i][1] = B[i]-A[i]; 47 | s[i][2] = A[i]-P[i]; 48 | } 49 | Vec3f u = cross(s[0], s[1]); 50 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 51 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 52 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 53 | } 54 | 55 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 56 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 57 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 58 | for (int i = 0; i < 3; i++) { 59 | for (int j = 0; j < 2; j++) { 60 | // x/w y/w 61 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 62 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 63 | } 64 | } 65 | 66 | Vec2i P; 67 | TGAColor color; 68 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 69 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 70 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 71 | proj<2>(pts[2]/pts[2][3]), P); 72 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 73 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 74 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 75 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 76 | bool discard = shader.fragment(c, color); 77 | if (!discard) { 78 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 79 | image.set(P.x, P.y, color); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/12_texture_again/our_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef OURGL_H 2 | #define OURGL_H 3 | #include "tgaimage.h" 4 | #include "geometry.h" 5 | 6 | extern Matrix ModelView; 7 | extern Matrix ViewPort; 8 | extern Matrix Projection; 9 | 10 | void viewport(int x, int y, int w, int h); 11 | void projection(float coeff =0.f); // coeff = -1/c 12 | void lookat(Vec3f eye, Vec3f center, Vec3f up); 13 | 14 | struct IShader { 15 | virtual ~IShader(); 16 | virtual Vec4f vertex(int iface, int nthvert) = 0; 17 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 18 | }; 19 | 20 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/12_texture_again/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/13_normal/Makefile: -------------------------------------------------------------------------------- 1 | SYSCONF_LINK = g++ -std=c++11 2 | CPPFLAGS = 3 | LDFLAGS = 4 | LIBS = -lm 5 | 6 | DESTDIR = ./ 7 | TARGET = main 8 | 9 | OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) 10 | 11 | all: $(DESTDIR)$(TARGET) 12 | 13 | $(DESTDIR)$(TARGET): $(OBJECTS) 14 | $(SYSCONF_LINK) -Wall $(LDFLAGS) -o $(DESTDIR)$(TARGET) $(OBJECTS) $(LIBS) 15 | 16 | $(OBJECTS): %.o: %.cpp 17 | $(SYSCONF_LINK) -Wall $(CPPFLAGS) -c $(CFLAGS) $< -o $@ 18 | 19 | clean: 20 | -rm -f $(OBJECTS) 21 | -rm -f $(TARGET) 22 | -rm -f *.tga 23 | 24 | -------------------------------------------------------------------------------- /code/13_normal/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/13_normal/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/13_normal/main -------------------------------------------------------------------------------- /code/13_normal/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tgaimage.h" 5 | #include "model.h" 6 | #include "our_gl.h" 7 | #include "geometry.h" 8 | 9 | #define CLAMP(t) ((t>1.f)?1.f:((t<0.f)?0.f:t)) 10 | 11 | Model *model = NULL; 12 | const int width = 600; 13 | const int height = 600; 14 | 15 | Vec3f light_dir(1, 1, 1); 16 | Vec3f eye(0, -1, 3); 17 | Vec3f center(0, 0, 0); 18 | Vec3f up(0, 1, 0); 19 | 20 | struct Shader: public IShader{ 21 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 22 | mat<4,4,float> uniform_M; //Projection*ModelView 23 | mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() 24 | 25 | virtual Vec4f vertex(int iface, int nthvert){ 26 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 27 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 28 | return ViewPort*Projection*ModelView*gl_Vertex; // transform to screen coords 29 | } 30 | 31 | virtual bool fragment(Vec3f bar, TGAColor &color){ 32 | Vec2f uv = varying_uv*bar; //interpolate uv for current Pixel 33 | Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); // transform normal vector 34 | Vec3f l = proj<3>(uniform_M *embed<4>(light_dir)).normalize(); // transfrom light direction 35 | float intensity = std::max(0.f, n*l); 36 | color = model->diffuse(uv)*intensity; //uv 37 | return false; // do not discard pixel 38 | } 39 | }; 40 | 41 | int main(int argc, char** argv){ 42 | if (2 == argc) { 43 | model = new Model(argv[1]); 44 | } else { 45 | model = new Model("obj/african_head.obj"); 46 | } 47 | 48 | lookat(eye, center, up); 49 | viewport(width/8, height/8, width*3/4, height*3/4); 50 | projection(-1.f/(eye-center).norm()); 51 | light_dir.normalize(); 52 | 53 | TGAImage image(width, height, TGAImage::RGB); 54 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 55 | 56 | Shader shader; 57 | shader.uniform_M = Projection*ModelView; 58 | shader.uniform_MIT = (Projection*ModelView).invert_transpose(); 59 | for (int i = 0; i < model->nfaces(); i++) { 60 | std::vector face = model->face(i); 61 | Vec4f screen_coords[3]; 62 | for (int j = 0; j < 3; j++) { 63 | screen_coords[j] = shader.vertex(i, j); 64 | } 65 | triangle(screen_coords, shader, image, zbuffer); 66 | } 67 | 68 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 69 | zbuffer.flip_vertically(); 70 | image.write_tga_file("output.tga"); 71 | zbuffer.write_tga_file("zbuffer.tga"); 72 | 73 | delete model; 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /code/13_normal/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), norms_(), uv_(), diffusemap_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "vn") { 22 | iss >> trash >> trash; 23 | Vec3f n; 24 | for (int i = 0; i < 3; i++) iss >> n[i]; 25 | norms_.push_back(n); 26 | }else if (line.substr(0,2)=="vt") { 27 | iss >> trash >> trash; 28 | Vec2f uv; 29 | for (int i = 0; i < 2; i++) iss >> uv[i]; 30 | uv_.push_back(uv); 31 | }else if (line.substr(0,2) == "f "){ 32 | std::vector f; 33 | Vec3i tmp; 34 | iss >> trash; 35 | while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) { 36 | for (int i = 0; i < 3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero 37 | f.push_back(tmp); 38 | } 39 | faces_.push_back(f); 40 | } 41 | } 42 | in.close(); 43 | } 44 | 45 | std::cerr << "# v#" << verts_.size() <<" f# " << faces_.size() <<" vt# "<< 46 | uv_.size() << " vn# " << norms_.size() << std::endl; 47 | load_texture(filename, "_diffuse.tga", diffusemap_); 48 | load_texture(filename,"_nm.tga", normalmap_); 49 | } 50 | 51 | Model::~Model(){} 52 | 53 | int Model::nverts(){ 54 | return (int)verts_.size(); 55 | } 56 | 57 | int Model::nfaces(){ 58 | return (int)faces_.size(); 59 | } 60 | 61 | std::vector Model::face(int idx){ 62 | std::vector face; 63 | for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); 64 | return face; 65 | } 66 | 67 | void Model::load_texture(std::string filename, const char *suffix, TGAImage& img){ 68 | std::string textfile(filename); 69 | size_t dot = textfile.find_last_of("."); 70 | if (dot != std::string::npos) { 71 | textfile = textfile.substr(0, dot) + std::string(suffix); 72 | std::cout << "textfile file " << textfile << "loading " << 73 | (img.read_tga_file(textfile.c_str()) ? "ok": "failed") << std::endl; 74 | img.flip_vertically(); 75 | } 76 | } 77 | 78 | Vec3f Model::normal(Vec2f uvf){ 79 | Vec2i uv(uvf[0]*normalmap_.get_width(), uvf[1]*normalmap_.get_height()); 80 | TGAColor c = normalmap_.get(uv[0], uv[1]); 81 | Vec3f res; 82 | // notice TGAColor is bgra, and in byte 83 | for (int i = 0; i < 3; i++) 84 | res[2-i] = (float)c[i]/255.f*2.f - 1.f; 85 | return res; 86 | } 87 | 88 | TGAColor Model::diffuse(Vec2f uvf){ 89 | Vec2i uv(uvf[0]*diffusemap_.get_width(), uvf[1]*diffusemap_.get_height()); 90 | return diffusemap_.get(uv[0],uv[1]); 91 | } 92 | 93 | Vec3f Model::vert(int iface, int nthvert){ 94 | return verts_[faces_[iface][nthvert][0]]; 95 | } 96 | 97 | Vec2f Model::uv(int iface, int nthvert){ 98 | return uv_[faces_[iface][nthvert][1]]; 99 | } 100 | 101 | Vec3f Model::normal(int iface, int nthvert){ 102 | int idx = faces_[iface][nthvert][2]; 103 | return norms_[idx].normalize(); 104 | } 105 | -------------------------------------------------------------------------------- /code/13_normal/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | #include "tgaimage.h" 7 | class Model { 8 | private: 9 | std::vector verts_; 10 | std::vector> faces_; //Vec3i means vertex/uv/normal 11 | std::vector norms_; 12 | std::vector uv_; 13 | TGAImage diffusemap_; 14 | TGAImage normalmap_; 15 | void load_texture(std::string filename, const char *suffix, TGAImage& img); 16 | public: 17 | Model(const char *filename); 18 | ~Model(); 19 | int nverts(); 20 | int nfaces(); 21 | Vec2f uv(int iface, int nthvert); 22 | Vec3f vert(int iface, int nthvert); 23 | Vec3f normal(int iface, int nthvert); 24 | Vec3f normal(Vec2f uv); 25 | std::vector face(int idx); 26 | TGAColor diffuse(Vec2f uvf); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /code/13_normal/obj/african_head_diffuse.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/13_normal/obj/african_head_diffuse.tga -------------------------------------------------------------------------------- /code/13_normal/obj/african_head_nm.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/13_normal/obj/african_head_nm.tga -------------------------------------------------------------------------------- /code/13_normal/our_gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "our_gl.h" 5 | 6 | Matrix ModelView; 7 | Matrix ViewPort; 8 | Matrix Projection; 9 | 10 | IShader::~IShader() {} 11 | 12 | void viewport(int x, int y, int w, int h){ 13 | ViewPort = Matrix::identity(); 14 | ViewPort[0][3] = x + w/2.f; 15 | ViewPort[1][3] = y + h/2.f; 16 | ViewPort[2][3] = 255/2.f; 17 | ViewPort[0][0] = w/2.f; 18 | ViewPort[1][1] = h/2.f; 19 | ViewPort[2][2] = 255/2.f; 20 | } 21 | 22 | void projection(float coeff) { 23 | Projection = Matrix::identity(); 24 | Projection[3][2] = coeff; 25 | } 26 | 27 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 28 | Vec3f z = (eye-center).normalize(); 29 | Vec3f x = cross(up,z).normalize(); 30 | Vec3f y = cross(z,x).normalize(); 31 | Matrix Minv = Matrix::identity(); 32 | Matrix Tr = Matrix::identity(); 33 | for (int i=0; i<3; i++) { 34 | Minv[0][i] = x[i]; 35 | Minv[1][i] = y[i]; 36 | Minv[2][i] = z[i]; 37 | Tr[i][3] = -center[i]; 38 | } 39 | ModelView = Minv*Tr; 40 | } 41 | 42 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 43 | Vec3f s[2]; 44 | for (int i=2; i--; ) { 45 | s[i][0] = C[i]-A[i]; 46 | s[i][1] = B[i]-A[i]; 47 | s[i][2] = A[i]-P[i]; 48 | } 49 | Vec3f u = cross(s[0], s[1]); 50 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 51 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 52 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 53 | } 54 | 55 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 56 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 57 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 58 | for (int i = 0; i < 3; i++) { 59 | for (int j = 0; j < 2; j++) { 60 | // x/w y/w 61 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 62 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 63 | } 64 | } 65 | 66 | Vec2i P; 67 | TGAColor color; 68 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 69 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 70 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 71 | proj<2>(pts[2]/pts[2][3]), P); 72 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 73 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 74 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 75 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 76 | bool discard = shader.fragment(c, color); 77 | if (!discard) { 78 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 79 | image.set(P.x, P.y, color); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/13_normal/our_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef OURGL_H 2 | #define OURGL_H 3 | #include "tgaimage.h" 4 | #include "geometry.h" 5 | 6 | extern Matrix ModelView; 7 | extern Matrix ViewPort; 8 | extern Matrix Projection; 9 | 10 | void viewport(int x, int y, int w, int h); 11 | void projection(float coeff =0.f); // coeff = -1/c 12 | void lookat(Vec3f eye, Vec3f center, Vec3f up); 13 | 14 | struct IShader { 15 | virtual ~IShader(); 16 | virtual Vec4f vertex(int iface, int nthvert) = 0; 17 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 18 | }; 19 | 20 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/13_normal/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/13_normal/output.tga -------------------------------------------------------------------------------- /code/13_normal/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/13_normal/zbuffer.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/13_normal/zbuffer.tga -------------------------------------------------------------------------------- /code/14_phong_light/Makefile: -------------------------------------------------------------------------------- 1 | SYSCONF_LINK = g++ -std=c++11 2 | CPPFLAGS = 3 | LDFLAGS = 4 | LIBS = -lm 5 | 6 | DESTDIR = ./ 7 | TARGET = main 8 | 9 | OBJECTS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) 10 | 11 | all: $(DESTDIR)$(TARGET) 12 | 13 | $(DESTDIR)$(TARGET): $(OBJECTS) 14 | $(SYSCONF_LINK) -Wall $(LDFLAGS) -o $(DESTDIR)$(TARGET) $(OBJECTS) $(LIBS) 15 | 16 | $(OBJECTS): %.o: %.cpp 17 | $(SYSCONF_LINK) -Wall $(CPPFLAGS) -c $(CFLAGS) $< -o $@ 18 | 19 | clean: 20 | -rm -f $(OBJECTS) 21 | -rm -f $(TARGET) 22 | -rm -f *.tga 23 | 24 | -------------------------------------------------------------------------------- /code/14_phong_light/geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | 3 | template <> template <> vec<3,int> :: vec(const vec<3,float> &v): 4 | x(int(v.x+.5f)), y(int(v.y+.5f)), z(int(v.z+.5f)) {} 5 | 6 | template <> template <> vec<3,float> :: vec(const vec<3,int> &v): 7 | x(v.x), y(v.y), z(v.z) {} 8 | 9 | template <> template <> vec<2,int> :: vec(const vec<2,float> &v): 10 | x(int(v.x+.5f)), y(int(v.y+.5f)) {} 11 | 12 | template <> template <> vec<2,float> :: vec(const vec<2,int> &v): 13 | x(v.x), y(v.y) {} 14 | -------------------------------------------------------------------------------- /code/14_phong_light/geometry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/geometry.o -------------------------------------------------------------------------------- /code/14_phong_light/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/main -------------------------------------------------------------------------------- /code/14_phong_light/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tgaimage.h" 5 | #include "model.h" 6 | #include "our_gl.h" 7 | #include "geometry.h" 8 | 9 | #define CLAMP(t) ((t>1.f)?1.f:((t<0.f)?0.f:t)) 10 | 11 | Model *model = NULL; 12 | const int width = 600; 13 | const int height = 600; 14 | 15 | Vec3f light_dir(1, 1, 1); 16 | Vec3f eye(0, -1, 3); 17 | Vec3f center(0, 0, 0); 18 | Vec3f up(0, 1, 0); 19 | 20 | struct Shader: public IShader{ 21 | mat<2,3,float> varying_uv; // write by vertex shader, read by fragment shader 22 | mat<4,4,float> uniform_M; //Projection*ModelView 23 | mat<4,4,float> uniform_MIT; // (Projection*ModelView).invert_transpose() 24 | 25 | virtual Vec4f vertex(int iface, int nthvert){ 26 | varying_uv.set_col(nthvert, model->uv(iface, nthvert)); 27 | Vec4f gl_Vertex = embed<4>(model->vert(iface, nthvert)); // read the vertex from obj file 28 | return ViewPort*Projection*ModelView*gl_Vertex; // transform to screen coords 29 | } 30 | 31 | virtual bool fragment(Vec3f bar, TGAColor &color){ 32 | Vec2f uv = varying_uv*bar; //interpolate uv for current Pixel 33 | Vec3f n = proj<3>(uniform_MIT*embed<4>(model->normal(uv))).normalize(); // transform normal vector 34 | Vec3f l = proj<3>(uniform_M *embed<4>(light_dir)).normalize(); // transfrom light direction 35 | Vec3f r = (n*(n*l*2.f) - l).normalize(); // reflected light 36 | float spec = pow(std::max(r.z, 0.0f), model->specular(uv)); // we're looking from z-axis 37 | float diff = std::max(0.f, n*l); 38 | TGAColor c = model->diffuse(uv); 39 | color = c; 40 | for (int i = 0; i < 3; i++) color[i] = std::min(5+c[i]*(diff+.6*spec),255); 41 | return false; // do not discard pixel 42 | } 43 | }; 44 | 45 | int main(int argc, char** argv){ 46 | if (2 == argc) { 47 | model = new Model(argv[1]); 48 | } else { 49 | model = new Model("obj/african_head.obj"); 50 | } 51 | 52 | lookat(eye, center, up); 53 | viewport(width/8, height/8, width*3/4, height*3/4); 54 | projection(-1.f/(eye-center).norm()); 55 | light_dir.normalize(); 56 | 57 | TGAImage image(width, height, TGAImage::RGB); 58 | TGAImage zbuffer(width, height, TGAImage::GRAYSCALE); 59 | 60 | Shader shader; 61 | shader.uniform_M = Projection*ModelView; 62 | shader.uniform_MIT = (Projection*ModelView).invert_transpose(); 63 | for (int i = 0; i < model->nfaces(); i++) { 64 | std::vector face = model->face(i); 65 | Vec4f screen_coords[3]; 66 | for (int j = 0; j < 3; j++) { 67 | screen_coords[j] = shader.vertex(i, j); 68 | } 69 | triangle(screen_coords, shader, image, zbuffer); 70 | } 71 | 72 | image.flip_vertically(); // i want to have the origin at the left bottom corner of the image 73 | zbuffer.flip_vertically(); 74 | image.write_tga_file("output.tga"); 75 | zbuffer.write_tga_file("zbuffer.tga"); 76 | 77 | delete model; 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /code/14_phong_light/main.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/main.o -------------------------------------------------------------------------------- /code/14_phong_light/model.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "model.h" 6 | 7 | Model::Model(const char *filename): verts_(), faces_(), norms_(), uv_(), diffusemap_() 8 | { 9 | std::ifstream in(filename, std::ifstream::in); 10 | if (in.is_open()) { 11 | std::string line; 12 | while (std::getline(in,line)) 13 | { 14 | char trash; 15 | std::istringstream iss(line); 16 | if(line.substr(0,2) == "v "){ 17 | iss >> trash; 18 | Vec3f v; 19 | for (int i = 0; i < 3; i++) iss >> v[i]; 20 | verts_.push_back(v); 21 | } else if (line.substr(0,2) == "vn") { 22 | iss >> trash >> trash; 23 | Vec3f n; 24 | for (int i = 0; i < 3; i++) iss >> n[i]; 25 | norms_.push_back(n); 26 | }else if (line.substr(0,2)=="vt") { 27 | iss >> trash >> trash; 28 | Vec2f uv; 29 | for (int i = 0; i < 2; i++) iss >> uv[i]; 30 | uv_.push_back(uv); 31 | }else if (line.substr(0,2) == "f "){ 32 | std::vector f; 33 | Vec3i tmp; 34 | iss >> trash; 35 | while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) { 36 | for (int i = 0; i < 3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero 37 | f.push_back(tmp); 38 | } 39 | faces_.push_back(f); 40 | } 41 | } 42 | in.close(); 43 | } 44 | 45 | std::cerr << "# v#" << verts_.size() <<" f# " << faces_.size() <<" vt# "<< 46 | uv_.size() << " vn# " << norms_.size() << std::endl; 47 | load_texture(filename, "_diffuse.tga", diffusemap_); 48 | load_texture(filename,"_nm.tga", normalmap_); 49 | load_texture(filename,"_spec.tga", specularmap_); 50 | } 51 | 52 | Model::~Model(){} 53 | 54 | int Model::nverts(){ 55 | return (int)verts_.size(); 56 | } 57 | 58 | int Model::nfaces(){ 59 | return (int)faces_.size(); 60 | } 61 | 62 | std::vector Model::face(int idx){ 63 | std::vector face; 64 | for (int i = 0; i < (int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]); 65 | return face; 66 | } 67 | 68 | void Model::load_texture(std::string filename, const char *suffix, TGAImage& img){ 69 | std::string textfile(filename); 70 | size_t dot = textfile.find_last_of("."); 71 | if (dot != std::string::npos) { 72 | textfile = textfile.substr(0, dot) + std::string(suffix); 73 | std::cout << "textfile file " << textfile << "loading " << 74 | (img.read_tga_file(textfile.c_str()) ? "ok": "failed") << std::endl; 75 | img.flip_vertically(); 76 | } 77 | } 78 | 79 | Vec3f Model::normal(Vec2f uvf){ 80 | Vec2i uv(uvf[0]*normalmap_.get_width(), uvf[1]*normalmap_.get_height()); 81 | TGAColor c = normalmap_.get(uv[0], uv[1]); 82 | Vec3f res; 83 | // notice TGAColor is bgra, and in byte 84 | for (int i = 0; i < 3; i++) 85 | res[2-i] = (float)c[i]/255.f*2.f - 1.f; 86 | return res; 87 | } 88 | 89 | TGAColor Model::diffuse(Vec2f uvf){ 90 | Vec2i uv(uvf[0]*diffusemap_.get_width(), uvf[1]*diffusemap_.get_height()); 91 | return diffusemap_.get(uv[0],uv[1]); 92 | } 93 | 94 | Vec3f Model::vert(int iface, int nthvert){ 95 | return verts_[faces_[iface][nthvert][0]]; 96 | } 97 | 98 | Vec2f Model::uv(int iface, int nthvert){ 99 | return uv_[faces_[iface][nthvert][1]]; 100 | } 101 | 102 | Vec3f Model::normal(int iface, int nthvert){ 103 | int idx = faces_[iface][nthvert][2]; 104 | return norms_[idx].normalize(); 105 | } 106 | 107 | float Model::specular(Vec2f uvf){ 108 | Vec2i uv(uvf[0]*specularmap_.get_width(), uvf[1]*specularmap_.get_height()); 109 | return specularmap_.get(uv[0],uv[1])[0]/1.f; 110 | } 111 | -------------------------------------------------------------------------------- /code/14_phong_light/model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H 2 | #define MODEL_H 3 | #include 4 | #include 5 | #include "geometry.h" 6 | #include "tgaimage.h" 7 | class Model { 8 | private: 9 | std::vector verts_; 10 | std::vector> faces_; //Vec3i means vertex/uv/normal 11 | std::vector norms_; 12 | std::vector uv_; 13 | TGAImage diffusemap_; 14 | TGAImage normalmap_; 15 | TGAImage specularmap_; 16 | void load_texture(std::string filename, const char *suffix, TGAImage& img); 17 | public: 18 | Model(const char *filename); 19 | ~Model(); 20 | int nverts(); 21 | int nfaces(); 22 | Vec2f uv(int iface, int nthvert); 23 | Vec3f vert(int iface, int nthvert); 24 | Vec3f normal(int iface, int nthvert); 25 | Vec3f normal(Vec2f uv); 26 | std::vector face(int idx); 27 | TGAColor diffuse(Vec2f uvf); 28 | float specular(Vec2f uv); 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /code/14_phong_light/model.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/model.o -------------------------------------------------------------------------------- /code/14_phong_light/obj/african_head_diffuse.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/obj/african_head_diffuse.tga -------------------------------------------------------------------------------- /code/14_phong_light/obj/african_head_nm.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/obj/african_head_nm.tga -------------------------------------------------------------------------------- /code/14_phong_light/obj/african_head_spec.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/obj/african_head_spec.tga -------------------------------------------------------------------------------- /code/14_phong_light/our_gl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "our_gl.h" 5 | 6 | Matrix ModelView; 7 | Matrix ViewPort; 8 | Matrix Projection; 9 | 10 | IShader::~IShader() {} 11 | 12 | void viewport(int x, int y, int w, int h){ 13 | ViewPort = Matrix::identity(); 14 | ViewPort[0][3] = x + w/2.f; 15 | ViewPort[1][3] = y + h/2.f; 16 | ViewPort[2][3] = 255/2.f; 17 | ViewPort[0][0] = w/2.f; 18 | ViewPort[1][1] = h/2.f; 19 | ViewPort[2][2] = 255/2.f; 20 | } 21 | 22 | void projection(float coeff) { 23 | Projection = Matrix::identity(); 24 | Projection[3][2] = coeff; 25 | } 26 | 27 | void lookat(Vec3f eye, Vec3f center, Vec3f up) { 28 | Vec3f z = (eye-center).normalize(); 29 | Vec3f x = cross(up,z).normalize(); 30 | Vec3f y = cross(z,x).normalize(); 31 | Matrix Minv = Matrix::identity(); 32 | Matrix Tr = Matrix::identity(); 33 | for (int i=0; i<3; i++) { 34 | Minv[0][i] = x[i]; 35 | Minv[1][i] = y[i]; 36 | Minv[2][i] = z[i]; 37 | Tr[i][3] = -center[i]; 38 | } 39 | ModelView = Minv*Tr; 40 | } 41 | 42 | Vec3f barycentric(Vec2f A, Vec2f B, Vec2f C, Vec2f P) { 43 | Vec3f s[2]; 44 | for (int i=2; i--; ) { 45 | s[i][0] = C[i]-A[i]; 46 | s[i][1] = B[i]-A[i]; 47 | s[i][2] = A[i]-P[i]; 48 | } 49 | Vec3f u = cross(s[0], s[1]); 50 | if (std::abs(u[2])>1e-2) // dont forget that u[2] is integer. If it is zero then triangle ABC is degenerate 51 | return Vec3f(1.f-(u.x+u.y)/u.z, u.y/u.z, u.x/u.z); 52 | return Vec3f(-1,1,1); // in this case generate negative coordinates, it will be thrown away by the rasterizator 53 | } 54 | 55 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer){ 56 | Vec2f bboxmin( std::numeric_limits::max(), std::numeric_limits::max()); 57 | Vec2f bboxmax(-std::numeric_limits::max(),-std::numeric_limits::max()); 58 | for (int i = 0; i < 3; i++) { 59 | for (int j = 0; j < 2; j++) { 60 | // x/w y/w 61 | bboxmin[j] = std::min(bboxmin[j], pts[i][j]/pts[i][3]); 62 | bboxmax[j] = std::max(bboxmax[j], pts[i][j]/pts[i][3]); 63 | } 64 | } 65 | 66 | Vec2i P; 67 | TGAColor color; 68 | for (P.x = bboxmin.x; P.x <= bboxmax.x; P.x++) { 69 | for (P.y = bboxmin.y; P.y <= bboxmax.y; P.y++) { 70 | Vec3f c = barycentric(proj<2>(pts[0]/pts[0][3]), proj<2>(pts[1]/pts[1][3]), 71 | proj<2>(pts[2]/pts[2][3]), P); 72 | float z = pts[0][2]*c.x + pts[1][2]*c.y + pts[2][2]*c.z; 73 | float w = pts[0][3]*c.x + pts[1][3]*c.y + pts[2][3]*c.z; 74 | int frag_depth = std::max(0, std::min(255, int(z/w+.5))); 75 | if (c.x < 0 || c.y < 0 || c.z < 0 || zbuffer.get(P.x, P.y)[0] > frag_depth ) continue; 76 | bool discard = shader.fragment(c, color); 77 | if (!discard) { 78 | zbuffer.set(P.x, P.y, TGAColor(frag_depth)); 79 | image.set(P.x, P.y, color); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /code/14_phong_light/our_gl.h: -------------------------------------------------------------------------------- 1 | #ifndef OURGL_H 2 | #define OURGL_H 3 | #include "tgaimage.h" 4 | #include "geometry.h" 5 | 6 | extern Matrix ModelView; 7 | extern Matrix ViewPort; 8 | extern Matrix Projection; 9 | 10 | void viewport(int x, int y, int w, int h); 11 | void projection(float coeff =0.f); // coeff = -1/c 12 | void lookat(Vec3f eye, Vec3f center, Vec3f up); 13 | 14 | struct IShader { 15 | virtual ~IShader(); 16 | virtual Vec4f vertex(int iface, int nthvert) = 0; 17 | virtual bool fragment(Vec3f bar, TGAColor &color) = 0; 18 | }; 19 | 20 | void triangle(Vec4f *pts, IShader &shader, TGAImage &image, TGAImage &zbuffer); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /code/14_phong_light/our_gl.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/our_gl.o -------------------------------------------------------------------------------- /code/14_phong_light/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/output.tga -------------------------------------------------------------------------------- /code/14_phong_light/tgaimage.h: -------------------------------------------------------------------------------- 1 | #ifndef __IMAGE_H__ 2 | #define __IMAGE_H__ 3 | 4 | #include 5 | 6 | #pragma pack(push,1) 7 | struct TGA_Header { 8 | char idlength; 9 | char colormaptype; 10 | char datatypecode; 11 | short colormaporigin; 12 | short colormaplength; 13 | char colormapdepth; 14 | short x_origin; 15 | short y_origin; 16 | short width; 17 | short height; 18 | char bitsperpixel; 19 | char imagedescriptor; 20 | }; 21 | #pragma pack(pop) 22 | 23 | struct TGAColor { 24 | unsigned char bgra[4]; 25 | unsigned char bytespp; 26 | 27 | TGAColor() : bgra(), bytespp(1) { 28 | for (int i=0; i<4; i++) bgra[i] = 0; 29 | } 30 | 31 | TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) { 32 | bgra[0] = B; 33 | bgra[1] = G; 34 | bgra[2] = R; 35 | bgra[3] = A; 36 | } 37 | 38 | TGAColor(unsigned char v) : bgra(), bytespp(1) { 39 | for (int i=0; i<4; i++) bgra[i] = 0; 40 | bgra[0] = v; 41 | } 42 | 43 | 44 | TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) { 45 | for (int i=0; i<(int)bpp; i++) { 46 | bgra[i] = p[i]; 47 | } 48 | for (int i=bpp; i<4; i++) { 49 | bgra[i] = 0; 50 | } 51 | } 52 | 53 | unsigned char& operator[](const int i) { return bgra[i]; } 54 | 55 | TGAColor operator *(float intensity) const { 56 | TGAColor res = *this; 57 | intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity)); 58 | for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity; 59 | return res; 60 | } 61 | }; 62 | 63 | class TGAImage { 64 | protected: 65 | unsigned char* data; 66 | int width; 67 | int height; 68 | int bytespp; 69 | 70 | bool load_rle_data(std::ifstream &in); 71 | bool unload_rle_data(std::ofstream &out); 72 | public: 73 | enum Format { 74 | GRAYSCALE=1, RGB=3, RGBA=4 75 | }; 76 | 77 | TGAImage(); 78 | TGAImage(int w, int h, int bpp); 79 | TGAImage(const TGAImage &img); 80 | bool read_tga_file(const char *filename); 81 | bool write_tga_file(const char *filename, bool rle=true); 82 | bool flip_horizontally(); 83 | bool flip_vertically(); 84 | bool scale(int w, int h); 85 | TGAColor get(int x, int y); 86 | bool set(int x, int y, TGAColor &c); 87 | bool set(int x, int y, const TGAColor &c); 88 | ~TGAImage(); 89 | TGAImage & operator =(const TGAImage &img); 90 | int get_width(); 91 | int get_height(); 92 | int get_bytespp(); 93 | unsigned char *buffer(); 94 | void clear(); 95 | }; 96 | 97 | #endif //__IMAGE_H__ 98 | 99 | -------------------------------------------------------------------------------- /code/14_phong_light/tgaimage.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/tgaimage.o -------------------------------------------------------------------------------- /code/14_phong_light/zbuffer.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/code/14_phong_light/zbuffer.tga -------------------------------------------------------------------------------- /images/AB_cross_AC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/AB_cross_AC.png -------------------------------------------------------------------------------- /images/Bresenham.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/Bresenham.png -------------------------------------------------------------------------------- /images/Phong_light_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/Phong_light_model.png -------------------------------------------------------------------------------- /images/affine.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/affine.jpg -------------------------------------------------------------------------------- /images/african_head_nm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/african_head_nm.png -------------------------------------------------------------------------------- /images/african_head_nm_tangent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/african_head_nm_tangent.png -------------------------------------------------------------------------------- /images/cos_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/cos_alpha.png -------------------------------------------------------------------------------- /images/directional_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/directional_light.png -------------------------------------------------------------------------------- /images/filledframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/filledframe.png -------------------------------------------------------------------------------- /images/filledrandom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/filledrandom.png -------------------------------------------------------------------------------- /images/flat_shading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/flat_shading.png -------------------------------------------------------------------------------- /images/gouraud_shading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/gouraud_shading.png -------------------------------------------------------------------------------- /images/gouraud_shading2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/gouraud_shading2.png -------------------------------------------------------------------------------- /images/light_energy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/light_energy.png -------------------------------------------------------------------------------- /images/light_scene01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/light_scene01.png -------------------------------------------------------------------------------- /images/light_scene02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/light_scene02.png -------------------------------------------------------------------------------- /images/math01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/math01.png -------------------------------------------------------------------------------- /images/math_loc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/math_loc.png -------------------------------------------------------------------------------- /images/normal_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/normal_texture.png -------------------------------------------------------------------------------- /images/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/output.png -------------------------------------------------------------------------------- /images/output.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/output.tga -------------------------------------------------------------------------------- /images/phong_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/phong_light.png -------------------------------------------------------------------------------- /images/points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/points.png -------------------------------------------------------------------------------- /images/prepare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/prepare.png -------------------------------------------------------------------------------- /images/r17-texture-mapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/r17-texture-mapping.png -------------------------------------------------------------------------------- /images/simple_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/simple_light.png -------------------------------------------------------------------------------- /images/simple_light2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/simple_light2.png -------------------------------------------------------------------------------- /images/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/texture.png -------------------------------------------------------------------------------- /images/texture02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/texture02.png -------------------------------------------------------------------------------- /images/texture02.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/texture02.tga -------------------------------------------------------------------------------- /images/texture_uv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/texture_uv.png -------------------------------------------------------------------------------- /images/triangle_left_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/triangle_left_right.png -------------------------------------------------------------------------------- /images/upper_down_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/upper_down_triangle.png -------------------------------------------------------------------------------- /images/uvwh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/uvwh.png -------------------------------------------------------------------------------- /images/wireframe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/wireframe.png -------------------------------------------------------------------------------- /images/z_buffer01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/z_buffer01.png -------------------------------------------------------------------------------- /images/zbufferhead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KrisYu/tinyrender/7f5f8e7226e77f152e69e10f8696022b2986344a/images/zbufferhead.png --------------------------------------------------------------------------------