GLSL #8: Compute Shaderを使った画像処理
目標
前回(下記参照)までの実装を組み合わせて用いることで,3DCGを描画した結果に対してCompute Shaderを使って画像処理(Sobelフィルタによるエッジ検出)を行うプログラムを作成する.
実装環境
- Windows 10 64bit
- Visual Studio 2015
- GLEW 3.2.1
- 設定方法はこちらを参照
- GLEW 1.13.0
- 設定方法はこちらを参照
- GLM
- 設定方法はこちらを参照
- DevIL
- 設定方法はこちらを参照
レンダリングの流れ
- パス1:第6回のパス1と同じ
- パス2:Compute Shaderに1の描画結果を渡し,Sobelフィルタをかける
サンプルプログラム
シェーダプログラムに関しては,ほとんどがこちらの丸コピです. 前回までに作成した以下のプログラムは変更せずそのまま利用します.
- window.h
- window.cpp
- model.h
- model.cpp
- modelTex.h
- modelTex.cpp
- shaderUtil.h
- execute関数を仮想関数としていたが,処理によって入出力が変わるため,削除した
- shaderUtil.cpp
main.cpp
// // main.cpp // #include <glm/glm.hpp> #include <glm/ext.hpp> #include "OpenGLWrapper/window.h" #include "OpenGLWrapper/shaderUtil.h" #include "OpenGLWrapper/modelTex.h" #include "OpenGLWrapper/fbo.h" class imCompShader : public gl::compShader { public: imCompShader( int w, int h, const string &compShaderName, GLint internalFormat = GL_RGBA8, GLenum format = GL_RGBA, GLenum type = GL_UNSIGNED_BYTE ) : gl::compShader(w, h, 1, internalFormat, format, type, compShaderName) { } void imCompShader::execute( GLuint inTexID ) { glUseProgram(prog); { // Input texture glBindImageTexture(0, inTexID, 0, GL_FALSE, 0, GL_READ_ONLY, internalFormat); // Output texture glBindImageTexture(1, texID, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalFormat); glDispatchCompute(width / 32, height / 32, depth); } glUseProgram(0); } }; class MyWindow : gl::window { private: gl::modelTex *myTex; gl::fbo *myFBO; imCompShader *mySobelComp; void path1() { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, myFBO->getFBOID()); { // Clear color and depth buffers glClearColor(0.3f, 0.5f, 0.8f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw glm::vec3 eye(cos(glfwGetTime()), sin(glfwGetTime()), 1.5f); glm::vec3 center(0.0f); glm::vec3 up(0.0f, 1.0f, 0.0f); glm::mat4 V = glm::lookAt(eye, center, up); glm::mat4 P = glm::perspective(3.14f / 4.0f, (float)width / height, 0.1f, 100.0f); myTex->setViewMat(V); myTex->setProjMat(P); myTex->render(); } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } void path2() { mySobelComp->execute(myFBO->getColorTexID()); myTex->setViewMat(glm::mat4()); myTex->setProjMat(glm::mat4()); myTex->render(mySobelComp->getTexID()); } public: MyWindow( int w, int h, const string &name, const string &texFileName, const string &vertShaderName, const string &fragShaderName, const string &compShaderName ) : window(w, h, name) { myTex = new gl::modelTex(texFileName, vertShaderName, fragShaderName); myFBO = new gl::fbo(w, h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE); mySobelComp = new imCompShader(w, h, compShaderName); } ~MyWindow() { if (myTex) delete myTex; if (myFBO) delete myFBO; if (mySobelComp) delete mySobelComp; } void render() { // Rendering loop while (!glfwWindowShouldClose(wnd)) { path1(); path2(); // Swap front and back buffers glfwSwapBuffers(wnd); // Poll for and process events glfwPollEvents(); } } }; int main() { MyWindow wnd( 512, 512, "GLFW 3 Window", "../data/me.jpg", "../shader/modelTex.vert", "../shader/modelTex.frag", "../shader/sobel.comp" ); wnd.render(); return 0; }
sample.comp
// // sobel.comp // #version 430 layout (binding = 0, rgba8) readonly uniform image2D dataIn; layout (binding = 1) writeonly uniform image2D dataOut; layout (local_size_x = 32, local_size_y = 32) in; void main(void) { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); vec3 px00 = imageLoad(dataIn, pos + ivec2(-1, 1)).rgb; vec3 px01 = imageLoad(dataIn, pos + ivec2( 0, 1)).rgb; vec3 px02 = imageLoad(dataIn, pos + ivec2( 1, 1)).rgb; vec3 px10 = imageLoad(dataIn, pos + ivec2(-1, 0)).rgb; vec3 px12 = imageLoad(dataIn, pos + ivec2( 1, 0)).rgb; vec3 px20 = imageLoad(dataIn, pos + ivec2(-1,-1)).rgb; vec3 px21 = imageLoad(dataIn, pos + ivec2( 0,-1)).rgb; vec3 px22 = imageLoad(dataIn, pos + ivec2( 1,-1)).rgb; // Sobel vec3 hori = -px00 - 2.0 * px10 -px20 + px02 + 2.0 * px12 + px22; vec3 vert = -px00 - 2.0 * px01 -px02 + px20 + 2.0 * px21 + px22; imageStore( dataOut, pos, vec4(sqrt(hori * hori + vert * vert), 0.0) ); }
結果
第6回で得た出力結果に対してエッジ検出をかけた結果が出力されました!目標達成です.