用 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