本章主要以代码为主,理论理解即可。详细分析代码
目录
光照贴图
光源分类
平行光
点光源
shader——点光源
聚光灯
聚光灯边缘优化——光强递减
源码解析
main
全局变量、句柄
main函数主体逻辑
createModel()
createTexture(const char* _filename)
ininShader (const char* _vertexPath, const char* _fragPath)
render()
shader
vsunShader.glsl
fsunShader.glsl
sceneShaderv.glsl
sceneShaderf.glsl
渲染结果
我们之前讨论的都是简单的点光源,并且没有考虑光源随距离的衰减。
为此,将其光源分为三类
如何区分简单点光源和平行光?
可以利用齐次向量的最后一个参数,即w。
考虑了光随距离衰减,提出了一种非线性的公式,其中d代表光源与像素点的距离。
对于公式中三个k系数,有一些经验值:
以往的不变的函数
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
//场景中有两种东西:物体和光源
uint VAO_cube = 0;
uint VAO_sun = 0;
//设置光源位置
glm::vec3 light_pos(1.0f);
glm::vec3 light_color(1.0f);
//光照贴图
uint _textureBox = 0;
uint _textureSpec = 0;
//类声明
Shader _shader_scene;
Shader _shader_sun;
ffImage* _pImage = nullptr;
Camera _camera;
//初始化投影矩阵、设置宽高
glm::mat4 _projMatrix(1.0f);
int _width = 800;
int _height = 600;
int main() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Core", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
glViewport(0, 0, _width, _height);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetCursorPosCallback(window, mouse_callback);
_camera.lookAt(glm::vec3(0.0f, 0.0f, 3.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_camera.setSpeed(0.001f);
VAO_cube = createModel();
VAO_sun = createModel();
light_pos = glm::vec3(2.0f, 0.0f, 0.0f);
light_color = glm::vec3(1.0f, 1.0f, 1.0f);
_textureBox = createTexture("res/box.png");
_textureSpec = createTexture("res/specular.png");
initShader("","");
while (!glfwWindowShouldClose(window))
{
processInput(window);
render();
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
值得注意的只有
VAO_cube = createModel();
VAO_sun = createModel();
light_pos = glm::vec3(2.0f, 0.0f, 0.0f);
light_color = glm::vec3(1.0f, 1.0f, 1.0f);
_textureBox = createTexture("res/box.png");
_textureSpec = createTexture("res/specular.png");
initShader("","");
这些初始化的操作。其中涉及了:
上述四个函数,接下来解析
uint createModel()
{
uint _VAO = 0;
uint _VBO = 0;
float vertices[] = {
//位置 UV 法线
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
};
glGenVertexArrays(1, &_VAO);
glBindVertexArray(_VAO);
glGenBuffers(1, &_VBO);
glBindBuffer(GL_ARRAY_BUFFER, _VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 3));
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(sizeof(float) * 5));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return _VAO;
}
是对顶点数据的初始化的VBO绑定操作。
uint createTexture(const char* _filename) {
_pImage = ffImage::readFromFile(_filename);
uint _texture = 0;
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _pImage->getWidth(), _pImage->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, _pImage->getData());
return _texture;
}
读取纹理贴图的操作
void initShader(const char* _vertexPath, const char* _fragPath)
{
_shader_sun.initShader("shader/vsunShader.glsl", "shader/fsunShader.glsl");
_shader_scene.initShader("shader/sceneShaderv.glsl", "shader/sceneShaderf.glsl");
}
其实这里并不需要参数,只是一种修改之前代码的写法。用硬编码的方式。
根据代码得知:我们只需要4个shader:
这里多提一嘴,fragmentShader中的fragment并不是像素,而是插值而来的图元,这两个概念并不相等。
在渲染中需要做:
void render()
{
//设置视口背景颜色,
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
//10个物体的位置
glm::vec3 cubePositions[] = {
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(2.0f, 5.0f, -15.0f),
glm::vec3(-1.5f, -2.2f, -2.5f),
glm::vec3(-3.8f, -2.0f, -12.3f),
glm::vec3(2.4f, -0.4f, -3.5f),
glm::vec3(-1.7f, 3.0f, -7.5f),
glm::vec3(1.3f, -2.0f, -2.5f),
glm::vec3(1.5f, 2.0f, -2.5f),
glm::vec3(1.5f, 0.2f, -1.5f),
glm::vec3(-1.3f, 1.0f, -1.5f)
};
//4个光源的位置
glm::vec3 pointLightPositions[] = {
glm::vec3(0.7f, 0.2f, 2.0f),
glm::vec3(2.3f, -3.3f, -4.0f),
glm::vec3(-4.0f, 2.0f, -12.0f),
glm::vec3(0.0f, 0.0f, -3.0f)
};
//调整摄像机朝向位置,调用glm::lootAt
_camera.update();
//初始化投影矩阵
_projMatrix = glm::perspective(glm::radians(45.0f), (float)_width / (float)_height, 0.1f, 100.0f);
//初始化模型矩阵,为单位阵
glm::mat4 _modelMatrix(1.0f);
//对模型进行平移变换
_modelMatrix = glm::translate(_modelMatrix, glm::vec3(0.0f, 0.0f, -3.0f));
//激活纹理,并绑定相应纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _textureBox);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, _textureSpec);
//渲染
_shader_scene.start();
_shader_scene.setVec3("view_pos", _camera.getPosition());
//传入物体材质属性
_shader_scene.setInt("myMaterial.m_specular", 1);
_shader_scene.setFloat("myMaterial.m_shiness", 32);
_shader_scene.setMatrix("_viewMatrix", _camera.getMatrix());
_shader_scene.setMatrix("_projMatrix", _projMatrix);
// directional light 平行光
_shader_scene.setVec3("_dirLight.m_direction", glm::vec3(-0.2f, -1.0f, -0.3f));
_shader_scene.setVec3("_dirLight.m_ambient", glm::vec3(0.05f, 0.05f, 0.05f));
_shader_scene.setVec3("_dirLight.m_diffuse", glm::vec3(0.4f, 0.4f, 0.4f));
_shader_scene.setVec3("_dirLight.m_specular", glm::vec3(0.5f, 0.5f, 0.5f));
// point light 点光源
// point light 1
_shader_scene.setVec3("_pointLight[0].m_pos", pointLightPositions[0]);
_shader_scene.setVec3("_pointLight[0].m_ambient", glm::vec3(0.05f, 0.05f, 0.05f));
_shader_scene.setVec3("_pointLight[0].m_diffuse", glm::vec3(0.8f, 0.8f, 0.8f));
_shader_scene.setVec3("_pointLight[0].m_specular", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setFloat("_pointLight[0].m_c", 1.0f);
_shader_scene.setFloat("_pointLight[0].m_l", 0.09);
_shader_scene.setFloat("_pointLight[0].m_q", 0.032);
// point light 2
_shader_scene.setVec3("_pointLight[1].m_pos", pointLightPositions[1]);
_shader_scene.setVec3("_pointLight[1].m_ambient", glm::vec3(0.05f, 0.05f, 0.05f));
_shader_scene.setVec3("_pointLight[1].m_diffuse", glm::vec3(0.8f, 0.8f, 0.8f));
_shader_scene.setVec3("_pointLight[1].m_specular", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setFloat("_pointLight[1].m_c", 1.0f);
_shader_scene.setFloat("_pointLight[1].m_l", 0.09);
_shader_scene.setFloat("_pointLight[1].m_q", 0.032);
// point light 3
_shader_scene.setVec3("_pointLight[2].m_pos", pointLightPositions[2]);
_shader_scene.setVec3("_pointLight[2].m_ambient", glm::vec3(0.05f, 0.05f, 0.05f));
_shader_scene.setVec3("_pointLight[2].m_diffuse", glm::vec3(0.8f, 0.8f, 0.8f));
_shader_scene.setVec3("_pointLight[2].m_specular", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setFloat("_pointLight[2].m_c", 1.0f);
_shader_scene.setFloat("_pointLight[2].m_l", 0.09);
_shader_scene.setFloat("_pointLight[2].m_q", 0.032);
// point light 4
_shader_scene.setVec3("_pointLight[3].m_pos", pointLightPositions[3]);
_shader_scene.setVec3("_pointLight[3].m_ambient", glm::vec3(0.05f, 0.05f, 0.05f));
_shader_scene.setVec3("_pointLight[3].m_diffuse", glm::vec3(0.8f, 0.8f, 0.8f));
_shader_scene.setVec3("_pointLight[3].m_specular", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setFloat("_pointLight[3].m_c", 1.0f);
_shader_scene.setFloat("_pointLight[3].m_l", 0.09);
_shader_scene.setFloat("_pointLight[3].m_q", 0.032);
// spotLight
_shader_scene.setVec3("_spotLight.m_pos", _camera.getPosition());//跟随相机
_shader_scene.setVec3("_spotLight.m_direction", _camera.getDirection());//跟随相机
_shader_scene.setVec3("_spotLight.m_ambient", glm::vec3(0.0f, 0.0f, 0.0f));
_shader_scene.setVec3("_spotLight.m_diffuse", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setVec3("_spotLight.m_specular", glm::vec3(1.0f, 1.0f, 1.0f));
_shader_scene.setFloat("_spotLight.m_c", 1.0f);
_shader_scene.setFloat("_spotLight.m_l", 0.09);
_shader_scene.setFloat("_spotLight.m_q", 0.032);
_shader_scene.setFloat("_spotLight.m_cutOff", glm::cos(glm::radians(12.5f)));
_shader_scene.setFloat("_spotLight.m_outCutOff", glm::cos(glm::radians(15.0f)));
//渲染10个物体
for (int i = 0; i < 10; i++)
{
//初始化单位矩阵(模型),对10个物体求各自的平移旋转变换
_modelMatrix = glm::mat4(1.0f);
_modelMatrix = glm::translate(_modelMatrix, cubePositions[i]);
_modelMatrix = glm::rotate(_modelMatrix, glm::radians(i * 20.0f), glm::vec3(0.0f, 1.0f, 0.0f));
_shader_scene.setMatrix("_modelMatrix", _modelMatrix);
glBindVertexArray(VAO_cube);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
_shader_scene.end();
_shader_sun.start();
_shader_sun.setMatrix("_modelMatrix", _modelMatrix);
_shader_sun.setMatrix("_viewMatrix", _camera.getMatrix());
_shader_sun.setMatrix("_projMatrix", _projMatrix);
//渲染4个点光源
for (int i = 0; i < 4; i++)
{
_modelMatrix = glm::mat4(1.0f);
_modelMatrix = glm::translate(_modelMatrix, pointLightPositions[i]);
_modelMatrix = glm::scale(_modelMatrix, glm::vec3(0.2f, 0.2f, 0.2f));
_shader_sun.setMatrix("_modelMatrix", _modelMatrix);
glBindVertexArray(VAO_sun);
glDrawArrays(GL_TRIANGLES, 0, 36);
}
_shader_sun.end();
}
//vsunShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;
out vec2 outUV;
uniform mat4 _modelMatrix;
uniform mat4 _viewMatrix;
uniform mat4 _projMatrix;
void main()
{
gl_Position = _projMatrix * _viewMatrix * _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0);
outUV = aUV;
};
从C++代码中设传入数据设置MVP矩阵,
#version 330 core
out vec4 FragColor;
in vec2 outUV;
uniform sampler2D ourTexture;
void main()
{
FragColor = vec4(1.0f , 1.0f ,1.0f ,1.0f );
};
光源的颜色直接设置为白色(1,1,1,1)
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUV;
layout (location = 2) in vec3 aNormal;
out vec2 outUV;
out vec3 outFragPos;
out vec3 outNormal;
uniform mat4 _modelMatrix;
uniform mat4 _viewMatrix;
uniform mat4 _projMatrix;
void main()
{
gl_Position = _projMatrix * _viewMatrix * _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0);
outUV = aUV;
outFragPos = vec3( _modelMatrix * vec4(aPos.x, aPos.y, aPos.z, 1.0));
outNormal = mat3(transpose(inverse(_modelMatrix))) * aNormal;
};
从C++代码中设传入数据设置MVP矩阵,设置法向量。
#version 330 core
out vec4 FragColor;
in vec2 outUV;
in vec3 outFragPos;
in vec3 outNormal;
//物体材质
struct Material
{
sampler2D m_diffuse;//0
sampler2D m_specular;//1
float m_shiness;
};
//平行光
struct DirLight
{
vec3 m_direction;
vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;
};
//点光源
struct PointLight
{
vec3 m_pos;
vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;
float m_c;
float m_l;
float m_q;
};
//聚光灯
struct SpotLight
{
vec3 m_pos;
vec3 m_direction;
float m_cutOff;
float m_outCutOff;
vec3 m_ambient;
vec3 m_diffuse;
vec3 m_specular;
float m_c;
float m_l;
float m_q;
};
#define MAX_POINT_NUMBER 4
uniform DirLight _dirLight;
uniform PointLight _pointLight[MAX_POINT_NUMBER];
uniform SpotLight _spotLight;
uniform Material myMaterial;
uniform vec3 view_pos;
vec3 calculateDir(DirLight _light, vec3 _normal, vec3 _viewDir){
vec3 _lightDir = normalize(_light.m_direction);
//环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse, outUV));
//漫反射
float _diff = max(dot(_normal,-_lightDir),0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse, outUV));
//镜面反射
vec3 _reflectDir = reflect(_lightDir, _normal);
float _spec = pow(max(dot(_reflectDir, _viewDir),0.0f),myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular, outUV));
return (_ambient + _diffuse + _specular);
}
vec3 calculatePoint(PointLight _light , vec3 _normal , vec3 _viewDir , vec3 _fragPos)
{
vec3 _lightDir = normalize( _fragPos - _light.m_pos);
//环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse , outUV));
//漫反射
float _diff = max(dot(_normal , -_lightDir) , 0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse , outUV));
//镜面反射
vec3 _reflectDir = reflect(_lightDir , _normal);
float _spec = pow(max(dot(_reflectDir , _viewDir) , 0.0f) , myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular , outUV));
//衰减系数计算
float _dist = length(_light.m_pos - _fragPos);
float _attenuation = 1.0f / (_light.m_c + _light.m_l * _dist + _light.m_q * _dist * _dist);
return (_ambient + _diffuse + _specular) * _attenuation;
}
vec3 calculateSpot(SpotLight _light , vec3 _normal , vec3 _viewDir , vec3 _fragPos)
{
vec3 _lightDir = normalize( _fragPos - _light.m_pos);
vec3 _spotDir= normalize(_light.m_direction);
float _cosTheta = dot(-_lightDir , -_spotDir);
float _epsilon = _light.m_cutOff - _light.m_outCutOff;
float _intensity = clamp((_cosTheta - _light.m_outCutOff) / _epsilon , 0.0f , 1.0f);
//环境光
vec3 _ambient = _light.m_ambient * vec3(texture(myMaterial.m_diffuse , outUV));
//漫反射
float _diff = max(dot(_normal , -_lightDir) , 0.0f);
vec3 _diffuse = _light.m_diffuse * _diff * vec3(texture(myMaterial.m_diffuse , outUV));
//镜面反射
vec3 _reflectDir = reflect(_lightDir , _normal);
float _spec = pow(max(dot(_reflectDir , _viewDir) , 0.0f) , myMaterial.m_shiness);
vec3 _specular = _light.m_specular * _spec * vec3(texture(myMaterial.m_specular , outUV));
//衰减系数计算
float _dist = length(_light.m_pos - _fragPos);
float _attenuation = 1.0f / (_light.m_c + _light.m_l * _dist + _light.m_q * _dist * _dist);
return (_ambient + _diffuse + _specular) * _attenuation * _intensity;
}
void main()
{
vec3 _normal = normalize(outNormal);
vec3 _viewDir = normalize(view_pos - outFragPos);
vec3 _result = calculateDir(_dirLight ,_normal , _viewDir);
for(int i = 0;i < MAX_POINT_NUMBER;i++)
{
_result += calculatePoint(_pointLight[i] , _normal , _viewDir , outFragPos);
}
_result += calculateSpot(_spotLight , _normal , _viewDir , outFragPos);
FragColor = vec4(_result ,1.0f);
//FragColor = vec4(vec3(gl_FragCoord.y / 600.0f) ,1.0f);
};
总体就是对之前理论的shader代码复现,最后将各个光源的贡献加在一起,作为FragColor输出。