0%

模型加载

使用 Assimp 导入模型。

Model.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
C++结构体有个特性,它们的内存布局是连续的(Sequential)
*/
// 顶点结信息 结构体
typedef struct __vertex {
glm::vec3 Position; // 位置向量
glm::vec3 Normal; // 法向量
glm::vec2 TexCoords; // 纹理坐标向量
glm::vec3 Tangent; // 切线向量
glm::vec3 Bitangent; // 副切线向量
}VertexInfo;

// 纹理信息 结构体
typedef struct __texture {
GLuint tId; // 纹理 ID
std::string type; // 纹理类型(漫反射贴图:texture_diffuse、镜面光贴图:texture_specular)
std::string path; // 纹理图片路径(用于与其它纹理进行比较)
}TextureInfo;


class Mesh
{
private:
// 渲染数据
GLuint m_VAO;
GLuint m_VBO;
GLuint m_EBO;


/**
设置网格数据
*/
void setupMesh();

public:
// 网格数据
std::vector<VertexInfo> m_vertices; // 顶点
std::vector<GLuint> m_indices; // 索引
std::vector<TextureInfo> m_textures; // 纹理

Mesh(std::vector<VertexInfo> vertices, std::vector<GLuint> indices, std::vector<TextureInfo> textures);

~Mesh();


/**
渲染网格

@param shader 着色器
*/
void draw(Shader shader);
};

Model.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#pragma mark - Public
Mesh::Mesh(std::vector<VertexInfo> vertices, std::vector<GLuint> indices, std::vector<TextureInfo> textures)
{
m_vertices = vertices;
m_indices = indices;
m_textures = textures;

setupMesh();
}

Mesh::~Mesh()
{

}

/*
设定一个命名标准:
每个漫反射纹理被命名为:texture_diffuseN,
每个镜面光纹理应该被命名为:texture_specularN,
其中 N 的范围是 1 到纹理采样器最大允许的数字。
*/
void Mesh::draw(Shader shader)
{
GLuint diffuseNr = 1;
GLuint specularNr = 1;
GLuint normalNr = 1;
GLuint heightNr = 1;

for (GLuint i = 0; i < m_textures.size(); i++)
{
// 在绑定之前先激活响应的纹理单元
glActiveTexture(GL_TEXTURE0 + i);
// 获取纹理序号(texture_diffuseN 中的 N)
std::string number;
std::string name = m_textures[i].type;
// 转换:数字 ——> 字符
if ("texture_diffuse" == name) // 漫反射纹理
{
number = std::to_string(diffuseNr++);
}
else if ("texture_specular" == name) // 镜面光纹理
{
number = std::to_string(specularNr++);
}
else if ("texture_normal" == name) // 法线纹理
{
number = std::to_string(normalNr++);
}
else if ("texture_height" == name) // 高度纹理
{
number = std::to_string(heightNr++);
}

// 设置纹理单元序号
shader.setUniformInt((name + number).c_str(), i);
glBindTexture(GL_TEXTURE_2D, m_textures[i].tId);
}

// 绘制网格
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES, (GLsizei)m_indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

// 设置回默认值
glActiveTexture(GL_TEXTURE0);
}


#pragma mark - Private

void Mesh::setupMesh()
{
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glGenBuffers(1, &m_EBO);

glBindVertexArray(m_VAO);

// 顶点数据
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(VertexInfo), &m_vertices[0], GL_STATIC_DRAW);

// 索引数据
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(GLuint), &m_indices[0], GL_STATIC_DRAW);

/*
结构体预处理指令 offsetof(s, m),第一个参数是一个结构体,第二个参数是这个结构体中变量的名字。
这个宏会返回那个变量距结构体头部的字节偏移量(Byte Offset)。
*/
// 顶点位置属性
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Position));

// 顶点法线属性
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void *)offsetof(VertexInfo, Normal));

// 纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, TexCoords));

// 纹理切线
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Tangent));

// 纹理双切线
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Bitangent));

// 解绑 VOA
glBindVertexArray(0);
}

效果

Model

Demo


阅读全文 »

网格

网格(Mesh):代表的是单个的可绘制实体。

自定义数据结构

