注:本文原文是learnopengl网站Diffuse irradiance一文,一直没人翻译,这两天我抽出时间阅读了这部分内容,无奈本人英文渣,完整的翻译实在拿不出,只能读懂大体含义,于是想先记录下来目前能读懂的内容以及一些心得。 原文地址:https://learnopengl.com/#!PBR/IBL/Diffuse-irradiance

一、一些简单介绍

这一节对应于原文的第一节,该节的大体意思主要是告诉我们漫反射辐照需要解决两个问题:1、需要计算来自各个方向的光线。2、解决如何实时、快速的求解反射率方程的积分。还表示这篇教程主要探讨环境光的漫反射所以环境光的镜面反射暂且不提。
至于解决方法,则是使用辐照贴图,即在原先的环境贴图的基础上,我们先对其各个部分进行卷积算出对应的辐照度贴图,然后采样的时候直接去采样辐照度贴图得出环境光照。而这一部分恰好对应了Opengl红宝书基于图像的光照模型那一部分内容。

二、PBR与HDR

这一部分的大体含义是,为了得到更好的PBR效果,我们对图片的要求较高,它的数据不能仅仅是【0,1.0】之间的LDR,必须是HDR,然后为了读取这种图片,我们需要使用stb_image.h这个头文件,其下载地址是:https://github.com/nothings/stb/blob/master/stb_image.h,HDR图片下载地是:http://www.hdrlabs.com/sibl/archive.html。
又因为原始的HDR图片是一个圆柱体贴图,而我们需要的是立方体贴图,所以我们需要使用直角坐标系与柱面坐标系的转换关系,对其进行采样。

三、计算辐照度贴图

这一部分主要是告诉我们如何计算其球面积分(因为对于一个点,我们要算出以它为中心的半球体表面所有光线对其的影响)。其主要办法是离散化,因为是对角度进行积分,所以我们要离散化的自然是角度。具体实现方式是,将球面坐标转化为笛卡尔坐标(此时是在切线空间中),然后通过TBN矩阵转换到世界坐标系。

四、PBR和简介辐射光线

这一节就是告诉我们采样后如何计算器PBR,没什么新的东西。

五、注释代码

1、主程序

