OpenGL.Shader: 10-Shadow implementation-FBO generates depth bitmap

OpenGL.Shader: 10-Shadow implementation-FBO depth bitmap

 

I feel that I haven't published an article for a while. There are many reasons. The main reason is that I am a bit busy recently, and the content of the new article is a bit too many and difficult, and it takes time to fully prepare.

First of all, let’s review the FBO. Our previous simple practice in OpenGL.ES on Android: 23-Watermark Recording  introduced what FBO is. At that time, we used it for off-screen rendering, with the help of its custom buffer color space. Solve the problem of transparency conflicts. I won’t repeat the discussion here about what an FBO is. It needs to be emphasized that an FBO is just a container, which can mount many types of rendering objects. This time we need to use another new rendering object-depth buffer.

Don't talk nonsense, go directly to FrameBufferObject.hpp

#pragma once
#ifndef FRAME_BUFFER_OBJECT_HPP
#define FRAME_BUFFER_OBJECT_HPP

#define FBO_NONE  0x0000
#define FBO_RGBA  0x0001
#define FBO_DEPTH 0x0002

#include <GLES3/gl3.h>
#include "zzr_common.h"

class   FrameBufferObject
{

public:
    unsigned int   _width;
    unsigned int   _height;
    unsigned int   _fboID;
    unsigned int   _rgbaTexId;
    unsigned int   _depthTexId;
    unsigned int   _depthRboId;
    unsigned int   _type;

public:
    FrameBufferObject()
    {
        _width  = 0;
        _height = 0;
        _fboID  = 0;
        _rgbaTexId = 0;
        _depthTexId = 0;
        _depthRboId = 0;
        _type   = FBO_NONE;
    }

    int getDepthTexId() {
        return _depthTexId;
    }
    int getRgbaTexId() {
        return _rgbaTexId;
    }
    bool    setup(int w, int h, int type)
    {
        _type = static_cast<unsigned int>(type);
        _width = static_cast<unsigned int>(w);
        _height = static_cast<unsigned int>(h);

        glGenFramebuffers(1, &_fboID);
        glBindFramebuffer(GL_FRAMEBUFFER, _fboID);

        if(_type == FBO_DEPTH) {
            createDepthTexture();
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexId, 0);
        }
        if(_type == FBO_RGBA) {
            createRgbaTexture();
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _rgbaTexId, 0);
            createDepthRenderBuffer();
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRboId);
        }

        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        GLenum err = glGetError();
        while(err!=GL_NO_ERROR) {
            LOGE("fbo.setup.glTexImage2D : 0x%08x\n", err);
            err = glGetError();
        }
        return  true;
    }

    void    begin()
    {
        GLenum err = glGetError();
        while(err!=GL_NO_ERROR) {
            LOGE("fbo.begin : 0x%08x\n", err);
            err = glGetError();
        }
        glBindFramebuffer(GL_FRAMEBUFFER, _fboID);

        if(_type == FBO_DEPTH) {
            glDrawBuffers(0, GL_NONE);
            glReadBuffer(GL_NONE);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _depthTexId, 0);
        }
        if(_type == FBO_RGBA) {
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _rgbaTexId, 0);
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRboId);
        }

        GLenum fbo_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if(fbo_status!=GL_FRAMEBUFFER_COMPLETE) {
            LOGE("glCheckFramebufferStatus check err : 0x%08x\n", fbo_status);
        }
    }

    void    end()
    {
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
    }


private:
    void    createDepthTexture()
    {
        glGenTextures(1, &_depthTexId);
        glBindTexture(GL_TEXTURE_2D, _depthTexId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _width, _height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
    }

    void    createRgbaTexture()
    {
        glGenTextures(1, &_rgbaTexId);
        glBindTexture(GL_TEXTURE_2D, _rgbaTexId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    }

    void    createDepthRenderBuffer()
    {
        glGenRenderbuffers( 1, &_depthRboId );
        glBindRenderbuffer( GL_RENDERBUFFER, _depthRboId );
        glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _width, _height );
    }
};
#endif // FRAME_BUFFER_OBJECT_HPP

The basic code is as above. According to the common situation, I divide FBO into two types, FBO_DEPTH (depth texture mode) and FBO_RGBA (color texture mode). Literally, FBO_RGBA is the kind of use introduced before. The difference is that the RenderBuffer of the depth attachment point (GL_DEPTH_ATTACHMENT) is added this time. This is because we are in the 3D model this time, and the depth information cannot be ignored. After we output to the FBO, we need a buffer to retain this depth information. Such fbo can completely retain all the information we need.

