Discard clip: for the rendering of transparent objects, you can discard the clip:
void main() { vec4 texColor = texture(texture1, TexCoords); if(texColor.a < 0.1) discard; FragColor = texColor; }
Blend: although it's good to discard the clip directly, it can't render a translucent image. We either render a clip or discard it completely. Blending needs to be enabled
glEnable(GL_BLEND);
Mixed formula:
Through glblendforc (glenum sfactor, glenum dfactor),
But there is occlusion problem:
Cause: when writing the depth buffer, the depth buffer will not check whether the fragment is transparent, so the transparent part will be written to the depth buffer like other values. The result is that the entire quadrilateral of the window is tested for depth regardless of its transparency. Even though the transparent parts should show the windows behind them, the depth test still discards them.
Question: why do transparent objects not block opaque objects, but mix them?
Define rendering order: (back to front)
Rendering principles:
- First draw all opaque objects.
- Sort all transparent objects.
- Draw all transparent objects in order.
std::map<float, glm::vec3> sorted; for (unsigned int i = 0; i < windows.size(); i++) { float distance = glm::length(camera.Position - windows[i]); sorted[distance] = windows[i]; }
for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) { model = glm::mat4(); model = glm::translate(model, it->second); shader.setMat4("model", model); glDrawArrays(GL_TRIANGLES, 0, 6); }
Main program:
#define STB_IMAGE_IMPLEMENTATION #include"STB_IMAGE/stb_image.h" #include<glad/glad.h> #include<GLFW/glfw3.h> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> #include <ShaderTest.h> #include <Texture.h> #include <Camera.h> #include <iostream> #include<vector> #include<map> void framebuffer_size_callback(GLFWwindow* window, int width, int height); void processInput(GLFWwindow* window); void mouse_callback(GLFWwindow* window, double xpos, double ypos); void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); unsigned int loadTexture(char const* texturePath); const unsigned int WINDOW_WIDTH = 800; const unsigned int WINDOW_HEIGHT = 600; glm::vec3 cameraPos = glm::vec3(-1.0f, -1.0f, 1.5f); glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f); glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f); Camera camera(glm::vec3(0.0f, 0.0f, 3.0f)); glm::vec3 lightDiretion(1.2f, 1.0f, 2.0f); bool firstMouse = true; double lastX = WINDOW_WIDTH / 2; double lastY = WINDOW_HEIGHT / 2; float yaw = -90.0f; float pitch = 0.0f; float fov = 45.0f; float lastTime = 0.0f; float deltaTime = 0.0f; int main() { //1 instantiate GLFW window glfwInit(); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //Create a window object GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "TestWindows", NULL, NULL); if (!window) { std::cout << "Failed to create Windows" << std::endl; //Release resources glfwTerminate(); return -1; } //Keep context glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); //Initialize GLAD if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { std::cout << "Failed to initialize GLAD" << std::endl; return -1; } //vertex data float cubeVertices[] = { // positions // texture Coords -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f, -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, -0.5f, 1.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.5f, -0.5f, 0.5f, 1.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, -0.5f, 0.5f, -0.5f, 0.0f, 1.0f }; float planeVertices[] = { // positions // texture Coords 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, -5.0f, -0.5f, 5.0f, 0.0f, 0.0f, -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, 5.0f, -0.5f, 5.0f, 2.0f, 0.0f, -5.0f, -0.5f, -5.0f, 0.0f, 2.0f, 5.0f, -0.5f, -5.0f, 2.0f, 2.0f }; //grass std::vector<glm::vec3> vegetation { glm::vec3(-1.5f, 0.0f, -0.48f), glm::vec3(1.5f, 0.0f, 0.51f), glm::vec3(0.0f, 0.0f, 0.7f), glm::vec3(-0.3f, 0.0f, -2.3f), glm::vec3(0.5f, 0.0f, -0.6f) }; float transparentVertices[] = { // positions // texture Coords (swapped y coordinates because texture is flipped upside down) 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, -0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.0f, 1.0f, 0.0f }; //2 create VAO,VBO,EBO unsigned int VAOID, VBOId; glGenVertexArrays(1, &VAOID); //The first parameter is the number of objects glGenBuffers(1, &VBOId); //Binding VAO glBindVertexArray(VAOID); //Bind to vertex buffer object glBindBuffer(GL_ARRAY_BUFFER, VBOId); //copy vertex data to buffer memory glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_COPY); //Parse vertex data and assign data to the vertex attribute array corresponding to 1 //The first parameter is the vertex attribute, as opposed to the shader program location //Positional attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); //Enable vertex attributes, disabled by default// glEnableVertexAttribArray(0); //Parameters: vertex attribute values //Texture attribute glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); //floor unsigned int PlaneVAOID, PlaneVBOID; glGenVertexArrays(1, &PlaneVAOID); //The first parameter is the number of objects glGenBuffers(1, &PlaneVBOID); //Binding VAO glBindVertexArray(PlaneVAOID); //Bind to vertex buffer object glBindBuffer(GL_ARRAY_BUFFER, PlaneVBOID); //copy vertex data to buffer memory glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_COPY); //Positional attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); //Enable vertex attributes, disabled by default// glEnableVertexAttribArray(0); //Parameters: vertex attribute values //Texture attribute glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); unsigned int GrassVAO, GrassVBO; glGenVertexArrays(1, &GrassVAO); glGenBuffers(1, &GrassVBO); glBindVertexArray(GrassVAO); glBindBuffer(GL_ARRAY_BUFFER, GrassVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(transparentVertices), transparentVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glBindVertexArray(0); //3. Create shader ShaderTest shaderTest("openGL_Blending.vs", "openGL_Blending.fs"); //4. Texture object unsigned int textureId = loadTexture("F:/C++project/LibsInclude/src/marble.jpg"); unsigned int textureId2 = loadTexture("F:/C++project/LibsInclude/src/metal.png"); unsigned int GrassTextureID = loadTexture("F:/C++project/LibsInclude/src/grass.png"); //Index texture cells shaderTest.use(); //Opening depth test glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); shaderTest.setInt("ourTexture", 0); //Circular rendering while (!glfwWindowShouldClose(window)) { float currentTime = glfwGetTime(); deltaTime = currentTime - lastTime; lastTime = currentTime; //input processInput(window); glfwSetCursorPosCallback(window, mouse_callback); glfwSetScrollCallback(window, scroll_callback); //Clear color and depth buffers glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Sorting transparent objects std::map<float, glm::vec3> SortDistance; for (unsigned int k = 0; k < vegetation.size(); k++) { float distance = glm::length(camera.Position - vegetation[k]); SortDistance[distance] = vegetation[k]; } //Binding texture units and texture objects glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureId); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureId2); //Activate shader program shaderTest.use(); glm::mat4 model = glm::mat4(1.0f); glm::mat4 projection = glm::mat4(1.0f); glm::mat4 view = camera.GetViewMatrix(); projection = glm::perspective(glm::radians(camera.Zoom), (float)WINDOW_WIDTH / (float)WINDOW_HEIGHT, 0.1f, 100.0f); shaderTest.setMat4("view", view); shaderTest.setMat4("projection", projection); glBindVertexArray(VAOID); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureId); model = glm::translate(model, glm::vec3(-1.0f, 0.0f, -1.0f)); shaderTest.setMat4("model", model); glDrawArrays(GL_TRIANGLES, 0, 36); model = glm::mat4(1.0f); model = glm::translate(model, glm::vec3(1.0f, 0.0f, 0.0f)); shaderTest.setMat4("model", model); glDrawArrays(GL_TRIANGLES, 0, 36); // floor glBindVertexArray(PlaneVAOID); glBindTexture(GL_TEXTURE_2D, textureId2); shaderTest.setMat4("model", glm::mat4(1.0f)); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); //Flowers and plants glBindVertexArray(GrassVAO); glBindTexture(GL_TEXTURE_2D, GrassTextureID); //Drawing from far to near for (std::map<float, glm::vec3>::reverse_iterator it = SortDistance.rbegin(); it != SortDistance.rend(); ++it) { model = glm::mat4(1.0f); model = glm::translate(model, it->second); shaderTest.setMat4("model", model); glDrawArrays(GL_TRIANGLES, 0, 6); } //Swap color buffer glfwSwapBuffers(window); //Check whether the event is triggered glfwPollEvents(); } //Delete VBO, VAO glDeleteVertexArrays(1, &VAOID); glDeleteVertexArrays(1, &PlaneVAOID); glDeleteVertexArrays(1, &GrassVAO); glDeleteBuffers(1, &VBOId); glDeleteBuffers(1, &PlaneVBOID); glDeleteBuffers(1, &GrassVBO); //Delete program object shaderTest.deleteProgram(); //Release resources glfwTerminate(); return 0; } //Callback function, the viewport changes when the window changes void framebuffer_size_callback(GLFWwindow* window, int width, int height) { glViewport(0, 0, width, height); } //Input control void processInput(GLFWwindow* window) { float moveSpeed = 0.5f * deltaTime; if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) { glfwSetWindowShouldClose(window, true); } if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { cameraPos += cameraFront * moveSpeed; } if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { cameraPos -= cameraFront * moveSpeed; } if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp))* moveSpeed; } if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp))* moveSpeed; } } //Mouse callback void mouse_callback(GLFWwindow* window, double xpos, double ypos) { if (firstMouse) { lastX = xpos; lastY = ypos; firstMouse = false; } float xoffset = xpos - lastX; float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; camera.ProcessMouseMovement(xoffset, yoffset); } //Define cursor callback function void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { camera.ProcessMouseScroll(yoffset); } //Loading texture unsigned int loadTexture(char const* texturePath) { unsigned int textureId; glGenTextures(1, &textureId); glBindTexture(GL_TEXTURE_2D, textureId); //Set texture wrapping glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //Texture sampling method glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //Load picture information int width, height, channels; unsigned char* data = stbi_load(texturePath, &width, &height, &channels, 0); if (data) { GLenum imageFormat; if (channels == 1) { imageFormat = GL_RED; } else if (channels == 3) { imageFormat = GL_RGB; } else if (channels == 4) { imageFormat = GL_RGBA; } glTexImage2D(GL_TEXTURE_2D, 0, imageFormat, width, height, 0, imageFormat, GL_UNSIGNED_BYTE, data); //Set multi-level fade mode glGenerateMipmap(GL_TEXTURE_2D); } else { std::cout << "Failed to load Firsttexture" << std::endl; //Free memory stbi_image_free(data); } return textureId; }
Result: