スポンサーリンク

GLSLのシェーダ関係の処理を関数化(C++)

GLSLを使おうとすると何かとコードが長くなる。状況に合わせて細かい調整ができるところが魅力なのだが練習やテストで毎回書くのは大変なので使いまわせそうなところだけでも関数化しておきたい。

prepare_shader.hpp

#pragma once

#include <string>
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <functional>

#include <gl/glew.h>
#pragma comment(lib,"glew32.lib")

enum class GLSLCompileCond {
    SUCCESS = 0,
    FILE_OPEN_ERROR = 1,
    COMPILE_ERROR = 2,
    LINK_ERROR = 3
};
//! @brief シェーダをソースコードからコンパイルする
//! @param [out] ShaderID 作成したシェーダのID
//! @param [in] shader_type GL_VERTEX_SHADER , GL_FRAGMENT_SHADER , GL_GEOMETRY_SHADER のいずれか
//! @param [in] sourcecode シェーダのソースコード
//! @param [out] error エラー出力先。エラーの内容を確認しないならnullptrを指定
GLSLCompileCond prepare_shader_bytext(
    GLuint* ShaderID,
    const GLenum shader_type, 
    const char* sourcecode,
    std::string* error = nullptr) {

    //////////////////////////////////////////////
      // シェーダを作ります
      // (作るといっても宣言みたいなもの)
    *ShaderID = glCreateShader(shader_type);

    glShaderSource(*ShaderID, 1, &sourcecode, NULL);
    glCompileShader(*ShaderID);

    ////////////////////////////////////////////
    // エラーチェック
    GLint Result = GL_FALSE;
    int InfoLogLength;

    // 頂点シェーダをチェックします。
    glGetShaderiv(*ShaderID, GL_COMPILE_STATUS, &Result);
    glGetShaderiv(*ShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    if (Result == GL_FALSE) {
        if (error) {
            std::vector<char> ShaderErrorMessage(InfoLogLength);
            glGetShaderInfoLog(*ShaderID, InfoLogLength, NULL, &ShaderErrorMessage[0]);
            *error = &ShaderErrorMessage[0];
        }
        return GLSLCompileCond::COMPILE_ERROR;
    }

    return GLSLCompileCond::SUCCESS;
}
//! @brief ファイルからシェーダを読み込む
//! @param [out] ShaderID シェーダの出力先
//! @param [in] shader_type GL_VERTEX_SHADER , GL_FRAGMENT_SHADER , GL_GEOMETRY_SHADERのいずれか
//! @param [in] shader_file_path シェーダファイルのパス
//! @param [out] エラーメッセージ出力先
GLSLCompileCond prepare_shader_byfile(
    GLuint* ShaderID,
    const GLenum shader_type,
    const char* shader_file_path,
    std::string* error = nullptr) {


    ////////////////////////////////////////////
    // ファイルから頂点シェーダを読み込みます。
    // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、
    // OpenGL命令は一つも無い。
    std::string ShaderCodeText;
    std::ifstream fileinput(shader_file_path, std::ios::in);
    if (fileinput.is_open())
    {
        std::stringstream sstr;
        sstr << fileinput.rdbuf();
        ShaderCodeText = sstr.str();
        fileinput.close();
    }
    else {
        if(error)
            *error = "file open failed";
        return GLSLCompileCond::FILE_OPEN_ERROR;
    }
    ////////////////////////////////////////////
    // 頂点シェーダをコンパイルします。
    return prepare_shader_bytext(ShaderID,shader_type,ShaderCodeText.c_str(),error);

}
//! @brief シェーダをリンクする
//! @param [out] ProgramID プログラムIDの保存先
//! @param [in] ShaderIDs シェーダ一覧
//! @param [in] geometryprogramparameter ジオメトリシェーダの設定をする関数
//! @param [out] error エラーメッセージ出力先
//! @return エラー状態
GLSLCompileCond link_program(
    GLuint* ProgramID,
    const std::vector<GLuint> ShaderIDs,
    std::function<void(GLuint)> geometryprogramparameter,
    std::string* error = nullptr) {

    GLint Result = GL_FALSE;
    int InfoLogLength;

    ////////////////////////////////////////
    // プログラムをリンクします。
    *ProgramID = glCreateProgram();

    for (GLuint shader : ShaderIDs) {
        glAttachShader(*ProgramID, shader);
    }

    if(geometryprogramparameter)
        geometryprogramparameter(*ProgramID);

    glLinkProgram(*ProgramID);

    ////////////////////////////////////////
    // プログラムをチェックします。
    glGetProgramiv(*ProgramID, GL_LINK_STATUS, &Result);
    glGetProgramiv(*ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
    std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1)));
    glGetProgramInfoLog(*ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);

    if (error) {
        *error = &ProgramErrorMessage[0];

        return GLSLCompileCond::LINK_ERROR;
    }

    return GLSLCompileCond::SUCCESS;
}
//! @brief データを転送
//! @param [in,out] buffer_name バッファ名へのポインタ
//! @param [in] data バッファの配列の先頭アドレス
//! @param [in] databytes データの大きさ
//! @param [in] usage GL_STATIC_DRAWまたはGL_DYNAMIC_DRAW
bool prepare_buffer(
    GLuint *buffer_name,
    const void* data,
    const size_t databytes,
    const GLenum usage
    ) {
    
    if (*buffer_name == 0) {
        glGenBuffers(1, buffer_name);
    }
    else {
        glDeleteBuffers(1, buffer_name);
        *buffer_name = 0;
        glGenBuffers(1, buffer_name);
    }
    if (*buffer_name != 0) {
        glBindBuffer(GL_ARRAY_BUFFER, *buffer_name);
        glBufferData(
            GL_ARRAY_BUFFER,
            databytes,
            data,
            usage);
        return true;
    }
    return false;

}
class EnableAndBindFArrayBuffer {
    GLuint m_layout;
public:
    //! @brief バッファを有効にする
    //! @param [in] layout
    //! @param [in] buffername
    //! @param [in] element_count
    //! @param [in] datatype
    //! @param [in] normalized
    //! @param [in] stride
    //! @param [in] offset 
    EnableAndBindFArrayBuffer(
        const GLuint layout,
        const GLuint buffername,
        const GLuint element_count,
        const GLenum datatype,
        const GLboolean normalized = false,
        const GLsizei stride = 0,
        const void* offset = nullptr
    ) {
        glEnableVertexAttribArray(layout);
        glBindBuffer(GL_ARRAY_BUFFER, buffername);
        glVertexAttribPointer(
            layout,
            element_count,
            datatype,
            normalized,
            stride,
            offset
        );
        m_layout = layout;
    }