For students who don’t understand, I try to comment the code related to depth——renderbuffer. The effect is as follows (draw the cube first and then the floor):

This is the effect of no depth test data, you can imagine how important this depth-renderbuffer is. Here is a brief introduction to the RenderBuffer rendering object. It is actually the same type of thing as the Texture object. They are both a container object used to save output in OpenGL. The Texture texture object is more flexible and can be passed to the shader for use, but Sometimes texture is not a panacea. Just like the current example, you need to store color information as well as depth information. At this time, Renderbuffer comes in handy.

 

So what is a depth bitmap? Let’s look at the code above. In FBO_DEPTH mode, the only difference between the depth bitmap created by the createDepthTexture function and createRgbaTexture is the parameters of glTexImage2D. Don’t look at these parameters. I really stepped on these parameters. These pits are not pits. I searched the entire network, including google, and there was no clear answer. I will tell you what I think. If you can understand it correctly, please leave a message for guidance.

void    createDepthTexture()
{
        glGenTextures(1, &_depthTexId);
        glBindTexture(GL_TEXTURE_2D, _depthTexId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _width, _height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
        //LOGE("createDepthTexture check err 4 : 0x%08x\n", glGetError());
}

In the OpenGL Wiki ( https://www.khronos.org/opengl/wiki/GLAPI/glTexImage2D ), the parameter description of glTexImage2D, the third parameter internalFormat is described as follows:

internalFormat
Specifies the number of color components in the texture. Must be one of base internal formats given in Table 1, one of the sized internal formats given in Table 2, or one of the compressed internal formats given in Table 3, below.

Then point to the three Tables below, and read the three tables carefully. The important point is that Tables 2 and 3 are actually Base Internal Formats that point to Table 1. Under the GL ES clipping on the mobile terminal, only the final GL_DEPTH_COMPONENT is retained, and the rest GL_DEPTH_COMPONENT16 / GL_DEPTH_COMPONENT24 / GL_DEPTH_COMPONENT32F are not applicable, no matter how many bit depths the current GPU supports (query through glGetIntegerv(GL_DEPTH_BITS, &depth_bits)), how many bit depths are configured for EGL (EglCore initialization), always use GLCOMPONENT_DEPTH_COMPONENT_DEPTH

Then the penultimate parameter Type is to determine what to use according to the bit depth set by EGL, 8 bits = GL_UNSIGNED_BYTE, 16 bits = GL_UNSIGNED_SHORT;

Well, after the correct configuration of depth_texture, we can save the output depth test to the texture, and output it as a texture to the screen.

Everyone pay attention to the depth bitmap in the upper left corner. At the beginning, we are close to the floor from the perspective. The closer we are to the front, the closer the depth value is to 0. Then I saw the outline of the cube, and the floor behind the cube also showed depth. (Look carefully, the gray is very light) Then release other parts of the code for easy understanding.

void ShadowFBORender::renderOnDraw(double elpasedInMilliSec)
{
    if (mEglCore == NULL || mWindowSurface == NULL) {
        LOGW("Skipping drawFrame after shutdown");
        return;
    }
    mWindowSurface->makeCurrent();

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glViewport(0,0, mViewWidth, mViewHeight);
    lightCube.render(mCamera3D);
    land.render(mCamera3D);

    renderDepthFBO();

    mWindowSurface->swapBuffers();
}

void ShadowFBORender::renderDepthFBO() {
    depthFBO.begin();
    {
        glEnable(GL_DEPTH_TEST);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
        glClearDepthf(1.0f);
        lightCube.render(mCamera3D);
        land.render(mCamera3D);
    }
    depthFBO.end();

    pip.setTextureId(depthFBO.getDepthTexId()); // 深度纹理
    //pip.setTextureId(depthFBO.getRgbaTexId());// 彩色纹理
    pip.render();
}

pip->PIPicture is a kind of object that uses orthogonal projection to draw texture on the screen. It is very simple and simple. Reference code  https://github.com/MrZhaozhirong/NativeCppApp  -> ShadowFBORender.cpp / FrameBufferObject.hpp / PIPicture.hpp

In the next chapter, we will discuss how to use depth bitmaps to achieve light shadows.

Guess you like

Origin blog.csdn.net/a360940265a/article/details/100096152