#include <string>#define GLEW_STATIC
#include <GL/glew.h>#include <GLFW/glfw3.h>#include <GL/shader.h>
#include <GL/camera.h>#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>#define STB_IMAGE_IMPLEMENTATION
#include <GL/stb_image.h>const GLuint SCR_WIDTH = 1280, SCR_HEIGHT = 720;Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;GLuint sphereVAO = 0;
GLuint indexCount;
void renderSphere()
{if (sphereVAO == 0){glGenVertexArrays(1, &sphereVAO);GLuint vbo, ebo;glGenBuffers(1, &vbo);glGenBuffers(1, &ebo);std::vector<glm::vec3> positions;std::vector<glm::vec2> uv;std::vector<glm::vec3> normals;std::vector<GLuint> indices;const GLuint X_SEGMENTS = 64;const GLuint Y_SEGMENTS = 64;const float PI = 3.14159265359;for (GLuint y = 0; y <= Y_SEGMENTS; ++y){for (GLuint x = 0; x <= X_SEGMENTS; ++x){float xSegment = (float)x / (float)X_SEGMENTS;float ySegment = (float)y / (float)Y_SEGMENTS;float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);float yPos = std::cos(ySegment * PI);float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);positions.push_back(glm::vec3(xPos, yPos, zPos));uv.push_back(glm::vec2(xSegment, ySegment));normals.push_back(glm::vec3(xPos, yPos, zPos));}}bool oddRow = false;for (int y = 0; y < Y_SEGMENTS; ++y){if (!oddRow) // even rows: y == 0, y == 2; and so on{for (int x = 0; x <= X_SEGMENTS; ++x){indices.push_back(y       * (X_SEGMENTS + 1) + x);indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);}}else{for (int x = X_SEGMENTS; x >= 0; --x){indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);indices.push_back(y       * (X_SEGMENTS + 1) + x);}}oddRow = !oddRow;}indexCount = indices.size();std::vector<float> data;for (int i = 0; i < positions.size(); ++i){data.push_back(positions[i].x);data.push_back(positions[i].y);data.push_back(positions[i].z);if (uv.size() > 0){data.push_back(uv[i].x);data.push_back(uv[i].y);}if (normals.size() > 0){data.push_back(normals[i].x);data.push_back(normals[i].y);data.push_back(normals[i].z);}}glBindVertexArray(sphereVAO);glBindBuffer(GL_ARRAY_BUFFER, vbo);glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(float), &data[0], GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);float stride = (3 + 2 + 3) * sizeof(float);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(3 * sizeof(float)));glEnableVertexAttribArray(2);glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(5 * sizeof(float)));}glBindVertexArray(sphereVAO);glDrawElements(GL_TRIANGLE_STRIP, indexCount, GL_UNSIGNED_INT, 0);
}
GLuint cubeVAO = 0;
GLuint cubeVBO = 0;
void renderCube()
{if (cubeVAO == 0){GLfloat vertices[] = {// Back face-1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // Bottom-left1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 0.0f, // bottom-right         1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f,  // top-right-1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f,  // bottom-left-1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 1.0f,// top-left// Front face-1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 0.0f,  // bottom-right1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f,  // top-right1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right-1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 1.0f,  // top-left-1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f,  // bottom-left// Left face-1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right-1.0f,  1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-left-1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f,  // bottom-left-1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left-1.0f, -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 0.0f,  // bottom-right-1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right// Right face1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right1.0f,  1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-right         1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f,  // bottom-right1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f,  // top-left1.0f, -1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-left     // Bottom face-1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 1.0f, // top-left1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f,// bottom-left1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left-1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 0.0f, // bottom-right-1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right// Top face-1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f,// top-left1.0f,  1.0f , 1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 1.0f, // top-right     1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right-1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f,// top-left-1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 0.0f // bottom-left        };glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &cubeVBO);glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindVertexArray(cubeVAO);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);}glBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);
}GLuint loadTexture(char const * path)
{GLuint textureID;glGenTextures(1, &textureID);int width, height, nrComponents;unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);if (data){GLenum format;if (nrComponents == 1)format = GL_RED;else if (nrComponents == 3)format = GL_RGB;else if (nrComponents == 4)format = GL_RGBA;glBindTexture(GL_TEXTURE_2D, textureID);glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);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_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Texture failed to load at path: " << path << std::endl;stbi_image_free(data);}return textureID;
}#pragma region "User input"bool keys[1024];
bool keysPressed[1024];
void Do_Movement()
{if (keys[GLFW_KEY_W])camera.ProcessKeyboard(FORWARD, deltaTime);if (keys[GLFW_KEY_S])camera.ProcessKeyboard(BACKWARD, deltaTime);if (keys[GLFW_KEY_A])camera.ProcessKeyboard(LEFT, deltaTime);if (keys[GLFW_KEY_D])camera.ProcessKeyboard(RIGHT, deltaTime);
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)glfwSetWindowShouldClose(window, GL_TRUE);if (key >= 0 && key <= 1024){if (action == GLFW_PRESS)keys[key] = true;else if (action == GLFW_RELEASE){keys[key] = false;keysPressed[key] = false;}}
}GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
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;camera.ProcessMouseMovement(xoffset, yoffset);
}void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{camera.ProcessMouseScroll(yoffset);
}#pragma endregionint main()
{glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);glfwWindowHint(GLFW_SAMPLES, 4);glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_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();glGetError();glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL); Shader pbrShader("pbr.vs", "pbr.frag");Shader equirectangularToCubemapShader("cubemap.vs", "equirectangular_to_cubemap.frag");Shader irradianceShader("cubemap.vs", "irradiance_convolution.frag");Shader backgroundShader("background.vs", "background.frag");pbrShader.Use();glUniform1i(glGetUniformLocation(pbrShader.Program, "irradianceMap"), 0);glUniform3f(glGetUniformLocation(pbrShader.Program, "albedo"), 0.5f, 0.0f, 0.0f);glUniform1f(glGetUniformLocation(pbrShader.Program, "ao"), 1.0f);backgroundShader.Use();glUniform1i(glGetUniformLocation(backgroundShader.Program, "environmentMap"), 0);glm::vec3 lightPositions[] = {glm::vec3(-10.0f,  10.0f, 10.0f),glm::vec3(10.0f,  10.0f, 10.0f),glm::vec3(-10.0f, -10.0f, 10.0f),glm::vec3(10.0f, -10.0f, 10.0f),};glm::vec3 lightColors[] = {glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f)};int nrRows = 7;int nrColumns = 7;float spacing = 2.5;// pbr: 设置帧缓冲// ----------------------GLuint captureFBO;GLuint captureRBO;glGenFramebuffers(1, &captureFBO);glGenRenderbuffers(1, &captureRBO);glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 512, 512);glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, captureRBO);// pbr: 读取HDR环境映射贴图// ---------------------------------stbi_set_flip_vertically_on_load(true);int width, height, nrComponents;float *data = stbi_loadf("Newport_Loft/Newport_Loft_Ref.hdr", &width, &height, &nrComponents, 0);GLuint hdrTexture;if (data){glGenTextures(1, &hdrTexture);glBindTexture(GL_TEXTURE_2D, hdrTexture);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);stbi_image_free(data);}else{std::cout << "Failed to load HDR image." << std::endl;}// pbr: 设置用于渲染的立方体贴图并挂载到帧缓冲上// ---------------------------------------------------------GLuint envCubemap;glGenTextures(1, &envCubemap);glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);for (GLuint i = 0; i < 6; ++i){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB16F, 512, 512, 0, GL_RGB, GL_FLOAT, nullptr);}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);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// pbr: 设置投影矩阵和观察矩阵用于分别捕获6各面的立方体贴图6各面的数据// ----------------------------------------------------------------------------------------------glm::mat4 captureProjection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f);glm::mat4 captureViews[] ={glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f,  0.0f,  0.0f), glm::vec3(0.0f, -1.0f,  0.0f)),glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f,  0.0f,  0.0f), glm::vec3(0.0f, -1.0f,  0.0f)),glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f,  1.0f,  0.0f), glm::vec3(0.0f,  0.0f,  1.0f)),glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f,  0.0f), glm::vec3(0.0f,  0.0f, -1.0f)),glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f,  0.0f,  1.0f), glm::vec3(0.0f, -1.0f,  0.0f)),glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f,  0.0f, -1.0f), glm::vec3(0.0f, -1.0f,  0.0f))};// pbr: 将圆柱形HDR环境贴图变为立方体贴图// ----------------------------------------------------------------------equirectangularToCubemapShader.Use();glUniform1i(glGetUniformLocation(equirectangularToCubemapShader.Program, "equirectangularMap"), 0);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, hdrTexture);glUniformMatrix4fv(glGetUniformLocation(equirectangularToCubemapShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(captureProjection));glViewport(0, 0, 512, 512); // 不要忘记将视口大小变为捕获数据的大小.glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);for (GLuint i = 0; i < 6; ++i){glUniformMatrix4fv(glGetUniformLocation(equirectangularToCubemapShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envCubemap, 0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);renderCube();}glBindFramebuffer(GL_FRAMEBUFFER, 0);// pbr: 创建一个辐照度立方体贴图, 并重新捕获FBO。// --------------------------------------------------------------------------------GLuint irradianceMap;glGenTextures(1, &irradianceMap);glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceMap);for (GLuint i = 0; i < 6; ++i){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB32F, 32, 32, 0, GL_RGB, GL_FLOAT, nullptr);}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);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);glBindRenderbuffer(GL_RENDERBUFFER, captureRBO);glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, 32, 32);// pbr: 通过创建一个辐照度贴图来解决用卷积计算漫反射积分的问题// -----------------------------------------------------------------------------irradianceShader.Use();glUniform1i(glGetUniformLocation(irradianceShader.Program, "environmentMap"), 0);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);glUniformMatrix4fv(glGetUniformLocation(irradianceShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(captureProjection));glViewport(0, 0, 32, 32); // 不要忘记将视口大小变为捕获数据的大小.glBindFramebuffer(GL_FRAMEBUFFER, captureFBO);for (GLuint i = 0; i < 6; ++i){glUniformMatrix4fv(glGetUniformLocation(irradianceShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(captureViews[i]));glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, irradianceMap, 0);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);renderCube();}glBindFramebuffer(GL_FRAMEBUFFER, 0);glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);pbrShader.Use();glUniformMatrix4fv(glGetUniformLocation(pbrShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));backgroundShader.Use();glUniformMatrix4fv(glGetUniformLocation(backgroundShader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);while (!glfwWindowShouldClose(window)){GLfloat currentFrame = glfwGetTime();deltaTime = currentFrame - lastFrame;lastFrame = currentFrame;glfwPollEvents();Do_Movement();glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 渲染场景,向最终着色器提供辐照度贴图// ------------------------------------------------------------------------------------------pbrShader.Use();glm::mat4 view = camera.GetViewMatrix();glUniformMatrix4fv(glGetUniformLocation(pbrShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));glUniform3fv(glGetUniformLocation(pbrShader.Program, "camPos"), 1, &camera.Position[0]);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_CUBE_MAP, irradianceMap);glm::mat4 model;for (int row = 0; row < nrRows; ++row){glUniform1f(glGetUniformLocation(pbrShader.Program, "metallic"), (float)row / (float)nrRows);for (int col = 0; col < nrColumns; ++col){glUniform1f(glGetUniformLocation(pbrShader.Program, "roughness"), glm::clamp((float)col / (float)nrColumns, 0.05f, 1.0f));model = glm::mat4();model = glm::translate(model, glm::vec3((float)(col - (nrColumns / 2)) * spacing,(float)(row - (nrRows / 2)) * spacing,-2.0f));glUniformMatrix4fv(glGetUniformLocation(pbrShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));renderSphere();}}for (GLuint i = 0; i < sizeof(lightPositions) / sizeof(lightPositions[0]); ++i){glm::vec3 newPos = lightPositions[i] + glm::vec3(sin(glfwGetTime() * 5.0) * 5.0, 0.0, 0.0);newPos = lightPositions[i];glUniform3fv(glGetUniformLocation(pbrShader.Program, ("lightPositions[" + std::to_string(i) + "]").c_str()), 1, &newPos[0]); glUniform3fv(glGetUniformLocation(pbrShader.Program, ("lightColors[" + std::to_string(i) + "]").c_str()), 1, &lightColors[i][0]);model = glm::mat4();model = glm::translate(model, newPos);model = glm::scale(model, glm::vec3(0.5f));glUniformMatrix4fv(glGetUniformLocation(pbrShader.Program, "model"), 1, GL_FALSE, glm::value_ptr(model));renderSphere();}//最后渲染天空盒backgroundShader.Use();glUniformMatrix4fv(glGetUniformLocation(backgroundShader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap);renderCube();glfwSwapBuffers(window);}glfwTerminate();return 0;
}

