テラByteの時代にキロByte

shader又はdemosceneに関係する事

C言語でdemoを作る。

demoを作るには、c言語は、避けて通れません。なのでc言語の話から。 c言語を使って作るのですが、c言語のスキルなんて、ほぼいらないです。なんとかなります。
以前iq氏のページを見たのですが、デバック用とリリース用のc言語のソースが載ってました。 考えてみれば、当たり前ですね。で、今回はデバック用のコードを載せてみました。

#include <windows.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <mmreg.h>
#include <stdio.h> 

static const char *vsh = \
"void main()"
"{"
    "gl_Position = gl_Vertex;"
"}";

static const char *fsh = \
"uniform vec2 resolution;"
"uniform float time;"
"void main()"
"{"
    "vec2 p = (2.0 * gl_FragCoord.xy - resolution.xy) / resolution.y;"
    "p += vec2(cos(time), sin(time)) * 0.5;"
    "float g = exp(-1.5 * dot(p,p));"
    "gl_FragColor = vec4(g, g, g, 1.0);"
"}";

const char *msh = \
"#version 330\n"
"out vec2 gain;"
"uniform float sampleRate;"
"void main()"
"{"
    "float time = float(gl_VertexID) / sampleRate;"
    "gain = vec2(sin(6.2831*440.0*fract(time)));"
"}";

#define U_RESOLUTION "resolution" 
#define U_SAMPLERATE "sampleRate" 
#define U_TIME "time" 
#define O_GAIN "gain" 


#define SND_DURATION    180
#define SAMPLE_RATE     44100
#define SND_NUMCHANNELS 2
#define XPOS 500
#define YPOS 200
#define XRES 640
#define YRES 480

#define SND_NUMSAMPLES  (SND_DURATION*SAMPLE_RATE)
#define SND_NUMSAMPLESC (SND_NUMSAMPLES*SND_NUMCHANNELS)

float samples[SND_NUMSAMPLESC];

