前置: 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;
}

然后对于投影矩阵,这次需要六个,表示各个方向:

  • 注意下投影矩阵的远平面距离,后面需要用到这个值以手动计算当前片段的深度
  • 六个lookat矩阵计算得到的摄像机六个上向量必然也要相互垂直
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<glm::mat4> 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方法中的逻辑:

  1. 现获取当前片段与光源的距离和方向,两个属性都要用
  2. 方向用于获取立方体贴图的深度,这个深度值在[0, 1]范围内,要将其乘回远平面得到实际深度距离
  3. 长度用于和②中获取的深度距离进行比较,看看哪一个更接近,以此决定当前的片段是否在阴影当中
  4. 一样考虑误差以解决阴影失真的问题
  5. 进行PCF采样,让阴影更平滑
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<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include"Shader.h"
#include"Camera.h"
#include"Light.h"
#include<glm/glm.hpp>
#include<glm/gtc/matrix_transform.hpp>
#include<glm/gtc/type_ptr.hpp>
#include"Mesh.h"
#include"Model.h"
#include<opengl/freeglut.h>
#include<SOIL.h>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<const GLchar*> 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<const GLchar*> 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<const GLchar*> 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;
}

OpenGL基础54:点光源阴影相关推荐

  1. OpenGL基础53:阴影映射(下)

    接上文:OpenGL基础52:阴影映射(上) 五.阴影失真 按照上文的计算的结果,一个很明显的问题是:对于参与计算深度贴图的物体,其表面可以看到这样的栅格状的阴影,这种常见的错误表现也叫做阴影失真(S ...

  2. OpenGL基础52:阴影映射(上)

    参考于:https://learnopengl.com/#!Advanced-Lighting/Shadows/Shadow-Mapping 一.游戏中的阴影 阴影是光线被阻挡的结果,当一个光源的光线 ...

  3. OpenGL 点光源阴影Point Shadows

    OpenGL点光源阴影Point Shadows 点光源阴影Point Shadows简介 生成深度立方体贴图 光空间的变换 深度着色器 万向阴影贴图 显示立方体贴图深度缓冲 PCF 点光源阴影Poi ...

  4. OpenGL基础23:平行光与点光源

    前面几章主要是针对物体,现在开始针对光源! 一.平行光 在 OpenGL基础18:光照基础 这一章里面讲了几种常见光源,先看平行光吧 一个很好的例子就是太阳光,因为离我们的距离过远,所以太阳光的特点就 ...

  5. OpenGL基础图形编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  6. OpenGL基础编程

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  7. 【转】OpenGL基础图形编程(一)

    原文:http://blog.chinaunix.net/uid-20638550-id-1909183.html  分类: 一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 ...

  8. OpenGL基础图形编程(转)

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

  9. OpenGL基础图形编程(一)

    一.OpenGL与3D图形世界 1.1.OpenGL使人们进入三维图形世界 我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体.我们又生活在一个 ...

最新文章

  1. C#PDF转Image
  2. 时装搭配_时装搭配修身T恤搭配紧身牛仔裤,时尚的穿搭,尽显时尚领导者
  3. react 原生html 插件,纯原生JS的瀑布流插件Macy.js,前端必备插件
  4. Android SlidingMenu插件的使用
  5. 前端学习(525):等分布局
  6. 基于JAVA+SpringMVC+Mybatis+MYSQL的企业审批管理系统
  7. Redis面试题及分布式集群
  8. tp中自定义跳转页面
  9. CSblog的学习记录
  10. 领导公开对你有成见,关键在于是否影响公司决策
  11. 无线网破解 跑字典 EWSA使用教程
  12. 为什么我不建议在阿里云官网报考ACP/ACE认证?
  13. 不要重新发明轮子_重新发明轮子
  14. 前苏格拉底时期的古希腊哲学流派
  15. ubuntu如何杀进程_ubuntu kill进程
  16. 数据结构c语言版陈越,数据结构 陈越
  17. selenium操作firefox
  18. spring test如何设置DebuggingClassWriter.DEBUG_LOCATION_PROPERTY
  19. SpringCloud - LCN分布式事务框架
  20. SE (Squeeze Excitation)模块

热门文章

  1. python是什么软件-软件开发是什么?
  2. python自动化办公要学多久-基于python实现自动化办公学习笔记
  3. 语音识别技术是什么_语音识别技术应用领域介绍
  4. 如何在Unity项目中添加语音识别?
  5. java特定用户登录_求教!!!用 jsp+servlet 怎样控制指定用户名登录页面啊!
  6. 广义表取表头表尾_数据结构广义表的递归算法
  7. 使用原生js实现邮箱模糊查询的效果
  8. java语法格式整理
  9. hal库串口dma卡死_HAL库版DMA循环模式串口数据收发
  10. Vue将echarts数据导出成excel文件