用 OpenGL 写 Stanford Bunny 渲染的 project 一共花了 4 天时间总算完成了 (中间还去打了疫苗, 昨天好难受, 现在味觉都有点怪怪的). 前 3 天都在画 fancy 三角形. 写完以后奇怪地感觉自己并没有学到太多的知识? 唉~ 就当自己写了一个 OBJ Viewer 吧... 反正 Ubuntu 上没有自带的. C++ 这个语言有点尴尬: 上有 python, Java, 下有 C, C++ 无处可去...
EAD, origin/master, master)
* c08a944 - (24 hours ago) :sparkles: refactor mesh class - Hanke Chen
* 4036b0b - (2 days ago) :sparkles: specular light - Hanke Chen
* 234a308 - (2 days ago) :sparkles: there should be diffuse light - Hanke Chen
* c279794 - (2 days ago) :sparkles: God says there should be light - Hanke Chen
* 0e30730 - (3 days ago) :sparkles: camera rotation - Hanke Chen
* d4825d2 - (3 days ago) :sparkles: camera movement - Hanke Chen
* 4e818ec - (4 days ago) :sparkle: add depth buffer - Hanke Chen
* 766ab32 - (4 days ago) :sparkles: rotating triangle - Hanke Chen
* 488da93 - (4 days ago) :sparkles: add scaling uniform to .vert; add shader compile error - Hanke Chen
* 702145f - (4 days ago) :sparkles: colored triangle - Hanke Chen
* fb0b8f5 - (4 days ago) :sparkles: reformat code - Hanke Chen
* 01ed88e - (4 days ago) :sparkles: first triangle - Hanke Chen

这一步需要写简单的 vertex shader 和 fragment shader.
// Shader Code
const char* vertexCode = "#version 460 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main() {\n"
"    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\n\0";
const char* fragmentCode = "#version 460 core\n"
"out vec4 FragColor;\n"
"void main() {\n"
"    FragColor = vec4(0.8f, 0.3f, 0.02f, 1.0f);\n"
"}\n\0";
除此之外还需要手动定义三角形的 vertex.
const GLfloat vertices[] = {
  -0.5f, -0.5f * float(sqrt(3)) / 3, 0.0f,
  0.5f, -0.5f * float(sqrt(3)) / 3, 0.0f,
  0.0f, 0.5f * float(sqrt(3)) * 2 / 3, 0.0f,
};
因为是 C++, 所以 OpenGL 的函数有特别多 (而且不是 Object Oriented 的写法). 需要定义 VAO (Vertex Array Object: 存储顶点信息), VBO (Vertex Buffer Object: 在 VAO 里面), 和 EBO (Edge Buffer Object: 储存每个三角形的 edge)
然后用 glfwSwapBuffers(window); 来绘制画面.

这一步加上之前说的 EBO, 并把 VAO, VBO, EBO 抽象出来, 这样就不用担心 OpenGL 的 bind() 和 unbind() 的问题了.
// connect vertices to triangles
GLuint indices[] = {
  0, 1, 2,
};

这一步渲染前加上三个 matrix: model, view, 和 projection, 并把这些信息送到 GPU 的 shader program 里. 加上 while() loop 里的时间差生成动画.

这一步加上一个叫 Camera 的 class 来实现用键盘操作实时更新几个矩阵. 注意 model 矩阵是不需要更新的. 用鼠标上下看的时候速度太快会有卡卡的效果, 是因为仰角超过 0 度会直接计算到 0+ 度而导致时而抬头时而低头, 解决的办法就是增大最低仰角 (5 degree 左右), 但也不能完全避免.

这一步计算平行光照. 一共有三种光照 diffuse (根据光源位置和 normal 进行光计算), ambient (无脑打光模拟漫反射), specular (根据相机和 normal 与光的角度计算直接反射光), 点光源懒得写了...
懒得写了...

本来想用 giTF 的, 但是 C++ json 太难读了. 你写一下 C++ 系列的语言就知道 C 体系的 library 有多难用. 连起码的业界统一的模型读取都得自己看文档从 0 开始写. 于是我选择了比较简单的 OBJ 格式来写... 手动 parse 文件好费力啊, 这要是放到 python 不直接一个 library 和一行调用函数直接搞定.
计算 normal 的时候要注意: 当 OBJ 不包含 normal 的时候, 可以用两种不同的方式计算 normal 达到不同的视觉效果:
normal 取相邻三角形的 normal 的中间值模型的光照没有考虑到模型的旋转, 懒得 implement 了. 至此, Bunny 项目完结撒花~
Table of Content