OpenGL_Rendering

Introduction

OpenGLStanford 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

Basic Shader and Triangle

Basic Shader and Triangle

Basic Shader and Triangle

这一步需要写简单的 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); 来绘制画面.

Vertex Color

Vertex Color

Vertex Color

这一步加上之前说的 EBO, 并把 VAO, VBO, EBO 抽象出来, 这样就不用担心 OpenGL 的 bind()unbind() 的问题了.

// connect vertices to triangles
GLuint indices[] = {
  0, 1, 2,
};

3D Transformation Matrix

3D Transformation Matrix

3D Transformation Matrix

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

Camera Movement

Camera Movement

Camera Movement

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

Diffuse, Specular Light and Depth Buffer

Diffuse, Specular Light and Depth Buffer

Diffuse, Specular Light and Depth Buffer

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

Texture

懒得写了...

Model Loading in OBJ format

Model Loading in OBJ format

Model Loading in OBJ format

本来想用 giTF 的, 但是 C++ json 太难读了. 你写一下 C++ 系列的语言就知道 C 体系的 library 有多难用. 连起码的业界统一的模型读取都得自己看文档从 0 开始写. 于是我选择了比较简单的 OBJ 格式来写... 手动 parse 文件好费力啊, 这要是放到 python 不直接一个 library 和一行调用函数直接搞定.

计算 normal 的时候要注意: 当 OBJ 不包含 normal 的时候, 可以用两种不同的方式计算 normal 达到不同的视觉效果:

  1. 棱角效果: 每个三角形拥有独立的三个顶点, 一个三角形内的顶点使用相同的 normal
  2. 平滑效果: 每个三角形的顶点非独立, 每个顶点的 normal 取相邻三角形的 normal 的中间值

模型的光照没有考虑到模型的旋转, 懒得 implement 了. 至此, Bunny 项目完结撒花~

Table of Content