先日2月21日(土)、2月22日(日)に、日本で唯一のデモパーティーであるTokyo Demo Fest 2015が開催されました。
大学の卒業制作展などが重なってしまって行けなかったのですが、次こそは参加したい…!という事で、とりあえず4kb introの作り方を調べてみました。
Tokyo Demo Fest 2015 – 日本で唯一のデモパーティ
4kb introというのは、4kb(たった英字4096文字分の大きさ!)で音楽も含めた映像作品です。
沢山の謎技術、ハードウェアに関する知識が使われているイメージがありますが、やり方さえ分かればそこまで難しくないと思います。
▼ Tokyo Demo Fest 2015にて公開されたyosshin様の4kb intro「Optical Circuit」
仕組み
既定のライブラリをリンクせず、「Crinkler」というリンカーを使ってビルドします。
「Crinkler」は、突っ込むだけで信じられないほど容量を削減してくれる魔法のツールです。
後はloopをgotoに置き換えたり、関数をinlineやマクロに置き換えたりといった地味な最適化をしていきます。
基本的にコードの読みやすさとexeのサイズはトレードオフなので、適度なラインを見極めてコードを書いていきましょう。
設定方法(Visual Studio 2013)
まず、いつも通りWindowsアプリケーション、空のプロジェクトで、プロジェクトの新規作成を行います。
次に、プロパティを開き、以下の設定を行います。
[すべての構成]
”構成プロパティ > 全般 > 文字セット” を 「マルチバイト文字セットを使用する」に変更
”構成プロパティ > VC++ディレクトリ > 実行可能ファイルディレクトリ” に 「$(SolutionDir)」を追加
”構成プロパティ > C/C++>全般 > 追加のインクルードディレクトリ” に「$(ProjectDir)」を追加
”構成プロパティ > リンカー > 入力 > すべての既定のライブラリの無視” を「はい (/NODEFAULTLIB)」に変更
”構成プロパティ > リンカー > 入力 > 追加の依存ファイル” に「opengl32.lib」を追加
[Debug]
“構成プロパティ>C/C++>コード生成>基本ランタイムチェック” を「既定」に変更
[Release]
”リンカー>コマンドライン>追加のオプション” に「/CRINKLER」を追加
”構成プロパティ>全般>プログラム全体の最適化” を「プログラム全体の最適化なし」に変更
次に、Crinklerをダウンロード、「crinkler.exe」を「link.exe」にリネームしてソリューションディレクトリに保存します。
The Crinkler executable file compressor
また、glext.hをダウンロード、ソリューションディレクトリ内にglフォルダを作り、そこに突っ込みます。
glext.h
あとは適当にコードを書いて出来上がり。
前回の記事で制作したシェーダーを元ネタにしています。
GLSL Sandboxで遊ぼう | Puzzle & Programing
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 |
#include <windows.h> #include <GL/gl.h> #include <GL/glext.h> //---------------------------------------------------------------- // シェーダーの定義 //---------------------------------------------------------------- static const char *vertexShaderSource = "varying vec3 org,dir;void main(){gl_Position=gl_Vertex;org=vec3(0,0,0);dir=normalize(-vec3(-gl_Vertex.x*1.333,-gl_Vertex.y,1));}"; static const char *fragmentShaderSource = "varying vec3 org,dir;uniform int time = 0;struct Ray{vec3 pos;vec3 dir;};float udRoundBox(vec3 p,vec3 b,float r){return length(max(abs(p)-b,0.0))-r;}vec3 repPos(vec3 p,vec3 c){return mod(p,c)-0.5*c;}float subFunc(vec3 pos){float a=mod(atan(pos.y,pos.x),3.1415926535/1.5)-3.1415926535/1.5/2.0;float xyLen=length(pos.xy);a-=pos.z;pos.xy=vec2(xyLen*sin(a),xyLen*cos(a));pos=repPos(pos,vec3(0.03));return udRoundBox(pos,vec3(0.0138),0.001);}float func(vec3 pos){float a=mod(atan(pos.y,pos.x),3.1415926535/1.5)-3.1415926535/1.5/2.0;float xyLen=length(pos.xy);a+=pos.z;pos.xy=vec2(xyLen*sin(a),xyLen*cos(a));pos=repPos(pos,vec3(0.33));return udRoundBox(pos,vec3(0.1),0.01);}float distFunc(vec3 pos){return max(-subFunc(pos),func(pos));}vec3 getNormal(vec3 pos){const float d=0.0001;return normalize(vec3(distFunc(pos+vec3(d,0,0))-distFunc(pos-vec3(d,0,0)),distFunc(pos+vec3(0,d,0))-distFunc(pos-vec3(0,d,0)),distFunc(pos+vec3(0,0,d))-distFunc(pos-vec3(0,0,d))));}void main(void){vec3 cameraPos=vec3(0.0,0.0,-10.0 + float(time) * 0.0002);Ray ray;ray.pos=cameraPos;ray.dir=dir;float d;for(int i=0;i<64;++i){d=distFunc(ray.pos);ray.pos+=d*ray.dir;if(abs(d)<0.001&&i!=0)break;}float light=(dot(getNormal(ray.pos),vec3(1,1,-1)));gl_FragColor=vec4(vec3(clamp(vec3(1.0,0.7,0.4)*light+(ray.pos-cameraPos).z*0.5,0.0,1.0)),1.0);}"; //---------------------------------------------------------------- // 画面サイズ、モード //---------------------------------------------------------------- #define SCREEN_WIDTH 1280 #define SCREEN_HEIGHT 720 //---------------------------------------------------------------- // グローバル変数 //---------------------------------------------------------------- // シェーダープログラム GLuint g_pProgram; // 一時的に使う変数。 // 容量削減のためあちこちで使いまわします。 GLuint g_pTemp; // デバイスコンテキスト HDC g_hDC; // ウィンドウハンドル HWND g_hWnd; // レンダリングコンテキスト HGLRC g_hGLRC; //---------------------------------------------------------------- // 継続可能か調べ、可能ならtrueを返す //---------------------------------------------------------------- inline bool CheckContinuable() { #ifndef FULL_SCREEN MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); if (msg.message == WM_NCLBUTTONDOWN && msg.wParam == HTCLOSE) { return false; } DispatchMessage(&msg); #endif return !GetAsyncKeyState(VK_ESCAPE); } //---------------------------------------------------------------- // シェーダープログラムをコンパイルする //---------------------------------------------------------------- inline void CreateShaderProgram() { g_pProgram = ((PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"))(); g_pTemp = ((PFNGLCREATESHADERPROC)(wglGetProcAddress("glCreateShader")))(GL_VERTEX_SHADER); ((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"))(g_pTemp, 1, &vertexShaderSource, 0); ((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(g_pTemp); ((PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"))(g_pProgram, g_pTemp); g_pTemp = ((PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"))(GL_FRAGMENT_SHADER); ((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"))(g_pTemp, 1, &fragmentShaderSource, 0); ((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(g_pTemp); ((PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"))(g_pProgram, g_pTemp); ((PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"))(g_pProgram); ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(g_pProgram); } //---------------------------------------------------------------- // ウィンドウ、OpenGLを初期化する //---------------------------------------------------------------- inline void InitializeWindow() { #ifdef FULL_SCREEN static DEVMODE dmScreenSettings = { "", 0, 0, sizeof(dmScreenSettings), 0, DM_PELSWIDTH | DM_PELSHEIGHT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN); g_hWnd = CreateWindow("edit", 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0); ShowCursor(0); #else g_hWnd = CreateWindow("edit", 0, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0, 0); #endif g_hDC = GetDC(g_hWnd); static const PIXELFORMATDESCRIPTOR g_pixelFormatDescriptor = { 0, 1, PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0 }; SetPixelFormat(g_hDC, ChoosePixelFormat(g_hDC, &g_pixelFormatDescriptor), &g_pixelFormatDescriptor); g_hGLRC = wglCreateContext(g_hDC); wglMakeCurrent(g_hDC, g_hGLRC); } //---------------------------------------------------------------- // エントリポイント //---------------------------------------------------------------- void WinMainCRTStartup() { InitializeWindow(); CreateShaderProgram(); DWORD startTime = GetTickCount(); while (CheckContinuable()) { g_pTemp = ((PFNGLGETUNIFORMLOCATIONARBPROC)wglGetProcAddress("glGetUniformLocation"))(g_pProgram, "time"); ((PFNGLUNIFORM1UIEXTPROC)wglGetProcAddress("glUniform1i"))(g_pTemp, startTime - (int)GetTickCount()); glRecti(1, 1, -1, -1); SwapBuffers(g_hDC); } wglMakeCurrent(NULL, NULL); wglDeleteContext(g_hGLRC); ReleaseDC(g_hWnd, g_hDC); PostQuitMessage(0); ExitProcess(0); } |
実行結果
ビルドして実行してみると、無事画面が表示されました。
サイズを確認してみると、1,352バイトでした!まだまだ詰め込んでいけそうです。
コメントを残す