    //! @brief デストラクタで終了処理
    ~EnableAndBindFArrayBuffer() {
        glDisableVertexAttribArray(m_layout);
    }
};
//! @brief コンテナにデータをpush_backする(4要素)
template<typename Container>
void push_4(
    Container& container,
    const typename Container::value_type a,
    const typename Container::value_type b,
    const typename Container::value_type c,
    const typename Container::value_type d) {
    container.push_back(a);
    container.push_back(b);
    container.push_back(c);
    container.push_back(d);
}

//! @brief コンテナにデータをpush_backする(3要素)
template<typename Container>
void push_3(
    Container& container, 
    const typename Container::value_type a, 
    const typename Container::value_type b, 
    const typename Container::value_type c) {
    container.push_back(a);
    container.push_back(b);
    container.push_back(c);
}

//! @brief コンテナにデータをpush_backする(2要素)
template<typename Container>
void push_2(
    Container& container, 
    const typename Container::value_type a,
    const typename Container::value_type b
    ) {
    container.push_back(a);
    container.push_back(b);
}
//! @brief 単位行列
//! @param [out] p 4x4行列
template<typename T>
void loadidentity44(T& p) {
    p[0] = 1;
    p[1] = 0;
    p[2] = 0;
    p[3] = 0;

    p[4] = 0;
    p[5] = 1;
    p[6] = 0;
    p[7] = 0;

    p[8] = 0;
    p[9] = 0;
    p[10] = 1;
    p[11] = 0;

    p[12] = 0;
    p[13] = 0;
    p[14] = 0;
    p[15] = 1;
}

