OpenGL教程:10. 光照贴图

第八节讲了光照和材质,但材质的实现一般要用到贴图。所以我们介绍几种常见的光照贴图。

漫反射贴图

漫反射贴图一般就是材质的原贴图,也就是材质长什么样,漫反射就什么样。

image-20221111203041768

Read more

OpenGL教程:9. Phong光照模型

冯氏光照是图形学中普遍使用的光照模型,是基本的光照系统。注意,这里的代码全部都是GLSL!

颜色

在物理中我们通常使用三原色模型表示颜色,其基本思想是任何颜色都可以由红蓝绿三种基色来合成,用向量表示:

一般来说,$R,G,B\in[0,255]$,不过在OpenGL中我们我们的三基色的取值范围为$(0,1)$。

下面介绍颜色吸收定理:

假设在世界坐标系下存在着一个光源,他发射出颜色为$(1.0,1.0,1.0)$的光,由RGB模型易知这种光的颜色是白色(符合我们的日常生活认知)。

那么我们为什么能看到某些物体的颜色?因为物体吸收了部分颜色,反射出来的颜色被我们看到了。因此,假设世界坐标系存在着另外一个物体,这个物体的本该具有的颜色为珊瑚红$(1.0,0.5,0.31)$。现在我们让光照射在这个物体上,问这个物体最终反射的颜色为什么?答案依然是珊瑚红,这是因为:

Read more

OpenGL教程:8. 摄像机坐标系

摄像机方向

摄像机是一种抽象的结构,它表示我们看东西的媒介,类似于眼睛。为了定义摄像机,我们给定摄像机的世界空间位置向量$\boldsymbol{eye}=(x_0,y_0,z_0,1)$,以及我们要观察的片元的世界空间位置向量$\boldsymbol{frag}$。

为了构建摄像机的坐标系,我们需要三个正交的基向量,而其中一个必须表示方向信息。首先可以计算出摄像机的方向向量:

image-20221020201410118

可以计算摄像机的视角方向向量,简称方向向量:

Read more

OpenGL教程:4. 手写一个Shader类

在前面的教程中,我们的着色器都是以const char的数组形式来传递的。但是这有个很严重的问题,非常不方面着色器的编写!后期的学习中,我们大部份都需要编写很多着色器的代码,如果在这种环境下编写会非常不方便的。所以,我们可以在另外一个文本中编写着色器,然后用C++的输入流来读取这个文本(这个文本的格式可以是txt,也可以是其他任何格式,但为了方便管理,推荐使用glsl格式或者vsh和fsh格式)。看来写一个Shader读取编译器势在必行。

并且,为了方便管理,我们最好写一个Shader类,专门管理着色器。

定义Shader类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Shader{
public:
//着色器id
unsigned int id;

//构造函数,读取着色器并编译。接受两个参数,分别为顶点着色器和片元着色器的路径
Shader(const std::string &vertexShaderPath, const std::string &fragmentShaderPath);
//析构函数,回收内存
~Shader();
//使用着色器的函数
void use();
//Uniform变量设置函数
void setBool(const std::string &name, bool value) const;
void setInt(const std::string &name, int value) const;
void setFloat(const std::string &name, float value) const;
void setVector3(const std::string &name, float x, float y, float z) const;
void setVector3(const std::string &name, glm::vec3 vec) const;
void setVector4(const std::string &name, float x, float y, float z, float w) const;
void setVector4(const std::string &name, glm::vec4 vec) const;
void setMatrix3(const std::string &name, glm::mat3 mat3) const;
void setMatrix4(const std::string &name, glm::mat4 mat4) const;
};
Read more

OpenGL教程:3. 绘制一个矩形与EBO

上一节我们实现了一个三角形的绘制。现在思考下如何绘制一个矩形?因为OpenGL中最小的绘制片元是一个三角面片(不考虑点和线哦),所以答案是绘制两个三角形就是一个矩形了。

顶点数据

首先确定顶点的浮点数据:

1
2
3
4
5
6
7
8
float vertices[] = {
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f, // 左上角
-0.5f, -0.5f, 0.0f, // 左下角
0.5f, 0.5f, 0.0f, // 右上角
};

因为是两个三角形,所以就有六个顶点。

VAO和VBO

然后定义矩形的vao和vbo,并解释数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//initialize vbo
unsigned int vbo;
glGenBuffers(1, &vbo); //spawn a new vbo
glBindBuffer(GL_ARRAY_BUFFER, vbo); //bind vbo with ARRAY_BUFFER
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices,
GL_STATIC_DRAW); //transport vertices data to buffer memory

//initialize vao
unsigned int vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

//set vertices pointer
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *) nullptr);
glEnableVertexAttribArray(0);
Read more

OpenGL教程:2. 绘制一个三角形

上一节我们使用了GLFW和GLAD。接下来我们来绘制三角形。在OpenGL中,最小绘制对象是三角面片。所有的对象都是由若干个三角面片组成的。绘制也比较简单,我们只需确定顶点以及其每个顶点的颜色就可以绘制了!

img

绘制过程如上,我们需要定义顶点的数据,然后交给六个着色器处理。其中顶点、集合和片段是我们可以人为自定义的阶段。着色器非常重要,他是运行在GPU的小程序。用于渲染图形。在OpenGL中,我们使用GLSL着色器语言。它类似于C语言,还是比较好上手的。

基本过程就是,定义顶点数据->顶点着色(绘制每个顶点的位置)->图元装配(根据绘制类型装配图元,后面会解释)->几何着色器->光栅化(确定图形的像素)->片元着色(绘制每个像素的颜色)->测试混合

所以,顶点着色器的对象是每个顶点,片元着色器的对象是每个像素!这点很重要。也就是说,我们会对每个顶点执行一次顶点着色器的程序,会对每个像素执行片元着色器。一般来说,像素的数目大于顶点的数目。所以片元着色器一般计算量会大于顶点着色器。

Read more

OpenGL教程:1. GLFW和GLAD的初始化

OpenGL是图形API,是一种规范。他作为最古老的图形API,无论是后起之秀DirectX和Vulkan,都遵循这种规范。

既然是一种API,那么他就是一种图形接口,供用户去实现一些图形的操作。具体来说,这一过程如下:

这说明,各大显卡厂商根据OpenGL制定相对应的显卡(不止是OpenGL,还有DirectX和Vulkan等图形API)。用户通过GLFW和GLAD“对接API”。最后才能实现真正的图形编程。可能你目前还看不懂这些术语,之后会好好解释的。

入门计算机图形学,需要有良好的数学基础,以及编码能力。我们主要采用C++语言编程OpenGL。需要说明的是,Python、Java和Golang都可以编程OpenGL,并且在这方面的语法是出奇的相似。建议挑选自己熟悉的语言学习。(但我仍然强烈推荐用C++,问就是效率高而且高自定义性)

最后,要强调的是,OpenGL是一个巨大的状态机,每一条gl的语句都是一种状态。这一点需要明确,这对后面对OpenGL代码的理解有着重要的作用。

Read more
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×