Mugichoko's blog

Mugichoko’s blog

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

GLSL #7: Compute Shader

目標

こちらの内容を参考に,とりあえずCompute Shaderが動作することを確認する. 尚,今回実装する内容は,前回(下記参照)までの実装に基づいております.

mugichoko.hatenablog.com

実装環境

レンダリングの流れ

  1. Compute Shaderを実行し,テクスチャに結果を書き込む
  2. 1の結果をmodelTexクラスを用いて画面に描画

サンプルプログラム

シェーダプログラムに関しては,ほとんどがこちらの丸コピです. 前回までに作成した以下のプログラムは変更せずそのまま利用します.

  • window.h
  • window.cpp
  • model.h
  • model.cpp
  • modelTex.h
  • modelTex.cpp

shaderUtil.h

前回から,以下を追加. 追加内容は,Compute Shaderをコンパイルするためのインタフェース「initializeProgram」とCompute Shader実装時のインタフェースとなる「compShader」クラス.

//
// shaderUtil.h
//

//
// 前回までの記述
//

namespace gl
{
    // Interface for compute shader
    void initializeProgram(
        GLuint &theProgram,
        const string &compShaderFileName
    );
    
    // ----- Compute shader handlar
    class compShader
    {
    protected:
        GLuint prog;
        GLuint texID;

        const GLint internalFormat;
        const GLsizei width;
        const GLsizei height;
        const GLuint depth;
        const GLenum format;
        const GLenum type;
    public:
        compShader(
            GLsizei width,
            GLsizei height,
            GLuint dpeth,
            GLint internalFormat,
            GLenum format,
            GLenum type,
            const string compShaderName
        );
        ~compShader();

        GLuint getTexID();
        GLint getInternalFormat();
        GLsizei getWidth();
        GLsizei getHeight();
        GLenum getFormat();
        GLenum getType();
        GLuint getDepth();

        virtual void execute() = 0;
    };
}

shaderUtil.cpp

ヘッダファイルに合わせて,前回から,以下を追加.

//
// shaderUtil.cpp
//
#include "shaderUtil.h"

//
// 前回までの記述
//

namespace gl
{
    void initializeProgram(
        GLuint &theProgram,
        const string &compShaderFileName
    )
    {
        try
        {
            cout << "[Shader Utility] Making a shader program..." << endl;

            vector<GLuint> shaderList;

            // ----- Load shaders
            stringstream ssComputeShader;
            readFile(compShaderFileName, ssComputeShader);

            // ----- Compile shaders
            shaderList.push_back(createShader(GL_COMPUTE_SHADER, ssComputeShader.str()));

            // ----- Link the shaders to the program
            theProgram = createProgram(shaderList);

            // ----- Delete
            std::for_each(shaderList.begin(), shaderList.end(), glDeleteShader);

            cout << "[Shader Utility] ... done!" << endl;
        }
        catch (string e)
        {
            cerr << "Error: " << e << endl;
            exit(EXIT_FAILURE);
        }
    }
    
    // ----- Compute shader handlar
    compShader::compShader(
        GLsizei width,
        GLsizei height,
        GLuint dpeth,
        GLint internalFormat,
        GLenum format,
        GLenum type,
        const string compShaderName
    ) : internalFormat(internalFormat), width(width), height(height), depth(dpeth), format(format), type(type)
    {
        // ----- Generate data texture
        glGenTextures(1, &texID);
        glBindTexture(GL_TEXTURE_2D, texID);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, NULL);

        // ----- Generate a compute shader program
        initializeProgram(prog, compShaderName);
    }
    compShader::~compShader()
    {
        glDeleteTextures(1, &texID);
        glDeleteProgram(prog);
    }

    GLuint compShader::getTexID()
    {
        return texID;
    }
    GLint compShader::getInternalFormat()
    {
        return internalFormat;
    }
    GLsizei compShader::getWidth()
    {
        return width;
    }
    GLsizei compShader::getHeight()
    {
        return height;
    }
    GLenum compShader::getFormat()
    {
        return format;
    }
    GLenum compShader::getType()
    {
        return type;
    }
    GLuint compShader::getDepth()
    {
        return depth;
    }
}

main.cpp

//
// main.cpp
//
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include "OpenGLWrapper/window.h"
#include "OpenGLWrapper/shaderUtil.h"
#include "OpenGLWrapper/modelTex.h"

class MyCompShader : public gl::compShader
{
public:
    MyCompShader(
        int w,
        int h,
        const string &compShaderName
    ) : gl::compShader(w, h, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT, compShaderName)
    {
    }

    void MyCompShader::execute()
    {
        static float frame(0.0f);

        glUseProgram(prog);
        {
            glBindImageTexture(0, texID, 0, GL_FALSE, 0, GL_WRITE_ONLY, internalFormat);

            glUniform1f(glGetUniformLocation(prog, "roll"), (float)frame * 0.01f);
            glDispatchCompute(width / 32, height / 32, depth);
        }
        glUseProgram(0);

        frame += 0.5f;
        if (frame >= FLT_MAX - 1.0f)
        {
            frame = 0.0f;
        }
    }
};

class MyWindow : gl::window
{
private:
    gl::modelTex *myTex;
    MyCompShader *myComp;

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(w, h, vertShaderName, fragShaderName);
        myComp = new MyCompShader(w, h, compShaderName);
    }
    ~MyWindow()
    {
        if (myTex) delete myTex;
        if (myComp) delete myComp;
    }

    void render()
    {
        // Rendering loop
        while (!glfwWindowShouldClose(wnd))
        {
            myComp->execute();
            glClearColor(0.3f, 0.5f, 0.8f, 0.0f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            myTex->render(myComp->getTexID());

            // 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/sample.comp"
    );
    wnd.render();

    return 0;
}

sample.comp

//
// sample.comp
//
#version 430

uniform float roll;
layout (binding = 0) writeonly uniform image2D data;

layout (local_size_x = 32, local_size_y = 32) in;

void main(void)
{
    ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);
    float localCoef = length(vec2(256, 256) - vec2(ivec2(gl_GlobalInvocationID.xy)));
    float val = 0.5 + 0.5 * sin(localCoef * 0.05 + roll);
    imageStore(
        data,
        storePos,
        vec4(val, val, val, 0)
        );
}

結果

動作しました!バッチリです.

youtu.be