使い方

main.cpp

#include <iostream>

#include <Windows.h>
#include "prepare_shader.hpp"
#include <gl/GL.h>
#include <gl/GLU.h>
#include <gl/freeglut.h>

#pragma comment(lib,"glew32.lib")

//ウィンドウの幅と高さ
int width, height;

GLuint buf_points;
GLuint buf_colors;

GLfloat mproj[16];
GLfloat mmodel[16];
GLuint programID;
const char* vtxfile = "default.vert";
const char* fragfile = "default.frag";

// シェーダとデータの設定
void init() {

    std::vector<GLfloat> colors;
    std::vector<GLfloat> points;

    GLfloat v = 0.8;

    push_3(colors, 0, 0, 1);//色 RGB
    push_3(points, 0, 0, 0);//座標 XYZ

    push_3(colors, 0, 1, 0);
    push_3(points, 0, v, 0);

    push_3(colors, 1, 0, 0);
    push_3(points, v, 0, 0);

    prepare_buffer(&buf_points, points.data(), points.size() * sizeof(GLfloat), GL_STATIC_DRAW);
    prepare_buffer(&buf_colors, colors.data(), colors.size() * sizeof(GLfloat), GL_STATIC_DRAW);

    GLuint vtxShader;
    GLuint flagShader;

    std::string verr;
    std::string ferr;
    prepare_shader_byfile(&vtxShader, GL_VERTEX_SHADER, vtxfile, &verr);
    prepare_shader_byfile(&flagShader, GL_FRAGMENT_SHADER, fragfile, &ferr);

    std::cout << verr << std::endl;
    std::cout << ferr << std::endl;

    std::string linkerr;
    link_program(&programID, { vtxShader ,flagShader }, nullptr, &linkerr);

    loadidentity44(mproj);
    loadidentity44(mmodel);
}

//描画関数
void disp(void) {

    glViewport(0, 0, width, height);

    glClearColor(0.2, 0.2, 0.2, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glUseProgram(programID);
    GLuint loc_ModelViewMatrix = glGetUniformLocation(programID, "ModelViewMatrix");
    GLuint loc_ProjectionMatrix = glGetUniformLocation(programID, "ProjectionMatrix");


    glUniformMatrix4fv(loc_ModelViewMatrix, 1, GL_FALSE, mmodel);
    glUniformMatrix4fv(loc_ProjectionMatrix, 1, GL_FALSE, mproj);

    {
        glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

        auto bindbufP = EnableAndBindFArrayBuffer(0, buf_points, 3, GL_FLOAT);
        auto bindbufC = EnableAndBindFArrayBuffer(1, buf_colors, 3, GL_FLOAT);

        glDrawArrays(GL_POINTS, 0,3);

    }
    glFlush();
}

//ウィンドウサイズの変化時に呼び出される
void reshape(int w, int h) {
    width = w; height = h;

    disp();
}

//エントリポイント
int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitWindowPosition(100, 50);
    glutInitWindowSize(500, 500);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);

    glutCreateWindow("sample");
    glutDisplayFunc(disp);
    glutReshapeFunc(reshape);

    glewInit();

    init();

    glutMainLoop();

    return 0;
}

default.vert

#version 460 core

layout (location = 0) in vec3 coord;
layout (location = 1) in vec3 incolor;

out vec4 vColor;

uniform mat4 ModelViewMatrix;
uniform mat4 ProjectionMatrix;

void main()
{
  gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(coord, 1.0);
  vColor = vec4(incolor, 1.0);
  gl_PointSize=30.0;
}

default.frag

#version 460 core

in vec4 vColor;

out vec4 FragColor;

void main()
{
  FragColor = vColor;
} 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: