ofFboを使わずにオフスクリーンレンダリング
FrameBufferObjectとは
FrameBufferObjectとはColorBuffer, DepthBuffer, StencileBufferを内包しているBufferのことです. これらのbufferに対してTextureやRenderBufferをアタッチしてあげることで利用することが出来ます. この図がわかりやすい→
サンプル
さきにコードを張ります.
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; }