授课语音

为模型增加直线光源

在 3D 渲染中,光源是决定物体如何与光线交互并最终呈现到屏幕上的关键。为了使场景中的模型具有更自然的光照效果,我们可以向渲染系统中添加不同类型的光源,比如点光源、方向光源和直线光源等。

在这里,我们将介绍如何为模型添加 直线光源,并结合 GLSL 来实现这一效果。

1. 直线光源的概念

直线光源通常是指一种具有方向的光源,其光线沿一个方向发射,但没有明确的起始位置。在计算机图形学中,通常称之为 方向光源(Directional Light)

  • 方向光源:这种光源没有位置,它仅有一个方向。例如,太阳的光线可以看作是方向光源,因为它照射到地面时,光线的方向基本上是固定的。

直线光源的主要特征是:

  • 它的光线沿着某个固定的方向传播。
  • 所有照射到物体上的光线都具有相同的方向,并且光强度通常是均匀的。

2. 直线光源的数学模型

为了在渲染中添加直线光源,我们需要设置一个光源的 方向向量。在 GLSL 着色器中,这个方向向量将影响每个片段的光照计算。

一般来说,光源对物体的照明效果与物体表面法线的夹角以及视角有关。我们常用 漫反射(Diffuse Reflection)模型来模拟直线光源的效果。漫反射强度和光源方向与法线的夹角有关系,越接近正面,反射越强。


3. 直线光源的 GLSL 实现

3.1 顶点着色器修改

在顶点着色器中,我们需要处理物体顶点的变换以及传递必要的信息(如法线向量、光源方向等)到片段着色器。假设我们的直线光源方向是 (lightDir)

顶点着色器代码如下:

#version 330 core

layout(location = 0) in vec3 position;  // 顶点位置
layout(location = 1) in vec3 normal;    // 顶点法线

out vec3 fragNormal;    // 传递给片段着色器的法线
out vec3 fragPosition;  // 传递给片段着色器的顶点位置

uniform mat4 model;     // 模型矩阵
uniform mat4 view;      // 视图矩阵
uniform mat4 projection; // 投影矩阵

void main() {
    fragPosition = vec3(model * vec4(position, 1.0)); // 变换后的顶点位置
    fragNormal = normalize(mat3(transpose(inverse(model))) * normal); // 变换后的法线
    gl_Position = projection * view * vec4(fragPosition, 1.0); // 顶点变换后的坐标
}

在这个顶点着色器中:

  • fragNormal 是变换后的法线,会传递给片段着色器。
  • fragPosition 是变换后的顶点位置,用于计算与光源的相对位置。

3.2 片段着色器修改

片段着色器中,我们将计算每个片段的颜色,包括光照和材质的影响。为了计算光照,我们使用 漫反射 反射模型。

片段着色器代码如下:

#version 330 core

in vec3 fragNormal;  // 从顶点着色器传来的法线
in vec3 fragPosition; // 从顶点着色器传来的顶点位置

out vec4 finalColor;  // 输出的颜色

uniform vec3 lightDir; // 直线光源的方向
uniform vec3 lightColor; // 光源的颜色
uniform vec3 objectColor; // 物体的颜色

void main() {
    // 计算法线和光源方向的点积
    float diff = max(dot(normalize(fragNormal), normalize(lightDir)), 0.0);

    // 计算最终颜色
    vec3 diffuse = diff * lightColor * objectColor;
    finalColor = vec4(diffuse, 1.0);  // 设置最终颜色,alpha值为1
}

在这个片段着色器中:

  • lightDir 是从外部传入的直线光源的方向向量。
  • fragNormal 是传递过来的顶点法线。
  • lightColorobjectColor 分别是光源的颜色和物体的颜色。
  • 通过 dot() 函数计算法线和光源方向的夹角(即漫反射光照强度),并乘以光源的颜色和物体的颜色,得到最终的像素颜色。

3.3 在 OpenGL 中传递光源参数

在 OpenGL 的主程序中,我们需要传递光源的方向和其他必要的参数给着色器。

// 设置光源的方向
glm::vec3 lightDir = glm::normalize(glm::vec3(-1.0f, -1.0f, -1.0f));  // 假设光源的方向是 (-1, -1, -1)

// 设置光源的颜色
glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f);  // 白色光

// 设置物体的颜色
glm::vec3 objectColor = glm::vec3(1.0f, 0.0f, 0.0f);  // 红色物体

// 向着色器传递光源方向
glUseProgram(shaderProgram);
glUniform3fv(glGetUniformLocation(shaderProgram, "lightDir"), 1, glm::value_ptr(lightDir));
glUniform3fv(glGetUniformLocation(shaderProgram, "lightColor"), 1, glm::value_ptr(lightColor));
glUniform3fv(glGetUniformLocation(shaderProgram, "objectColor"), 1, glm::value_ptr(objectColor));

在这段代码中,我们设置了光源的方向为 (-1, -1, -1),光源的颜色为白色,并且物体的颜色为红色。


4. 小结

通过在 GLSL 中实现直线光源,我们可以为渲染的 3D 场景添加方向光源效果。具体步骤包括:

  1. 在顶点着色器中传递顶点的位置和法线数据。
  2. 在片段着色器中计算光照效果,使用点积计算漫反射强度。
  3. 在 OpenGL 程序中设置光源的参数并传递给着色器。

通过这种方式,我们能够实现更真实的光照效果,使得 3D 渲染更加生动和自然。

去1:1私密咨询

系列课程: