这一章原理并不复杂,涉及到了前面的“帧缓冲”、“HDR”技术,用到了5对shader,绘制的内容相对多一些,整体理解起来多花了点时间。知识点串起来也是个很好的复习。
一、原理
1. 为什么要用泛光?
泛光bloom技术模拟现实场景中高亮光照效果,通过对光照增加模糊,形成一圈泛光,增强光照的辐射。
2. 泛光的实现
泛光实现流程- 正常绘制场景
- 输出两路缓冲(1-正常输出;2-高亮部分)
- 第2路高亮缓冲-->高斯模糊
- 两路缓冲叠加,采用HDR输出最终效果
bloom用到了高斯模糊算法
高斯曲线
如果严格按照高斯模糊算法,进行2×32的四方形区域采样,我们必须对每个fragment从一个纹理中采样1024次,计算量偏大。
幸运的是,高斯方程有个非常巧妙的特性,它允许我们把二维方程分解为两个更小的方程:一个描述水平权重,另一个描述垂直权重。我们首先用水平权重在整个纹理上进行水平模糊,然后在经改变的纹理上进行垂直模糊。利用这个特性,结果是一样的,但是可以节省难以置信的性能,因为我们现在只需做32+32次采样,不再是1024了!这叫做两步高斯模糊 两步高斯模糊两步高斯模糊的效果也不太理想,代码实现中,进行了5次“两步高斯模糊”
3. 代码结构
用到的帧缓冲、shader比较多,先梳理下:
3个帧缓冲,注意,第一个帧缓冲还要绑定一个渲染对象,渲染对象绑定一个深度缓冲,第2、3个帧缓冲处理的是平面数据,不需要深度缓冲: 用到了3个帧缓冲4组着色器: 4组着色器 按照shader使用顺序理解代码逻辑 渲染流程
二、实现效果
1次两步高斯模糊 | 5次两步高斯模糊 |
---|
能明显看到,进行了5次两步高斯模糊后,泛光效果更好
三、完整代码
7.bloom.fs
#version 330 core
layout(location = 0) out vec4 FragColor;
layout(location = 1) out vec4 BrightColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
} fs_in;
struct Light {
vec3 Position;
vec3 Color;
};
uniform Light lights[4];
uniform sampler2D diffuseTexture;
uniform vec3 viewPos;
void main()
{
vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb;
vec3 normal = normalize(fs_in.Normal);
// ambient
vec3 ambient = 0.0 * color;
// lighting
vec3 lighting = vec3(0.0);
vec3 viewDir = normalize(viewPos - fs_in.FragPos);
for (int i = 0; i < 4; i++) {
vec3 lightDir = normalize(lights[i].Position - fs_in.FragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 result = lights[i].Color * diff * color;
//attenuation (use quadratic as we have gama correction)
float distance = length(fs_in.FragPos - lights[i].Position);
result *= 1.0/ (distance * distance);
lighting += result;
}
vec3 result = ambient + lighting;
//check whether result is higher than some threshold, if so, output as bloom threshold color
float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
if (brightness > 1.0)
{
BrightColor = vec4(result, 1.0);
}
else
{
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
}
FragColor = vec4(result, 1.0);
}
7.bloom_final.fs
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aNormal;
layout(location = 2) in vec2 aTexCoords;
out VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
} vs_out;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
void main()
{
vs_out.FragPos = vec3(model * vec4(aPos, 1.0));
vs_out.TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vs_out.Normal = normalize(normalMatrix * aNormal);
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
bloom_final.fs
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D scene;
uniform sampler2D bloomBlur;
uniform bool bloom;
uniform float exposure;
void main(){
const float gamma = 2.2;
vec3 hdrColor = texture(scene, TexCoords).rgb;
vec3 bloomColor = texture(bloomBlur, TexCoords).rgb;
if (bloom){
hdrColor += bloomColor; //additive blending
}
//tone mapping
vec3 result = vec3(1.0) - exp(-hdrColor * exposure);
// also gamma correct while we're at it
result = pow(result, vec3(1.0 / gamma));
FragColor = vec4(result, 1.0);
}
7.bloom_final.vs
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
7.blur.fs
#version 330 core
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D image;
uniform bool horizontal;
uniform float weight[5] = float[] (0.2270270270, 0.1945945946, 0.1216216216, 0.0540540541, 0.0162162162);
void main()
{
vec2 tex_offset = 1.0 / textureSize(image, 0); //get size of sigle texe1
vec3 result = texture(image, TexCoords).rgb * weight[0];
if(horizontal)
{
for (int i = 1; i < 5; ++i) {
result += texture(image, TexCoords + vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
result += texture(image, TexCoords - vec2(tex_offset.x * i, 0.0)).rgb * weight[i];
}
}
else
{
for(int i = 1; i < 5; ++i)
{
result += texture(image, TexCoords + vec2(0.0, tex_offset.y * i)).rgb * weight[i];
result += texture(image, TexCoords - vec2(0.0, tex_offset.y * i)).rgb * weight[i];
}
}
FragColor = vec4(result, 1.0);
}
7.blur.vs
#version 330 core
layout(location = 0) in vec3 aPos;
layout(location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
void main()
{
TexCoords = aTexCoords;
gl_Position = vec4(aPos, 1.0);
}
7.light_box.fs
#version 330 core
layout(location = 0) out vec4 FragColor;
layout(location = 1) out vec4 BrightColor;
in VS_OUT {
vec3 FragPos;
vec3 Normal;
vec2 TexCoords;
} fs_in;
uniform vec3 lightColor;
void main()
{
FragColor = vec4(lightColor, 1.0);
float brightness = dot(FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
if(brightness > 1.0)
{
BrightColor = vec4(FragColor.rgb, 1.0);
}
else{
BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
主程序
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader.h"
#include "camera.h"
#include "model.h"
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);
unsigned int loadTexture(const char *path);
unsigned int loadCubemap(vector<std::string> faces);
void renderScene (const Shader &shader);
void renderCube();
void RenderQuad();
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
bool blinn = false;
bool blinnKeyPressed = false;
bool gammaEnabled = true;
bool gammaKeyPressed = false;
bool bloom = true;
bool hdr = true; //change with 'space'
float exposure = 1.0f; // change with Q and E
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
float lastX = (float)SCR_WIDTH / 2.0;
float lastY = (float)SCR_HEIGHT / 2.0;
bool firstMouse = true;
// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;
int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "天哥学opengl", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// tell GLFW to capture our mouse
// glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// glPolygonMode(GL_FRONT_AND_BACK ,GL_LINE );
// configure global opengl state
// -----------------------------
glEnable(GL_DEPTH_TEST);
// build and compile shaders
// -------------------------
Shader shader("7.bloom.vs", "7.bloom.fs");
Shader shaderLight("7.bloom.vs", "7.light_box.fs");
Shader shaderBlur("7.blur.vs", "7.blur.fs");
Shader shaderBloomFinal("7.bloom_final.vs", "7.bloom_final.fs");
// load textures
unsigned int woodTexture = loadTexture("resource/wood.png");
unsigned int containerTexture = loadTexture("resource/container2.png");
// configure (floating point) framebuffers
unsigned int hdrFBO;
glGenFramebuffers(1, &hdrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);
// create 2 floating point color buffers (1 for normal rendering, other for brightness treshold values)
unsigned int colorBuffers[2];
glGenTextures(2, colorBuffers);
for (unsigned int i = 0; i < 2; i++) {
glBindTexture(GL_TEXTURE_2D, colorBuffers[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_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); // we clamp to the edge as the blur filter would otherwise sample repeated texture values!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// attach texture to framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, colorBuffers[i], 0);
}
// create and attach depth buffer (renderbuffer)
unsigned int rboDepth;
glGenRenderbuffers(1, &rboDepth);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboDepth);
// tell OpenGL which color attachments we'll use(of this framebuffer) for rendering
unsigned int attachments[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, attachments);
// finally check if framebuffer is complete
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
std::cout << "Framebuffer not complete!" << std::endl;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ping-pong-framebuffer for blurring
unsigned int pingpongFBO[2];
unsigned int pingpongColorbuffers[2];
glGenFramebuffers(2, pingpongFBO);
glGenTextures(2, pingpongColorbuffers);
for (unsigned int i = 0; i < 2; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[i]);
glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, SCR_WIDTH, SCR_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); // we clamp to the edge as the blur filter would otherwise sample repeated texture values!
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, pingpongColorbuffers[i], 0);
// also check if framebuffers are complete (no need for depth buffer)
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "Framebuffer not complete!" << std::endl;
}
// lighting info
// -------------
// positions
std::vector<glm::vec3> lightPositions;
lightPositions.push_back(glm::vec3( 0.0f, 0.5f, 1.5f));
lightPositions.push_back(glm::vec3(-4.0f, 0.5f, -3.0f));
lightPositions.push_back(glm::vec3( 3.0f, 0.5f, 1.0f));
lightPositions.push_back(glm::vec3(-.8f, 2.4f, -1.0f));
// colors
std::vector<glm::vec3> lightColors;
lightColors.push_back(glm::vec3(5.0f, 5.0f, 5.0f));
lightColors.push_back(glm::vec3(10.0f, 0.0f, 0.0f));
lightColors.push_back(glm::vec3(0.0f, 0.0f, 15.0f));
lightColors.push_back(glm::vec3(0.0f, 5.0f, 0.0f));
// shader configuration
shader.use();
shader.setInt("diffuseTexture", 0);
shaderBlur.use();
shaderBlur.setInt("image", 0);
shaderBloomFinal.use();
shaderBloomFinal.setInt("scene", 0);
shaderBloomFinal.setInt("bloomBlur", 1);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
float currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
processInput(window);
glBindFramebuffer(GL_FRAMEBUFFER, hdrFBO);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 projection = glm::perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
glm::mat4 view = camera.GetViewMatrix();
glm::mat4 model;
shader.use();
shader.setMat4("projection", projection);
shader.setMat4("view", view);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, woodTexture);
// set lighting uniforms
for (unsigned int i = 0; i < lightPositions.size(); i++) {
shader.setVec3("lights[" + std::to_string(i) + "].Position", lightPositions[i]);
shader.setVec3("lights[" + std::to_string(i) + "].Color", lightColors[i]);
}
shader.setVec3("viewPos", camera.Position);
// create one large cube that acts as the floor
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, -1.0f, 0.0));
model = glm::scale(model, glm::vec3(12.5f, 0.5f, 12.5f));
shader.setMat4("model", model);
renderCube();
// then create multiple cubes as the scenery
glBindTexture(GL_TEXTURE_2D, containerTexture);
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 1.5f, 0.0));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(2.0f, 0.0f, 1.0));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-1.0f, -1.0f, 2.0));
model = glm::rotate(model, glm::radians(60.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(0.0f, 2.7f, 4.0));
model = glm::rotate(model, glm::radians(23.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
model = glm::scale(model, glm::vec3(1.25));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-2.0f, 1.0f, -3.0));
model = glm::rotate(model, glm::radians(124.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-3.0f, 0.0f, 0.0));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
// finally show all the light sources as bright cubes
shaderLight.use();
shaderLight.setMat4("projection", projection);
shaderLight.setMat4("view", view);
for (unsigned int i=0; i < lightPositions.size(); i++) {
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(lightPositions[i]));
model = glm::scale(model, glm::vec3(0.25f));
shaderLight.setMat4("model", model);
shaderLight.setVec3("lightColor", lightColors[i]);
renderCube();
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// 2.blur bright fragments with two-pass Gaussian Blur
bool horizontal = true, first_iteration = true;
unsigned int amount = 2;
shaderBlur.use();
for (unsigned int i= 0 ; i < amount; i++) {
glBindFramebuffer(GL_FRAMEBUFFER, pingpongFBO[horizontal]);
shaderBlur.setInt("horizontal", horizontal);
glBindTexture(GL_TEXTURE_2D, first_iteration ? colorBuffers[1] : pingpongColorbuffers[!horizontal]);// bind texture of other framebuffer (or scene if first iteration)
RenderQuad();
horizontal = !horizontal;
if (first_iteration) {
first_iteration = false;
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//3. now render floating point color buffer to 2D quad and tonemap HDR colors to default framebuffer's(clamped) color range
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shaderBloomFinal.use();
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, colorBuffers[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, pingpongColorbuffers[!horizontal]);
shaderBloomFinal.setInt("bloom", bloom);
shaderBloomFinal.setFloat("exposure", exposure);
RenderQuad();
// std::cout << "bloom" << (bloom ? "on" : "off") << " | exposure: " << exposure << std::endl;
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
bool startRecord = false;
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS && !gammaKeyPressed)
{
gammaEnabled = !gammaEnabled;
gammaKeyPressed = true;
}
if (glfwGetKey(window, GLFW_KEY_B) == GLFW_RELEASE)
{
gammaKeyPressed = false;
}
if (glfwGetKey(window, GLFW_KEY_Y))
{
std::cout << "Y" << std::endl;
startRecord = true;
firstMouse = true;
}
if (glfwGetKey(window, GLFW_KEY_N))
{
std::cout << "N" << std::endl;
startRecord = false;
}
if (startRecord) {
return;
}
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
camera.ProcessKeyboard(FORWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
camera.ProcessKeyboard(BACKWARD, deltaTime);
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
camera.ProcessKeyboard(LEFT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
camera.ProcessKeyboard(RIGHT, deltaTime);
if (glfwGetKey(window, GLFW_KEY_Q) == GLFW_PRESS)
exposure -= 0.5 * deltaTime;
if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
exposure += 0.5 * deltaTime;
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS && !gammaKeyPressed)
{
hdr = !hdr;
gammaKeyPressed = true;
}
if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_RELEASE)
{
gammaKeyPressed = false;
}
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
// glfw: whenever the mouse moves, this callback is called
// -------------------------------------------------------
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
// std::cout << "xpos : " << xpos << std::endl;
// std::cout << "ypos : " << ypos << std::endl;
if (startRecord) {
return;
}
if (firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
float xoffset = xpos - lastX;
float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
lastX = xpos;
lastY = ypos;
// std::cout << "xoffset : " << xoffset << std::endl;
// std::cout << "yoffset : " << yoffset << std::endl;
camera.ProcessMouseMovement(xoffset, yoffset);
}
// glfw: whenever the mouse scroll wheel scrolls, this callback is called
// ----------------------------------------------------------------------
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
// utility function for loading a 2D texture from file
// ---------------------------------------------------
unsigned int loadTexture(char const * path)
{
unsigned int 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;
}
unsigned int loadCubemap(vector<std::string> faces)
{
unsigned int textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++) {
unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
stbi_image_free(data);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);
}
return textureID;
}
void renderScene(const Shader &shader)
{
// room cube
glm::mat4 model = glm::mat4(1.0f);
model = glm::scale(model, glm::vec3(5.0f));
shader.setMat4("model", model);
glDisable(GL_CULL_FACE); // note that we disable culling here since we render 'inside' the cube instead of the usual 'outside' which throws off the normal culling methods.
shader.setInt("reverse_normals", 1); // A small little hack to invert normals when drawing cube from the inside so lighting still works.
renderCube();
shader.setInt("reverse_normals", 0); // and of course disable it
glEnable(GL_CULL_FACE);
// cubes
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(4.0f, -3.5f, 0.0));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(2.0f, 3.0f, 1.0));
model = glm::scale(model, glm::vec3(0.75f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-3.0f, -1.0f, 0.0));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-1.5f, 1.0f, 1.5));
model = glm::scale(model, glm::vec3(0.5f));
shader.setMat4("model", model);
renderCube();
model = glm::mat4(1.0f);
model = glm::translate(model, glm::vec3(-1.5f, 2.0f, -3.0));
model = glm::rotate(model, glm::radians(60.0f), glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
model = glm::scale(model, glm::vec3(0.75f));
shader.setMat4("model", model);
renderCube();
}
// renderCube() renders a 1x1 3D cube in NDC.
// -------------------------------------------------
unsigned int cubeVAO = 0;
unsigned int cubeVBO = 0;
void renderCube()
{
// initialize (if necessary)
if (cubeVAO == 0)
{
float vertices[] = {
// back face
-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, 1.0f, 1.0f, // top-right
1.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-left
1.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, 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 face
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, // bottom-right
1.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-right
1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, // top-left
1.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-right
1.0f, -1.0f, -1.0f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, // top-left
1.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, 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-left
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, 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);
// fill buffer
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// link vertex attributes
glBindVertexArray(cubeVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// render Cube
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
}
// RenderQuad() Renders a 1x1 quad in NDC
unsigned int quadVAO = 0;
unsigned int quadVBO;
void RenderQuad()
{
if (quadVAO == 0)
{
GLfloat quadVertices[] = {
// Positions // Texture Coords
-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,
};
// Setup plane VAO
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
}
glBindVertexArray(quadVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);
}
网友评论