• OpenGL学习——16.多光源


    前情提要:本文代码源自Github上的学习文档“LearnOpenGL”,我仅在源码的基础上加上中文注释。本文章不以该学习文档做任何商业盈利活动,一切著作权归原作者所有,本文仅供学习交流,如有侵权,请联系我删除。LearnOpenGL原网址:https://learnopengl.com/ 请大家多多支持原作者!


    计算机图形学中,光照是创造逼真和吸引人的场景的关键要素之一。光照可以赋予物体深度、明暗和真实感,使其在屏幕上生动地呈现出来。在OpenGL中,通过使用光源和相应的光照模型,我们可以实现各种令人惊叹的光照效果。

    然而,单一光源有时无法满足我们对场景真实感的要求。在某些情况下,我们可能需要多个光源来模拟各种光照条件,例如室内灯光、夜晚景观或光源的位置变化等。这就引出了OpenGL中的多光源渲染。

    多光源渲染是指在一个场景中使用多个光源进行照明计算的技术。它使得我们能够更加逼真地模拟各种光照情况,并为场景带来更多的细节和层次感。无论是增强游戏体验、创建逼真的渲染效果,还是构建交互式的虚拟现实场景,多光源渲染都是一个强大而有用的工具。

    在本博客文章中,我们将深入探讨OpenGL中的多光源渲染技术。我们将了解如何设置和管理多个光源,如何计算多光源的光照效果,并将通过实例演示如何在OpenGL中实现多光源渲染。无论您是OpenGL初学者还是有经验的开发者,本文都将为您提供一个全面的指南,帮助您理解和应用多光源渲染的概念和技术。

    让我们开始探索OpenGL中的多光源渲染,并为我们的场景带来更多的光照变化和视觉效果吧!

    项目结构:

    vs_multiple_lights.txt着色器代码:

    1. #version 330 core
    2. layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
    3. layout (location = 1) in vec3 aNormal;
    4. layout (location = 2) in vec2 aTexCoords;
    5. out vec3 FragPos;
    6. out vec3 Normal;
    7. out vec2 TexCoords;
    8. out mat4 View;
    9. uniform mat4 model;
    10. uniform mat4 view;
    11. uniform mat4 projection;
    12. void main()
    13. {
    14. gl_Position = projection * view * model * vec4(aPos, 1.0);
    15. FragPos = vec3(model * vec4(aPos, 1.0));
    16. Normal = mat3(transpose(inverse(model))) * aNormal;
    17. TexCoords = aTexCoords;
    18. View = view;
    19. }

    fs_multiple_lights.txt着色器代码:

    1. #version 330 core
    2. // 材质
    3. struct Material {
    4. sampler2D diffuse;
    5. sampler2D specular;
    6. float shininess;
    7. };
    8. // 定向光
    9. struct DirLight {
    10. vec3 direction;
    11. vec3 ambient;
    12. vec3 diffuse;
    13. vec3 specular;
    14. };
    15. uniform DirLight dirLight;
    16. // 点光源
    17. struct PointLight {
    18. vec3 position;
    19. float constant;
    20. float linear;
    21. float quadratic;
    22. vec3 ambient;
    23. vec3 diffuse;
    24. vec3 specular;
    25. };
    26. #define NR_POINT_LIGHTS 4
    27. uniform PointLight pointLights[NR_POINT_LIGHTS];
    28. // 聚光
    29. struct SpotLight {
    30. sampler2D spotlightMap;
    31. float cutOff;
    32. float outerCutOff;
    33. vec3 position;
    34. vec3 direction;
    35. float constant;
    36. float linear;
    37. float quadratic;
    38. vec3 ambient;
    39. vec3 diffuse;
    40. vec3 specular;
    41. };
    42. uniform SpotLight spotLight;
    43. out vec4 FragColor; // 输出片段颜色
    44. in vec3 FragPos;
    45. in vec3 Normal;
    46. in vec2 TexCoords;
    47. in mat4 View;
    48. uniform vec3 viewPos;
    49. uniform Material material;
    50. vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir);
    51. vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
    52. vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
    53. void main()
    54. {
    55. // 属性
    56. vec3 norm = normalize(Normal);
    57. vec3 viewDir = normalize(viewPos - FragPos);
    58. // 第一阶段:定向光照
    59. vec3 result = CalcDirLight(dirLight, norm, viewDir);
    60. // 第二阶段:点光源
    61. for(int i = 0; i < NR_POINT_LIGHTS; i++)
    62. result += CalcPointLight(pointLights[i], norm, FragPos, viewDir);
    63. // 第三阶段:聚光
    64. result += CalcSpotLight(spotLight, norm, FragPos, viewDir);
    65. FragColor = vec4(result, 1.0);
    66. }
    67. // 计算定向光(Calculate Direction Light)
    68. vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir)
    69. {
    70. vec3 lightDir = normalize(-light.direction);
    71. // 漫反射着色
    72. float diff = max(dot(normal, lightDir), 0.0);
    73. // 镜面光着色
    74. vec3 reflectDir = reflect(-lightDir, normal);
    75. float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    76. // 合并结果
    77. vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
    78. vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
    79. vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    80. return (ambient + diffuse + specular);
    81. }
    82. // 计算点光源(Calculate Point Light)
    83. vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    84. {
    85. vec3 lightDir = normalize(light.position - fragPos);
    86. // 漫反射着色
    87. float diff = max(dot(normal, lightDir), 0.0);
    88. // 镜面光着色
    89. vec3 reflectDir = reflect(-lightDir, normal);
    90. float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    91. // 衰减
    92. float distance = length(light.position - fragPos);
    93. float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    94. // 合并结果
    95. vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
    96. vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
    97. vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    98. ambient *= attenuation;
    99. diffuse *= attenuation;
    100. specular *= attenuation;
    101. return (ambient + diffuse + specular);
    102. }
    103. // 计算聚光(Calculate Spot Light)
    104. vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
    105. {
    106. // 切光角
    107. vec3 lightDir = normalize(light.position - fragPos);
    108. float theta = dot(lightDir, normalize(-light.direction));
    109. float epsilon = light.cutOff - light.outerCutOff;
    110. float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0);
    111. // 执行光照计算
    112. if(theta > light.outerCutOff)
    113. {
    114. vec3 lightDir = normalize(light.position - fragPos);
    115. // 漫反射着色
    116. float diff = max(dot(normal, lightDir), 0.0);
    117. // 镜面光着色
    118. vec3 reflectDir = reflect(-lightDir, normal);
    119. float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    120. // 图案
    121. vec4 view = View * vec4(fragPos, 1.0);
    122. vec2 texcoord = normalize(view.xyz).xy;
    123. texcoord.y *= -1;
    124. // 衰减
    125. float distance = length(light.position - fragPos);
    126. float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
    127. // 合并结果
    128. vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
    129. vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));
    130. vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
    131. vec3 spotdiffuse = diff * vec3(texture(light.spotlightMap, ((texcoord) / 0.7 + 0.5)));
    132. ambient *= attenuation;
    133. diffuse *= attenuation * intensity;
    134. specular *= attenuation * intensity;
    135. spotdiffuse *= attenuation * intensity;
    136. return (ambient + diffuse + specular + spotdiffuse);
    137. }
    138. }

    vs_light_cube.txt着色器代码:

    1. #version 330 core
    2. layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为 0
    3. uniform mat4 model;
    4. uniform mat4 view;
    5. uniform mat4 projection;
    6. void main()
    7. {
    8. gl_Position = projection * view * model * vec4(aPos, 1.0);
    9. }

    fs_light_cube.txt着色器代码:

    1. #version 330 core
    2. out vec4 FragColor; // 输出片段颜色
    3. uniform vec3 lightCubeColor;
    4. void main()
    5. {
    6. FragColor = vec4(lightCubeColor, 1.0);
    7. }

    SHADER_H.h头文件代码:

    1. #ifndef SHADER_H
    2. #define SHADER_H
    3. #include ;
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. /* 着色器类 */
    12. class Shader
    13. {
    14. public:
    15. /* 着色器程序 */
    16. unsigned int shaderProgram;
    17. /* 构造函数,从文件读取并构建着色器 */
    18. Shader(const char* vertexPath, const char* fragmentPath)
    19. {
    20. std::string vertexCode;
    21. std::string fragmentCode;
    22. std::ifstream vShaderFile;
    23. std::ifstream fShaderFile;
    24. /* 保证ifstream对象可以抛出异常: */
    25. vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    26. fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    27. try
    28. {
    29. /* 打开文件 */
    30. vShaderFile.open(vertexPath);
    31. fShaderFile.open(fragmentPath);
    32. std::stringstream vShaderStream, fShaderStream;
    33. /* 读取文件的缓冲内容到数据流中 */
    34. vShaderStream << vShaderFile.rdbuf();
    35. fShaderStream << fShaderFile.rdbuf();
    36. /* 关闭文件处理器 */
    37. vShaderFile.close();
    38. fShaderFile.close();
    39. /* 转换数据流到string */
    40. vertexCode = vShaderStream.str();
    41. fragmentCode = fShaderStream.str();
    42. }
    43. catch (std::ifstream::failure e)
    44. {
    45. std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;
    46. }
    47. /* string类型转化为char字符串类型 */
    48. const char* vShaderCode = vertexCode.c_str();
    49. const char* fShaderCode = fragmentCode.c_str();
    50. /* 着色器 */
    51. unsigned int vertex, fragment;
    52. int success;
    53. /* 信息日志(编译或运行报错信息) */
    54. char infoLog[512];
    55. /* 顶点着色器 */
    56. vertex = glCreateShader(GL_VERTEX_SHADER);
    57. glShaderSource(vertex, 1, &vShaderCode, NULL);
    58. /* 编译 */
    59. glCompileShader(vertex);
    60. /* 打印编译错误(如果有的话) */
    61. glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
    62. if (!success)
    63. {
    64. glGetShaderInfoLog(vertex, 512, NULL, infoLog);
    65. std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    66. };
    67. /* 片段着色器 */
    68. fragment = glCreateShader(GL_FRAGMENT_SHADER);
    69. glShaderSource(fragment, 1, &fShaderCode, NULL);
    70. /* 编译 */
    71. glCompileShader(fragment);
    72. /* 打印编译错误(如果有的话) */
    73. glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
    74. if (!success)
    75. {
    76. glGetShaderInfoLog(fragment, 512, NULL, infoLog);
    77. std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
    78. }
    79. /* 着色器程序 */
    80. shaderProgram = glCreateProgram();
    81. /* 连接顶点着色器和片段着色器到着色器程序中 */
    82. glAttachShader(shaderProgram, vertex);
    83. glAttachShader(shaderProgram, fragment);
    84. /* 链接着色器程序到我们的程序中 */
    85. glLinkProgram(shaderProgram);
    86. /* 打印连接错误(如果有的话) */
    87. glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    88. if (!success)
    89. {
    90. glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    91. std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    92. }
    93. /* 删除着色器,它们已经链接到我们的程序中了,已经不再需要了 */
    94. glDeleteShader(vertex);
    95. glDeleteShader(fragment);
    96. }
    97. /* 激活着色器程序 */
    98. void use()
    99. {
    100. glUseProgram(shaderProgram);
    101. }
    102. /* 实用程序统一函数,Uniform工具函数,用于设置uniform类型的数值 */
    103. // ------------------------------------------------------------------------
    104. void setBool(const std::string& name, bool value) const
    105. {
    106. glUniform1i(glGetUniformLocation(shaderProgram, name.c_str()), (int)value);
    107. }
    108. // ------------------------------------------------------------------------
    109. void setInt(const std::string& name, int value) const
    110. {
    111. glUniform1i(glGetUniformLocation(shaderProgram, name.c_str()), value);
    112. }
    113. // ------------------------------------------------------------------------
    114. void setFloat(const std::string& name, float value) const
    115. {
    116. glUniform1f(glGetUniformLocation(shaderProgram, name.c_str()), value);
    117. }
    118. // ------------------------------------------------------------------------
    119. void setVec2(const std::string& name, const glm::vec2& value) const
    120. {
    121. glUniform2fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);
    122. }
    123. void setVec2(const std::string& name, float x, float y) const
    124. {
    125. glUniform2f(glGetUniformLocation(shaderProgram, name.c_str()), x, y);
    126. }
    127. // ------------------------------------------------------------------------
    128. void setVec3(const std::string& name, const glm::vec3& value) const
    129. {
    130. glUniform3fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);
    131. }
    132. void setVec3(const std::string& name, float x, float y, float z) const
    133. {
    134. glUniform3f(glGetUniformLocation(shaderProgram, name.c_str()), x, y, z);
    135. }
    136. // ------------------------------------------------------------------------
    137. void setVec4(const std::string& name, const glm::vec4& value) const
    138. {
    139. glUniform4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, &value[0]);
    140. }
    141. void setVec4(const std::string& name, float x, float y, float z, float w) const
    142. {
    143. glUniform4f(glGetUniformLocation(shaderProgram, name.c_str()), x, y, z, w);
    144. }
    145. // ------------------------------------------------------------------------
    146. void setMat2(const std::string& name, const glm::mat2& mat) const
    147. {
    148. glUniformMatrix2fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);
    149. }
    150. // ------------------------------------------------------------------------
    151. void setMat3(const std::string& name, const glm::mat3& mat) const
    152. {
    153. glUniformMatrix3fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);
    154. }
    155. // ------------------------------------------------------------------------
    156. void setMat4(const std::string& name, const glm::mat4& mat) const
    157. {
    158. glUniformMatrix4fv(glGetUniformLocation(shaderProgram, name.c_str()), 1, GL_FALSE, &mat[0][0]);
    159. }
    160. /* 删除着色器程序 */
    161. void deleteProgram()
    162. {
    163. glDeleteProgram(shaderProgram);
    164. }
    165. };
    166. #endif

    camera.h头文件代码:

    1. #ifndef CAMERA_H
    2. #define CAMERA_H
    3. #include
    4. #include
    5. #include
    6. #include
    7. /* 定义摄影机移动的几个可能选项。 */
    8. enum Camera_Movement {
    9. /* 前进 */
    10. FORWARD,
    11. /* 后退 */
    12. BACKWARD,
    13. /* 左移 */
    14. LEFT,
    15. /* 右移 */
    16. RIGHT,
    17. /* 上升 */
    18. RISE,
    19. /* 下降 */
    20. FALL
    21. };
    22. /* 默认摄像机参数 */
    23. /* 偏航角 */
    24. const float YAW = -90.0f;
    25. /* 俯仰角 */
    26. const float PITCH = 0.0f;
    27. /* 速度 */
    28. const float SPEED = 2.5f;
    29. /* 鼠标灵敏度 */
    30. const float SENSITIVITY = 0.1f;
    31. /* 视野 */
    32. const float ZOOM = 70.0f;
    33. /* 一个抽象的摄影机类,用于处理输入并计算相应的欧拉角、向量和矩阵,以便在OpenGL中使用 */
    34. class Camera
    35. {
    36. public:
    37. /* 摄影机属性 */
    38. /* 位置 */
    39. glm::vec3 Position;
    40. /* 朝向 */
    41. glm::vec3 Front;
    42. /* 上轴 */
    43. glm::vec3 Up;
    44. /* 右轴 */
    45. glm::vec3 Right;
    46. /* 世界竖直向上方向 */
    47. glm::vec3 WorldUp;
    48. /* 偏航角 */
    49. float Yaw;
    50. /* 俯仰角 */
    51. float Pitch;
    52. /* 摄影机选项 */
    53. /* 移动速度 */
    54. float MovementSpeed;
    55. /* 鼠标灵敏度 */
    56. float MouseSensitivity;
    57. /* 视野 */
    58. float Zoom;
    59. /* 矢量的构造函数 */
    60. Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
    61. {
    62. Position = position;
    63. WorldUp = up;
    64. Yaw = yaw;
    65. Pitch = pitch;
    66. updateCameraVectors();
    67. }
    68. /* 标量的构造函数 */
    69. Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch) : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM)
    70. {
    71. Position = glm::vec3(posX, posY, posZ);
    72. WorldUp = glm::vec3(upX, upY, upZ);
    73. Yaw = yaw;
    74. Pitch = pitch;
    75. updateCameraVectors();
    76. }
    77. /* 返回使用欧拉角和LookAt矩阵计算的视图矩阵 */
    78. glm::mat4 GetViewMatrix()
    79. {
    80. return glm::lookAt(Position, Position + Front, Up);
    81. }
    82. /* 处理从任何类似键盘的输入系统接收的输入。接受相机定义ENUM形式的输入参数(从窗口系统中提取) */
    83. void ProcessKeyboard(Camera_Movement direction, float deltaTime)
    84. {
    85. float velocity = MovementSpeed * deltaTime;
    86. if (direction == FORWARD)
    87. Position += Front * velocity;
    88. if (direction == BACKWARD)
    89. Position -= Front * velocity;
    90. if (direction == LEFT)
    91. Position -= Right * velocity;
    92. if (direction == RIGHT)
    93. Position += Right * velocity;
    94. if (direction == RISE)
    95. Position += WorldUp * velocity;
    96. if (direction == FALL)
    97. Position -= WorldUp * velocity;
    98. }
    99. /* 处理从鼠标输入系统接收的输入。需要x和y方向上的偏移值。 */
    100. void ProcessMouseMovement(float xoffset, float yoffset, GLboolean constrainPitch = true)
    101. {
    102. xoffset *= MouseSensitivity;
    103. yoffset *= MouseSensitivity;
    104. Yaw += xoffset;
    105. Pitch += yoffset;
    106. /* 确保当俯仰角超出范围时,屏幕不会翻转 */
    107. if (constrainPitch)
    108. {
    109. if (Pitch > 89.0f)
    110. Pitch = 89.0f;
    111. if (Pitch < -89.0f)
    112. Pitch = -89.0f;
    113. }
    114. /* 使用更新的欧拉角更新“朝向”、“右轴”和“上轴” */
    115. updateCameraVectors();
    116. }
    117. /* 处理从鼠标滚轮事件接收的输入 */
    118. void ProcessMouseScroll(float yoffset)
    119. {
    120. Zoom -= (float)yoffset;
    121. if (Zoom < 10.0f)
    122. Zoom = 10.0f;
    123. if (Zoom > 120.0f)
    124. Zoom = 120.0f;
    125. }
    126. private:
    127. /* 根据摄影机的(更新的)欧拉角计算摄影机朝向 */
    128. void updateCameraVectors()
    129. {
    130. /* 计算新的摄影机朝向 */
    131. glm::vec3 front;
    132. front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch));
    133. front.y = sin(glm::radians(Pitch));
    134. front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch));
    135. Front = glm::normalize(front);
    136. /* 还重新计算右轴和上轴 */
    137. /* 重新规范(修正)向量,因为当它们的长度越接近0或向上向下看得多时,将会导致移动速度变慢 */
    138. Right = glm::normalize(glm::cross(Front, WorldUp));
    139. Up = glm::normalize(glm::cross(Right, Front));
    140. }
    141. };
    142. #endif

    stb_image.h头文件下载地址:

    https://github.com/nothings/stb/blob/master/stb_image.h

    (需要科学上网)

    container2.png图片:

    (请右键图片另存为到你的项目文件夹中)

    container2_specular.png图片:

    (请右键图片另存为到你的项目文件夹中)

    bat.jpg图片:

    (请右键图片另存为到你的项目文件夹中)

    stb_image_S.cpp源文件代码:

    1. /* 预处理器会修改头文件,让其只包含相关的函数定义源码 */
    2. #define STB_IMAGE_IMPLEMENTATION
    3. /* 图像加载头文件 */
    4. #include "stb_image.h"

    MultipleLights.cpp源文件代码:

    1. /*
    2. *
    3. * OpenGL学习——16.多光源
    4. * 2024年2月18日
    5. *
    6. */
    7. #include
    8. #include "glad/glad.h"
    9. #include "GLFW/glfw3.h"
    10. #include "glad/glad.c"
    11. #include
    12. #include
    13. #include
    14. /* 着色器头文件 */
    15. #include "SHADER_H.h"
    16. /* 摄影机头文件 */
    17. #include "camera.h"
    18. /* 图像加载头文件 */
    19. #include "stb_image.h"
    20. #pragma comment(lib, "glfw3.lib")
    21. #pragma comment(lib, "opengl32.lib")
    22. /* 屏幕宽度 */
    23. const int screenWidth = 1600;
    24. /* 屏幕高度 */
    25. const int screenHeight = 900;
    26. /* 摄影机初始位置 */
    27. Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
    28. float lastX = screenWidth / 2.0f;
    29. float lastY = screenHeight / 2.0f;
    30. bool firstMouse = true;
    31. /* 两帧之间的时间 */
    32. float deltaTime = 0.0f;
    33. float lastFrame = 0.0f;
    34. /* 灯光位置 */
    35. glm::vec3 lightPos(0.0f, 0.0f, -2.0f);
    36. /* 这是framebuffer_size_callback函数的定义,该函数用于处理窗口大小变化的回调函数。当窗口的大小发生变化时,该函数会被调用,
    37. 它会设置OpenGL视口(Viewport)的大小,以确保渲染结果正确显示。 */
    38. void framebuffer_size_callback(GLFWwindow* window, int width, int height)
    39. {
    40. glViewport(0, 0, width, height);
    41. }
    42. /* 处理用户输入 */
    43. void processInput(GLFWwindow* window)
    44. {
    45. /* 退出 */
    46. if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
    47. glfwSetWindowShouldClose(window, true);
    48. /* 前进 */
    49. if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    50. camera.ProcessKeyboard(FORWARD, deltaTime);
    51. /* 后退 */
    52. if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
    53. camera.ProcessKeyboard(BACKWARD, deltaTime);
    54. /* 左移 */
    55. if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
    56. camera.ProcessKeyboard(LEFT, deltaTime);
    57. /* 右移 */
    58. if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
    59. camera.ProcessKeyboard(RIGHT, deltaTime);
    60. /* 上升 */
    61. if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS)
    62. camera.ProcessKeyboard(RISE, deltaTime);
    63. /* 下降 */
    64. if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS)
    65. camera.ProcessKeyboard(FALL, deltaTime);
    66. }
    67. /* 鼠标回调函数 */
    68. void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
    69. {
    70. float xpos = static_cast<float>(xposIn);
    71. float ypos = static_cast<float>(yposIn);
    72. if (firstMouse)
    73. {
    74. lastX = xpos;
    75. lastY = ypos;
    76. firstMouse = false;
    77. }
    78. float xoffset = xpos - lastX;
    79. float yoffset = lastY - ypos;
    80. lastX = xpos;
    81. lastY = ypos;
    82. camera.ProcessMouseMovement(xoffset, yoffset);
    83. }
    84. /* 滚轮回调函数 */
    85. void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
    86. {
    87. camera.ProcessMouseScroll(static_cast<float>(yoffset));
    88. }
    89. /* 纹理加载函数 */
    90. unsigned int loadTexture(char const* path)
    91. {
    92. unsigned int textureID;
    93. glGenTextures(1, &textureID);
    94. int width, height, nrComponents;
    95. unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
    96. if (data)
    97. {
    98. GLenum format;
    99. if (nrComponents == 1)
    100. format = GL_RED;
    101. else if (nrComponents == 3)
    102. format = GL_RGB;
    103. else if (nrComponents == 4)
    104. format = GL_RGBA;
    105. glBindTexture(GL_TEXTURE_2D, textureID);
    106. glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    107. glGenerateMipmap(GL_TEXTURE_2D);
    108. //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    109. //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    110. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    111. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    112. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    113. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    114. stbi_image_free(data);
    115. }
    116. else
    117. {
    118. std::cout << "Texture failed to load at path: " << path << std::endl;
    119. stbi_image_free(data);
    120. }
    121. return textureID;
    122. }
    123. int main()
    124. {
    125. /* 这是GLFW库的初始化函数,用于初始化GLFW库的状态以及相关的系统资源。 */
    126. glfwInit();
    127. /* 下面两行代码表示使用OpenGL“3.3”版本的功能 */
    128. /* 这行代码设置OpenGL上下文的主版本号为3。这意味着我们希望使用OpenGL “3.几”版本的功能。 */
    129. glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    130. /* 这行代码设置OpenGL上下文的次版本号为3。这表示我们希望使用OpenGL “几.3”版本的功能。 */
    131. glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    132. /* 这行代码设置OpenGL的配置文件为核心配置文件(Core Profile)。核心配置文件是3.2及以上版本引入的,移除了一些已经被认为过时或不推荐使用的功能。 */
    133. glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    134. /* 这行代码的作用是设置OpenGL上下文为向前兼容模式,但该程序无需向后兼容,所以注释掉 */
    135. //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
    136. /* 这行代码创建一个名为"LearnOpenGL"的窗口,窗口的初始宽度为800像素,高度为600像素。最后两个参数为可选参数,用于指定窗口的监视器(显示器),
    137. 在此处设置为NULL表示使用默认的显示器。函数返回一个指向GLFWwindow结构的指针,用于表示创建的窗口。 */
    138. GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", NULL, NULL);
    139. /* 这是一个条件语句,判断窗口是否成功创建。如果窗口创建失败,即窗口指针为NULL,执行if语句块内的代码。 */
    140. if (window == NULL)
    141. {
    142. /* 这行代码使用C++标准输出流将字符串"Failed to create GLFW window"打印到控制台。即打印出“GLFW窗口创建失败”的错误信息。 */
    143. std::cout << "Failed to create GLFW window" << std::endl;
    144. /* 这行代码用于终止GLFW库的运行,释放相关的系统资源。 */
    145. glfwTerminate();
    146. /* 这是main函数的返回语句,表示程序异常结束并返回-1作为退出码。在C++中,返回负数通常表示程序发生错误或异常退出。 */
    147. return -1;
    148. }
    149. /* 这行代码将指定的窗口的上下文设置为当前上下文。它告诉OpenGL将所有渲染操作应用于指定窗口的绘图缓冲区。
    150. * 这是为了确保OpenGL在正确的窗口上进行渲染。 */
    151. glfwMakeContextCurrent(window);
    152. /* 这是一个条件语句,用于检查GLAD库的初始化是否成功。gladLoadGLLoader函数是GLAD库提供的函数,用于加载OpenGL函数指针。
    153. glfwGetProcAddress函数是GLFW库提供的函数,用于获取特定OpenGL函数的地址。这行代码将glfwGetProcAddress函数的返回值转换为GLADloadproc类型,
    154. 并将其作为参数传递给gladLoadGLLoader函数。如果初始化失败,即返回值为假(NULL),则执行if语句块内的代码。 */
    155. if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    156. {
    157. /* 这行代码使用C++标准输出流将字符串"Failed to initialize GLAD"打印到控制台。即打印出“GLAD库初始化失败”的错误信息。 */
    158. std::cout << "Failed to initialize GLAD" << std::endl;
    159. /* 这是main函数的返回语句,表示程序异常结束并返回-1作为退出码。在C++中,返回负数通常表示程序发生错误或异常退出。 */
    160. return -1;
    161. }
    162. /* 渲染之前必须告诉OpenGL渲染窗口的尺寸大小,即视口(Viewport),这样OpenGL才只能知道怎样根据窗口大小显示数据和坐标。 */
    163. /* 这行代码设置窗口的维度(Dimension),glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。 */
    164. /* 实际上也可以将视口的维度设置为比GLFW的维度小,这样子之后所有的OpenGL渲染将会在一个更小的窗口中显示,
    165. * 这样子的话我们也可以将一些其它元素显示在OpenGL视口之外。 */
    166. glViewport(0, 0, screenWidth, screenHeight);
    167. /* 这行代码设置了窗口大小变化时的回调函数,即当窗口大小发生变化时,framebuffer_size_callback函数会被调用。 */
    168. glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    169. /* 鼠标回调 */
    170. glfwSetCursorPosCallback(window, mouse_callback);
    171. /* 滚轮回调 */
    172. glfwSetScrollCallback(window, scroll_callback);
    173. /* 隐藏光标 */
    174. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    175. /* 开启深度测试 */
    176. glEnable(GL_DEPTH_TEST);
    177. /* 着色器文件 */
    178. Shader lightingShader("vs_multiple_lights.txt", "fs_multiple_lights.txt");
    179. Shader lightCubeShader("vs_light_cube.txt", "fs_light_cube.txt");
    180. /* 定义顶点坐标数据的数组 */
    181. float vertices[] =
    182. {
    183. // 顶点坐标 // 法向量 //纹理坐标
    184. // +X面
    185. 0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上角
    186. 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角
    187. 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 左下角
    188. 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角
    189. // -X面
    190. -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上角
    191. -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // 右下角
    192. -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // 左下角
    193. -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // 左上角
    194. // +Y面
    195. 0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, // 右上角
    196. 0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下角
    197. -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, // 左下角
    198. -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 左上角
    199. // -Y面
    200. 0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // 右上角
    201. 0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下角
    202. -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下角
    203. -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, // 左上角
    204. // +Z面
    205. 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // 右上角
    206. 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, // 右下角
    207. -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角
    208. -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, // 左上角
    209. // -Z面
    210. -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f, // 右上角
    211. -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, // 右下角
    212. 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, // 左下角
    213. 0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f // 左上角
    214. };
    215. /* 定义索引数据的数组 */
    216. unsigned int indices[] =
    217. {
    218. // 注意索引从0开始! 此例的索引(0,1,2,3)就是顶点数组vertices的下标,这样可以由下标代表顶点组合成矩形
    219. // +X面
    220. 0, 1, 3, // 第一个三角形
    221. 1, 2, 3, // 第二个三角形
    222. // -X面
    223. 4, 5, 7, // 第一个三角形
    224. 5, 6, 7, // 第二个三角形
    225. // +Y面
    226. 8, 9, 11, // 第一个三角形
    227. 9, 10, 11, // 第二个三角形
    228. // -Y面
    229. 12, 13, 15, // 第一个三角形
    230. 13, 14, 15, // 第二个三角形
    231. // +Z面
    232. 16, 17, 19, // 第一个三角形
    233. 17, 18, 19, // 第二个三角形
    234. // -Z面
    235. 20, 21, 23, // 第一个三角形
    236. 21, 22, 23, // 第二个三角形
    237. };
    238. /* 方块的位置 */
    239. glm::vec3 cubePositions[] = {
    240. glm::vec3(0.0f, 0.0f, 0.0f),
    241. glm::vec3(2.0f, 5.0f, -7.0f),
    242. glm::vec3(-1.5f, -2.2f, -2.5f),
    243. glm::vec3(-3.8f, -2.0f, -6.3f),
    244. glm::vec3(2.4f, -0.4f, -3.5f),
    245. glm::vec3(-1.7f, 3.0f, -7.5f),
    246. glm::vec3(1.3f, -2.0f, -2.5f),
    247. glm::vec3(1.5f, 2.0f, -4.5f),
    248. glm::vec3(3.5f, 0.2f, -1.5f),
    249. glm::vec3(-1.3f, 1.0f, -1.5f)
    250. };
    251. /* 光源的位置 */
    252. glm::vec3 pointLightPositions[] = {
    253. glm::vec3(0.7f, 0.2f, 2.0f),
    254. glm::vec3(2.3f, -3.3f, -4.0f),
    255. glm::vec3(-4.0f, 2.0f, -12.0f),
    256. glm::vec3(0.0f, 0.0f, -3.0f)
    257. };
    258. /* 光源的颜色 */
    259. glm::vec3 pointLightColors[] = {
    260. glm::vec3(1.0f, 1.0f, 1.0f),
    261. glm::vec3(1.0f, 0.0f, 0.0f),
    262. glm::vec3(0.0f, 1.0f, 0.0f),
    263. glm::vec3(0.0f, 0.0f, 1.0f)
    264. };
    265. /* 创建顶点数组对象(cubeVAO)(lightCubeVAO),顶点缓冲对象(VBO)和元素缓冲对象(EBO) */
    266. unsigned int cubeVAO, lightCubeVAO;
    267. unsigned int VBO;
    268. unsigned int EBO;
    269. glGenVertexArrays(1, &cubeVAO);
    270. glGenVertexArrays(1, &lightCubeVAO);
    271. glGenBuffers(1, &VBO);
    272. glGenBuffers(1, &EBO);
    273. /* cubeVAO */
    274. /* 绑定顶点数组对象,顶点缓冲对象和元素缓冲对象 */
    275. glBindVertexArray(cubeVAO);
    276. glBindBuffer(GL_ARRAY_BUFFER, VBO);
    277. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    278. /* 将顶点数据复制到顶点缓冲对象中 */
    279. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    280. /* 将索引数据复制到元素缓冲对象中 */
    281. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    282. /* 设置顶点属性指针,指定如何解释顶点数据 */
    283. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); // 顶点坐标
    284. /* 启用顶点属性 */
    285. glEnableVertexAttribArray(0);
    286. /* 设置顶点属性指针,指定如何解释顶点数据 */
    287. glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); // 法向量
    288. /* 启用顶点属性 */
    289. glEnableVertexAttribArray(1);
    290. glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    291. glEnableVertexAttribArray(2);
    292. /* lightCubeVAO */
    293. /* 绑定顶点数组对象,顶点缓冲对象和元素缓冲对象 */
    294. glBindVertexArray(lightCubeVAO);
    295. glBindBuffer(GL_ARRAY_BUFFER, VBO);
    296. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    297. /* 将顶点数据复制到顶点缓冲对象中 */
    298. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    299. /* 将索引数据复制到元素缓冲对象中 */
    300. glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    301. /* 设置顶点属性指针,指定如何解释顶点数据 */
    302. glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); // 顶点坐标
    303. /* 启用顶点属性 */
    304. glEnableVertexAttribArray(0);
    305. /* 解绑顶点数组对象,顶点缓冲对象和元素缓冲对象 */
    306. glBindVertexArray(0);
    307. glBindBuffer(GL_ARRAY_BUFFER, 0);
    308. glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    309. /* 材质 */
    310. unsigned int diffuseMap = loadTexture("container2.png");
    311. unsigned int specularMap = loadTexture("container2_specular.png");
    312. unsigned int spotlightMap = loadTexture("bat.jpg");
    313. lightingShader.use();
    314. /* 材质漫反射 */
    315. lightingShader.setInt("material.diffuse", 0);
    316. /* 材质镜面反射 */
    317. lightingShader.setInt("material.specular", 1);
    318. /* 手电筒纹理 */
    319. lightingShader.setInt("spotLight.spotlightMap", 2);
    320. /* 这是一个循环,只要窗口没有被要求关闭,就会一直执行循环内的代码。 */
    321. while (!glfwWindowShouldClose(window))
    322. {
    323. float currentFrame = static_cast<float>(glfwGetTime());
    324. deltaTime = currentFrame - lastFrame;
    325. lastFrame = currentFrame;
    326. /* 这行代码调用processInput函数,用于处理用户输入。 */
    327. processInput(window);
    328. /* 这行代码设置清空颜色缓冲区时的颜色。在这个示例中,将颜色设置为浅蓝色。 */
    329. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    330. /* 这行代码清空颜色缓冲区,以准备进行下一帧的渲染。 */
    331. glClear(GL_COLOR_BUFFER_BIT);
    332. /* 清除深度缓冲 */
    333. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    334. /* 使用着色器程序 */
    335. lightingShader.use();
    336. /* 摄影机位置 */
    337. lightingShader.setVec3("viewPos", camera.Position);
    338. /* 灯光特性 */
    339. glm::vec3 lightColor;
    340. lightColor.x = static_cast<float>(1.0f);
    341. lightColor.y = static_cast<float>(1.0f);
    342. lightColor.z = static_cast<float>(1.0f);
    343. glm::vec3 diffuseColor = lightColor * glm::vec3(0.8f);
    344. glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
    345. /* 平行光 */
    346. glm::vec3 sun_direction(-(float)sin(glfwGetTime()), -(float)cos(glfwGetTime()), 0.0f);
    347. lightingShader.setVec3("dirLight.direction", sun_direction);
    348. lightingShader.setVec3("dirLight.ambient", ambientColor);
    349. lightingShader.setVec3("dirLight.diffuse", diffuseColor);
    350. lightingShader.setVec3("dirLight.specular", 1.0f, 1.0f, 1.0f);
    351. /* 点光源 */
    352. for (int i = 0; i < 4; i++)
    353. {
    354. std::stringstream ss;
    355. ss.str(""); // 清空字符串流
    356. ss << "pointLights[" << i << "].position";
    357. std::string position = ss.str();
    358. ss.str(""); // 清空字符串流
    359. ss << "pointLights[" << i << "].ambient";
    360. std::string ambient = ss.str();
    361. ss.str(""); // 清空字符串流
    362. ss << "pointLights[" << i << "].diffuse";
    363. std::string diffuse = ss.str();
    364. ss.str(""); // 清空字符串流
    365. ss << "pointLights[" << i << "].specular";
    366. std::string specular = ss.str();
    367. ss.str(""); // 清空字符串流
    368. ss << "pointLights[" << i << "].constant";
    369. std::string constant = ss.str();
    370. ss.str(""); // 清空字符串流
    371. ss << "pointLights[" << i << "].linear";
    372. std::string linear = ss.str();
    373. ss.str(""); // 清空字符串流
    374. ss << "pointLights[" << i << "].quadratic";
    375. std::string quadratic = ss.str();
    376. /* 灯光特性 */
    377. glm::vec3 lightColor = pointLightColors[i];
    378. glm::vec3 diffuseColor = lightColor * glm::vec3(0.8f);
    379. glm::vec3 ambientColor = diffuseColor * glm::vec3(0.2f);
    380. /* 光照属性设置 */
    381. lightingShader.setVec3(position.c_str(), pointLightPositions[i]);
    382. lightingShader.setVec3(ambient.c_str(), ambientColor);
    383. lightingShader.setVec3(diffuse.c_str(), diffuseColor);
    384. lightingShader.setVec3(specular.c_str(), 1.0f, 1.0f, 1.0f);
    385. /* 衰减 */
    386. lightingShader.setFloat(constant.c_str(), 1.0f);
    387. lightingShader.setFloat(linear.c_str(), 0.09f);
    388. lightingShader.setFloat(quadratic.c_str(), 0.032f);
    389. }
    390. /* 聚光 */
    391. lightingShader.setVec3("spotLight.position", camera.Position);
    392. lightingShader.setVec3("spotLight.direction", camera.Front);
    393. lightingShader.setFloat("spotLight.cutOff", glm::cos(glm::radians(17.0f)));
    394. lightingShader.setFloat("spotLight.outerCutOff", glm::cos(glm::radians(20.0f)));
    395. lightingShader.setVec3("spotLight.ambient", ambientColor);
    396. lightingShader.setVec3("spotLight.diffuse", diffuseColor);
    397. lightingShader.setVec3("spotLight.specular", 1.0f, 1.0f, 1.0f);
    398. /* 衰减 */
    399. lightingShader.setFloat("spotLight.constant", 1.0f);
    400. lightingShader.setFloat("spotLight.linear", 0.09f);
    401. lightingShader.setFloat("spotLight.quadratic", 0.032f);
    402. /* 材质特性 */
    403. lightingShader.setFloat("material.shininess", 64.0f);
    404. /* 视角矩阵 */
    405. glm::mat4 view = glm::mat4(1.0f);
    406. view = camera.GetViewMatrix();
    407. /* 透视矩阵 */
    408. glm::mat4 projection = glm::mat4(1.0f);
    409. projection = glm::perspective(glm::radians(camera.Zoom), (float)screenWidth / (float)screenHeight, 0.1f, 100.0f);
    410. /* 将视图矩阵的值传递给对应的uniform */
    411. lightingShader.setMat4("view", view);
    412. /* 将投影矩阵的值传递给对应的uniform */
    413. lightingShader.setMat4("projection", projection);
    414. /* 模型矩阵 */
    415. glm::mat4 model;
    416. /* 绑定顶点数组对象 */
    417. glBindVertexArray(cubeVAO);
    418. for (unsigned int i = 0; i < 10; i++)
    419. {
    420. /* 计算每个对象的模型矩阵,并在绘制之前将其传递给着色器 */
    421. model = glm::mat4(1.0f);
    422. /* 移动 */
    423. model = glm::translate(model, cubePositions[i]);
    424. /* 旋转 */
    425. model = glm::rotate(model, (float)glfwGetTime() * (i + 1) / 5, glm::vec3(-0.5f + ((float)i / 20.0), 1.0f, 0.0f));
    426. /* 将模型矩阵的值传递给对应的uniform */
    427. lightingShader.setMat4("model", model);
    428. /* 绑定漫反射贴图 */
    429. glActiveTexture(GL_TEXTURE0);
    430. glBindTexture(GL_TEXTURE_2D, diffuseMap);
    431. /* 绑定镜面反射贴图 */
    432. glActiveTexture(GL_TEXTURE1);
    433. glBindTexture(GL_TEXTURE_2D, specularMap);
    434. /* 绑定手电筒贴图 */
    435. glActiveTexture(GL_TEXTURE2);
    436. glBindTexture(GL_TEXTURE_2D, spotlightMap);
    437. /* 绘制矩形 */
    438. glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
    439. }
    440. /* 使用着色器程序 */
    441. lightCubeShader.use();
    442. /* 将投影矩阵的值传递给对应的uniform */
    443. lightCubeShader.setMat4("projection", projection);
    444. /* 将视图矩阵的值传递给对应的uniform */
    445. lightCubeShader.setMat4("view", view);
    446. for (unsigned int i = 0; i < 4; i++)
    447. {
    448. /* 灯方块颜色 */
    449. lightCubeShader.setVec3("lightCubeColor", pointLightColors[i]);
    450. /* 赋值为单位矩阵 */
    451. model = glm::mat4(1.0f);
    452. /* 移动 */
    453. model = glm::translate(model, pointLightPositions[i]);
    454. /* 缩放 */
    455. model = glm::scale(model, glm::vec3(0.2f));
    456. /* 将模型矩阵的值传递给对应的uniform */
    457. lightCubeShader.setMat4("model", model);
    458. /* 绑定顶点数组对象 */
    459. glBindVertexArray(lightCubeVAO);
    460. /* 绘制矩形 */
    461. glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
    462. }
    463. /* 这行代码交换前后缓冲区,将当前帧的渲染结果显示到窗口上。 */
    464. glfwSwapBuffers(window);
    465. /* 这行代码处理窗口事件,例如键盘输入、鼠标移动等。它会检查是否有事件发生并触发相应的回调函数。 */
    466. glfwPollEvents();
    467. }
    468. /* 删除顶点数组对象 */
    469. glDeleteVertexArrays(1, &cubeVAO);
    470. /* 删除顶点缓冲对象 */
    471. glDeleteBuffers(1, &VBO);
    472. /* 删除元素缓冲对象 */
    473. glDeleteBuffers(1, &EBO);
    474. /* 删除着色器程序 */
    475. lightingShader.deleteProgram();
    476. lightCubeShader.deleteProgram();
    477. /* 这行代码终止GLFW库的运行,释放相关的系统资源。 */
    478. glfwTerminate();
    479. /* 程序结束,返回0 */
    480. return 0;
    481. }

    运行结果:

    注意!该程序操作方式如下:

    WSAD键控制前后左右移动,空格键飞行,shift键下降,
    鼠标移动控制视角,鼠标滚轮控制视野缩放。
    Esc键退出程序。

    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    另外在运行程序时,请打开键盘的英文大写锁定,
    否则按shift之后会跳出中文输入法。
    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


    您的建议、疑问或其他想法对于改善和扩展本文的内容至关重要。
    如果您有任何关于多光源渲染的经验或故事,我非常欢迎您在评论区与我们分享。您可以分享您在实际项目中应用多光源渲染的经验,或者提出对于该技术的疑问和探索。您的见解和观点能够为其他读者提供更深入的理解和启发,同时也有助于我们共同构建一个充满知识和想法交流的社区。
    无论您是OpenGL初学者、有经验的开发者还是对计算机图形学和渲染技术感兴趣的爱好者,您的参与都将对本文的完善产生积极影响。通过您的反馈和互动,我们可以一起探索更多关于多光源渲染的细节、技巧和应用场景。
    所以,请不要犹豫!在下方的评论区分享您的想法、问题或经验吧!我期待与您的互动,让这篇博客文章变得更加丰富、有益和互动。谢谢您的支持!当您阅读这篇关于OpenGL多光源渲染的博客文章时,我希望能够与您建立一个积极互动的社区。

  • 相关阅读:
    数学建模学习(73):用Python敏感性分析,如此轻松简单
    【微信小程序开发】微信小程序、大前端之flex布局方式详细解析
    如何阅读论文?
    满级人类教学!字节1000页数据算法笔记泄露:连续霸榜GitHub一周
    凑钱(贪心算法)
    Numpy入门[18]——数组读写
    【无标题】H5页面解决点击页面处关闭键盘,触发了页面的事件的问题
    面试题:volatile能否保证线程安全
    入行数据分析要知道什么是数据&数据分析
    H264基本原理
  • 原文地址:https://blog.csdn.net/weixin_73550502/article/details/136181061