2、PBR着色器(这里是对于灯光和环境光的计算)

#version 330 corelayout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoords;
layout (location = 2) in vec3 normal;out vec2 TexCoords;
out vec3 WorldPos;
out vec3 Normal;uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;void main()
{TexCoords = texCoords;WorldPos = vec3(model * vec4(pos, 1.0f));Normal = mat3(model) * normal;gl_Position = projection * view * vec4(WorldPos, 1.0f);
}
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
in vec3 WorldPos;
in vec3 Normal;
in mat3 TBN;// 材质属性
uniform vec3 albedo;
uniform float metallic;
uniform float roughness;
uniform float ao;// 辐照度贴图
uniform samplerCube irradianceMap;// 灯光
uniform vec3 lightPositions[4];
uniform vec3 lightColors[4];uniform vec3 camPos;const float PI = 3.14159265359;
// ----------------------------------------------------------------------------
float DistributionGGX(vec3 N, vec3 H, float roughness)
{float a = roughness*roughness;float a2 = a*a;float NdotH = max(dot(N, H), 0.0);float NdotH2 = NdotH*NdotH;float nom   = a2;float denom = (NdotH2 * (a2 - 1.0) + 1.0);denom = PI * denom * denom;return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySchlickGGX(float NdotV, float roughness)
{float r = (roughness + 1.0);float k = (r*r) / 8.0;float nom   = NdotV;float denom = NdotV * (1.0 - k) + k;return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{float NdotV = max(dot(N, V), 0.0);float NdotL = max(dot(N, L), 0.0);float ggx2 = GeometrySchlickGGX(NdotV, roughness);float ggx1 = GeometrySchlickGGX(NdotL, roughness);return ggx1 * ggx2;
}
// ----------------------------------------------------------------------------
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
// ----------------------------------------------------------------------------
void main()
{       vec3 N = Normal;vec3 V = normalize(camPos - WorldPos);vec3 R = reflect(-V, N); // 按照普通入射角计算反射; 如果是非电解质(比如塑料)让F0 // 为0.04如果是金属则使用它们自身的反射率    vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic);// 反射率方程vec3 Lo = vec3(0.0);for(int i = 0; i < 4; ++i) {// 计算每个灯光的辐射vec3 L = normalize(lightPositions[i] - WorldPos);                           //入射方向vec3 H = normalize(V + L);                                                  //半程向量float distance = length(lightPositions[i] - WorldPos);                      //距离float attenuation = 1.0 / (distance * distance);                            //衰减vec3 radiance = lightColors[i] * attenuation;                               //辐射强度// 双向反射函数float NDF = DistributionGGX(N, H, roughness);                               //计算正态分布函数          float G   = GeometrySmith(N, V, L, roughness);                              //计算几何函数vec3 F    = fresnelSchlick(max(dot(H, V), 0.0), F0);                        //菲涅尔方程vec3 nominator    = NDF * G * F;                                            //分子float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;  //分母(加上0.001以防止除以0)vec3 brdf = nominator / denominator;                                        //双向反射函数数值// 镜面反射等于菲涅尔方程的值vec3 kS = F;//为了能量守恒,漫反射和镜面反射不能超过1.0(除非该表面自身发光)所以Kd=1.0-kSvec3 kD = vec3(1.0) - kS;// 只有非金属或者混合一部分金属的物体存在漫反射,纯金属不存在漫反射kD *= 1.0 - metallic;                   // 计算光照强度float NdotL = max(dot(N, L), 0.0);        // 添加到出射辐射率Lo += (kD * albedo / PI + brdf) * radiance * NdotL; // 我们在计算BRDF的时候已经乘上KS了,所以我们不要再乘一次KS}   // 环境光照计算 (我们现在使用辐照度贴图来计算环境光的影响)vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);vec3 kD = 1.0 - kS;kD *= 1.0 - metallic;     vec3 irradiance = texture(irradianceMap, N).rgb;                                //从辐照度贴图中获取辐射强度vec3 diffuse      = irradiance * albedo;vec3 ambient = (kD * diffuse) * ao;                                             //现在暂时只计算漫反射辐照度vec3 color = ambient + Lo;// HDR tonemappingcolor = color / (color + vec3(1.0));// 伽马校正color = pow(color, vec3(1.0/2.2)); FragColor = vec4(color , 1.0);
}

3、圆柱转立方体着色器

#version 330 core
layout (location = 0) in vec3 pos;out vec3 WorldPos;uniform mat4 projection;
uniform mat4 view;void main()
{WorldPos = pos;  gl_Position =  projection * view * vec4(WorldPos, 1.0);
}
#version 330 core
out vec4 FragColor;
in vec3 WorldPos;uniform sampler2D equirectangularMap;                   //圆柱贴图采样器const vec2 invAtan = vec2(0.1591, 0.3183);
vec2 SampleSphericalMap(vec3 v)
{vec2 uv = vec2(atan(v.z, v.x), asin(v.y));          //直角坐标系与柱面坐标系之间的转换uv *= invAtan;uv += 0.5;return uv;
}void main()
{       vec2 uv = SampleSphericalMap(normalize(WorldPos));vec3 color = texture(equirectangularMap, uv).rgb;FragColor = vec4(color, 1.0);
}

4、辐照度贴图着色器

#version 330 core
layout (location = 0) in vec3 pos;out vec3 WorldPos;uniform mat4 projection;
uniform mat4 view;void main()
{WorldPos = pos;  gl_Position =  projection * view * vec4(WorldPos, 1.0);
}
#version 330 core
out vec4 FragColor;
in vec3 WorldPos;uniform samplerCube environmentMap;                                         //立方体贴图采样器const float PI = 3.14159265359f;void main()
{       vec3 N = normalize(WorldPos);vec3 irradiance = vec3(0.0);   //计算TBN矩阵vec3 up    = vec3(0.0, 1.0, 0.0);vec3 right = cross(up, N);up         = cross(N, right);float sampleDelta = 0.025f;                                             //设置采样增量float nrSamples = 0.0f;                                                 //记录一共有多少个采样for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta){for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta){// 将球面坐标转变为笛卡尔坐标 (在切线空间中)vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));// 将切线空间变换到世界空间vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; //计算该采样区域的辐射量irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta);nrSamples++;}}irradiance = PI * irradiance * (1.0 / float(nrSamples));                //取平均辐射量FragColor = vec4(irradiance, 1.0);
}

5、天空盒着色器

#version 330 core
layout (location = 0) in vec3 pos;uniform mat4 projection;
uniform mat4 view;out vec3 WorldPos;void main()
{WorldPos = pos;mat4 rotView = mat4(mat3(view));                            //让天空盒随着摄像机移动使其永远位于天空盒中心vec4 clipPos = projection * rotView * vec4(WorldPos, 1.0);gl_Position = clipPos.xyww;
}
#version 330 core
out vec4 FragColor;
in vec3 WorldPos;uniform samplerCube environmentMap;void main()
{       vec3 envColor = texture(environmentMap, WorldPos).rgb;// HDR tonemap and gamma correctenvColor = envColor / (envColor + vec3(1.0));envColor = pow(envColor, vec3(1.0/2.2)); FragColor = vec4(envColor, 1.0);
}

漫反射辐照——并不是很完全的翻译相关推荐

  1. 金山翻译很方便离线翻译

    下载金山快译.金山词霸 金山词霸  右击  开启取词,开启划词 非常方便翻译,无论是dw,notepad++都可以直接翻译了

  2. 我认为这种计算机卖的很好英语翻译,英语翻译

    1.王晓的英语讲得很好. Wang Xiao speaks good English. 2.该厂产品的特点是工艺精美,经久耐用. The products of this factory are ch ...

  3. 镜面反射辐照模型——不完全的翻译

    首先给出PBR的反射方程: 上节教程主要解决的是漫反射部分,使用了辐照贴图(irradiance map),而这次解决的是镜面反射部分.我们通过类比解决漫反射辐照的方式来解决镜面反射辐照问题,但我们要 ...

  4. 搜狗发布智能翻译机,看上去小众实际上野心很大?

    这几天注意到,搜狗在合作伙伴大会上,发布了两款翻译类智能硬件:"搜狗旅行翻译宝"和"搜狗速记翻译笔",两款产品搭配双麦克风阵列降噪,支持英日韩俄德等17种语言与 ...

  5. DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(3/5)漫反射积分项

    目录 3.4.漫反射项的重要性采样计算 3.4.1.漫反射项的二重积分形式极其近似预积分计算 3.4.2.漫反射辐照度积分项的直接积分计算 3.4.3.漫反射辐照度积分项的蒙特卡洛积分重要性采样计算 ...

  6. [图形学] 基于图像的照明:漫反射辐照度

    reference: https://learnopengl.com/PBR/IBL/Diffuse-irradiance IBL或者基于图像的照明是一组照明物体的技术,它不像前一个教程中直接分析光, ...

  7. Esfog_UnityShader教程_漫反射DiffuseReflection

    这篇是系列教程的第三篇,最近工作比较紧,所以这个周六周日就自觉去加了刚回来就打开电脑补上这篇,这个系列的教程我会尽量至少保证一周写一篇的.如果大家看过我的上一篇教程<Esfog_UnitySha ...

  8. 在线识图翻译_拍照翻译ios版下载在线识图翻译-免费拍照翻译软件苹果版下载...

    拍照翻译app在我们的学习中扮演着很重要的角色,当我们遇到看不懂的英文时就可以通过它来帮我们翻译,还不用一个字一个字输入,只要拍照即可快速翻译.这款软件涵盖了各国语言资源,用户可以使用这款软件进行各国 ...

  9. 腾讯AI Lab:深度解读AI辅助翻译的研究及应用

    11月13日,深圳 - 腾讯AI Lab今日发布了一款AI辅助翻译产品 - "腾讯辅助翻译"(Transmart),可满足用户快速翻译的需求,用AI辅助人工翻译提高效率和质量.该产 ...

最新文章

  1. VCTransitionsLibrary –自定义iOS交互式转场动画的库
  2. 2018 German Collegiate Programming Contest (GCPC 18)
  3. 编程打怪升级之路2018-06-01
  4. C# 系统应用之获取IE浏览记录和IE地址栏输入网址
  5. 【C#】解析C#中JSON.NET的使用
  6. testflight怎么做版本更新_如何使用TestFlight进行App构建版本测试
  7. 工欲善其事必先利其器,用Emmet提高HTML编写速度
  8. 过来人的亲身经验告诉你,如何从菜鸟晋升月薪过万的测试工程师
  9. 契约锁“7大”签约避坑指南,帮您化解99%的合同签署麻烦
  10. 起底 Telegram 的发家史:Pavel Durov 的游戏才刚刚开始
  11. ACM/IOI 历年国家集训队论文集和论文算法分类整理
  12. PS新手,常用的几种技巧干货,值得收藏!
  13. 面朝大海——我的2016
  14. vue 项目实现短信发送
  15. 嵌入式linux培训教程,嵌入式Linux开发学习之Linux文件系统学习
  16. 于二〇〇八年十一月二十六日有所思而作
  17. mysql把集群改成单机_nacos单机迁移至集群
  18. 基于数据安全的风险评估-脆弱性识别
  19. 【Docker】搭建Docker私库Harbor
  20. WeLink协作文档,职场的贴心助手

热门文章

  1. GMT中文字体显示配置
  2. 如何监控ActiveMQ
  3. android 电池监测工具,安卓最强电池监控器Battery Monitor Widget Pro
  4. Drupal7_2:安装drupal
  5. 全国计算机等级考试二级C语言考试题
  6. 用google搜索图书的方法
  7. wdb_2018_3rd_soEasy
  8. 科普 | 什么是ChatGPT?试用ChatGPT,ChatGPT的启示!
  9. 记2019.7日照夏令营
  10. USACO美国信息学奥赛竞赛12月份开赛,中国学生备赛指南