OpenGL.Shader:10-シャドウの実装-FBO深度ビットマップ
しばらく記事を公開していない気がしますが、理由はたくさんあります。主な理由は、最近少し忙しくて、新記事の内容が少なすぎて難しすぎて、準備に時間がかかるからです。
まず、FBOを確認しましょう。Android上のOpenGL.ESでの以前の簡単な練習:23-Watermark Recording は、FBOとは何かを紹介しました。当時、カスタムバッファーの色空間を利用して、FBOをオフスクリーンレンダリングに使用していました。透明性の競合の問題を解決します。ここでは、FBOとは何かについての説明は繰り返しません。FBOは単なるコンテナであり、さまざまな種類のレンダリングオブジェクトをマウントできることを強調する必要があります。今回は、別の新しいレンダリングオブジェクト深度バッファーを使用する必要があります。
ナンセンスな話をしないで、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
基本的なコードは上記のとおりです。一般的な状況に応じて、FBOをFBO_DEPTH(深度テクスチャモード)とFBO_RGBA(カラーテクスチャモード)の2つのタイプに分けます。文字通り、FBO_RGBAは以前に紹介した種類の使用法です。違いは、今回は深度アタッチメントポイント(GL_DEPTH_ATTACHMENT)のRenderBufferが追加されることです。これは、今回は3Dモデルであり、深度情報を無視できないためです。FBOに出力した後、この深度情報を保持するためのバッファが必要です。このようなfboは、必要なすべての情報を完全に保持できます。
理解できない学生のために、私は深さに関連するコードにコメントしようとしています-renderbuffer。効果は次のとおりです(最初にキューブを描画し、次に床を描画します)。
これは深度テストデータがない場合の影響です。この深度レンダリングバッファの重要性を想像できます。RenderBufferレンダリングオブジェクトの簡単な紹介です。これは実際にはTextureオブジェクトと同じタイプのものです。どちらもOpenGLで出力を保存するために使用されるコンテナオブジェクトです。Textureテクスチャオブジェクトはより柔軟性があり、シェーダーに渡して使用できますが、テクスチャが万能薬ではない場合もあります。現在の例と同様に、深度情報だけでなく色情報も保存する必要があります。現時点では、Renderbufferが便利です。
では、深度ビットマップとは何ですか?上記のコードを見てみましょう。FBO_DEPTHモードでcreateDepthTexture関数によって作成された深度ビットマップでは、createRgbaTextureとの唯一の違いはglTexImage2Dのパラメーターです。これらのパラメーターは見ないでください。実際にこれらのパラメーターを踏んだのです。これらのピットはピットではありません。グーグルも含めてネットワーク全体を検索しましたが、明確な答えはありません。私の考えをお伝えします。正しく理解できたら、メッセージを残してください。
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()); }
OpenGL Wiki(https://www.khronos.org/opengl/wiki/GLAPI/glTexImage2D)では、glTexImage2Dのパラメーターの説明、3番目のパラメーターinternalFormatは次のように説明されています。
internalFormat
テクスチャ内のカラーコンポーネントの数を指定します。表1に示されている基本内部フォーマットのいずれか、表2に示されているサイズ変更された内部フォーマットのいずれか、または以下の表3に示されている圧縮内部フォーマットのいずれかである必要があります。
次に、以下の3つの表をポイントし、3つの表を注意深く読んでください。重要な点は、表2と3は、実際には表1を指す基本内部形式であるということです。モバイル端末のGL ESクリッピングでは、最後のGL_DEPTH_COMPONENTのみが保持され、残りは保持されます。 GL_DEPTH_COMPONENT16 / GL_DEPTH_COMPONENT24 / GL_DEPTH_COMPONENT32Fは、現在のGPUがビット深度の数(glGetIntegerv(GL_DEPTH_BITS、&depth_bits)クエリを介して)、EGL構成ビット深度(EglCore初期化)をサポートしている場合でも、常にGLCOMPONENT_Pを使用します。
次に、最後から2番目のパラメータTypeは、EGLによって設定されたビット深度に従って何を使用するかを決定することです。8ビット= GL_UNSIGNED_BYTE、16ビット= GL_UNSIGNED_SHORT;
さて、depth_textureを正しく設定した後、出力深度テストをテクスチャに保存し、それをテクスチャとして画面に出力できます。
誰もが左上隅の深度ビットマップに注目しています。最初は、視点から見て床に近いです。正面に近づくほど、深度値は0に近くなります。それから私は立方体の輪郭を見ました、そして立方体の後ろの床も深さを示しました。(注意深く見てください、灰色は非常に明るいです)次に、理解しやすいようにコードの他の部分を解放します。
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は、直交投影を使用して画面にテクスチャを描画するオブジェクトの一種で、非常にシンプルでシンプルです。参照コード https://github.com/MrZhaozhirong/NativeCppApp->ShadowFBORender.cpp / FrameBufferObject.hpp / PIPicture.hpp
次の章では、深度ビットマップを使用して明るい影を実現する方法について説明します。