OpenGL.Shader: implementación de 10 sombras: FBO genera un mapa de bits de profundidad

OpenGL.Shader: implementación de 10 sombras-mapa de bits de profundidad FBO

 

Siento que no he publicado un artículo por un tiempo, hay muchas razones. La razón principal es que estoy un poco ocupado recientemente, y el contenido del nuevo artículo es demasiado complicado y difícil, y lleva tiempo prepararlo.

En primer lugar, revisemos el FBO. Nuestra práctica anterior simple en OpenGL.ES en Android: 23-Watermark Recording  introdujo lo que es FBO. En ese momento, lo usamos para el renderizado fuera de la pantalla, con la ayuda de su espacio de color de búfer personalizado. Resuelve el problema de los conflictos de transparencia. No repetiré la discusión aquí sobre qué es un FBO. Es necesario enfatizar que un FBO es solo un contenedor, que puede montar muchos tipos de objetos de renderizado. Esta vez necesitamos usar otro búfer de profundidad de objeto de renderizado nuevo.

No digas tonterías, ve directamente a 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

El código básico es el anterior. De acuerdo con la situación común, divido FBO en dos tipos, FBO_DEPTH (modo de textura de profundidad) y FBO_RGBA (modo de textura de color). Literalmente, FBO_RGBA es el tipo de uso introducido antes. La diferencia es que el RenderBuffer del punto de conexión de profundidad (GL_DEPTH_ATTACHMENT) se agrega esta vez. Esto se debe a que esta vez estamos en el modelo 3D y la información de profundidad no se puede ignorar. Después de enviar la salida al FBO, necesitamos un búfer para retener esta información de profundidad. Tal fbo puede retener completamente toda la información que necesitamos.

Para los estudiantes que no entienden, trato de comentar el código relacionado con la profundidad, renderbuffer. El efecto es el siguiente (dibuja primero el cubo y luego el suelo):

Este es el efecto de no tener datos de prueba de profundidad, puede imaginar lo importante que es este búfer de renderizado de profundidad. Aquí hay una breve introducción al objeto de renderizado RenderBuffer. En realidad, es el mismo tipo de cosa que el objeto Texture. Ambos son un objeto contenedor que se usa para guardar la salida en OpenGL. El objeto Texture es más flexible y se puede pasar al sombreador para su uso, pero A veces, la textura no es una panacea. Al igual que en el ejemplo actual, es necesario almacenar la información de color y la información de profundidad. En este momento, Renderbuffer es útil.

 

Entonces, ¿qué es un mapa de bits de profundidad? Veamos el código anterior. En el mapa de bits de profundidad creado por la función createDepthTexture en modo FBO_DEPTH, la única diferencia con createRgbaTexture son los parámetros de glTexImage2D. No mires estos parámetros. Realmente pisé estos parámetros. Estos pozos no son pozos. Busqué en toda la red, incluido Google, y no obtuve una respuesta clara. Te diré lo que pienso. Si lo entiendes correctamente, deja un mensaje como guía.

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 ()); }








En OpenGL Wiki ( https://www.khronos.org/opengl/wiki/GLAPI/glTexImage2D ), la descripción del parámetro de glTexImage2D, el tercer parámetro internalFormat se describe de la siguiente manera:

internalFormat
Especifica el número de componentes de color en la textura. Debe ser uno de los formatos internos básicos que se indican en la Tabla 1, uno de los formatos internos de tamaño que se indican en la Tabla 2 o uno de los formatos internos comprimidos que se indican en la Tabla 3, a continuación.

Luego, señale las siguientes tres Tablas y lea las tres tablas con atención. El punto importante es que las Tablas 2 y 3 son en realidad Formatos internos base que apuntan a la Tabla 1. Bajo el corte GL ES del terminal móvil, solo se retiene el GL_DEPTH_COMPONENT final y el resto GL_DEPTH_COMPONENT16 / GL_DEPTH_COMPONENT24 / GL_DEPTH_COMPONENT32F no son aplicables, sin importar cuántas profundidades de bits admita la GPU actual (consulta a través de glGetIntegerv (GL_DEPTH_BITS, & depth_bits)), cuántas profundidades de bits siempre se configuran para el uso de EGLEPTH_ONCOM

Luego, el penúltimo parámetro Tipo es determinar qué usar de acuerdo con la profundidad de bits establecida por EGL, 8 bits = GL_UNSIGNED_BYTE, 16 bits = GL_UNSIGNED_SHORT;

Bueno, después de la configuración correcta de depth_texture, podemos guardar la prueba de profundidad de salida en la textura y enviarla como una textura a la pantalla.

Todos presten atención al mapa de bits de profundidad en la esquina superior izquierda. Al principio, estamos cerca del piso desde la perspectiva. Cuanto más cerca estemos del frente, más cerca del valor de profundidad es 0. Luego vi el contorno del cubo, y el piso detrás del cubo también mostró profundidad. (Mire con atención, el gris es muy claro) Luego suelte otras partes del código para una fácil comprensión.

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 es un tipo de objeto que utiliza proyección ortogonal para dibujar textura en la pantalla, es muy simple y simple. Código de referencia  https://github.com/MrZhaozhirong/NativeCppApp  -> ShadowFBORender.cpp / FrameBufferObject.hpp / PIPicture.hpp

En el próximo capítulo, discutiremos cómo usar mapas de bits de profundidad para lograr sombras claras.

Supongo que te gusta

Origin blog.csdn.net/a360940265a/article/details/100096152
Recomendado
Clasificación