网格(Mesh)最少需要的数据有:

  • 一系列的顶点(每个顶点包含):
    • 一个位置向量
    • 一个法向量
    • 一个纹理坐标向量
  • 用于索引绘制的索引
  • 纹理形式的材质数据(漫反射/镜面光贴图)

Mesh.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <iostream>
#include <vector>
#include "Shader.hpp"

/*
C++结构体有个特性,它们的内存布局是连续的(Sequential)
*/
// 顶点结信息 结构体
typedef struct __vertex {
glm::vec3 Position; // 位置向量
glm::vec3 Normal; // 法向量
glm::vec2 TexCoords; // 纹理坐标向量
glm::vec3 Tangent; // 切线向量
glm::vec3 Bitangent; // 副切线向量
}VertexInfo;

// 纹理信息 结构体
typedef struct __texture {
GLuint tId; // 纹理 ID
std::string type; // 纹理类型(漫反射贴图:texture_diffuse、镜面光贴图:texture_specular)
std::string path; // 纹理图片路径(用于与其它纹理进行比较)
}TextureInfo;


class Mesh
{
private:
// 渲染数据
GLuint m_VAO;
GLuint m_VBO;
GLuint m_EBO;

// 网格数据
std::vector<VertexInfo> m_vertices; // 顶点
std::vector<GLuint> m_indices; // 索引
std::vector<TextureInfo> m_textures; // 纹理

/**
设置网格数据
*/
void setupMesh();

public:

Mesh(std::vector<VertexInfo> vertices, std::vector<GLuint> indices, std::vector<TextureInfo> textures);

~Mesh();

/**
绘制网格

@param shader 着色器
*/
void draw(Shader shader);
};

Mesh.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#pragma mark - Public
#pragma mark -- 构造函数
Mesh::Mesh(std::vector<VertexInfo> vertices, std::vector<GLuint> indices, std::vector<TextureInfo> textures)
{
m_vertices = vertices;
m_indices = indices;
m_textures = textures;

setupMesh();
}

#pragma mark -- 析构函数
Mesh::~Mesh()
{

}

#pragma mark -- 绘制网格
/*
设定一个命名标准:
每个漫反射纹理被命名为:texture_diffuseN,
每个镜面光纹理应该被命名为:texture_specularN,
其中 N 的范围是 1 到纹理采样器最大允许的数字。
*/
void Mesh::draw(Shader shader)
{
GLuint diffuseNr = 1;
GLuint specularNr = 1;
GLuint normalNr = 1;
GLuint heightNr = 1;

for (GLuint i = 0; i < m_textures.size(); i++)
{
// 在绑定之前先激活响应的纹理单元
glActiveTexture(GL_TEXTURE0 + i);
// 获取纹理序号(texture_diffuseN 中的 N)
std::string number;
std::string name = m_textures[i].type;
// 转换:数字 ——> 字符
if ("texture_diffuse" == name) // 漫反射纹理
{
number = std::to_string(diffuseNr++);
}
else if ("texture_specular" == name) // 镜面光纹理
{
number = std::to_string(specularNr++);
}
else if ("texture_normal" == name) // 法线纹理
{
number = std::to_string(normalNr++);
}
else if ("texture_height" == name) // 高度纹理
{
number = std::to_string(heightNr++);
}

// 设置纹理单元序号
shader.setUniformInt((name + number).c_str(), i);
glBindTexture(GL_TEXTURE_2D, m_textures[i].tId);
}

// 绘制网格
glBindVertexArray(m_VAO);
glDrawElements(GL_TRIANGLES, (GLsizei)m_indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);

// 设置回默认值
glActiveTexture(GL_TEXTURE0);
}


#pragma mark - Private
#pragma mark -- 设置网格数据
void Mesh::setupMesh()
{
glGenVertexArrays(1, &m_VAO);
glGenBuffers(1, &m_VBO);
glGenBuffers(1, &m_EBO);

glBindVertexArray(m_VAO);

// 顶点数据
glBindBuffer(GL_ARRAY_BUFFER, m_VBO);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(VertexInfo), &m_vertices[0], GL_STATIC_DRAW);

// 索引数据
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(GLuint), &m_indices[0], GL_STATIC_DRAW);

/*
结构体预处理指令 offsetof(s, m),第一个参数是一个结构体,第二个参数是这个结构体中变量的名字。
这个宏会返回那个变量距结构体头部的字节偏移量(Byte Offset)。
*/
// 顶点位置属性
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Position));

