GLFW3で等高面に応じた色の3次元グラフを作る
前回の続き。複数のセンサーの出力結果と相関を時間軸でみるのには3次元グラフが便利かなということでいろいろググったらこんなサイトに
OpenGL + GLFWで表示してみる
Z軸の等高面で色が変化するようになっていますが、あまり真面目に作っていません。上記のコードのようにswitch~caseで行うか、HSV→RGB変換を扱う範囲の最大最小値で正規化してやるほうがよいと思います。詳細はget_color関数をご覧ください
グラフのメモリがついてないのはまた後でということで。
式はfunc_t関数内の式を計算して表示しています。
#define GLFW_INCLUDE_GLU #include <math.h> #include <stdio.h> #include <GLFW/glfw3.h> #include <Windows.h> #include <locale.h> #pragma comment(lib, "GLFW3.lib") #pragma comment(lib, "opengl32.lib") #pragma comment(lib, "glu32.lib") #define MIN -5.0 // Z軸の最小 #define MAX 5.0 // z軸の最大 #define DX 0.8 // ポリゴンの大きさ。あまり小さいと動作が緩慢に、大きすぎるといびつな形になる #define DY 0.8 // 同上 #define MATH_PI 3.141592653589793238662643383279 static double eye_z = -70.0; int ROTATE_INDICATE; // 1: rotate , 0: do not rotate float a2rad(double angle) { return (float)(angle / 180 * MATH_PI); } double r2deg(float radian) { return (double)(radian * 180 / MATH_PI); } double func_t(double x, double y){ return 4.8 * cos(sqrt(x * x + y * y) / 1.0); } void get_color(GLubyte *color, double z) { color[0] = (GLubyte)fmod(((z - MIN) * 255 / (MAX - MIN) + 250) , 256); color[1] = (GLubyte)fmod(((z - MIN) * 255 / (MAX - MIN) + 50) , 256); color[2] = (GLubyte)fmod(((z - MIN) * 255 / (MAX - MIN) + 100) , 256); } void reshape(int width, int height) { static GLfloat lightPosition[4] = { 0.25f, 1.0f, 0.25f, 0.0f }; static GLfloat lightDiffuse[3] = { 1.0f, 1.0f, 1.0f }; static GLfloat lightAmbient[3] = { 0.25f, 0.25f, 0.5f }; static GLfloat lightSpecular[3] = { 1.0f, 1.0f, 1.0f }; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glShadeModel(GL_SMOOTH); glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (double)width / (double)height, 0.1, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.5, 1.5, eye_z, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0); glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular); } void draw_polygon(double x0, double y0, double z0, double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3) { GLubyte color[3]; glBegin(GL_POLYGON); get_color(color, z0); glColor3ub(color[0], color[1], color[2]); glVertex3d(x0, y0, z0); get_color(color, z1); glColor3ub(color[0], color[1], color[2]); glVertex3d(x1, y1, z1); get_color(color, z2); glColor3ub(color[0], color[1], color[2]); glVertex3d(x2, y2, z2); get_color(color, z3); glColor3ub(color[0], color[1], color[2]); glVertex3d(x3, y3, z3); glEnd(); } void edit_xyz_axis(void){ glBegin(GL_LINES); glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(50.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(0.0f, 50.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 25.0f); glVertex3f(0.0f, 0.0f, 0.0f); glEnd(); } void display_polygon(){ static GLfloat diffuse[3] = { 1.0f, 1.0f, 1.0f }; static GLfloat ambient[3] = { 0.25f, 0.25f, 0.25f }; static GLfloat specular[3] = { 1.0f, 1.0f, 1.0f }; static GLfloat shininess[1] = { 32.0f }; static float angle = 0.f; glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT, GL_SPECULAR, specular); glMaterialfv(GL_FRONT, GL_SHININESS, shininess); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_COLOR_MATERIAL); glEnable(GL_DEPTH_TEST); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); if (ROTATE_INDICATE == 1){ ROTATE_INDICATE = 0; angle += 2.5f; if (angle > 360.0f) { angle -= 360.0f; } } if (ROTATE_INDICATE == -1){ ROTATE_INDICATE = 0; angle -= 2.5f; if (angle < 0.0f) { angle += 360.0f; } } glRotatef(angle, 1.0f, 1.0f, 0.0f); glPushMatrix(); double x, y; for (y = -25; y <= 25; y += DY) for (x = -25; x <= 25; x += DX) { draw_polygon(x, y, func_t(x, y), x + DX, y, func_t(x + DX, y), x + DX, y + DY, func_t(x + DX, y + DY), x, y + DY, func_t(x, y + DY)); } edit_xyz_axis(); glPopMatrix(); } void idle() { Sleep(10); } void error_callback(int eror, const char* description) { fputs(description, stderr); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){ if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); else if (key == GLFW_KEY_PAGE_DOWN && action == GLFW_PRESS) eye_z = eye_z - 2.5; else if (key == GLFW_KEY_PAGE_UP && action == GLFW_PRESS) eye_z = eye_z + 2.5; else if (key == GLFW_KEY_DOWN && action == GLFW_REPEAT | GLFW_PRESS) ROTATE_INDICATE = -1; else if (key == GLFW_KEY_UP && action == GLFW_REPEAT | GLFW_PRESS) ROTATE_INDICATE = +1; } //static void mouse_callback(GLFWwindow* window, ) LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); return 0; default: break; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nShowCmd) { int width, height; // コールバックの設定 glfwSetErrorCallback(error_callback); // GLFWを初期化 if (!glfwInit()) exit(EXIT_FAILURE); GLFWwindow* window = glfwCreateWindow(400, 400,"3D-graph display test", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); // メインループ while (!glfwWindowShouldClose(window)) { glfwGetFramebufferSize(window, &width, &height); reshape(width, height); idle(); display_polygon(); glfwSwapBuffers(window); // イベントのポーリング glfwPollEvents(); } // Windowの削除 glfwDestroyWindow(window); // GLFWの終了処理 glfwTerminate(); exit(EXIT_SUCCESS); }
結果
PageUp / PageDownで拡大縮小
カーソルキー上下で回転します
結論
GLFWはGLUT同様ウィンドハンドラ周りの心配しなくても面倒見の良いライブラリだけど.Net環境でダイアログベースで開発できなくてつらい。なのでGLFW使ったお勉強はここまでにしようと思います。
◆参考
c++ - Using GLFW to render and WinAPI to handle messages - Stack Overflow