Mugichoko's blog

読者です 読者をやめる 読者になる 読者になる

Mugichoko’s blog

プログラミングを中心としたメモ書き.

GLSL #8: Compute Shaderを使った画像処理

目標

前回(下記参照)までの実装を組み合わせて用いることで,3DCGを描画した結果に対してCompute Shaderを使って画像処理(Sobelフィルタによるエッジ検出)を行うプログラムを作成する.

mugichoko.hatenablog.com

実装環境

レンダリングの流れ

  1. パス1:第6回のパス1と同じ
  2. パス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回で得た出力結果に対してエッジ検出をかけた結果が出力されました!目標達成です.

youtu.be