前置: OpenGL基础53:阴影映射(下)
之前成功实现了平行光阴影,生成阴影贴图时使用的矩阵是正交矩阵,若是想要实现点光源的阴影效果,那么理论上只需要修改投影矩阵为透视矩阵就好了,看上去并不是很难
但是要得到点光源阴影贴图要考虑到的是:如何渲染出周围所有的场景,像摄像机它是有一定视角的,而点光源确是360°全面照射,因此对于点光源的阴影贴图一张肯定是不够的,它只能展示出一个方向
下图是90°透视投影的视觉范围,朝向为x轴正方向,那么只需要再对剩下的5个方向各来一次投影理论上就可以得到全方位的深度贴图了, 而这6张贴图正一一对应着立方体贴图的6个面,所以可以直接渲染到立方体贴图一步到位
第一次使用立方体贴图是在《OpenGL基础36:天空盒》,这应该是第二次
把立方体贴图附加到帧缓冲上并不难,只需要修改下之前的getAttachmentTexture()方法:
//获取帧缓冲立方体贴图
GLuint getAttachmentCubeTexture()
{
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
for (GLuint i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
然后对于投影矩阵,这次需要六个,表示各个方向:
void DrawPointLightShadowMap(Shader shader, int width, int height, int index)
{
if (pointLight.size() < index + 1)
{
cout << "错误,无法找到对应的点光源数据" << endl;
return;
}
shader.Use();
glm::vec3 lightPos = pointLight[index].position;
float farPlane = pointLight[index].farPlane;
glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), (GLfloat)width / (GLfloat)height, 0.1f, farPlane);
std::vector shadowTransforms;
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0, -1.0, 0.0)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, -1.0, 0.0), glm::vec3(0.0, 0.0, -1.0)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, 1.0), glm::vec3(0.0, -1.0, 0.0)));
shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(0.0, 0.0, -1.0), glm::vec3(0.0, -1.0, 0.0)));
for (GLuint i = 0; i < 6; i++)
glUniformMatrix4fv(glGetUniformLocation(shader.Program, ("shadowMat[" + std::to_string(i) + "]").c_str()), 1, GL_FALSE, glm::value_ptr(shadowTransforms[i]));
glUniform1f(glGetUniformLocation(shader.Program, "farPlane"), farPlane);
glUniform3fv(glGetUniformLocation(shader.Program, "lightPos"), 1, &lightPos[0]);
}
顶点着色器和几何着色器如下:
这次用到了几何着色器,是为了一次渲染到整个立方体贴图:在顶点着色器中将顶点变换到世界空间,接着几何着色器再负责将所有世界空间的顶点变换到6个不同的光空间
相当于就是片段着色器负责变一个魔术:给它1个世界空间坐标,它返还给你6个光空间坐标
几何着色器片段变量 gl_Layer:指定发散出的基本图形送到立方体贴图的哪个面
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 5) in mat4 model;
void main()
{
gl_Position = model * vec4(position, 1.0f);
}
/
#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 18) out;
uniform mat4 shadowMat[6];
out vec4 FragPos;
void main()
{
for(int face = 0; face < 6; face++)
{
gl_Layer = face;
for(int i = 0; i < 3; i++)
{
FragPos = gl_in[i].gl_Position;
gl_Position = shadowMat[face] * FragPos;
EmitVertex();
}
EndPrimitive();
}
}
在片段着色器中,需要手动计算深度
深度的计算公式就是片段与光源的距离 / 平头截体远平面距离
#version 330 core
in vec4 FragPos;
uniform vec3 lightPos;
uniform float farPlane;
void main()
{
float lightDistance = length(FragPos.xyz - lightPos);
lightDistance = lightDistance / farPlane;
gl_FragDepth = lightDistance;
}
一样,如果得到这样的效果,说明这部分对了,因为是立方体贴图,所以可以360°环绕看下
相对于之前的平行光阴影,有了万象阴影贴图后实现点光源阴影会更加简单,因为不再需要在顶点着色器中将顶点转入光空间了,要修改的只有片段着色器
ShadowCalculation方法是唯一需要修改的部分,判断当前的片段是否被遮挡,如果被遮挡,在下面计算对应光照时就不考虑当前光源对片段颜色的贡献,而对于点光源阴影,ShadowCalculation方法中的逻辑:
float ShadowCalculation(vec3 fragPos, PointLight light)
{
vec3 sampleOffsetDirections[20] = vec3[]
(
vec3(1, 1, 1), vec3(1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1),
vec3(1, 1, -1), vec3(1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
vec3(1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1),
vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, -1, -1), vec3(0, 1, -1)
);
vec3 fragToLight = fragPos - light.position;
float fragDepth = length(fragToLight);
float texelSize = texelSize = (1.0 + (fragDepth / light.farPlane)) / 25.0;
float bias = 0.0004;
float shadow = 0;
for(int i = 0; i < 20; i++)
{
float closestDepth = texture(shadowMap, fragToLight + sampleOffsetDirections[i] * texelSize).r;
closestDepth *= light.farPlane;
if(fragDepth - bias > closestDepth)
shadow += 0.93f;
}
shadow /= 20;
return shadow;
}
参考自:https://learnopengl.com/#!Advanced-Lighting/Shadows/Point-Shadows
如果没有问题的话就可以得到下面的效果
完整的主代码:
#include
#include
#define GLEW_STATIC
#include
#include"Shader.h"
#include"Camera.h"
#include"Light.h"
#include
#include
#include
#include"Mesh.h"
#include"Model.h"
#include
#include
bool keys[1024];
Camera camera;
GLfloat lastX, lastY;
bool firstMouse = true;
bool openSpotLight = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
GLuint loadCubemap(vector faces);
GLuint* getAttachmentTexture(GLuint num, GLboolean isDepth = false);
GLuint getAttachmentCubeTexture();
GLuint getMultiSampleTexture(GLuint samples); //MSAA几重采样
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void cameraMove();
void renderSkyBox();
void renderQuad();
Light InitLight(Shader shader);
const GLuint WIDTH = 800, HEIGHT = 600;
const GLuint SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024;
int main(void)
{
//------------------------------------------------------初始化---------------------------------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glewExperimental = GL_TRUE;
glewInit();
int width, height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(0, 0, width, height);
Shader shaderObj("ObjVShader.vert", "ObjFShader.frag", "ObjGShader.geom");
Shader shaderObjToDebug("ObjNormalVShader.vert", "ObjNormalFShader.frag", "ObjNormalGShader.geom");
Shader shaderObjWithoutTex("ObjVShaderWithoutTex.vert", "ObjFShaderWithoutTex.frag");
Shader shaderLight("LightVShader.vert", "LightFShader.frag");
Shader shaderSkyBox("SkyboxVShader.vert", "SkyboxFShader.frag");
Shader shaderScreen("ScreenVShader.vert", "ScreenFShader.frag");
Shader shaderBlur("GaussVShader.vert", "GaussFShader.frag");
Shader shaderDepth("DepthVShader.vert", "DepthFShader.frag", "DepthGShader.geom");
//------------------------------------------------------数据定义---------------------------------------------------------
glm::vec3 positions[] =
{
glm::vec3(-1.0f, 0.0f, 4.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 0.89f, 0.0f),
glm::vec3(0.0f, 1.78f, 0.0f),
glm::vec3(-2.0f, 0.0f, 0.0f),
glm::vec3(-2.0f, 0.89f, 0.0f),
glm::vec3(-3.0f, 0.0f, 0.0f),
glm::vec3(-2.0f, 0.0f, 1.0f),
};
glm::vec3 wallPositions[] =
{
glm::vec3(-1, 0.3, -0.802),
glm::vec3(-5.07, 0.3, -0.8),
glm::vec3(3.07, 0.3, -0.8),
};
//------------------------------------------------------UBO---------------------------------------------------------
GLuint UBO;
glGenBuffers(1, &UBO);
glBindBuffer(GL_UNIFORM_BUFFER, UBO);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferRange(GL_UNIFORM_BUFFER, 0, UBO, 0, 2 * sizeof(glm::mat4));
//------------------------------------------------------帧缓冲---------------------------------------------------------
GLuint FBO, RBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
GLuint texNum = 2;
GLuint* textureColorBuffer = getAttachmentTexture(texNum);
glGenRenderbuffers(1, &RBO);
glBindRenderbuffer(GL_RENDERBUFFER, RBO);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
for (GLuint i = 0; i < texNum; i++)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textureColorBuffer[i], 0);
GLuint attachments[5] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4 };
glDrawBuffers(texNum, attachments);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RBO);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLuint gaussFBO[2];
GLuint* gaussColorbuffers = getAttachmentTexture(2);
glGenFramebuffers(2, gaussFBO);
for (GLuint i = 0; i < 2; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[i]);
glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gaussColorbuffers[i], 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
GLuint depthFBO;
glGenFramebuffers(1, &depthFBO);
glBindFramebuffer(GL_FRAMEBUFFER, depthFBO);
GLuint depthColorBuffer = getAttachmentCubeTexture();
glBindTexture(GL_TEXTURE_CUBE_MAP, depthColorBuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthColorBuffer, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE); //不需要考虑颜色
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//------------------------------------------------------设置状态、加载模型和天空盒---------------------------------------------------------
glEnable(GL_CULL_FACE);
glEnable(GL_MULTISAMPLE);
glEnable(GL_STENCIL_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_PROGRAM_POINT_SIZE);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); //模板测试和深度测试都成功时,将对应像素的模板值设置为用glStencilFunc函数设置的ref值
glEnable(GL_BLEND);
glEnable(GL_PROGRAM_POINT_SIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Model wood("Object/wood/file.fbx", "Object/wood/file.fbx");
Model ground("Object/ground/ground.fbx", "Object/ground");
Model wall("Object/wall/stonewall.fbx", "Object/wall");
Model lightObj("Object/light/file.fbx", "Object/light");
vector faces;
faces.push_back("Texture/Skybox/StarryNight1024/Right.jpg");
faces.push_back("Texture/Skybox/StarryNight1024/Left.jpg");
faces.push_back("Texture/Skybox/StarryNight1024/Up.jpg");
faces.push_back("Texture/Skybox/StarryNight1024/Down.jpg");
faces.push_back("Texture/Skybox/StarryNight1024/Back.jpg");
faces.push_back("Texture/Skybox/StarryNight1024/Front.jpg");
GLuint cubemapTexture = loadCubemap(faces);
//------------------------------------------------------实例化---------------------------------------------------------
glm::mat4* modelMatrices;
modelMatrices = new glm::mat4[1000];
glm::mat4 model = glm::mat4(1.0f);
for (GLuint i = 0; i <= 7; i++)
{
model = glm::translate(glm::mat4(1.0f), positions[i]);
model = glm::scale(model, glm::vec3(0.01f));
modelMatrices[i] = model;
}
wood.UpdateModelMatrix(modelMatrices, 8);
GLint groundIndex = 0;
for (int i = -1; i <= 2; i++)
{
for (int j = 0; j <= 2; j++)
{
model = glm::translate(glm::mat4(1.0f), glm::vec3(i * 3.52f, -0.05f, j * 3.72f));
model = glm::scale(model, glm::vec3(0.1f));
model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
modelMatrices[groundIndex++] = model;
}
}
ground.UpdateModelMatrix(modelMatrices, groundIndex);
for (GLuint i = 0; i <= 2; i++)
{
model = glm::translate(glm::mat4(1.0f), wallPositions[i]);
model = glm::scale(model, glm::vec3(0.15, 0.3, 0.15));
model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
modelMatrices[i] = model;
}
wall.UpdateModelMatrix(modelMatrices, 3);
model = glm::translate(glm::mat4(1.0f), glm::vec3(-4.0f, 2.3f, -0.25f));
model = glm::scale(model, glm::vec3(0.015, 0.015, 0.015));
model = glm::rotate(model, glm::radians(90.0f), glm::vec3(-1.0f, 0.0f, 0.0f));
modelMatrices[0] = model;
lightObj.UpdateModelMatrix(modelMatrices, 1);
delete[] modelMatrices;
//------------------------------------------------------渲染ing---------------------------------------------------------
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
cameraMove();
shaderObj.Use();
Light light = InitLight(shaderObj);
//--------------------------------------------------绘制阴影贴图--------------------------------------------------
glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, depthFBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
shaderDepth.Use();
light.DrawPointLightShadowMap(shaderDepth, SHADOW_WIDTH, SHADOW_HEIGHT, 0);
wall.Draw(shaderObj, 3);
wood.Draw(shaderObj, 8);
ground.Draw(shaderObj, groundIndex + 1);
//--------------------------------------------------绘制场景-----------------------------------------------------
glViewport(0, 0, WIDTH, HEIGHT);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (GLfloat)WIDTH / (GLfloat)HEIGHT, 0.1f, 100.0f);
glBindBuffer(GL_UNIFORM_BUFFER, UBO);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(view));
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(projection));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
shaderObj.Use();
light.AppAllLightToShader(shaderObj.Program);
glUniform3f(glGetUniformLocation(shaderObj.Program, "viewPos"), camera.Position.x, camera.Position.y, camera.Position.z);
glActiveTexture(GL_TEXTURE7);
glUniform1i(glGetUniformLocation(shaderObj.Program, "shadowMap"), 7);
glBindTexture(GL_TEXTURE_CUBE_MAP, depthColorBuffer);
wall.Draw(shaderObj, 3);
wood.Draw(shaderObj, 8);
ground.Draw(shaderObj, groundIndex + 1);
lightObj.Draw(shaderObj, 1);
//--------------------------------------------------天空盒-----------------------------------------------------
shaderSkyBox.Use();
view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(shaderSkyBox.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glActiveTexture(GL_TEXTURE1);
glUniform1i(glGetUniformLocation(shaderSkyBox.Program, "skybox"), 1);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
renderSkyBox();
//--------------------------------------------------高斯模糊-----------------------------------------------------
GLuint amount = 10;
bool horizontal = true;
bool first_iteration = true;
shaderBlur.Use();
for (GLuint i = 0; i < amount; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, gaussFBO[horizontal]);
glUniform1i(glGetUniformLocation(shaderBlur.Program, "horizontal"), horizontal);
glActiveTexture(GL_TEXTURE1);
glUniform1i(glGetUniformLocation(shaderBlur.Program, "gaussTexture"), 1);
glBindTexture(GL_TEXTURE_2D, first_iteration ? textureColorBuffer[1] : gaussColorbuffers[!horizontal]);
renderQuad();
horizontal = !horizontal;
if (first_iteration)
first_iteration = false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//--------------------------------------------------默认帧缓冲--------------------------------------------------
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
shaderScreen.Use();
glActiveTexture(GL_TEXTURE1);
glUniform1i(glGetUniformLocation(shaderScreen.Program, "screenTexture"), 1);
glBindTexture(GL_TEXTURE_2D, textureColorBuffer[0]);
glActiveTexture(GL_TEXTURE2);
glUniform1i(glGetUniformLocation(shaderScreen.Program, "bloomTexture"), 2);
glBindTexture(GL_TEXTURE_2D, gaussColorbuffers[!horizontal]);
renderQuad();
glfwSwapBuffers(window);
}
//------------------------------------------------------解绑---------------------------------------------------------
glDeleteFramebuffers(1, &FBO);
glfwTerminate();
return 0;
}
//初始化光照数据并传入着色器
Light InitLight(Shader shader)
{
glm::vec3 pointLightPositions[] =
{
glm::vec3(-4.0f, 2.6f, -0.25f),
glm::vec3(1000000.0f, 0.8f, 2.0f),
glm::vec3(1000000.0f, 0.8f, 1.0f),
};
Light light;
light.AddSunLight(glm::vec3(-0.2f, -1.0f, -0.3f), glm::vec3(0.05f, 0.05f, 0.05f), glm::vec3(0.06f, 0.06f, 0.06f));
for (int i = 0; i <= 2; i++)
light.AddPointLight(glm::vec3(pointLightPositions[i].x, pointLightPositions[i].y, pointLightPositions[i].z), glm::vec3(0.6, 0.6, 0.6), glm::vec3(0.45, 0.45, 0.45));
if(openSpotLight)
{
light.AddSpotLight(glm::vec3(camera.Position.x, camera.Position.y, camera.Position.z), glm::vec3(camera.Front.x, camera.Front.y, camera.Front.z),
glm::vec3(0.6f, 0.6f, 0.6f), glm::vec3(0.8f, 0.8f, 0.8f));
}
return light;
}
//渲染全屏贴图
GLuint quadVAO = 0;
GLuint quadVBO;
void renderQuad()
{
if (quadVAO == 0)
{
GLfloat quadVertices[] =
{
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
};
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
//渲染天空盒
GLuint skyboxVAO = 0;
GLuint skyboxVBO;
void renderSkyBox()
{
GLfloat skyboxVertices[] =
{
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
if (skyboxVAO == 0)
{
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
glDepthFunc(GL_LEQUAL);
glBindVertexArray(skyboxVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS);
}
//获取多采样纹理(用于抗锯齿,参数为采样数量)
GLuint getMultiSampleTexture(GLuint samples)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, WIDTH, HEIGHT, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
return texture;
}
//加载立方体贴图(天空盒)
GLuint loadCubemap(vector faces)
{
GLuint textureID;
glGenTextures(1, &textureID);
int width, height;
unsigned char* image;
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
for (GLuint i = 0; i < faces.size(); i++)
{
image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
SOIL_free_image_data(image);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
//获取帧缓冲贴图(支持MRT,第一个参数为贴图数量,第二个参数表示是否为仅深度贴图)
GLuint* getAttachmentTexture(GLuint num, GLboolean isDepth)
{
GLuint *textureID;
textureID = new GLuint[num];
glGenTextures(num, textureID);
for (GLuint i = 0; i < num; i++)
{
glBindTexture(GL_TEXTURE_2D, textureID[i]);
if (isDepth)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[] = { 1.0, 1.0, 1.0, 1.0 };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, WIDTH, HEIGHT, 0, GL_RGBA, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
glBindTexture(GL_TEXTURE_2D, 0);
return textureID;
}
//获取帧缓冲立方体贴图
GLuint getAttachmentCubeTexture()
{
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
for (GLuint i = 0; i < 6; ++i)
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
//摄像机移动
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
void cameraMove()
{
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
GLfloat cameraSpeed = 1.0f * deltaTime;
if (keys[GLFW_KEY_W])
camera.ProcessKeyboard(Camera_Movement(FORWARD), deltaTime);
if (keys[GLFW_KEY_S])
camera.ProcessKeyboard(Camera_Movement(BACKWARD), deltaTime);
if (keys[GLFW_KEY_A])
camera.ProcessKeyboard(Camera_Movement(LEFT), deltaTime);
if (keys[GLFW_KEY_D])
camera.ProcessKeyboard(Camera_Movement(RIGHT), deltaTime);
}
//按键输入
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key == GLFW_KEY_TAB && action == GLFW_PRESS)
openSpotLight = !openSpotLight;
if (action == GLFW_PRESS) //如果当前是按下操作
keys[key] = true;
else if (action == GLFW_RELEASE) //松开键盘
keys[key] = false;
}
//鼠标滚轮输入
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
//鼠标输入
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
GLfloat sensitivity = 0.05;
xoffset *= sensitivity;
yoffset *= sensitivity;
camera.ProcessMouseMovement(xoffset, yoffset);
}
完整的片段着色器代码:
考虑了几乎所有的东西:物体材质、3种不同光源、5种光照贴图、泛光与阴影等
#version 330 core
struct Material
{
sampler2D texture_diffuse1; //贴图
sampler2D texture_specular1; //镜面贴图
sampler2D texture_normal1; //法线贴图
sampler2D texture_emission1; //放射光贴图
sampler2D texture_height1; //高度贴图
};
uniform samplerCube shadowMap;
struct SunLight //平行光
{
vec3 direction;
vec3 diffuse;
vec3 specular;
};
struct PointLight //点光源
{
vec3 position;
vec3 diffuse;
vec3 specular;
float k0, k1, k2;
float farPlane;
};
struct SpotLight //聚光灯
{
vec3 position;
vec3 direction;
vec3 diffuse;
vec3 specular;
float k0, k1, k2;
float cutOff, outCutOff;
};
uniform Material material;
uniform SunLight sunLight;
uniform PointLight pointLights[3];
uniform SpotLight spotLights[3];
vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir);
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir);
float ShadowCalculation(vec3 fragPos, PointLight light);
bool CheckNormalTex();
layout (location = 0) out vec4 fragColor;
layout (location = 1) out vec4 lightColor;
uniform vec3 viewPos;
in vec2 texIn;
in vec3 fragPosIn;
in vec3 normalIn;
in mat3 TBN;
vec2 texCoords;
void main()
{
texCoords = texIn;
vec3 normal = normalize(normalIn); //法向量
vec3 fragPos = fragPosIn; //当前片段坐标
vec3 viewDir = normalize(viewPos - fragPos); //观察方向
if (gl_FragCoord.x <= 800)
{
const float minLayers = 8;
const float maxLayers = 36;
float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir))); //样本数
float layerDepth = 1.0 / numLayers; //每层样本深度
float currentLayerDepth = 0; //当前测试到的样本深度
vec3 newViewDir = transpose(TBN) * viewDir;
vec2 deltaTexCoords = newViewDir.xy * 0.06 / numLayers; //单层样本对应的纹理偏移
float lastDepth = 0;
vec2 currentTexCoords = texCoords;
float currentDepthMapValue = texture(material.texture_height1, currentTexCoords).r;
while(currentLayerDepth < currentDepthMapValue) //在找到纹理深度小于等于测试深度的采样点的时候退出循环
{
lastDepth = currentDepthMapValue - currentLayerDepth;
currentLayerDepth += layerDepth; //继续测试下一个偏移
currentTexCoords -= deltaTexCoords; //确定正确的纹理采样点
currentDepthMapValue = texture(material.texture_height1, currentTexCoords).r; //获取纹理深度
}
texCoords = currentTexCoords;
if (lastDepth != 0) //插值得到最终的纹理坐标
{
float currentDepth = currentLayerDepth - currentDepthMapValue;
float weight = currentDepth / (currentDepth + lastDepth);
vec2 finalTexCoords = (currentTexCoords + deltaTexCoords) * weight + currentTexCoords * (1 - weight);
texCoords = finalTexCoords;
}
}
if (CheckNormalTex() && gl_FragCoord.x <= 800) //如果存在法线贴图
{
normal = texture(material.texture_normal1, texCoords).rgb;
normal = normalize(normal * 2.0 - vec3(1.0));
normal = normalize(TBN * normal);
}
vec3 result = 0.025 * vec3(texture(material.texture_diffuse1, texCoords));
result = result + CalcSunLight(sunLight, normal, viewDir);
for (int i = 0; i <= 0; i++)
{
float shadow = ShadowCalculation(fragPos, pointLights[i]);
result = result + (1.0 - shadow) * CalcPointLight(pointLights[i], normal, fragPos, viewDir);
}
result = result + CalcSpotLight(spotLights[0], normal, fragPos, viewDir);
result = result + vec3(texture(material.texture_emission1, texCoords));
fragColor = vec4(result, 1.0f);
float brightness = dot(fragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 2.0 || vec3(texture(material.texture_emission1, texCoords)) != vec3(0.0))
lightColor = vec4(fragColor.rgb, 1.0);
}
float ShadowCalculation(vec3 fragPos, PointLight light)
{
vec3 sampleOffsetDirections[20] = vec3[]
(
vec3(1, 1, 1), vec3(1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1),
vec3(1, 1, -1), vec3(1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1),
vec3(1, 1, 0), vec3(1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0),
vec3(1, 0, 1), vec3(-1, 0, 1), vec3(1, 0, -1), vec3(-1, 0, -1),
vec3(0, 1, 1), vec3(0, -1, 1), vec3(0, -1, -1), vec3(0, 1, -1)
);
vec3 fragToLight = fragPos - light.position;
float fragDepth = length(fragToLight);
float texelSize = (1.0 + (fragDepth / light.farPlane)) / 25.0;
float bias = 0.0004;
float shadow = 0;
for(int i = 0; i < 20; i++)
{
float closestDepth = texture(shadowMap, fragToLight + sampleOffsetDirections[i] * texelSize).r;
closestDepth *= light.farPlane;
if(fragDepth - bias > closestDepth)
shadow += 0.93f;
}
shadow /= 20;
return shadow;
}
vec3 CalcSunLight(SunLight light, vec3 normal, vec3 viewDir)
{
vec3 lightDir = normalize(-light.direction);
float diff = max(dot(normal, lightDir), 0.0f);
vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
vec3 reflectDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
return diffuse + specular;
}
vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
// vec3 fragToLight = fragPos - light.position;
// float fragDepth = length(fragToLight) / farPlane;
vec3 lightDir = normalize(light.position - fragPos);
float diff = max(dot(normal, lightDir), 0.0f);
vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
vec3 reflectDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
float dis = length(light.position - fragPos);
float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
diffuse *= attenuation;
specular *= attenuation;
return diffuse + specular;
}
vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir)
{
vec3 lightDir = normalize(light.position - fragPos);
float theta = dot(lightDir, normalize(-light.direction));
float lightSoft = clamp((theta - light.outCutOff) / (light.cutOff - light.outCutOff), 0.0f, 1.0f);
float diff = max(dot(normal, lightDir), 0.0f);
vec3 diffuse = light.diffuse * (diff * vec3(texture(material.texture_diffuse1, texCoords)));
vec3 reflectDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, reflectDir), 0.0), 16);
vec3 specular = light.specular * (spec * vec3(texture(material.texture_specular1, texCoords)));
float dis = length(light.position - fragPos);
float attenuation = 1.0f / (light.k0 + light.k1 * dis + light.k2 * (dis * dis));
diffuse *= attenuation * lightSoft;
specular *= attenuation * lightSoft;
return diffuse + specular;
}
bool CheckNormalTex()
{
vec3 normalTex = texture(material.texture_normal1, texCoords).rgb;
if (normalTex == vec3(0.0))
return false;
return true;
}