• 【OpenGL】学习笔记#6


    有了光照,就应该有阴影。所以,今天我们来整个阴影。

    说到阴影,我们自然而然地会想到初中里,关于阴影的产生原因:光在均匀介质中沿直线传播。

    所以,我们暂且把我们的光视为平行光,从纹理角度来考虑阴影。一个显而易见的想法是:

    判断一个点距离光源的远近:

    设此点为A,设光源为L,设A与L距离为x,设射线LA交到的第一个点是B,那么,如果线段LB < LA,说明这个点被挡住了,那么它就处于阴影之下。

    话是这么说,可是这么实现呢?让我们来一步步分析。

    首先,我们要知道要获取哪些,很显然只需要两个信息:一个是LB,一个是LA。LA很好取得,已知两点坐标只需要算一下距离即可。那么LB怎么求呢?

    提示:深度测试

    相信已经有吊大的同学想到了,深度测试可以自动筛选出距离摄像机最近的点(深度z最小的点)来为我们绘制片元。这样,每个片元就保存了距离其最近的点。

    说到片元大家一定都懂了,还记得我们之前说的帧缓冲吗?我们只需要利用一下帧缓冲和深度测试即可。

    有的不认真看前面的同学就会问了,那怎么才能利用深度测试来测距离光源最近的深度大小呢?不要忘记我们之前学到的矩阵。这次只需要让MVP矩阵稍微更改一下,M不变,V,摄像机矩阵View,只需要将lookAt里摄像机的坐标改为光源的坐标。P,由于是平行光,所以阴影使用正交矩阵,由于要映射全部顶点,所以范围需要如此设置(详见下方代码)。

    然后,在帧缓冲的着色器里我们gl_Position = depthMVP * (vertex_modelspace);就可以愉快地获取光源摄像机视角里的顶点了。在片元着色器里只输出低精度深度,这将大大提高我们的速度。

    有了这张深度纹理之后,把它扔到正常着色器里(用于渲染场景的着色器)。对于正常顶点着色器,只需要对于每个顶点也乘上depthMVP得到光源摄像机处的坐标depthVertex,然后显然Z轴就是距离LA(不是真正的距离,但是足够我们判断了),LB就是深度纹理中UV坐标为depthVertex.xy的z属性。由此,即可简单判断。

    总结一下,流程为:

    1.获得以光源为摄像机的正交视角下的一张包括各个片元的深度的纹理A

    2.获得以光源为摄像机的正交视角下的所有顶点的深度B

    3.如果A<B,则该顶点被遮挡,对于该片元绘制阴影

    彩蛋:如果把正交ortho改成透视perspective会怎样?文末揭晓。

    接下来是代码,由于这一节没有包含新的API和新结构,代码我就全部放上去了。(注意,帧缓冲附件中必须带一个颜色附件,否则会报错,我被这个问题坑了好几个小时)

    // Include standard headers
    #include <stdio.h>
    #include <stdlib.h>
    #include <vector>
    
    // Include GLEW
    #include <GL/glew.h>
    
    // Include GLFW
    #include <GLFW/glfw3.h>
    GLFWwindow* window;
    
    // Include GLM
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    using namespace glm;
    
    #include <common/shader.hpp>
    #include <common/texture.hpp>
    #include <common/controls.hpp>
    #include <common/objloader.hpp>
    #include <common/vboindexer.hpp>
    
    int main( void )
    {
        // Initialise GLFW
        if( !glfwInit() )
        {
            fprintf( stderr, "Failed to initialize GLFW
    " );
            getchar();
            return -1;
        }
    
        glfwWindowHint(GLFW_SAMPLES, 4);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
        // Open a window and create its OpenGL context
        window = glfwCreateWindow( 1024, 768, "Shadows", NULL, NULL);
        if( window == NULL ){
            fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.
    " );
            getchar();
            glfwTerminate();
            return -1;
        }
        glfwMakeContextCurrent(window);
        
        // We would expect width and height to be 1024 and 768
        int windowWidth = 1024;
        int windowHeight = 768;
        // But on MacOS X with a retina screen it'll be 1024*2 and 768*2, so we get the actual framebuffer size:
        glfwGetFramebufferSize(window, &windowWidth, &windowHeight);
    
        // Initialize GLEW
        glewExperimental = true; // Needed for core profile
        if (glewInit() != GLEW_OK) {
            fprintf(stderr, "Failed to initialize GLEW
    ");
            getchar();
            glfwTerminate();
            return -1;
        }
    
        // Ensure we can capture the escape key being pressed below
        glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
        // Hide the mouse and enable unlimited mouvement
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        
        // Set the mouse at the center of the screen
        glfwPollEvents();
        glfwSetCursorPos(window, 1024/2, 768/2);
    
        // Dark blue background
        glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
    
        // Enable depth test
        glEnable(GL_DEPTH_TEST);
    
        // Accept fragment if it closer to the camera than the former one
        glDepthFunc(GL_LESS); 
    
        // Cull triangles which normal is not towards the camera
        glEnable(GL_CULL_FACE);
    
        GLuint VertexArrayID;
        glGenVertexArrays(1, &VertexArrayID);
        glBindVertexArray(VertexArrayID);
    
        // Create and compile our GLSL program from the shaders
        GLuint depthProgramID = LoadShaders( "DepthRTT.vertexshader", "DepthRTT.fragmentshader" );
    
        // Get a handle for our "MVP" uniform
        GLuint depthMatrixID = glGetUniformLocation(depthProgramID, "depthMVP");
    
        // Load the texture
        GLuint Texture = loadDDS("uvmap.DDS");
        
        // Read our .obj file
        std::vector<glm::vec3> vertices;
        std::vector<glm::vec2> uvs;
        std::vector<glm::vec3> normals;
        bool res = loadOBJ("room_thickwalls.obj", vertices, uvs, normals);
    
        std::vector<unsigned short> indices;
        std::vector<glm::vec3> indexed_vertices;
        std::vector<glm::vec2> indexed_uvs;
        std::vector<glm::vec3> indexed_normals;
        indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs, indexed_normals);
    
        // Load it into a VBO
    
        GLuint vertexbuffer;
        glGenBuffers(1, &vertexbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
        glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW);
    
        GLuint uvbuffer;
        glGenBuffers(1, &uvbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
        glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW);
    
        GLuint normalbuffer;
        glGenBuffers(1, &normalbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
        glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW);
    
        // Generate a buffer for the indices as well
        GLuint elementbuffer;
        glGenBuffers(1, &elementbuffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);
    
    
        // ---------------------------------------------
        // Render to Texture - specific code begins here
        // ---------------------------------------------
    
        // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
        GLuint FramebufferName = 0;
        glGenFramebuffers(1, &FramebufferName);
        glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
    
        // Depth texture. Slower than a depth buffer, but you can sample it later in your shader
        GLuint depthTexture;
        glGenTextures(1, &depthTexture);
        glBindTexture(GL_TEXTURE_2D, depthTexture);
        glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, 1024, 1024, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
             
        glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);
        
        GLuint renderedTexture;
        glGenTextures(1, &renderedTexture);
        
        // "Bind" the newly created texture : all future texture functions will modify this texture
        glBindTexture(GL_TEXTURE_2D, renderedTexture);
    
        // Give an empty image to OpenGL ( the last "0" means "empty" )
        glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, windowWidth, windowHeight, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);
    
        // Poor filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        
        glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
    
    
        // No color output in the bound framebuffer, only depth.
        //glDrawBuffer(GL_NONE);
        
        GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
        glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers
    
    
        // Always check that our framebuffer is ok
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
            return false;
    
        
        // The quad's FBO. Used only for visualizing the shadowmap.
        static const GLfloat g_quad_vertex_buffer_data[] = { 
            -1.0f, -1.0f, 0.0f,
             1.0f, -1.0f, 0.0f,
            -1.0f,  1.0f, 0.0f,
            -1.0f,  1.0f, 0.0f,
             1.0f, -1.0f, 0.0f,
             1.0f,  1.0f, 0.0f,
        };
    
        GLuint quad_vertexbuffer;
        glGenBuffers(1, &quad_vertexbuffer);
        glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW);
    
        // Create and compile our GLSL program from the shaders
        GLuint quad_programID = LoadShaders( "Passthrough.vertexshader", "SimpleTexture.fragmentshader" );
        GLuint texID = glGetUniformLocation(quad_programID, "texture_uniform");
    
    
        // Create and compile our GLSL program from the shaders
        GLuint programID = LoadShaders( "ShadowMapping.vertexshader", "ShadowMapping.fragmentshader" );
    
        // Get a handle for our "myTextureSampler" uniform
        GLuint TextureID  = glGetUniformLocation(programID, "myTextureSampler");
    
        // Get a handle for our "MVP" uniform
        GLuint MatrixID = glGetUniformLocation(programID, "MVP");
        GLuint ViewMatrixID = glGetUniformLocation(programID, "V");
        GLuint ModelMatrixID = glGetUniformLocation(programID, "M");
        GLuint DepthBiasID = glGetUniformLocation(programID, "DepthBiasMVP");
        GLuint ShadowMapID = glGetUniformLocation(programID, "shadowMap");
        
        // Get a handle for our "LightPosition" uniform
        GLuint lightInvDirID = glGetUniformLocation(programID, "LightInvDirection_worldspace");
    
    
        
        do{
    
            // Render to our framebuffer
            glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
            glViewport(0,0,1024,1024); // Render on the whole framebuffer, complete from the lower left corner to the upper right
    
            // We don't use bias in the shader, but instead we draw back faces, 
            // which are already separated from the front faces by a small distance 
            // (if your geometry is made this way)
            glEnable(GL_CULL_FACE);
            glCullFace(GL_BACK); // Cull back-facing triangles -> draw only front-facing triangles
    
            // Clear the screen
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            // Use our shader
            glUseProgram(depthProgramID);
    
            glm::vec3 lightInvDir = glm::vec3(0.5f,2,2);
    
            // Compute the MVP matrix from the light's point of view
            glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20);
            glm::mat4 depthViewMatrix = glm::lookAt(lightInvDir, glm::vec3(0,0,0), glm::vec3(0,1,0));
            // or, for spot light :
            //glm::vec3 lightPos(5, 20, 20);
            //glm::mat4 depthProjectionMatrix = glm::perspective<float>(45.0f, 1.0f, 2.0f, 50.0f);
            //glm::mat4 depthViewMatrix = glm::lookAt(lightPos, lightPos-lightInvDir, glm::vec3(0,1,0));
    
            glm::mat4 depthModelMatrix = glm::mat4(1.0);
            glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix;
    
            // Send our transformation to the currently bound shader, 
            // in the "MVP" uniform
            glUniformMatrix4fv(depthMatrixID, 1, GL_FALSE, &depthMVP[0][0]);
    
            // 1rst attribute buffer : vertices
            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
            glVertexAttribPointer(
                0,  // The attribute we want to configure
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                0,                  // stride
                (void*)0            // array buffer offset
            );
    
            // Index buffer
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
    
            // Draw the triangles !
            glDrawElements(
                GL_TRIANGLES,      // mode
                indices.size(),    // count
                GL_UNSIGNED_SHORT, // type
                (void*)0           // element array buffer offset
            );
    
            glDisableVertexAttribArray(0);
    
    
    
            // Render to the screen
            glBindFramebuffer(GL_FRAMEBUFFER, 0);
            glViewport(0,0,windowWidth,windowHeight); // Render on the whole framebuffer, complete from the lower left corner to the upper right
    
            glEnable(GL_CULL_FACE);
            glCullFace(GL_BACK); // Cull back-facing triangles -> draw only front-facing triangles
    
            // Clear the screen
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            // Use our shader
            glUseProgram(programID);
    
            // Compute the MVP matrix from keyboard and mouse input
            computeMatricesFromInputs();
            glm::mat4 ProjectionMatrix = getProjectionMatrix();
            glm::mat4 ViewMatrix = getViewMatrix();
            //ViewMatrix = glm::lookAt(glm::vec3(14,6,4), glm::vec3(0,1,0), glm::vec3(0,1,0));
            glm::mat4 ModelMatrix = glm::mat4(1.0);
            glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
            
            glm::mat4 biasMatrix(
                0.5, 0.0, 0.0, 0.0, 
                0.0, 0.5, 0.0, 0.0,
                0.0, 0.0, 0.5, 0.0,
                0.5, 0.5, 0.5, 1.0
            );
    
            glm::mat4 depthBiasMVP = biasMatrix*depthMVP;
    
            // Send our transformation to the currently bound shader, 
            // in the "MVP" uniform
            glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
            glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, &ModelMatrix[0][0]);
            glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, &ViewMatrix[0][0]);
            glUniformMatrix4fv(DepthBiasID, 1, GL_FALSE, &depthBiasMVP[0][0]);
    
            glUniform3f(lightInvDirID, lightInvDir.x, lightInvDir.y, lightInvDir.z);
    
            // Bind our texture in Texture Unit 0
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, Texture);
            // Set our "myTextureSampler" sampler to use Texture Unit 0
            glUniform1i(TextureID, 0);
    
            glActiveTexture(GL_TEXTURE1);
            glBindTexture(GL_TEXTURE_2D, depthTexture);
            glUniform1i(ShadowMapID, 1);
    
            // 1rst attribute buffer : vertices
            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
            glVertexAttribPointer(
                0,                  // attribute
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                0,                  // stride
                (void*)0            // array buffer offset
            );
    
            // 2nd attribute buffer : UVs
            glEnableVertexAttribArray(1);
            glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
            glVertexAttribPointer(
                1,                                // attribute
                2,                                // size
                GL_FLOAT,                         // type
                GL_FALSE,                         // normalized?
                0,                                // stride
                (void*)0                          // array buffer offset
            );
    
            // 3rd attribute buffer : normals
            glEnableVertexAttribArray(2);
            glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
            glVertexAttribPointer(
                2,                                // attribute
                3,                                // size
                GL_FLOAT,                         // type
                GL_FALSE,                         // normalized?
                0,                                // stride
                (void*)0                          // array buffer offset
            );
    
            // Index buffer
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
    
            // Draw the triangles !
            glDrawElements(
                GL_TRIANGLES,      // mode
                indices.size(),    // count
                GL_UNSIGNED_SHORT, // type
                (void*)0           // element array buffer offset
            );
    
            glDisableVertexAttribArray(0);
            glDisableVertexAttribArray(1);
            glDisableVertexAttribArray(2);
    
    
            // Optionally render the shadowmap (for debug only)
    
            // Render only on a corner of the window (or we we won't see the real rendering...)
            glViewport(0,0,512,512);
    
            // Use our shader
            glUseProgram(quad_programID);
    
            // Bind our texture in Texture Unit 0
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, depthTexture);
            // Set our "renderedTexture" sampler to use Texture Unit 0
            glUniform1i(texID, 0);
    
            // 1rst attribute buffer : vertices
            glEnableVertexAttribArray(0);
            glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
            glVertexAttribPointer(
                0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
                3,                  // size
                GL_FLOAT,           // type
                GL_FALSE,           // normalized?
                0,                  // stride
                (void*)0            // array buffer offset
            );
    
            // Draw the triangle !
            // You have to disable GL_COMPARE_R_TO_TEXTURE above in order to see anything !
            //glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles
            glDisableVertexAttribArray(0);
    
    
            // Swap buffers
            glfwSwapBuffers(window);
            glfwPollEvents();
    
        } // Check if the ESC key was pressed or the window was closed
        while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
               glfwWindowShouldClose(window) == 0 );
    
        // Cleanup VBO and shader
        glDeleteBuffers(1, &vertexbuffer);
        glDeleteBuffers(1, &uvbuffer);
        glDeleteBuffers(1, &normalbuffer);
        glDeleteBuffers(1, &elementbuffer);
        glDeleteProgram(programID);
        glDeleteProgram(depthProgramID);
        glDeleteProgram(quad_programID);
        glDeleteTextures(1, &Texture);
        glDeleteTextures(1, &renderedTexture);
    
        glDeleteFramebuffers(1, &FramebufferName);
        glDeleteTextures(1, &depthTexture);
        glDeleteBuffers(1, &quad_vertexbuffer);
        glDeleteVertexArrays(1, &VertexArrayID);
    
        // Close OpenGL window and terminate GLFW
        glfwTerminate();
    
        return 0;
    }
    main.cpp

    着色器代码相当简单,这里就不放了。感兴趣的读者可以看下一篇,我将会在下一篇整体分析一个OpenGL程序应当具备的基本结构和一些具体问题。

    附程序效果:

     别问为什么五颜六色,问就是我把猴子的贴图不小心放进文件夹了,别说还挺好看

    彩蛋答案:聚光灯

  • 相关阅读:
    读取组合单元格
    Spire.XLS:一款Excel处理神器
    linq
    LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别
    合并单元格
    web sec / ssd / sshd
    linux——cat之查看cpu信息、显示终端、校验内存.............
    MATLAB mcr lib的环境变量书写
    Linux查看库依赖方法
    判断当前所使用python的版本和来源
  • 原文地址:https://www.cnblogs.com/dudujerry/p/13586665.html
Copyright © 2020-2023  润新知