材料
現実の世界では、各オブジェクトは光に対して異なる反応を示します。スチール製の花瓶はセラミック製の花瓶よりも光沢があり、木製のケースはスチール製のケースほど光を反射しません。また、各オブジェクトはスペキュラ ハイライトに対して異なる反応を示します。一部のオブジェクトは多くの光を散乱 (Scatter) しませんが、多くの光を反射 (Reflect) します。結果は、より小さなハイライト (Highlight) のように見えます。一部のオブジェクトは大きく散乱し、より大きな半径のハイライトを生成します。OpenGL で複数のタイプのオブジェクトをシミュレートする場合は、各オブジェクトのマテリアル プロパティを個別に定義する必要があります。
前のチュートリアルでは、オブジェクトと光の色を指定してオブジェクトの画像出力を定義し、それを周囲光と鏡面強度の要素と組み合わせました。オブジェクトを記述するとき、マテリアル カラーを定義するために、アンビエント ライティング、ディフューズ ライティング、スペキュラー ライティングの 3 つのライティング要素を使用できます。各要素に色を割り当てることで、オブジェクトの色出力をきめ細かく制御できます。次に、これら 3 つの色に鏡面要素を追加します。必要なすべてのマテリアル プロパティは次のとおりです。
#version 330 core
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
}
uniform Material material;
フラグメント シェーダーでは、オブジェクトのマテリアル プロパティを格納するための構造 (Struct) を作成します。それらを個々の均一な値として保存することもできますが、構造体として保存する方がより整理されています。最初に構造体のレイアウトを定義し、次に、新しく作成された構造体をその型として持つ均一な変数を宣言します。
ご覧のとおり、Phong ライティング モデルの各要素に対してカラー ベクトルを定義します。アンビエント マテリアル ベクトルは、アンビエント ライティングでオブジェクトが反射する色を定義します。通常、これはオブジェクトと同じ色です。拡散マテリアル ベクトルは、拡散照明下のオブジェクトの色を定義します。ディフューズ カラーは、必要なオブジェクト カラーに設定されます (環境光と同様)。スペキュラー マテリアル ベクトルは、オブジェクトがスペキュラー ライティングの影響を受ける (またはオブジェクト固有のスペキュラー ハイライト カラーを反映する) 色を設定します。最後に、光沢はスペキュラ ハイライトの散乱/半径に影響します。
これらの 4 つの要素はオブジェクトのマテリアルを定義し、それによって多くの現実世界のマテリアルをシミュレートできます。これは、外界の実際のマテリアルをシミュレートするいくつかのマテリアル プロパティを示す devernay.free.fr のリストです。下の画像は、いくつかの現実世界のマテリアルがキューブに与える影響を示しています。
ご覧のとおり、オブジェクトのマテリアル プロパティを正しく割り当てると、オブジェクトの関連プロパティのスケールが変化するようです。この効果は明らかに人目を引くものですが、ほとんどの現実的な効果には、立方体だけでなく、より複雑な形状が必要になります。後のチュートリアルでは、より複雑な形状について説明します。
オブジェクトに適切なマテリアルを適用することは非常に困難であり、多くの実験と豊富な経験が必要です。そのため、間違ったマテリアルを設定すると、オブジェクトの画質が損なわれることがよくあります。
このようなマテリアル システムをシェーダーに実装してみましょう。
セット素材
フラグメント シェーダーで均一なマテリアル構造体を作成したので、次に新しいマテリアル プロパティに対応するようにライティングの計算を変更します。すべてのマテリアル要素は構造体に格納されているため、uniform 変数マテリアルから取得できます。
void main()
{
// 环境光
vec3 ambient = lightColor * material.ambient;
// 漫反射光
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
flaot diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflece(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
ご覧のとおり、必要な場所でマテリアル構造体のすべてのプロパティを取得します。今回はマテリアル カラーを使用して、結果の出力の色を計算します。オブジェクトの各マテリアル プロパティは、対応するライト エレメントで乗算されます。
適切なユニフォームを設定することで、アプリケーションでオブジェクトのマテリアルを設定できます。GLSL の構造体は、uniform を設定するときに特別とは見なされません。構造体の値は均一な変数のラッパーとして機能するため、構造体にデータを入力したい場合は、構造体の各要素に均一な値を設定する必要がありますが、今回は構造体名のプレフィックスを付けます。
GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");
glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f(matShineLoc, 32.0f);
アンビエント要素とディフューズ要素をオブジェクトに表示させたい色に設定し、オブジェクトのスペキュラー要素を中程度の明るさの色に設定します; スペキュラー要素が指定されたオブジェクトにあまり強い影響を与えないようにします. また、光沢を 32 に設定します。アプリケーション内のオブジェクトのマテリアルに簡単に影響を与えることができるようになりました。
プログラムを実行すると、次の結果が得られます:
)
変ですね。
完全なコード
頂点シェーダー:
#version 330 core
layout( location = 0 ) in vec3 position;
layout( location = 1 ) in vec3 normal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat3 normatr;
out vec3 FragPos;
out vec3 Normal;
void main()
{
gl_Position = projection * view * model * vec4(position, 1.0f);
FragPos = vec3(model * vec4(position, 1.0f));
Normal = normatr * normal;
}
フラグメント シェーダー:
#version 330 core
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;
// 输出顶点的颜色
out vec4 color;
// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;
void main()
{
// 环境光
vec3 ambient = lightColor * material.ambient;
// 漫反射光
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = lightColor * (diff * material.diffuse);
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = lightColor * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
メインプログラム:
// 标准输出
#include <string>
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// 着色器类和摄像机类
#include "Shader.h"
#include "Camera.h"
// GLM 数学库
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// 图片数据读取库
#include <SOIL.h>
// 定义窗口大小
GLuint screenWidth = 800, screenHeight = 600;
// GLFW窗口需要注册的函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
// 处理摄像机位置移动的函数
void Do_Movement();
// 定义摄像机
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
// 定义数组存储按键信息
bool keys[1024];
//
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
glm::vec3 lightPos(1.2f, 1.0f, -0.5f);
// The MAIN function, from here we start our application and run our Game loop
int main()
{
// 初始化GLFW
glfwInit();
// 设置glfw使用的OpenGL版本
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
// 设置使用OpenGL的核心模式
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 设置窗口大小可改变性 为不可变
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
// GLFW会自动创建一个每像素4个子采样点的深度和样本缓冲。这也意味着所有缓冲的大小都增长了4倍。
glfwWindowHint(GLFW_SAMPLES, 4);
// 创建GLFW的窗口
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr);
// 设置window窗口线程为主线程
glfwMakeContextCurrent(window);
// 注册窗口的事件
glfwSetKeyCallback(window, key_callback); // 按键检测
glfwSetCursorPosCallback(window, mouse_callback); // 鼠标移动检测
glfwSetScrollCallback(window, scroll_callback); // 滚轮滑动检测
// 设置隐藏光标模式
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// 设置GLEW为更现代的模式
glewExperimental = GL_TRUE;
// 初始化GLEW
glewInit();
// 设置视口的位置和大小(位置是相对于窗口左下角的)
glViewport(0, 0, screenWidth, screenHeight);
// 开启深度测试功能
glEnable(GL_DEPTH_TEST);
// 根据顶点着色器和片段着色器位置创建着色器程序
Shader lightingShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\vertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\fragmentShader.txt");
Shader lampShader("C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightVertexShader.txt", "C:\\Users\\32156\\source\\repos\\LearnOpenGL\\Shader\\lightFragmentShader.txt");
// 顶点数据(位置+纹理坐标)
GLfloat vertices[] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f
};
// 设置VBO和VAO
GLuint VBO, VAO;
// 先申请VAO和VBO的显存
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
// 绑定VAO
glBindVertexArray(VAO);
// 绑定VBO
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 将顶点数据传输到显存中
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 位置数据解析
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindVertexArray(0); // 解绑VAO
GLuint lightVAO;
glGenVertexArrays(1, &lightVAO);
glBindVertexArray(lightVAO);
// 只需要绑定VBO不用再次设置VBO的数据,因为容器(物体)的VBO数据中已经包含了正确的立方体顶点数据
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// 设置灯立方体的顶点属性指针(仅设置灯的顶点数据)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);
// 游戏循环
while (!glfwWindowShouldClose(window))
{
// 计算帧相差时间
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// 窗口的事件检测
glfwPollEvents();
// 根据事件移动摄像机
Do_Movement();
// 设置清空屏幕颜色缓存所使用颜色
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
// 清空 屏幕颜色缓存、深度缓存
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 被光源照亮的普通物体的着色器
lightingShader.Use();
GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor");
GLint lightColorLoc = glGetUniformLocation(lightingShader.Program, "lightColor");
GLint lightPosLoc = glGetUniformLocation(lightingShader.Program, "lightPos");
GLint viewPosLoc = glGetUniformLocation(lightingShader.Program, "viewPos");
glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f);// 我们所熟悉的珊瑚红
glUniform3f(lightColorLoc, 1.0f, 1.0f, 1.0f); // 依旧把光源设置为白色
glUniform3f(lightPosLoc, lightPos.x, lightPos.y, lightPos.z);
glUniform3f(viewPosLoc, camera.Position.x, camera.Position.y, camera.Position.z);
// 创建观察矩阵( 只要创建矩阵记得初始化= glm::mat4(1.0f) )
glm::mat4 view = glm::mat4(1.0f);
// 获取观察矩阵(根据摄像机的状态)
view = camera.GetViewMatrix();
// 创建投影矩阵
glm::mat4 projection = glm::mat4(1.0f);
// 计算投影矩阵(fov视野为摄像机的属性camera.Zoom)
projection = glm::perspective(camera.Zoom, (float)screenWidth / (float)screenHeight, 0.1f, 1000.0f);
// 计算顶点着色器中矩阵的位置值
GLint modelLoc = glGetUniformLocation(lightingShader.Program, "model");
GLint viewLoc = glGetUniformLocation(lightingShader.Program, "view");
GLint projLoc = glGetUniformLocation(lightingShader.Program, "projection");
// 将观察矩阵和投影矩阵传入对应的位置(记得转换)
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
// 绑定VAO
glBindVertexArray(VAO);
// 计算模型矩阵
glm::mat4 model = glm::mat4(1.0f);
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
// 根据模型矩阵model计算正规矩阵normatr。inverse取逆和transpose转置 函数使用glm对应函数
GLuint normalLoc = glGetUniformLocation(lightingShader.Program, "normatr");
glm::mat3 normatr = (glm::mat3)(glm::transpose(glm::inverse(model)));
// 向物体的顶点着色器传入正规矩阵normatr
glUniformMatrix3fv(normalLoc, 1, GL_FALSE, glm::value_ptr(normatr));
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// 设置材质
GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");
glUniform3f(matAmbientLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matDiffuseLoc, 1.0f, 0.5f, 0.31f);
glUniform3f(matSpecularLoc, 0.5f, 0.5f, 0.5f);
glUniform1f(matShineLoc, 32.0f);
// 使用光源着色器
lampShader.Use();
modelLoc = glGetUniformLocation(lampShader.Program, "model");
viewLoc = glGetUniformLocation(lampShader.Program, "view");
projLoc = glGetUniformLocation(lampShader.Program, "projection");
glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));
model = glm::mat4(1.0f);
GLfloat lightX = sin(glfwGetTime());
GLfloat lightZ = cos(glfwGetTime());
GLfloat lightY = 1;
lightPos = glm::vec3(lightX, lightY, lightZ);
model = glm::translate(model, lightPos);
model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cube
glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
glBindVertexArray(lightVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// 交换前后缓冲区
glfwSwapBuffers(window);
}
// 释放VAO和VBO的显存
glDeleteVertexArrays(1, &VAO);
glDeleteVertexArrays(1, &lightVAO);
glDeleteBuffers(1, &VBO);
// 释放glfw的内存
glfwTerminate();
return 0;
}
// 根据按键信息移动摄像机
void Do_Movement()
{
// 如果某个键按下,就执行摄像机对应的方法(更新摄像机的位置)
if (keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime);
if (keys[GLFW_KEY_S])
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (keys[GLFW_KEY_A])
camera.ProcessKeyboard(LEFT, deltaTime);
if (keys[GLFW_KEY_D])
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// 按键回调函数
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
// 如果按下ESE则设置窗口应该关闭
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key >= 0 && key < 1024)
{
// 如果按下某一个键,则设置其keys为true,如果松开则设置回false
if (action == GLFW_PRESS)
keys[key] = true;
else if (action == GLFW_RELEASE)
keys[key] = false;
}
}
// 鼠标移动的回调函数
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
// 第一次进窗口鼠标坐标很大,所以第一次调用函数我们需要把它设置为一个正常的值
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
// 计算鼠标在竖直方向和水平方向的偏移(屏幕坐标系往右x大,但往下是y大,所以运算顺序不同)
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos; // Reversed since y-coordinates go from bottom to left
// 更新鼠标的坐标
lastX = xpos;
lastY = ypos;
// 调用摄像机的鼠标移动函数(根据位置偏移更新摄像机方式)
camera.ProcessMouseMovement(xoffset, yoffset);
}
// 鼠标滚动的回调函数
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
// 调用摄像机的鼠标滚动函数(根据位置偏移更新摄像机fov视野大小)
camera.ProcessMouseScroll(yoffset);
}
光の性質
このオブジェクトは明るすぎます。物体が明るすぎるのは、環境、拡散反射、鏡面の 3 色の光源がすべて完全に反射するためです。光源は、環境要素、拡散要素、鏡面要素の強度が同時に異なります。前のチュートリアルでは、環境とスペキュラー強度を 1 つの強度値で変更することで、この問題を解決しました。同じシステムを実行したいのですが、今回は各ライト要素の強度ベクトルを指定します。lightColor が vec3(1.0) であると仮定すると、コードは次のようになります。
vec3 ambient = vec3(1.0f) * material.ambient;
vec3 diffuse = vec3(1.0f) * (diff * material.diffuse);
vec3 specular = vec3(1.0f) * (spec * material.specular);
したがって、オブジェクトの各マテリアル プロパティは、各ライト エレメントの完全な強度を返します。これらの vec3(1.0) 値は、各光源に個別に影響を与えることができます。これは通常、私たちが望むものです。オブジェクトのアンビエント要素は立方体の色を完全に表示しますが、アンビエント要素は最終的な色に大きな影響を与えるべきではないため、ライトのアンビエントの明るさをより小さな値に設定してアンビエントを制限する必要があります。色:
vec3 result = vec3(0.1f) * material.ambient;
同じ方法で、拡散光源と鏡面光源の強度に影響を与えることができます。これは、前のチュートリアルで行ったことと非常に似ています。各ライト要素に個別に影響を与えるいくつかのライト プロパティを作成したと言えます。ライトのプロパティのマテリアル構造体に似たものを作成したいと考えています。
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
光源の周囲光、拡散光、鏡面光はすべて異なる明るさを持っています。アンビエント ライトは通常、比較的低い明るさに設定されます。これは、アンビエント カラーが目立ちすぎないようにするためです。光源のディフューズ要素は通常、希望する光の色 (多くの場合、明るい白) に設定されます。鏡面反射要素は通常、タイプ vec3(1.0f) の完全な強度のグローに設定されます。留意すべきことは、ライトの位置も構造体に追加することです。
マテリアル ユニフォームと同様に、フラグメント シェーダーを更新する必要があります。
vec3 ambient = light.ambient * material.ambient;
vec3 diffuse = light.diffuse * (diff * material.diffuse);
vec3 specular = light.specular * (spec * material.specular);
次に、アプリケーションでライトの明るさを設定する必要があります。
GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");
glUniform3f(lightAmbientLoc, 0.2f, 0.2f, 0.2f);
glUniform3f(lightDiffuseLoc, 0.5f, 0.5f, 0.5f);// 让我们把这个光调暗一点,这样会看起来更自然
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);
ライトがオブジェクトのすべてのマテリアルにどのように影響するかを調整したので、前のチュートリアルのようにより視覚的な出力が得られます。今回は、オブジェクトの照明とマテリアルを完全に制御できます:) オブジェクトの外観の変更は比較的簡単です
。
もっと面白いことをしよう!
完全なコード
フラグメント シェーダー:
#version 330 core
struct Material
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
struct Light
{
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
// 输入顶点位置和法向量
in vec3 FragPos;
in vec3 Normal;
// 输出顶点的颜色
out vec4 color;
// 顶点本身的颜色
uniform vec3 objectColor;
// 光源的颜色和位置
uniform vec3 lightColor;
uniform vec3 lightPos;
// 观察者的位置(世界坐标)
uniform vec3 viewPos;
void main()
{
// 环境光
vec3 ambient = light.ambient * material.ambient;
// 漫反射光
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = light.diffuse * (diff * material.diffuse);
// 镜面高光
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
vec3 specular = light.specular * (spec * material.specular);
vec3 result = ambient + diffuse + specular;
color = vec4(result, 1.0f);
}
異なる光源色
これまでのところ、光源の色はオブジェクトの各要素の強度を変更するためだけに使用しており (白から灰色、黒の範囲の色を選択することによって)、オブジェクトの実際の色には影響を与えていません (単に強度)。ライトのプロパティに簡単にアクセスできるようになったので、時間をかけて色を変更して、興味深い効果を実現できます。すべてがフラグメント シェーダーで既に行われているため、光の色の変更は簡単で、すぐに興味深い効果を作成できます:) ご覧のとおり、
さまざま
な光の色がオブジェクトの色出力に大きく影響します。光の色はオブジェクトによって反射される色に直接影響するため (色のチュートリアルで説明したことを思い出すかもしれません)、視覚的な出力に大きな影響を与えます。
sin と glfwGetTime を使用してライトのアンビエント カラーとディフューズ カラーを変更すると、時間の経過とともに光源のカラーを簡単に変更できます。
glm::vec3 lightColor; lightColor.x = sin(glfwGetTime() * 2.0f);
lightColor.y = sin(glfwGetTime() * 0.7f);
lightColor.z = sin(glfwGetTime() * 1.3f);
glm::vec3 diffuseColor = lightColor * glm::vec3(0.5f);
glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
glUniform3f(lightAmbientLoc, ambientColor.x, ambientColor.y, ambientColor.z);
glUniform3f(lightDiffuseLoc, diffuseColor.x, diffuseColor.y, diffuseColor.z);```
練習
チュートリアルの冒頭で行ったように、いくつかの材料特性に基づいて現実世界のオブジェクトをシミュレートできますか? マテリアル テーブルの環境光の色は、拡散光の色とは異なる場合があることに注意してください。これは、シミュレーションで光の強度が考慮されていないためです。光の色の強度を vec(1.0) に変更する必要があります。 f) 正しい結果を出力するには:
コア コード:
// 设置材质
GLint matAmbientLoc = glGetUniformLocation(lightingShader.Program, "material.ambient");
GLint matDiffuseLoc = glGetUniformLocation(lightingShader.Program, "material.diffuse");
GLint matSpecularLoc = glGetUniformLocation(lightingShader.Program, "material.specular");
GLint matShineLoc = glGetUniformLocation(lightingShader.Program, "material.shininess");
glUniform3f(matAmbientLoc, 0.135f, 0.2225f, 0.1575f);
glUniform3f(matDiffuseLoc, 0.54f, 0.89f, 0.63f);
glUniform3f(matSpecularLoc, 0.316228f, 0.316228f, 0.316228f);
glUniform1f(matShineLoc, 32.0f);
// 设置光源属性
GLint lightAmbientLoc = glGetUniformLocation(lightingShader.Program, "light.ambient");
GLint lightDiffuseLoc = glGetUniformLocation(lightingShader.Program, "light.diffuse");
GLint lightSpecularLoc = glGetUniformLocation(lightingShader.Program, "light.specular");
glUniform3f(lightAmbientLoc, 1.0f, 1.0f, 1.0f);
glUniform3f(lightDiffuseLoc, 1.0f, 1.0f, 1.0f);// 让我们把这个光调暗一点,这样会看起来更自然
glUniform3f(lightSpecularLoc, 1.0f, 1.0f, 1.0f);