ofFboを使わずにオフスクリーンレンダリング

FrameBufferObjectとは

FrameBufferObjectとはColorBuffer, DepthBuffer, StencileBufferを内包しているBufferのことです. これらのbufferに対してTextureやRenderBufferをアタッチしてあげることで利用することが出来ます. この図がわかりやすい→

qiita.com

サンプル

さきにコードを張ります.

GLuint frameBuffer, depthBuffer;
GLuint colorTexture;
void ofApp::createFrameBuffer() {
    //FrameBufferを作成
    glGenFramebuffers(1, & frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    ofVec2f res = ofVec2f(ofGetWidth(), ofGetHeight());

    glGenTextures(1, &colorTexture);
    glBindTexture(GL_TEXTURE_2D, colorTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, res.x, res.y,
        0, GL_RGB, GL_FLOAT, NULL);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    //----FrameBufferにtextureをアタッチ(ColorBufferとしてアタッチされているTextureBuffer)
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);

    GLuint attachment = GL_COLOR_ATTACHMENT0;
    glDrawBuffers(1, &attachment);

    glGenRenderbuffers(1, &depthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
    //glTexImage2D的な立ち位置
    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, res.x, res.y);
    //attach
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

}

FrameBufferObjectの周り

まず初めにFrameBufferObjectをglGenFramebuffers(1, & frameBuffer)作成します.その後,生成したFrameBufferObjectをbind状態(有効状態)にします.こうすることによって今後する手続きが現在注目しているFrameBufferObjectへ行われることになります.

ColorBuffer

次にColorBufferを作っていきます.これは何も難しいことはなく2DのTextureを生成する手順と変わりはありません.ただ画像から画素の値を読み込むわけではないので最後の引数はnullにしています. Texture周りがよくわからない場合はここら辺を参考に

https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml http://wisdom.sakura.ne.jp/system/opengl/gl23.html http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20040914

ColorBufferにアタッチするためのTextureが生成出来たらglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)でアタッチしていきます.そしてアタッチが出来たら今度はFrameBufferObjectにアタッチするためglBindRenderbuffer(GL_RENDERBUFFER, depthBuffer)を呼んでます.

glDrawBuffersについて説明を飛ばしましたが,これは主にMRT(マルチレンダーターゲット)という技術を使用するときに重要になるます.引数内で指定したバッファにshader内で行ったレンダリング結果が入るようになります.

DepthBuffer

最後にDepthを情報を格納するためのBufferです.これがないとglEnable(GL_DEPTH_TEST)とはを呼んでも参照する深度情報がないので奥行きの関係がおかしくなります. ますはRenderBufferをDepthBufferにアタッチしてみようと思います.まずはRenderBufferObjectの作成glGenRenderbuffers(1, &depthBuffer)そしてbindした後, glRenderbufferStorageという関数を呼びます.これは何をしているかというとTextureでいうTexImage2D的な立ち位置でどんな構成のBufferを作るかっていう設定をしています.今回は深度を格納したいのでGL_DEPTH_COMPONENTを選択します. 最後にこれをFramebufferに関連づけるための関数glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer)を呼んでいます.

***追記: 理由ははっきりとわかりませんが,TextureをDepthBufferとしたら描画がされませんでした.

オフスクリーンレンダリング

1Pass目で作成したFrameBufferObjectにレンダリングをしてShader内部でColorBufferへレンダリング結果を書きこんでいます.そして2pass目そのTextureを用いて絵を出しています.今回は2Pass目で簡単に色を反転させてみましたオフスクリーンレンダリングの利点としてこのようなマルチパスがあります.この技術を使うことでぼかしや発光,映り込みなど色々なことが出来るので重要な技術です.

void ofApp::setup(){
    createFrameBuffer();
    mesh = ofSpherePrimitive(100, 16).getMesh();
    glEnable(GL_DEPTH_TEST);
    geometryPass.load("shaders/geom");
    renderPass.load("shaders/render");

    float w = 1.0;
    float h = 1.0;
    quad.setMode(OF_PRIMITIVE_TRIANGLE_FAN);
    quad.addVertex(ofVec3f(w, h, 0.0));
    quad.addTexCoord(ofVec2f(1.0f, 1.0f));
    quad.addVertex(ofVec3f(w, -h, 0.0));
    quad.addTexCoord(ofVec2f(1.0f, 0.0f));
    quad.addVertex(ofVec3f(-w, -h, 0.0));
    quad.addTexCoord(ofVec2f(0.0f, 0.0f));
    quad.addVertex(ofVec3f(-w, h, 0.0));
    quad.addTexCoord(ofVec2f(0.0f, 1.0f));

    cam.lookAt(ofVec3f(0.0, 0.0, 0.0), ofVec3f(0.0, 1.0, 0.0));
}
//--------------------------------------------------------------
void ofApp::draw(){
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    geometryPass.begin();
    cam.begin();
    ofMatrix4x4 proj, view, model;
    view = ofGetCurrentViewMatrix();
    proj = cam.getProjectionMatrix();
    cam.end();
    geometryPass.setUniformMatrix4f("model", model);
    geometryPass.setUniformMatrix4f("view", view);
    geometryPass.setUniformMatrix4f("proj", proj);
    mesh.draw();
    geometryPass.end();
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    
    //-----------------------------------------
    renderPass.begin();
    renderPass.setUniformTexture("colorBuffer", GL_TEXTURE_2D, colorTexture, 0);
    quad.draw(OF_MESH_FILL);
    renderPass.end();
}

layout (location = 0) out vec4 colorTextureの部分で先のglDrawBuffersによってレンダリング結果がColoBuffer(Texture)に格納されています.

//vert
#version 330 core
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

in vec3 position;
in vec4 color;

out vec4 Color;

void main(){
  gl_Position = proj * view * model * vec4(position, 1.0);
  Color = vec4(1.0, 0.0, 0.0, 1.0);
}

//-------------------------------
//frag
#version 330 core
layout (location = 0) out vec4 colorTexture;

in vec4 Color;

void main(){
  colorTexture = Color;
}
//vert
#version 330 core

in vec3 position;
in vec2 texcoord;

out vec2 TexCoord;
void main(){
  TexCoord = texcoord;
  gl_Position = vec4(position, 1.0);
}
//-----------------------------
//frag
#version 330 core
uniform sampler2D colorBuffer;
in vec2 TexCoord;
out vec4 c;

void main(){
  c = texture(colorBuffer, TexCoord);
  c.rgb = vec3(1.0) - c.rgb;
}