Windows 10で昔ながらのOpenGLを使った3DのCGの表示というのをやってみた。(2020年に いまさらOpenGL、、)

なぜ3DのCGかというと、最近M5Stack/M5StickCマイコンを使ってIMU(慣性計測ユニット)のジャイロセンサーと加速度センサーと地磁気センサーの出力の合計9軸分を計測して、9DoFの出力を取得するというのを趣味で作り中で、その出力結果をPCで3DのCGで表示して確認したかったからだ。
昔、ジャイロセンサー無しで加速度センサーと地磁気センサーだけを使って6DoFの計測をして、物体の向き(姿勢)を計算して、3DのCG表示をさせるというのを作っていたことがある。そのときに使ったのがOpenGLだった。というか、自分はそれしか3DのCGのライブラリを知らない。当時はOpenGLをWindows上(Windows XP~Windows7)で使っていた。OpenGL 1.1くらいの時だ。
今(2020年)、OpenGLをWindows 10上で使うにはどうするのがよいのか、調べてみた。
1.OpenGLを使う方法
本家のOpenGLは、もうWindows用とかで使えるアプリ開発用ライブラリを公開していないし、配布もしていない。
参考: OpenGL – Wikipedia
ただし、機能としてはWindowsにXPの時代くらい(?)からずっとOpenGLはOSに組み込まれていて、Windows10でもちゃんと残っているので、APIを叩けばOpenGLの機能は使うことができる。(OSに組み込まれているというよりは、NVIDIAとかのGPUのドライバにいまだに組み込まれているというべきかも。)
追記、
OpenGL自体をインストールしなくても、まだ配布されているGLUTというライブラリだけダウンロードして使えばちゃんとOpenGLを使用できるそうだ。
GLUT公式: GLUT – The OpenGL Utility Toolkit
2.freeGLUTを使う方法
OpenGL互換のfreeGLUTというプロジェクトは、OpenGLのライブラリの公開終了後も開発がしばらく続いていたが、SourceForgeにあったプロジェクトページは無くなってしまっている。もうソースコードは入手できないようだ。個人のホームページでライブラリのバイナリを配布しているページが見つかるので、そちらから現在でもバイナリとしては入手は可能だ。
しかし、このライブラリを組み込んだ実行ファイルをVisualStudio2019でビルドして生成して、実行しようとしてみたところ、Windows Defenderが反応してウイルスと反応してしまって実行できず、そのあと数十秒後に勝手にexeファイルが自動削除されてしまった。ダウンロードした実行ファイルが安全でないということでブロックされる現象に似ているのだが、それよりも強い警告でブロックされて、その画面には実行を可能にする選択肢は無い。自動削除されてしまう前に実行ファイルを右クリックからのWindowsDefenderでチェックする機能を使ってチェックし、ウイルスが検出されたという表示が出てくる画面にて、選択肢が削除と検疫と許可というのが出てくるので、許可をすると削除されなくなり、実行ができるようになる。
こんな状態だと自分のプロジェクトにfreeGLUTを組み込んだものを使うのは気持ち悪いし、他人に実行ファイルを配布したあと、ウイルス誤認問題が発生したら面倒なことになるので、配布するソフトにfreeGLUTを組み込むのは避けたほうがよさそうだ。
3.SDLを使う方法
SDL(Simple DirectMedia Layer)というライブラリがあり、これもOpenGLのAPIをサポートしている。2020年でもまだプロジェクトとしては生きていて、開発や配布は続いている。
ただし、使用できるのはOpenGLの基本のライブラリのみで、OpenGLのGLUTで拡張された便利な機能は持っていない。ポリゴンを構成する座標データとか自分で管理して表示させるしかないので、簡単な直方体くらいまでは表示するのはそこまで長いコードを書かなくても楽にできるのだが、複雑なことをやらせようとするとかなり自前でコードを書かなくてはならない。
昔、WiiリモコンhackをしたときにWiiリモコンの動きを表示させるソフトを作るのにSDL 1.2使ってみたことがあった。
今回SDL 1.2系の最終版を試しに動かしてみたが、普通に動かすことができた。移行は保留だ。
次のようなコードをVisual Studio 2019で32bit版のSDLを使ってビルドして動かしてみた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | #include <windows.h> #include <process.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "SDL/SDL.h" #include "SDL/SDL_opengl.h" #define ACC_GAIN 26 #ifdef __cplusplus FILE iob[] = { *stdin, *stdout, *stderr }; extern "C" { FILE * __cdecl _iob(void) { return iob; } } #endif int HandleEvent(SDL_Event *event) { int done; done=0; switch( event->type ) { case SDL_KEYDOWN: //printf("key '%s' pressed\n", SDL_GetKeyName(event->key.keysym.sym)); if ( event->key.keysym.sym == SDLK_ESCAPE ) { done=1; } break; case SDL_QUIT: done=1; break; } return done; } int Do_sdl_event() { int done; done=0; SDL_Event event; while( SDL_PollEvent(&event) ) { done=HandleEvent(&event); } return done; } int RunGLTest() { Uint32 video_flags; int value; int bpp = 0; int rgb_size[3]; int w = 640; int h = 480; int done = 0; static float jx=0,jy=0,jz=0; GLenum gl_error; char* sdl_error; float cube[8][3]= { { 0.5, 0.4, -1.5}, { 0.5, -0.4, -1.5}, {-0.5, -0.4, -1.5}, {-0.5, 0.4, -1.5}, {-0.5, 0.4, 1.5}, { 0.5, 0.4, 1.5}, { 0.5, -0.4, 1.5}, {-0.5, -0.4, 1.5} }; putenv("SDL_VIDEODRIVER=directx"); if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0 ) { printf("Couldn't initialize SDL: %s\n",SDL_GetError()); exit( 1 ); } SDL_ShowCursor(SDL_DISABLE); video_flags = SDL_OPENGL; if ( SDL_GetVideoInfo()->vfmt->BitsPerPixel <= 8 ) { bpp=8; } else { bpp=16; } switch (bpp) { case 8: rgb_size[0]=3; rgb_size[1]=3; rgb_size[2]=2; break; case 16: rgb_size[0]=5; rgb_size[1]=5; rgb_size[2]=5; break; default: rgb_size[0]=8; rgb_size[1]=8; rgb_size[2]=8; break; } SDL_GL_SetAttribute( SDL_GL_RED_SIZE, rgb_size[0] ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, rgb_size[1] ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, rgb_size[2] ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 ); SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1 ); if ( SDL_SetVideoMode( w, h, bpp, video_flags ) == NULL ) { printf("Couldn't set GL mode: %s\n", SDL_GetError()); SDL_Quit(); getch(); exit(1); } SDL_WM_SetCaption( "GL-test", "testgl" ); glViewport( 0, 0, w, h ); glMatrixMode( GL_PROJECTION ); glLoadIdentity( ); glOrtho( -2.0, 2.0, -2.0, 2.0, -20.0, 20.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity( ); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glShadeModel(GL_SMOOTH); while( !done ) { jx = jx+0.1; jy = jy+0.25; jz = jz+0.314; glClearColor( 0.0, 0.0, 0.0, 1.0 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Matrix reset // Rotation glRotatef(jz , 0, 1.0, 0); glRotatef(jy, 1.0, 0, 0); glRotatef(jx, 0, 0, 1.0); // draw CUBE glBegin( GL_QUADS ); glColor3f(1.0, 1.0, 1.0); glVertex3fv(cube[0]);glVertex3fv(cube[1]);glVertex3fv(cube[2]);glVertex3fv(cube[3]); glColor3f(0.9, 0.9, 0.9); glVertex3fv(cube[3]);glVertex3fv(cube[4]);glVertex3fv(cube[7]);glVertex3fv(cube[2]); glColor3f(0.8, 0.8, 0.8); glVertex3fv(cube[0]);glVertex3fv(cube[5]);glVertex3fv(cube[6]);glVertex3fv(cube[1]); glColor3f(0.7, 0.7, 0.7); glVertex3fv(cube[5]);glVertex3fv(cube[4]);glVertex3fv(cube[7]);glVertex3fv(cube[6]); glColor3f(1.0, 1.0, 1.0); glVertex3fv(cube[5]);glVertex3fv(cube[0]);glVertex3fv(cube[3]);glVertex3fv(cube[4]); glColor3f(0.8, 0.8, 0.8); glVertex3fv(cube[6]);glVertex3fv(cube[1]);glVertex3fv(cube[2]);glVertex3fv(cube[7]); glEnd( ); SDL_GL_SwapBuffers( ); // GL error check , SDL error check gl_error = glGetError( ); sdl_error = SDL_GetError( ); if( gl_error != GL_NO_ERROR ) { printf("testgl: OpenGL error: %d\n", gl_error ); } if( sdl_error[0] != '\0' ) { printf("testgl: SDL error '%s'\n", sdl_error); SDL_ClearError(); } // SDL event process done=Do_sdl_event(); // wait SDL_Delay(10); } SDL_Quit(); return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ) { RunGLTest(); return 0; } |

そこそこ短いコードで3DのCGを動かすことができた。GLUTを使えばもう少し楽なのだけど。
(前に動かしてみたのと同じだが、Windows10上でVisual Studio 2019で動かすことができた。)
(その他、OpenGL用のライブラリとかあるかもしれないですが、よく分からないので使っている人とかコメントで教えて頂けるとありがたいです。)
—
結論としては、OpenGLについてはこんなありさまだったので、短いコードで簡単に 3DのCG表示ができるライブラリとして使い続けるのは難しいかなあと思った。
DXライブラリでDirectXの3D CG機能を使うことができ、そちらの方がよさそうということで、試し中。
今の時代はVulkanですかね