1頂点入力、1頂点出力のジオメトリシェーダを作って動作確認を行う。
シェーダのコンパイル等は依然作ったprepare_shader.hppを転用する。ただしサンプルとしてはlink_programを使うと読みにくくなるので、ジオメトリシェーダサンプル用に書き直す。
https://suzulang.com/glsl-shader-my-functions-on-cpp
#version 460 core layout (location = 0) in vec3 coord; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 ModelViewMatrix; uniform mat4 ProjectionMatrix; void main() { gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(coord, 1.0); vertexColor = vec4(incolor, 1.0); gl_PointSize=30.0; }
#version 460 layout (points) in; //入力は頂点単位 layout (points) out; //出力も頂点単位 layout (max_vertices = 1) out; // 最大出力頂点数 //geometry shaderへの入力は配列で受け取らなければいけない //ただし名前はvertex shader側のoutと一致させる in vec4 vertexColor[]; // 出力は普通。名前はfragment shader側のinと一致させる out vec4 vColor; void main() { // gl_inにデータが入っている。今回は頂点ごとの処理なので要素数1. // つまりgl_in[0]のみが有効 gl_Position = gl_in[0].gl_Position; gl_PointSize=30.0; vColor = vertexColor[0]; EmitVertex(); // ラスタライザに頂点を送る EndPrimitive();//プリミティブの区切り。GL_LINE_STRIPなどを複数実行するときは複数回呼び出す }
#version 460 core in vec4 vColor; out vec4 FragColor; void main() { FragColor = vColor; }
#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"; const char* geomfile = "default.geom";
//! @brief プログラムのリンクを行う(geometryシェーダ使用時)。 //! @details ジオメトリシェーダがある場合はglLinkProgramの前に設定を行わなければいけない。 //! 別に関数を用意した方がわかりやすいのでここではこれを使う GLSLCompileCond link_program_with_geometry( GLuint* ProgramID, GLuint vertexShaderID, GLuint geometryShaderID, GLuint fragmentShaderID, std::string* error = nullptr) { GLint Result = GL_FALSE; int InfoLogLength; //////////////////////////////////////// // プログラムをリンクします。 *ProgramID = glCreateProgram(); glAttachShader(*ProgramID, vertexShaderID); glAttachShader(*ProgramID, geometryShaderID); glAttachShader(*ProgramID, fragmentShaderID); //////////////////////////////////////// // ジオメトリシェーダを使うときの設定をここでする // この作業は glAttachShaderとglLinkProgramの間に入れる glProgramParameteri(*ProgramID, GL_GEOMETRY_VERTICES_OUT, 3);//ジオメトリシェーダからの最大出力頂点数 glProgramParameteri(*ProgramID, GL_GEOMETRY_INPUT_TYPE, GL_POINTS);//ジオメトリシェーダには頂点が入力される glProgramParameteri(*ProgramID, GL_GEOMETRY_OUTPUT_TYPE, GL_POINTS);//ジオメトリシェーダからは頂点が出力される 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; }
void init() { std::vector<GLfloat> colors; std::vector<GLfloat> points; GLfloat v = 0.5; push_3(colors, 1, 0, 0); push_3(points, v, 0, 0); push_3(colors, 0, 1, 0); push_3(points, 0, v, 0); push_3(colors, 0, 0, 1); push_3(points, 0, 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; GLuint geomShader; std::string verr; std::string ferr; std::string gerr; prepare_shader_byfile(&vtxShader, GL_VERTEX_SHADER, vtxfile, &verr); prepare_shader_byfile(&flagShader, GL_FRAGMENT_SHADER, fragfile, &ferr); prepare_shader_byfile(&geomShader, GL_GEOMETRY_SHADER, geomfile, &gerr);//ジオメトリシェーダのコンパイル std::cout << verr << std::endl; std::cout << ferr << std::endl; std::cout << gerr << std::endl; std::string linkerr; link_program_with_geometry( &programID, vtxShader, flagShader, geomShader, &linkerr ); std::cout << linkerr << std::endl; 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; }
GLSLを使おうとすると何かとコードが長くなる。状況に合わせて細かい調整ができるところが魅力なのだが練習やテストで毎回書くのは大変なので使いまわせそうなところだけでも関数化しておきたい。
#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; }
#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; }
#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; }
#version 460 core in vec4 vColor; out vec4 FragColor; void main() { FragColor = vColor; }
以下のようなcopy関数を作りたい場合、テンプレート引数Tがarrayの時はresizeできない一方で、vectorの時はresizeしなければならない。
これを同じように扱えるようにしたい。具体的にはresize関数の有無で処理を分けたい。
#include <iostream> #include <vector> #include <array>
template<typename T> T copy(T& data) { T tmp; // vectorの時はresizeしないと要素数 0 で落ちる // arrayの時はresizeが存在しないのでコンパイルできない //tmp.resize(3); tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; return tmp; }
int main() { std::vector<float> vec{ 1.0f,1.0f,1.0f }; std::array<float, 3> ary{ 2.0f,2.0f,2.0f }; auto dup_vec = copy(vec); //これをしたい時はresizeを有効に auto dup_ary = copy(ary); //これをしたい時はresizeを無効に printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]); printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]); getchar(); }
やっていることはメンバ関数resizeの存在の判定をして構造体を切り替えているだけ。
#include <type_traits> namespace szl { struct has_resize_impl {
// Tにresizeメンバ関数がある場合はこちらが有効(実体化はしない) // 戻り値がstd::true_typeの関数宣言 template<typename T> static auto check_has_resize (T& o) ->decltype( o.resize(0), std::true_type() );
// Tにresizeがない場合はこちらが有効(実体化はしない) // 戻り値がstd::false_typeの関数宣言 static auto check_has_resize(...) ->decltype( std::false_type() );
}; //プライマリテンプレート template<typename T> struct _myresize_;
//! @brief resizeがある場合呼び出される template<> struct _myresize_<std::true_type> { template<typename T> static void resize_func(T& t, const size_t size) { if (size > t.size()) { t.resize(size); } } };
//!j @brief resizeがない場合に呼び出される。何もしない。 template<> struct _myresize_<std::false_type> { template<typename T> static void resize_func(T& t, const size_t size) { } };
//! @brief resize関数があるオブジェクトに対してのみresizeを実行する //! @param [in,out] obj 配列型のオブジェクト //! @param [in] template<typename T> void resize_if_needed(T& obj, const size_t size) { // resizeを持つオブジェクトなら std::true_type // resizeを持たないオブジェクトなら std::false_type using type = decltype(has_resize_impl::check_has_resize(obj)); // resizeあり/なしバージョンの関数を呼び出す _myresize_<type>::resize_func(obj, size); }
}
#include <iostream> #include <vector> #include <array> #include"has_resize.hpp"
template<typename T> T copy_general(T& data) { T tmp; //型に応じてresizeをする関数としない関数を切り替える szl::resize_if_needed(tmp, 3); tmp[0] = data[0]; tmp[1] = data[1]; tmp[2] = data[2]; return tmp; }
int main() { std::vector<float> vec{ 1.0f,1.0f,1.0f }; std::array<float, 3> ary{ 2.0f,2.0f,2.0f }; auto dup_vec = copy_general(vec); auto dup_ary = copy_general(ary); printf("%lf %lf %lf\n", dup_vec[0], dup_vec[1], dup_vec[2]); printf("%lf %lf %lf\n", dup_ary[0], dup_ary[1], dup_ary[2]); getchar(); }
Editモードに入り、ループカット(Ctrl+R)で切れ目を入れ、Z方向に4だけ移動する。底面を選択し縮小した後、Editモードを抜ける。
[a]で全ての頂点を選択し、[+]ボタンでVertexGroupを作成し名前を「Deform」として[Assign]する。
現在、"Deform"グループには全ての頂点が登録されているので、「グループから除外したい頂点を選択し」[Remove]ボタンを押す。
モディファイアが多いので他のオブジェクトを用意しモディファイア群だけを複製する。
「①新しいモデル」「②Rockのモディファイア群を設定したモデル」の順番に選択し、[Ctrl+L]→Modifiresを選択しコピーする。
なおここではBlender 2.90で試す。
以降、以下のようにモディファイア(とObject Data Properties)を編集していく。
BaseDisplacementNoiseテクスチャを以下のように設定
SecondaryDisplacementNoiseテクスチャを設定。殆どBaseDisplacementNoiseと同じで、sizeを3.0に変更する
これは追加するだけで、パラメータの変更はしない。
形状はこれで(ほぼ)完了。めずらしくチュートリアルに極めて近い物ができた。次回は残っているライティングとかもやってみる。
テクスチャはポリゴンに貼るが、そのポリゴンの色が何色かで張り付けた結果に違いが出る。
以下は、テクスチャを一枚用意し、それをglColor3dでポリゴンの色を指定した上に張り付けた結果。
//描画関数 void disp(void) { glViewport(0, 0, winwidth, winheight); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); double v = 0.95;
//下地の色を設定 glColor3d(1, 0, 0);
//テクスチャを有効化 glBindTexture(GL_TEXTURE_2D, texname); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2d(0, 1); glVertex2d(-v, -v); glTexCoord2d(1, 1); glVertex2d(v, -v); glTexCoord2d(1, 0); glVertex2d(v, v); glTexCoord2d(0, 0); glVertex2d(-v, v); glEnd(); glFlush(); }
glActiveTextureを使うと、下地の色用のテクスチャと、張り付けたい画像のテクスチャを同時に指定できる。「同時に指定できる」とはつまり、普通glBindTextureを一度実行したらほかのテクスチャは無効になるが、glActiveTextureを使えば二つのglBindTextureを有効にできるという意味。
OpenGLにはテクスチャスロットのような概念がある。
glActiveTexture(GL_TEXTURE0);
で、「これからスロット名"GL_TEXTURE0"の設定をする」という意味になる。
そして、一つのスロットで一度に使えるテクスチャは一枚だけで、これは「最後にglBindTextureで指定したテクスチャ」だが、一度に複数のスロットを使えるので、各スロットで glEnable(GL_TEXTURE_2D)でそのスロットを有効にしてやれば、テクスチャを複数使うことができる。
#include <iostream> #include <Windows.h> #include <gl/glew.h>// glActiveTextureに必要 #include <gl/GL.h> #include <gl/GLU.h> #include <gl/freeglut.h> #pragma comment(lib,"glew32.lib") #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" // freeglut: // http://freeglut.sourceforge.net/ //ウィンドウの幅と高さ int winwidth, winheight; GLuint texname_img; GLuint texname_block;
//下地用のテクスチャ void init_tex_block() { unsigned char pixels[] = { 255, 0, 0, 0,255, 0, 0, 0,255, 255,255,255 }; glGenTextures(1, &texname_block); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texname_block); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels ); }
//画像読み込み,テクスチャとしてロード //pngの読み込みに STBライブラリを使用 //https://suzulang.com/stb-library-image-read/ //https://github.com/nothings/stb void init_tex_img() { unsigned char* pixels; int bpp; int imgwidth; int imgheight; // ファイルを読み込み、画像データを取り出す // 最後の引数でピクセルあたりのバイト数を強制できる pixels = stbi_load(R"(C:\Users\szl\Desktop\bird-1232416_640.png)", &imgwidth, &imgheight, &bpp, 0); glGenTextures(1, &texname_img); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texname_img); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, imgwidth, imgheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels ); // メモリ上の画像データを破棄 stbi_image_free(pixels); }
//描画関数 void disp(void) { glViewport(0, 0, winwidth, winheight); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_CULL_FACE); double v = 0.95;
glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,texname_block);
glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texname_img);
glBegin(GL_QUADS); glMultiTexCoord2f(GL_TEXTURE0, 0, 1); glMultiTexCoord2f(GL_TEXTURE1, 0, 1); glVertex2d(-v, -v); glMultiTexCoord2f(GL_TEXTURE0, 1, 1); glMultiTexCoord2f(GL_TEXTURE1, 1, 1); glVertex2d(v, -v); glMultiTexCoord2f(GL_TEXTURE0, 1, 0); glMultiTexCoord2f(GL_TEXTURE1, 1, 0); glVertex2d(v, v); glMultiTexCoord2f(GL_TEXTURE0, 0, 0); glMultiTexCoord2f(GL_TEXTURE1, 0, 0); glVertex2d(-v, v); glEnd(); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { winwidth = w; winheight = h; disp(); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(640, 480); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glewInit(); init_tex_img(); init_tex_block(); glutMainLoop(); return 0; }
glMaterialfvで着色する場合、第一引数にGL_FRONTかGL_BACKを指定して切り替える
glColor3fで着色する場合、カリングを使い裏面のみ描画、表面のみ描画を切り替えて二回レンダリングする。
//ポリゴンを描画 void render() { float v = 0.7; glBegin(GL_QUADS); glVertex3f(-v, -v, 0.0); glVertex3f(v, -v, 0.0); glVertex3f(v, v, 0.0); glVertex3f(-v, v, 0.0); glEnd(); }
void draw_by_color() { glEnable(GL_CULL_FACE);//カリングを有効化 glCullFace(GL_FRONT); glColor4f(1, 0, 0, 1); render(); glCullFace(GL_BACK); glColor4f(0, 1, 0, 1); render(); }
void draw_by_material() { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); GLfloat white[4]{ 1,1,1,1 }; GLfloat red[4]{ 1,0,0,1 }; GLfloat green[4]{ 0,1,0,1 }; GLfloat pos[4]{ 0,0,0,1 }; glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 1);//両面に光を当てる glLightfv(GL_LIGHT0, GL_POSITION, pos);//ライト位置設定 glLightfv(GL_LIGHT0, GL_AMBIENT, white);//環境光色設定 glMaterialfv(GL_FRONT, GL_AMBIENT, red);//表面の材質 glMaterialfv(GL_BACK, GL_AMBIENT, green);//裏面の材質 render();//オブジェクトのレンダリング glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); }
void draw() { ///////////////////// // 描画 glViewport(0, 0, width, height); glClearColor(0.3, 0.3, 0.3, 1); glClear(GL_COLOR_BUFFER_BIT); Sleep(1); angle++; glPushMatrix(); glRotated(angle, 1, 0, 0); //draw_by_color();//この二つの関数切り替え draw_by_material(); glPopMatrix(); // ///////////////////// glFlush(); glfwSwapBuffers(_window); }
以前フレームバッファをやってみたときに、glFramebufferTexture2D関数を使ったが、第二引数に与えたGL_COLOR_ATTACHMENT0について言及しなかった(気がする)。当時は調べて納得はしていたが改めて読み返して混乱したのでここで明確にしておきたい。
glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); /* ... */ glGenTextures(1, texture0); glGenTextures(1, texture1); /* ... */ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture0, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, texture1, 0);
このように指定し、フラグメントシェーダ側で以下のように出力する。
#version 460 core layout (location = 0) out vec4 DefaultOut;// 0、つまりGL_COLOR_ATTACHMENT0 layout (location = 1) out vec4 RedOnly; // 1、つまりGL_COLOR_ATTACHMENT1 in vec4 vertexColor; void main() { DefaultOut = vertexColor; RedOnly = vec4(vertexColor[0],0,0,1); }
つまり、GL_COLOR_ATTACHMENT0はフラグメントシェーダのlocationを指定している。
#include <memory> #include <iostream> #include <vector> #include <algorithm> #include <array> #include<windows.h> #include <gl/glew.h> #include "CglfwBase.hpp" #pragma warning(disable:4996) #pragma comment(lib,"opengl32.lib") #pragma comment(lib,"glew32.lib")
//! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const TCHAR* const fname, const int vmax, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = _wfopen(fname, L"wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); }
//! @brief フレームバッファに関する変数と機能をまとめる class FramebufferTest { public: GLuint ID_texture_0; // テクスチャ1 GLuint ID_texture_1; // テクスチャ2 GLuint ID_fbo;// フレームバッファ int width, height;//テクスチャのサイズ private: GLuint ID_depth;
//! @brief テクスチャを一枚作成し、フレームバッファに関連付ける void generate_texture(GLuint *texname, GLenum attachment) { glGenTextures(1, texname); glBindTexture(GL_TEXTURE_2D, *texname); //テクスチャを転送する。 //ただし最後がnullptrなので実質転送しない glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); //テクスチャのフィルタリングの設定 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //描画先の設定 // attachmentへの書き込みで、texnameに書き込まれるようにする glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, *texname, 0); }
public:
// フレームバッファオブジェクトの作成と設定を行う // 途中で描画先のテクスチャも作成する void prepare(int WIDTH,int HEIGHT) { width = WIDTH; height = HEIGHT; ////////////////////////////////////////////////////// //フレームバッファの作成 // 削除はglDeleteFrameBuffersを使用する glGenFramebuffers(1, &ID_fbo); glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
////////////////////////////////////////////////////// //テクスチャバッファの作成 // 削除はglDeleteTextures generate_texture(&ID_texture_0, GL_COLOR_ATTACHMENT0); generate_texture(&ID_texture_1, GL_COLOR_ATTACHMENT1); // fragment shaderの // layout (location = 0) ・・・ GL_COLOR_ATTACHMENT0 // layout (location = 1) ・・・ GL_COLOR_ATTACHMENT1 // が書き込み先であることを教える(?) GLenum DrawBuffers[2] = { GL_COLOR_ATTACHMENT0 , GL_COLOR_ATTACHMENT1 }; glDrawBuffers(2, DrawBuffers); // "2"はDrawBuffersのサイズです。
////////////////////////////////////////////////////// // デプスバッファの作成 // レンダーバッファの一種なので、削除はglDeleteRenderbuffersを使用する glGenRenderbuffers(1, &ID_depth); glBindRenderbuffer(GL_RENDERBUFFER, ID_depth); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ID_depth); /////////////////////////////////////////////////// // フレームバッファのエラーチェック。 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) return;/*エラーデータ*/ }
}; class MyWindow :public MyWindowBase { int width; int height; FramebufferTest myfb; GLuint vertexbufferName; GLuint colorbufferName; public:
//! @brief glew,glfwの設定 フレームバッファの初期化、シェーダの作成、データの作成 virtual void setting()override { glewInit(); glfwGetFramebufferSize(_window, &width, &height); //フレームバッファの初期化 myfb.prepare(width, height); //シェーダに渡す描画データの作成 prepare_data(); //シェーダの作成 prepare_shaders(); }
//! @brief キーイベント。フレームバッファによって書き込んだテクスチャデータをファイルへ出力 virtual void key(int key, int scancode, int action, int mods)override { //テクスチャ(ID_texture_output)に書き込んだデータを取り出す領域を用意 std::vector<GLubyte> texdata; texdata.resize(myfb.width*myfb.height * 3); //////////////////////////////////////////////////// //テクスチャのデータを取り出す glBindTexture(GL_TEXTURE_2D, myfb.ID_texture_0); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &texdata[0]); // 取り出したテクスチャをファイルに書き出す pnmP3_Write(LR"(C:\test\texture0.ppm)", 255, width, height, texdata.data()); //////////////////////////////////////////////////// //テクスチャのデータを取り出す glBindTexture(GL_TEXTURE_2D, myfb.ID_texture_1); glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, &texdata[0]); // 取り出したテクスチャをファイルに書き出す pnmP3_Write(LR"(C:\test\texture1.ppm)", 255, width, height, texdata.data()); }
//! @brief 描画関数 virtual void draw()override { //以後の描画はフレームバッファに対して行われる glBindFramebuffer(GL_FRAMEBUFFER, myfb.ID_fbo); draw_core(); //以後の描画は画面に対して行われる glBindFramebuffer(GL_FRAMEBUFFER, 0); draw_core(); glfwSwapBuffers(_window); }
//! @brief 描画関数本体
void draw_core() { ///////////////////// // 描画 glViewport(0, 0, width, height); glClearColor(0.3, 0.3, 0.3, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(ProgramID); //gl_PointSizeを有効にする glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // 頂点バッファ glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glVertexAttribPointer( 0, // 属性0 3, // 1要素の要素数 x y z GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // カラーバッファ glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glVertexAttribPointer( 1, // 属性1 4, // 1要素の要素数 r g b a GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // 5頂点を描く glDrawArrays(GL_POINTS, 0, 5); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); // ///////////////////// glFlush(); } GLuint VertexShaderID; GLuint FragmentShaderID; GLuint ProgramID;
//! @brief シェーダの作成
//! シェーダコードを直接埋め込んで、compileshaderを呼び出している
void prepare_shaders() { const char* vshader = R"(
#version 460 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 incolor; out vec4 vertexColor; uniform mat4 gl_ModelViewMatrix; uniform mat4 gl_ProjectionMatrix; void main() { gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * vec4(aPos, 1.0); vertexColor = vec4(incolor, 1.0); gl_PointSize=30.0; // 頂点のサイズ(ピクセル) }
)"; VertexShaderID = compileshader(GL_VERTEX_SHADER, vshader); const char* fshader = R"(
#version 460 core layout (location = 0) out vec4 DefaultOut;// 0、つまりGL_COLOR_ATTACHMENT0 layout (location = 1) out vec4 RedOnly; // 1、つまりGL_COLOR_ATTACHMENT1 //out vec4 FragColor; in vec4 vertexColor; void main() { DefaultOut = vertexColor; RedOnly = vec4(vertexColor[0],0,0,1); }
)"; FragmentShaderID = compileshader(GL_FRAGMENT_SHADER, fshader); GLint Result = GL_FALSE; int InfoLogLength; //////////////////////////////////////// // プログラムをリンクします。 fprintf(stdout, "Linking program\n"); ProgramID = glCreateProgram(); glAttachShader(ProgramID, VertexShaderID); glAttachShader(ProgramID, FragmentShaderID); 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]); fprintf(stdout, "%s\n", &ProgramErrorMessage[0]); }
//! @brief シェーダに渡す描画データの作成 void prepare_data() { using point_t = std::array<GLfloat, 3>; //モデルの座標用 x y z using color_t = std::array<GLfloat, 4>;//テクスチャ座標用 r g b a ///////////////////////////////// //バッファとデータ point_t vertex[5]; color_t color[5]; const int elemcount = 5; ////////////////////////////////////////// vertex[0] = { -0.7, 0.7, 0 }; vertex[1] = { -0.7,-0.7, 0 }; vertex[2] = { 0.7,-0.7, 0 }; vertex[3] = { 0.7, 0.7, 0 }; vertex[4] = { 0 , 0 , 0 }; color[0] = { 1.0,0.0,0.0,1.0 }; color[1] = { 0.0,1.0,0.0,1.0 }; color[2] = { 0.0,0.0,1.0,1.0 }; color[3] = { 1.0,0.0,1.0,1.0 }; color[4] = { 1.0,1.0,1.0,1.0 }; ////////////////////////////////////////// // バッファの転送 glGenBuffers(1, &vertexbufferName); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glBufferData(GL_ARRAY_BUFFER, 3 * elemcount * sizeof(GLfloat), vertex, GL_STATIC_DRAW); glGenBuffers(1, &colorbufferName); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glBufferData(GL_ARRAY_BUFFER, 4 * elemcount * sizeof(GLfloat), color, GL_STATIC_DRAW); }
//! @brief シェーダをコンパイル・リンク GLuint compileshader(GLenum type,const char* VertexShaderCode) { GLuint shaderID; ////////////////////////////////////////////// // シェーダを作ります // (作るといっても宣言みたいなもの) shaderID = glCreateShader(type); //////////////////////////////////////////// // 頂点シェーダをコンパイルします。 printf("Compiling shader... \n"); glShaderSource(shaderID, 1, &VertexShaderCode, NULL); glCompileShader(shaderID); //////////////////////////////////////////// // エラーチェック GLint Result = GL_FALSE; int InfoLogLength; // 頂点シェーダをチェックします。 glGetShaderiv(shaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == FALSE) { std::vector<char> VertexShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(shaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]); } return shaderID; }
virtual void framebuffer_size(int _width, int _height)override { width = _width; height = _height; } }; int main() { MyWindow win; win.init(600,600,"glfw window"); win.setting(); win.loop(); }
画面出力ができたら、キーを何か押すとc:\test\texture0.ppmとtexture1.ppmを出力する。
画像が逆転しているのはOpenGLは左下原点なので仕方がない。
glfwSetWindowUserPointerでthisポインタを指定し、非メンバ関数であるコールバック関数の第一引数からthisを取り出せるようにすれば、間接的にだが自作クラスのメンバ関数を呼び出す事ができる。
#pragma once #pragma comment(lib,"glfw3.lib") #include <gl/glfw/glfw3.h> class MyWindowBase { protected: GLFWwindow* _window; protected:
// コールバック関数から呼ばれるメンバ関数群 //////////////////////////
virtual void key(int key, int scancode, int action, int mods) {} virtual void mouse_button(int button, int action, int mods) {} virtual void mouse_move(double xpos, double ypos) {} virtual void mouse_wheel(double xoffset, double yoffset) {} virtual void framebuffer_size(int width, int height) {} ////////////////////////////////////////////// ////////////////////////////////////////////// virtual void setting() {} virtual void draw() {}
protected:
// GLFWに指定するコールバック関数(staticメンバ関数) ////////////////////////// // GLFWのコールバック関数にはこれを指定する。glfwSetWindowUserPointerで指定したthisポインタを // glfwGetWindowUserPointerで取得し、メンバ関数を呼び出す
static void key_callback(GLFWwindow* pwin, int key, int scancode, int action, int mods) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->key(key, scancode, action, mods); } static void mouse_button_callback(GLFWwindow* pwin, int button, int action, int mods) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_button(button, action, mods); } static void mouse_move_callback(GLFWwindow* pwin, double xpos, double ypos) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_move(xpos, ypos); } static void mouse_wheel_callback(GLFWwindow* pwin, double xoffset, double yoffset) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->mouse_wheel(xoffset, yoffset); } static void framebuffer_size_callback(GLFWwindow* pwin, int width, int height) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->framebuffer_size(width, height); } static void draw_callback(GLFWwindow* pwin) { MyWindowBase* myw = static_cast<MyWindowBase*>(glfwGetWindowUserPointer(pwin)); myw->draw(); }
public:
// ここでGLFWの初期化を行う ///////////////////////////////// int init(int winwidth,int winheight,const char* wintitle) {
// GLFW の初期化 if (glfwInit() == GL_FALSE) { // 初期化に失敗したら終了 return 1; }
// ウィンドウを作成 _window = glfwCreateWindow( winwidth, //width winheight, //height wintitle,//title NULL, //monitor NULL //share ); //////////////////////////////////////////////////////////////////////////////// // ウィンドウを作成できなければ終了 if (_window == nullptr) { glfwTerminate(); return 1; }
//////////////////////////////////////////////////////////////////////////////// // 自身のポインタをGLFWwindowオブジェクトにセット glfwSetWindowUserPointer(_window, this);
//////////////////////////////////////////// // コールバック関数の設定 glfwSetKeyCallback(_window, MyWindowBase::key_callback); glfwSetMouseButtonCallback(_window, MyWindowBase::mouse_button_callback); glfwSetScrollCallback(_window, MyWindowBase::mouse_wheel_callback); glfwSetCursorPosCallback(_window, MyWindowBase::mouse_move_callback); glfwSetFramebufferSizeCallback(_window, MyWindowBase::framebuffer_size_callback); glfwSetWindowRefreshCallback(_window, MyWindowBase::draw_callback); glfwMakeContextCurrent(_window); }
// イベントループ /////////////////////////////////
void loop() { while (glfwWindowShouldClose(_window) == GL_FALSE) { draw(); // イベント取得 glfwPollEvents(); } glfwTerminate(); }
};
#include <memory> #include <iostream> #include<windows.h> #include "CglfwBase.hpp" #pragma comment(lib,"opengl32.lib") class MyWindow :public MyWindowBase { int width; int height; public:
virtual void setting()override { glfwGetFramebufferSize(_window, &width, &height); } virtual void key(int key, int scancode, int action, int mods)override { // https://www.glfw.org/docs/latest/group__keys.html#ga99aacc875b6b27a072552631e13775c7 // action ... GLFW_PRESS , GLFW_REPEAT , GLFW_RELEASE //特殊キー if (key == GLFW_KEY_UP && action == GLFW_PRESS) { } if (key == GLFW_KEY_DOWN && action == GLFW_PRESS) { } if (key == GLFW_KEY_LEFT && action == GLFW_PRESS) { } if (key == GLFW_KEY_RIGHT && action == GLFW_PRESS) { } if (key >= GLFW_KEY_A && key <= GLFW_KEY_Z) { if (action == GLFW_PRESS) { const char* key_name = glfwGetKeyName(key, 0); printf("key - %s\n", key_name); } } } virtual void mouse_button(int button, int action, int mods)override { double xpos, ypos; glfwGetCursorPos(_window, &xpos, &ypos); if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { printf("mouse L pressed\n"); } if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) { printf("mouse L released\n"); } if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) { } if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) { } if (button == GLFW_MOUSE_BUTTON_MIDDLE && action == GLFW_PRESS) { } draw(); } virtual void mouse_move(double xpos, double ypos)override { printf("mouse is moving\n"); draw(); } // マウスホイールを処理するコールバック関数 virtual void mouse_wheel(double xoffset, double yoffset)override { if (yoffset < 0) { } if (yoffset > 0) { } draw(); } virtual void framebuffer_size(int _width, int _height)override { width = _width; height = _height; } virtual void draw()override { ///////////////////// // 描画 glViewport(0, 0, width, height); glClearColor(0.3, 0.3, 0.3, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float v = 0.7; glBegin(GL_TRIANGLES); glColor3f(0, 0, 1); glVertex3f(-v, -v, 0.0); glColor3f(1, 0, 1); glVertex3f(v, -v, 0.0); glColor3f(1, 1, 1); glVertex3f(v, v, 0.0); glEnd(); // ///////////////////// glFlush(); glfwSwapBuffers(_window); }
}; int main() { MyWindow win; win.init(600,600,"glfw window"); win.setting(); win.loop(); }