// 顶点法线属性
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void *)offsetof(VertexInfo, Normal));

// 纹理坐标
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, TexCoords));

// 纹理切线
glEnableVertexAttribArray(3);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Tangent));

// 纹理双切线
glEnableVertexAttribArray(4);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(VertexInfo), (void*)offsetof(VertexInfo, Bitangent));

// 解绑 VOA
glBindVertexArray(0);
}

阅读全文 »

3D 模型

3D 模型 通常是在 Blender3DS Max 或者 Maya 3D建模工具(3D Modeling Tool)中制作完成;并使用 UV映射(uv-mapping) 的手段来应用贴图。这些工具将会在导出到模型文件的时候自动生成所有的 顶点坐标顶点法线 以及 纹理坐标

模型的文件格式有很多种,每一种都会以它们自己的方式来导出模型数据。例如:

  • Wavefront的.obj模型文件格式:只包含了模型数据以及材质信息(如,模型颜色漫反射/镜面光贴图
  • Collada模型文件格式:包含模型、光照、多种材质、动画数据、摄像机、完整的场景信息等等。

模型加载库

Assimp(Open Asset Import Library):能够导入很多种不同的模型文件格式(并也能够导出部分的格式),并将所有的模型数据加载至 Assimp 的通用数据结构中。一个简化的 Assimp 数据结构模型如图:

图片来源:learnopengl.com

  • 和材质和网格(Mesh)一样,所有的场景/模型数据都包含在Scene对象中。Scene对象也包含了场景根节点的引用。
  • 场景的Root node(根节点)可能包含子节点(和其它的节点一样),包含了一系列指向场景对象中mMeshes数组中储存的网格(组合模型的每个单独的形状)数据的索引。Scene下的mMeshes数组储存了真正的Mesh对象,节点中的mMeshes数组保存的只是场景中网格数组的索引。
  • 一个Mesh对象本身包含了渲染所需要的所有相关数据,像是顶点位置法向量纹理坐标面(Face)**和物体的材质**。
  • 一个网格包含了多个Face(Face代表的是物体的渲染图元(Primitive)(三角形、方形、点))。一个面包含了组成图元的顶点的索引
  • 一个网格也包含了一个Material对象,它包含了一些函数能让我们获取物体的材质属性,比如说颜色纹理贴图(比如漫反射和镜面光贴图)。

一个网格是在OpenGL中绘制物体所需的最小单位(顶点数据、索引和材质属性);一个模型(通常)会包括多个网格。

构建Assimp

阅读全文 »

GLSL 函数

GLSL 中的函数和 C 函数很相似,它有一个函数名、一个返回值类型,如果函数不是在 main 函数之前声明的,则必须在代码文件顶部声明一个原型。

多光源

当在场景中使用多个光源时,由于每一个光源都会对片段产生一定的影响,所以需要一个单独的颜色向量,将每个光源对片段的影响累加,以得出最终的片段颜色。大体的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
out vec4 FragColor;        /* 片段颜色 */

void main()
{
/* 定义一个输出颜色值 */
vec3 output;

/* 计算(并累加)定向光源对片段的影响 */
output += someFunctionToCalculateDirectionalLight();

/* 计算(并累加)所有点光源对片段的影响 */
for(int i = 0; i < nr_of_point_lights; i++)
{
output += someFunctionToCalculatePointLight();
}

/* 计算(并累加)其他聚光源对片段的影响 */
output += someFunctionToCalculateSpotLight();

/* 片段最终颜色 */
FragColor = vec4(output, 1.0);
}

效果

MultipleLights

Demo


参考

阅读全文 »

知识点

  • 颜色向量(Color Vector):一个通过 红绿蓝(RGB) 分量的组合描绘大部分真实颜色的向量。(一个物体的颜色实际上是该物体所不能吸收的反射颜色分量。)
  • 冯氏光照模型(Phong Lighting Model):一个通过计算 环境光漫反射镜面光 分量的值来估计真实光照的模型。
  • 环境光照(Ambient Lighting):通过给每个没有被光照的物体很小的亮度,使其不是完全黑暗的,从而对全局光照进行估计。
  • 漫反射着色(Diffuse Shading):一个顶点/片段与光线方向越接近,光照会越强。(使用法向量来计算角度。)
  • 法向量(Normal Vector):一个垂直于平面的单位向量。
  • 法线矩阵(Normal Matrix):一个 3x3 矩阵,或者说是没有平移的模型(或者模型-观察)矩阵。它也被以某种方式修改(逆转置),从而在应用非统一缩放时,保持法向量朝向正确的方向;否则法向量会在使用非统一缩放时被扭曲。
  • 镜面光照(Specular Lighting):当观察者视线靠近光源在表面的反射线时会显示的镜面高光。镜面光照是由 观察者的方向光源的方向 和设定高光分散量的 反光度 值三个量共同决定的。
  • 冯氏着色(Phong Shading):冯氏光照模型应用在 片段着色器
  • Gouraud着色(Gouraud shading):冯氏光照模型应用在 顶点着色器 上。在使用很少数量的顶点时会产生明显的瑕疵。会得到效率提升但是损失了视觉质量。
  • GLSL结构体(GLSL struct):一个类似于 C 的结构体,用作着色器变量的容器。大部分时间用来管理输入/输出/uniform。
  • 材质(Material):一个物体反射的 环境光漫反射镜面光 颜色。这些东西设定了物体所拥有的颜色。
  • 光照属性(Light(properties)):一个光的 环境光漫反射镜面光强度。可以使用任何颜色值,对每一个冯氏分量(Phong Component)定义光源发出的颜色/强度。
  • 漫反射贴图(Diffuse Map):一个设定了每个片段中 漫反射颜色 的纹理图片。
  • 镜面光贴图(Specular Map):一个设定了每一个片段的 镜面光强度/颜色 的纹理贴图。(仅在物体的特定区域显示镜面高光。)
  • 定向光(Directional Light):只有一个方向的光源。(它被建模为不管距离有多长所有光束都是平行而且其方向向量在整个场景中保持不变。)
  • 点光源(Point Light):一个在场景中有位置的,光线逐渐衰减的光源。
  • 衰减(Attenuation):光随着距离减少强度的过程,通常应用在 点光源聚光
  • 聚光(Spotlight):一个被定义为在某一个方向上的 锥形的光源
  • 手电筒(Flashlight):一个摆放在观察者视角的 聚光
  • GLSL uniform数组(GLSL Uniform Array):一个uniform值数组。(它的工作原理和C语言数组大致一样,只是不能动态分配内存。)

参考

教程来源:https://learnopengl.com/

阅读全文 »

投光物

投光物(Light Caster):将光 投射(Cast)到物体的光源。

定向光

定向光(Directional Light):一个处于无限远的光源(也叫做平行光),看似所有光线都朝着某一方向传播。

当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行;不论物体和/或者观察者的位置,看起来好像所有的光都来自于同一个方向。(例如:太阳。)
图片来源于:learnopengl.com

通过定义一个光线 方向向量 而不是位置向量来模拟一个定向光。

定向光的判定

  • 位置向量 定义为一个 vec4 时,必需将 w 分量设置为 1.0,这样 变换投影 才能正确应用。
  • 方向向量 定义为一个 vec4 时,必需将 w 分量设置为 0.0,这样 位移 时就不会有任何的效果(因为它仅仅代表的是方向)。

这样就可以通过检测 w分量 是否等于 1.0 来判断是光的 位置向量 还是光的 方向向量 了:(这正是旧 OpenGL(固定函数式)决定光源是定向光还是位置光源(Positional Light Source)的方法,并根据它来调整光照。)

1
2
3
4
5
6
7
8
if(0.0 == lightVector.w) // 注意浮点数据类型的误差
{
// 执行定向光照计算
}
else if(lightVector.w == 1.0)
{
// 根据光源的位置做光照计算
}
阅读全文 »

光照贴图

漫反射贴图

漫反射贴图(Diffuse Map):在光照场景中,使用一张覆盖物体的图像,使其能够逐片段索引其独立的颜色值来代表一个物体的漫反射颜色的纹理图。

镜面光贴图

镜面光贴图(Specular Map):镜面高光的强度可以通过图像每个像素的亮度来获取,让物体的某些部分以不同的强度显示镜面高光。

使用 PhotoshopGimp 之类的工具,将 漫反射纹理 转换为 镜面光纹理 还是比较容易的,只需要剪切掉一些部分,将图像转换为黑白的,并增加亮度/对比度就好了。

放射光贴图

  • 放射光贴图(Emission Map):是一个储存了每个片段的 发光值(Emission Value) 的贴图。
  • 发光值(Emission Value):是一个包含(假设)光源的物体 发光(Emit) 时可能显现的颜色,这样物体就能够忽略光照条件进行 发光(Glow)

例如,游戏中某个物体在发光的时候,你通常看到的就是放射光贴图(比如 机器人的眼,或是箱子上的灯带)。

效果

阅读全文 »

现实世界的物体,对光产生不同的反应;针对镜面高光,也有不同的反应;物体在反射光的时候,如果散射的光比较少,则会产生较小的高光点,如果散射的光比较多,则会产生较大的高光点。

材质属性

当描述一个物体的时候,一般是通过一个 材质颜色(Material Color)反光度(Shininess) 来控制材质的属性;其中材质颜色包括:环境光照(Ambient Lighting)漫反射光照(Diffuse Lighting)镜面光照(Specular Lighting)。如下结构体:

1
2
3
4
5
6
7
/* 材质属性 */
struct Material {
vec3 ambient; /* 环境光照 */
vec3 diffuse; /* 漫反射光照 */
vec3 specular; /* 镜面光照 */
float shininess; /* 反光度 */
};
  • ambient:在环境光照下这个物体反射得是什么颜色(通常这和 物体颜色 是相同的)。
  • diffuse:在漫反射光照下物体的颜色(同样:漫反射颜色也要设置为需要的物体颜色)。
  • specular:镜面光照对物体的颜色影响(或者甚至可能反射一个物体特定的镜面高光颜色)。
  • shininess:影响镜面高光的散射/半径。

光照属性

光源对 环境光漫反射镜面光 分量也具有着不同的 强度

1
2
3
4
5
6
7
/* 光照属性 */
struct Light {
vec3 position; /* 光源位置 */
vec3 ambient; /* 环境光照·强度 */
vec3 diffuse; /* 漫反射光照·强度 */
vec3 specular; /* 镜面光照·强度 */
};
  • 环境光照:通常设置为一个比较低的强度(这样环境光颜色不会太过显眼)。
  • 漫反射光照:通常设置为光所具有的颜色(通常是一个比较明亮的白色)。
  • 镜面光照:通常会保持为 vec3(1.0)(以最大强度发光)。

材质属性表

阅读全文 »

冯氏光照模型(Phong Lighting Model)

冯氏光照模型的主要结构由 3 个分量组成:环境(Ambient)漫反射(Diffuse)镜面(Specular)光照。

图片来源于:learnopengl.com

  1. 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),因此物体几乎永远不会是完全黑暗的。为了模拟这个,使用一个 环境光照常量,永远会给物体一些颜色。
  2. 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact);是冯氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
  3. 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

环境光照

一个简化的全局照明模型,使用一个很小的常量(光照)颜色,添加到物体片段的最终颜色中,这样子的话即便场景中没有直接的光源也能看起来存在有一些发散的光。环境光照添加到场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* fragmeng shader */

#version 330 core

out vec4 FragColor; /* 输出,指定片段颜色 */

uniform vec3 objectColor; /* 物体颜色 */
uniform vec3 lightColor; /* 光照颜色 */

void main()
{
/* ========== '环境光照'处理 ========== */
float ambientStrength = 0.1; /* 环境因子(强度) */
vec3 ambient = ambientStrength * lightColor; /* 环境光分量 = 环境因子 * 光照 */

vec3 result = ambient * objectColor; /* 最终光照 = 环境光分量 * 物体颜色 */
FragColor = vec4(result, 1.0); /* 片段颜色输出 */
}

漫反射光照

漫反射光照 使物体上与光线方向越接近的片段能从光源处获得更多的亮度,即光线与片段法向量之间的夹角越小,该片段最终显示的颜色就越亮,反之,越暗。如图:
图片来源于:learnopengl.com

所以,计算漫反射光照需要:

阅读全文 »

颜色

颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成,它们通常被缩写为RGB.

在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色

物体颜色:指的是物体从一个光源反射各个颜色分量的大小。

效果

Colors

Demo


参考

教程来源:https://learnopengl.com/

阅读全文 »