#pragma comment(lib,"glew32.lib")
#include <gl/glew.h>
#include <GL/glut.h>
#include <vector>
#include <vector>
#include <sstream>
#include <fstream>
#include <algorithm>
////////////////////////////////////////////
//フレームバッファへの描画サイズ
int width;
int height;
//フレームバッファ関連のバッファID
GLuint ID_fbo;
GLuint ID_texture_output;
GLuint ID_depth;
////////////////////////////////////////////
// 二次元の三角形を四個
GLfloat vtx[4 * 3 * 2];
// 各頂点に設定するID
GLuint vID[4 * 3];
//描画するオブジェクト用のバッファのID
GLuint ID_Triangles;
GLuint ID_Triangle_Index;
////////////////////////////////////////////
// シェーダのプログラムID
GLuint ID_program_disp;
GLuint ID_program_idrender;
///////////////////////////////////////////
//テクスチャ(ID_texture_output)に書き込んだデータを取り出す領域を用意
std::vector<GLuint> texdata;
void prepare_shaders(GLuint* programID,const char* fn_vertex, const char* fn_fragment);
void prepare_databuffer();
void prepare_framebuffer();
void display(void);
//マウスでクリックした位置のオブジェクトIDをコンソールに出力
void mouse(int button, int state, int x, int y) {
if (button != GLUT_LEFT_BUTTON || state != GLUT_DOWN) return;
display();
unsigned int id = texdata[width * y + x];
printf("---- %d\n", id);
}
//ウィンドウサイズ固定用
void keepwindowsize(int w, int h) {
glutReshapeWindow(width, height);
}
///////////////////////////////////////////////////
// エントリポイント
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA);
glutCreateWindow(argv[0]);
glutDisplayFunc(display);
glutMouseFunc(mouse);
//フレームバッファで書き込む先の画像サイズを500x500にする
width = 500;
height = 500;
//IDの書き出し先
texdata.resize(width * height, 0);
//ウィンドウサイズをフレームバッファの描画サイズと同じにする
glutInitWindowSize(width, height);
//ウィンドウサイズは固定にしておきたい
glutReshapeFunc(keepwindowsize);
// glewの初期化
glewInit();
// シェーダ準備
prepare_shaders(&ID_program_disp,"vertex_disp.shader","fragment_disp.shader");
prepare_shaders(&ID_program_idrender,"vertex_id.shader", "fragment_id.shader");
//フレームバッファ準備
prepare_framebuffer();
//データ準備
prepare_databuffer();
glutMainLoop();
return 0;
}
void display(void)
{
{
//以後の描画はフレームバッファに対して行われる
glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
glUseProgram(ID_program_idrender);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, width, height);
glLoadIdentity();
// 頂点バッファを有効化
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles);
glVertexAttribPointer(
0, // シェーダ内のlayoutとあわせる
2, // 1要素の要素数(x,y)で2要素
GL_FLOAT, // タイプ
GL_FALSE, // 正規化しない(データが整数型の時)
0, // ストライド
(void*)0 // 配列バッファオフセット
);
// IDバッファを有効化
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, ID_Triangle_Index);
glVertexAttribIPointer(//バッファが整数の時はglVertexAttribIPointerを使う
1, // シェーダ内のlayoutとあわせる
1, // 1要素の要素数 GLuintが一つで1要素
GL_UNSIGNED_INT, // タイプ
0, // ストライド
(void*)0 // 配列バッファオフセット
);
glDrawArrays(GL_TRIANGLES, 0, 12);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
/////////////////////////////////////////
//テクスチャのデータを取り出す
glBindTexture(GL_TEXTURE_2D, ID_texture_output);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, &texdata[0]);
//texdataはデータのIDが入ったwidht*height個のGLuint型配列
//上下反転 座標系がウィンドウズとOpenGLで上下逆になっている
for (int ya = 0; ya < height / 2; ya++) {
int yb = height - ya - 1;
if (ya == yb)break;
for (size_t x = 0; x < width; x++) {
int pa = ya * width + x;
int pb = yb * width + x;
std::swap(texdata[pa], texdata[pb]);
}
}
glFlush();
}
{
//ここから先は画面に出力する
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(ID_program_disp);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glViewport(0, 0, width, height);//ウィンドウサイズをこれに固定している
// 頂点バッファ
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles);
glVertexAttribPointer(
0, // シェーダ内のlayoutとあわせる
2, // 1要素の要素数(x,y)で2要素
GL_FLOAT, // タイプ
GL_FALSE, // 正規化しない(データが整数型の時)
0, // ストライド
(void*)0 // 配列バッファオフセット
);
glDrawArrays(GL_TRIANGLES, 0, 12);
glDisableVertexAttribArray(0);
glFlush();
}
}
void prepare_databuffer() {
////////////////////////////////////////////////////////
// 頂点作成
{
int ofs_obj = 3 * 2;//一つのオブジェクトのサイズは二次元3頂点
int ofs_vtx = 2;//一つの頂点のサイズは二次元
//三角形1
vtx[(0 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1
vtx[(0 * ofs_obj) + (0 * ofs_vtx) + (1)] = 1.0;
vtx[(0 * ofs_obj) + (1 * ofs_vtx) + (0)] = 1.0; //頂点2
vtx[(0 * ofs_obj) + (1 * ofs_vtx) + (1)] = -1.0;
vtx[(0 * ofs_obj) + (2 * ofs_vtx) + (0)] = -1.0; //頂点3
vtx[(0 * ofs_obj) + (2 * ofs_vtx) + (1)] = -1.0;
//三角形2
vtx[(1 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1
vtx[(1 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.7;
vtx[(1 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.7; //頂点2
vtx[(1 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.7;
vtx[(1 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.7; //頂点3
vtx[(1 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.7;
//三角形3
vtx[(2 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1
vtx[(2 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.4;
vtx[(2 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.4; //頂点2
vtx[(2 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.4;
vtx[(2 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.4; //頂点3
vtx[(2 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.4;
//三角形4
vtx[(3 * ofs_obj) + (0 * ofs_vtx) + (0)] = 0.0; //頂点1
vtx[(3 * ofs_obj) + (0 * ofs_vtx) + (1)] = 0.2;
vtx[(3 * ofs_obj) + (1 * ofs_vtx) + (0)] = 0.2; //頂点2
vtx[(3 * ofs_obj) + (1 * ofs_vtx) + (1)] = -0.2;
vtx[(3 * ofs_obj) + (2 * ofs_vtx) + (0)] = -0.2; //頂点3
vtx[(3 * ofs_obj) + (2 * ofs_vtx) + (1)] = -0.2;
}
////////////////////////////////////////////////////////
// 頂点ID作成
{
int ofs_obj = 3;//一つのオブジェクトのサイズは3頂点×1番号
//三角形1
vID[(0 * ofs_obj) + 0 ] = 1; //頂点1
vID[(0 * ofs_obj) + 1 ] = 1; //頂点2
vID[(0 * ofs_obj) + 2 ] = 1; //頂点3
//三角形2
vID[(1 * ofs_obj) + 0 ] = 2; //頂点1
vID[(1 * ofs_obj) + 1 ] = 2; //頂点2
vID[(1 * ofs_obj) + 2 ] = 2; //頂点3
//三角形3
vID[(2 * ofs_obj) + 0 ] = 3; //頂点1
vID[(2 * ofs_obj) + 1 ] = 3; //頂点2
vID[(2 * ofs_obj) + 2 ] = 3; //頂点3
//三角形4
vID[(3 * ofs_obj) + 0 ] = 4; //頂点1
vID[(3 * ofs_obj) + 1 ] = 4; //頂点2
vID[(3 * ofs_obj) + 2 ] = 4; //頂点3
}
////////////////////////////////////////////////////////
//バッファ作成
glGenBuffers(1, &ID_Triangles);
glBindBuffer(GL_ARRAY_BUFFER, ID_Triangles);
glBufferData(
GL_ARRAY_BUFFER,
4 * 3 * 2 * sizeof(GLfloat),
vtx,
GL_STATIC_DRAW);
glGenBuffers(1, &ID_Triangle_Index);
glBindBuffer(GL_ARRAY_BUFFER, ID_Triangle_Index);
glBufferData(
GL_ARRAY_BUFFER,
4 * 3 * sizeof(GLuint),
vID,
GL_STATIC_DRAW);
}
void prepare_framebuffer() {
//////////////////////////////////////////////////////
//フレームバッファの作成
// 削除はglDeleteFrameBuffersを使用する
glGenFramebuffers(1, &ID_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
//////////////////////////////////////////////////////
{
//テクスチャバッファの作成 (ID記録用)
// 削除はglDeleteTextures
glGenTextures(1, &ID_texture_output);
glBindTexture(GL_TEXTURE_2D, ID_texture_output);
//テクスチャを転送する。
//ただし最後がnullptrなので実質転送しない
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
//テクスチャのフィルタリングの設定
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
//////////////////////////////////////////////////////
// デプスバッファの作成
// レンダーバッファの一種なので、削除は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);
//////////////////////////////////////////////////////
//描画先の設定
// 「GL_COLOR_ATTACHMENT0」 が ID_texture_outputのテクスチャを表すようにする
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ID_texture_output, 0);
///////////////////////////////////////////////////
//描画バッファのリストをセットする
//この作業によってフラグメントシェーダの(layout=0) out の出力先がテクスチャになる
glBindFramebuffer(GL_FRAMEBUFFER, ID_fbo);
GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers); // "1"はDrawBuffersのサイズです。
///////////////////////////////////////////////////
// フレームバッファのエラーチェック。
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return;/*エラーデータ*/
}
//シェーダのコンパイルとリンクを行う関数
void prepare_shaders(GLuint* programID,const char* fn_vertex, const char* fn_fragment){
GLint Result = GL_FALSE;
int InfoLogLength;
//////////////////////////////////////////////
//////////////////////////////////////////////
//頂点シェーダ
// シェーダを作ります
// (作るといっても宣言みたいなもの)
GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
////////////////////////////////////////////
// ファイルから頂点シェーダを読み込みます。
std::string VertexShaderCode;
std::ifstream VertexShaderStream(fn_vertex, std::ios::in);
if (VertexShaderStream.is_open())
{
std::stringstream sstr;
sstr << VertexShaderStream.rdbuf();
VertexShaderCode = sstr.str();
VertexShaderStream.close();
}
////////////////////////////////////////////
// 頂点シェーダをコンパイルします。
printf("Compiling shader : %s\n", fn_vertex);
char const* VertexSourcePointer = VertexShaderCode.c_str();
glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL);
glCompileShader(VertexShaderID);
////////////////////////////////////////////
// エラーチェック
// 頂点シェーダをチェックします。
glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (Result == FALSE) {
std::vector<char> VertexShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]);
}
//////////////////////////////////////////////
//////////////////////////////////////////////
//フラグメントシェーダ
// シェーダを作ります。
GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
/////////////////////////////////////////////
// ファイルからフラグメントシェーダを読み込みます。
std::string FragmentShaderCode;
std::ifstream FragmentShaderStream(fn_fragment, std::ios::in);
if (FragmentShaderStream.is_open()) {
std::stringstream sstr;
sstr << FragmentShaderStream.rdbuf();
FragmentShaderCode = sstr.str();
FragmentShaderStream.close();
}
/////////////////////////////////////////////
// フラグメントシェーダをコンパイルします。
printf("Compiling shader : %s\n", fn_fragment);
char const* FragmentSourcePointer = FragmentShaderCode.c_str();
glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL);
glCompileShader(FragmentShaderID);
/////////////////////////////////////////////
// フラグメントシェーダをチェックします。
glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (Result == GL_FALSE) {
std::vector<char> FragmentShaderErrorMessage(InfoLogLength);
glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]);
}
////////////////////////////////////////////
////////////////////////////////////////////
// プログラムのリンク
////////////////////////////////////////
// プログラムをリンクします。
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]);
}