OGLplus を使ってシェーダで遊んでみた

実行時にシェーダファイルをリロードできるようにして遊んでみました。

[main.cpp]

#include <vector>
#include <GL/glew.h>

#include <oglplus/gl.hpp>
// OGLplus 0.5.0 以降だと near far を undef しておかないと動かない
#undef near
#undef far

//#include <oglplus/all.hpp>
#include <oglplus/context.hpp>
#include <oglplus/buffer.hpp>
#include <oglplus/vertex_attrib.hpp>
#include <oglplus/vertex_array.hpp>
#include <oglplus/shader.hpp>
#include <oglplus/program.hpp>
#include <oglplus/pixel_data.hpp>
#include <oglplus/image.hpp>

#include <GL/Glut.h>

#include <cstdlib>
#include <string>
#include <gl/graphics.hpp>
#include <boost/thread.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include <sstream>

namespace glp = oglplus;
typedef glp::Context glc;


template<typename Shader, typename Char>
boost::optional<std::string>
shader_compile(Shader& shader, Char const* source){
    shader.Source(source);
    try{
        shader.Compile();
        return boost::none;
    }
    catch (glp::CompileError const& error) {
        std::cout << error.what() << std::endl;
        return error.Log();
    }
}

template<typename Shader, typename Char>
boost::optional<std::string>
shader_compile(Shader& shader, std::basic_ifstream<Char> const& file){
    std::stringstream buffer;
    buffer << file.rdbuf();
    return shader_compile(shader, buffer.str().c_str());
}


struct triangle : boost::noncopyable{
    
    triangle(std::initializer_list<GLfloat> triangle_verts){
        load_shader();

        // bind the VAO for the triangle
        vertex_array.Bind();

        // bind the VBO for the triangle vertices
        verts.Bind(glp::Buffer::Target::Array);
        // upload the data
        glp::Buffer::Data(glp::Buffer::Target::Array,
            triangle_verts.size(),
            triangle_verts.begin()
        );
        // setup the vertex attribs array for the vertices
        glp::VertexAttribArray vert_attr(prog, "Position");
        vert_attr.Setup(3, glp::DataType::Float);
        vert_attr.Enable();
    }
    
    void
    render() const{
        vertex_array.Bind();
        glc::DrawArrays(glp::PrimitiveType::Triangles, 0, 3);
    }
    void
    reload_shader(std::string name = "basic"){
        prog.DetachShader(vs);
        prog.DetachShader(fs);
        load_shader(name);
        std::cout << "reloaded shader" << std::endl;
    }

private:
    void
    load_shader(std::string name = "basic"){
        if(auto error = shader_compile(vs, std::ifstream(name + ".vert"))){
            std::cout << "==== VertexShader Compile Error ====" << std::endl;
            std::cout << *error << std::endl;
            return;
        }

        if(auto error = shader_compile(fs, std::ifstream(name + ".frag"))){
            std::cout << "==== FragmentShader Compile Error ====" << std::endl;
            std::cout << *error << std::endl;
            return;
        }
        // attach the shaders to the program
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        // link and use it
        prog.Link();
        prog.Use();
    }

    // Vertex shader
    glp::VertexShader vs;

    // Fragment shader
    glp::FragmentShader fs;

    // Program
    glp::Program prog;

    // A vertex array object for the rendered triangle
    glp::VertexArray vertex_array;
    // VBO for the triangle's vertices
    glp::Buffer verts;
};


int
main(int argc, char *argv[]){
    namespace glp = oglplus;
    typedef glp::Context glc;
    
    gl::graphics g(argc, argv, 500, 500, "test");
    
    // glew の初期化
    auto err = glewInit();
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }
    
    glc::ClearColor(0.5f, 0.7f, 0.9f, 1.0f);
    glc::ClearDepth(1.0f);

    // △ポリゴンの生成
    triangle obj1({
        0.0f, 0.0f, 0.0f,
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f
    });

    triangle obj2({
         0.0f,  0.0f, 0.0f,
        -1.0f,  0.0f, 0.0f,
         0.0f, -1.0f, 0.0f
    });
    //----------------------------------------------------------------------    
    
    gl::displayfunc(g, [&](){
        glc::Clear().ColorBuffer().DepthBuffer();
        obj1.render();
        obj2.render();
    });
    
    gl::reshapefunc(g, [&](int width, int height){
        glc::Viewport(width, height);
    });

    // キー入力イベント
    gl::keyboardfunc(g, [&obj1](unsigned char key, int x, int y){
        if(key == 'q') return exit(0);
        if(key == 'u') return obj1.reload_shader();
    });

    // idle
    gl::idlefunc(g, [&](){
        boost::this_thread::sleep(boost::posix_time::milliseconds(16));
        glutPostRedisplay();
    });

    g.run();
    
    return 0;
}

[basic.vert]

#version 330
in vec3 Position;
out vec4 vertColor;

void main(void){
    gl_Position = vec4(Position, 1.0);
    vec3 color = (Position + 1.0) / 2.0;
    vertColor = vec4(color, 1.0);
}

[basic.frag]

#version 330
in vec4 vertColor;
out vec4 fragColor;

void main(void){
    fragColor = vertColor;
}

[出力]


実行時に変更出来るのでシェーダ楽しい。
次は画像か3D辺りを描画させたいですね。
しかし、今だに OGLplus(OpenGL)の描画ロジックがよく分からない…。

[OGLplus]

  • 0.6.0