void checkShader(GLuint shader){
    GLint compile_status = 0;
    ((PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"))(shader, GL_COMPILE_STATUS, &compile_status);
    if(compile_status != GL_TRUE){
        GLint info_length;
        GLsizei buffer_size;
        ((PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv"))(shader, GL_INFO_LOG_LENGTH, &info_length);
        GLchar message[info_length];
        ((PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog"))(shader, info_length, &buffer_size, message);
        printf( message);
    }
}

void compile(GLuint program, const char *source, GLenum type){
    GLuint shader = ((PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader"))(type);
    ((PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource"))(shader, 1, &source, 0);
    ((PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader"))(shader);
    checkShader(shader);
    ((PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader"))(program, shader);
}

int main(){
    HWND hWnd = CreateWindow((LPCSTR)0xC018,"demoscene",WS_OVERLAPPEDWINDOW|WS_VISIBLE,XPOS,YPOS,XRES,YRES,0,0,0,0);
    HDC hDC = GetDC(hWnd);
    PIXELFORMATDESCRIPTOR pfd={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(hDC, ChoosePixelFormat(hDC, &pfd) , &pfd);
    HGLRC  hGLrc = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hGLrc);
    
    GLuint programMzk = ((PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"))();
    compile(programMzk, msh, GL_VERTEX_SHADER);
    const GLchar* outs[] = {O_GAIN};
    ((PFNGLTRANSFORMFEEDBACKVARYINGSPROC)wglGetProcAddress("glTransformFeedbackVaryings"))(programMzk, 1, outs, GL_INTERLEAVED_ATTRIBS);
    ((PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"))(programMzk);
    ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(programMzk);
    GLuint tmp;
    ((PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers"))(1, &tmp);
    ((PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer"))(GL_ARRAY_BUFFER, tmp);
    ((PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData"))(GL_ARRAY_BUFFER, SND_NUMSAMPLESC*sizeof(float), 0, GL_STATIC_READ);
    ((PFNGLBINDBUFFERBASEPROC)wglGetProcAddress("glBindBufferBase"))(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tmp);
    tmp = ((PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation"))(programMzk, U_SAMPLERATE);
    ((PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f"))(tmp, (float)SAMPLE_RATE);
    glEnable(GL_RASTERIZER_DISCARD);
    ((PFNGLBEGINTRANSFORMFEEDBACKPROC)wglGetProcAddress("glBeginTransformFeedback"))(GL_POINTS);
    glDrawArrays(GL_POINTS, 0, SND_NUMSAMPLES);
    ((PFNGLENDTRANSFORMFEEDBACKPROC)wglGetProcAddress("glEndTransformFeedback"))();
    glDisable(GL_RASTERIZER_DISCARD);
    ((PFNGLGETBUFFERSUBDATAPROC)wglGetProcAddress("glGetBufferSubData"))(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(samples), samples);


    GLint program, loc;
    program = ((PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram"))();
    compile(program, vsh, GL_VERTEX_SHADER);
    compile(program, fsh, GL_FRAGMENT_SHADER);
    ((PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram"))(program);
    ((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(program);
    loc = ((PFNGLGETUNIFORMLOCATIONARBPROC)wglGetProcAddress("glGetUniformLocation"))(program, U_RESOLUTION);
    ((PFNGLUNIFORM2FPROC)wglGetProcAddress("glUniform2f"))(loc, (float)XRES, (float)YRES);


    WAVEFORMATEX wave_format = {
        WAVE_FORMAT_IEEE_FLOAT,
        SND_NUMCHANNELS,
        SAMPLE_RATE,
        SAMPLE_RATE*sizeof(float)*SND_NUMCHANNELS,
        sizeof(float)*SND_NUMCHANNELS,
        sizeof(float)*8,
        0
    };
    WAVEHDR wave_hdr = {(LPSTR)samples, sizeof(samples)};
    HWAVEOUT hWaveOut;
    waveOutOpen(&hWaveOut, WAVE_MAPPER, &wave_format, (DWORD_PTR)hWnd, 0, CALLBACK_WINDOW);
    waveOutPrepareHeader(hWaveOut, &wave_hdr, sizeof(wave_hdr));
    waveOutWrite(hWaveOut, &wave_hdr, sizeof(wave_hdr));
    static MMTIME mmt = { TIME_SAMPLES };    
    MSG msg;
    int done=0;
    int fps = 0;
    int cnt = 0;
    float t0 = 0.0;
    while (!done) {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_NCLBUTTONDOWN && msg.wParam == HTCLOSE) done=1;
            DispatchMessage(&msg);
        }
        if (GetAsyncKeyState(VK_ESCAPE)) done = 1;
        if (mmt.u.sample == SND_NUMSAMPLES) done = 1;
        waveOutGetPosition(hWaveOut, &mmt, sizeof(mmt));
        float time = (float)mmt.u.sample / (float)SAMPLE_RATE;
        
        loc = ((PFNGLGETUNIFORMLOCATIONARBPROC)wglGetProcAddress("glGetUniformLocation"))(program, U_TIME);
        ((PFNGLUNIFORM1FPROC)wglGetProcAddress("glUniform1f"))(loc, time);
        glRects(1, 1, -1, -1);
        
        SwapBuffers(hDC);
        if (time - t0 > 1.0) {
            fps = cnt;
            cnt = -1;
            t0 = time;
        }
        cnt++;
        printf("\r  FPS : %d Time : %5.1f", fps, time);
    }
    
    waveOutReset(hWaveOut);
    waveOutUnprepareHeader(hWaveOut,&wave_hdr,sizeof(WAVEHDR));
    waveOutClose(hWaveOut);

    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(hGLrc);
    ReleaseDC(hWnd, hDC);
    PostQuitMessage(0);
    ExitProcess(0);
    return 0;
}

では、これをdemo.cという名で保存してgcc.exeコンパイルしてみます。 私はmingw-w64のgccを使ってます。

gcc demo.c -O3 -Wall -lopengl32 -lgdi32 -lwinmm -o demo.exe

これで、デモが出来上がりました。このデモはエスケープキーで終了します。デモを作るのに必要なc言語のソースのプロトタイプが手に入りました。ここから派生させて自分で作りやすい環境に整備していきます。次は、このソースを元にpythonのctypesを使い、pythonでデモを作れるように持って行きます。その後webGL との関連性とか研究して、pythonを中心にwebGLとdemoを行ったり来たりしていきたいと思います。

リリース用のソースは

qiita.com

に載ってます。