テラByteの時代にキロByte

shader又はdemosceneに関係する事

demoのc言語のソースをpythonに移植。

c言語のソースをpythonに移植しました。だいたい似た感じでつくっているので、この2つを見ていけば、python<->c言語は出来るようになると思います。関数の引数とか手順に関して言えば、全く同じです。pythonの場合は、フラグの定数とか、構造体を別途、書かなければなりませんが、一回手に入れれば使いまわすだけです。
このデモはエスケープキーで終了します。

from OpenGL.GL import *
from OpenGL.WGL import *
from ctypes import *
from ctypes.wintypes import *
import sys
import time

vsh = """
void main(){
  gl_Position = gl_Vertex;
}
"""

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);
  }
"""

msh = """
#version 330
out vec2 gain;
uniform float sampleRate;
vec2 mainSound(float time)
{
  return vec2(sin(6.2831*440.0*fract(time)));
}

void main()
{
  float time = float(gl_VertexID) / sampleRate;
  gain = mainSound(time) ; 
}
"""

winmm = windll.winmm 
kernel32 = windll.kernel32
user32 = windll.user32

WS_POPUP = 0x80000000
WS_OVERLAPPEDWINDOW = 0xcf0000
WS_VISIBLE = 0x10000000
PM_REMOVE = 1
WM_NCLBUTTONDOWN = 161
HTCLOSE = 20
VK_ESCAPE = 27
PFD_SUPPORT_OPENGL = 32
PFD_DOUBLEBUFFER = 1
WAVE_FORMAT_IEEE_FLOAT = 3
WAVE_MAPPER = -1 
TIME_SAMPLES = 2

class WAVEFORMATEX(Structure): 
    _fields_ = [ 
            ("wFormatTag", WORD), 
            ("nChannels", WORD), 
            ("nSamplesPerSec", DWORD), 
            ("nAvgBytesPerSec", DWORD), 
            ("nBlockAlign", WORD), 
            ("wBitsPerSample", WORD), 
            ("cbSize", WORD) 
    ]

class WAVEHDR(Structure): 
    pass 
LPWAVEHDR = POINTER(WAVEHDR) 
WAVEHDR._fields_ = [ 
    ("lpData", LPSTR), 
    ("dwBufferLength", DWORD), 
    ("dwBytesRecorded", DWORD), 
    ("dwUser", POINTER(DWORD)), 
    ("dwFlags", DWORD), 
    ("dwLoops", DWORD), 
    ("lpNext", LPWAVEHDR), 
    ("reserved", POINTER(DWORD)) 
]

class SMPTE(Structure):
     _fields_ = [ 
        ("hour", BYTE),
        ("min", BYTE),
        ("sec", BYTE),
        ("frame", BYTE),
        ("fps", BYTE),
        ("dummy", BYTE),
        ("pad"[2], BYTE)
     ]

class MIDI(Structure):
     _fields_ = [ 
        ("songptrpos", DWORD)
     ]

class U(Union):
     _fields_ = [ 
        ("ms", DWORD),
        ("sample", DWORD),
        ("cb", DWORD),
        ("ticks", DWORD),
        ("smpte", SMPTE),
        ("midi", MIDI),
    ]

class MMTIME(Structure):
     _fields_ = [ 
        ("wType", UINT),
        ("u", U),
    ]

XRES = 640
YRES = 480
SND_DURATION    = 180
SAMPLE_RATE     = 44100
SND_NUMCHANNELS = 2
SND_NUMSAMPLES  = (SND_DURATION*SAMPLE_RATE)
SND_NUMSAMPLESC = (SND_NUMSAMPLES*SND_NUMCHANNELS)
samples = (c_float * SND_NUMSAMPLESC)()

    
def main():
    hdc = user32.GetDC(user32.CreateWindowExA(0,0xC018,0,WS_OVERLAPPEDWINDOW|WS_VISIBLE,0,0,XRES,YRES,0,0,0,0))
    #hdc = user32.GetDC(user32.CreateWindowExA(0,0xC018,0,WS_POPUP|WS_VISIBLE,0,0,XRES,YRES,0,0,0,0))
    pfd = 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(hdc,ChoosePixelFormat(hdc, pfd), pfd)
    wglMakeCurrent(hdc, wglCreateContext(hdc))
    
    # mzk init
    programM = glCreateProgram()
    shader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(shader, msh)
    glCompileShader(shader)
    if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
      raise RuntimeError(glGetShaderInfoLog(shader).decode())
    glAttachShader(programM, shader)
    outs = cast((c_char_p*1)(b"gain"), POINTER(POINTER(c_char)))
    glTransformFeedbackVaryings(programM, 1, outs, GL_INTERLEAVED_ATTRIBS)
    glLinkProgram(programM)
    glUseProgram(programM)
    vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(GL_ARRAY_BUFFER, sizeof(samples), None, GL_STATIC_DRAW)
    glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo)
    glUniform1f(glGetUniformLocation(programM, "sampleRate"), SAMPLE_RATE)
    glEnable(GL_RASTERIZER_DISCARD)
    glBeginTransformFeedback(GL_POINTS)
    glDrawArrays(GL_POINTS, 0, SND_NUMSAMPLES)
    glEndTransformFeedback()
    glDisable(GL_RASTERIZER_DISCARD)
    glGetBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(samples), byref(samples))

    # gfx init
    global program
    program = glCreateProgram()
    shader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(shader, vsh)
    glCompileShader(shader)
    if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
      raise RuntimeError(glGetShaderInfoLog(shader).decode())
    glAttachShader(program, shader)
    shader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(shader, fsh)
    glCompileShader(shader)
    if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE:
      raise RuntimeError(glGetShaderInfoLog(shader).decode())
    glAttachShader(program, shader)
    glLinkProgram(program)
    glUseProgram(program)
    glUniform2f(glGetUniformLocation(program, "resolution"), XRES, YRES)
    
    wh = WAVEHDR(cast(samples, LPSTR), sizeof(samples)) 
    wfx = WAVEFORMATEX(
        WAVE_FORMAT_IEEE_FLOAT,
        SND_NUMCHANNELS,
        SAMPLE_RATE,
        SAMPLE_RATE*sizeof(c_float)*SND_NUMCHANNELS,
        sizeof(c_float)*SND_NUMCHANNELS,
        sizeof(c_float)*8,
        0
    )
    hwo = HANDLE(0) 
    winmm.waveOutOpen(byref(hwo), WAVE_MAPPER, byref(wfx), 0, 0, 0)
    winmm.waveOutPrepareHeader(hwo, byref(wh), sizeof(WAVEHDR))
    winmm.waveOutWrite(hwo, byref(wh), sizeof(WAVEHDR)) 
    mmt = MMTIME(TIME_SAMPLES)
    msg = MSG()
    lpmsg = pointer(msg)
    fps, cnt, s0 = 0, 0, 0
    while True:
        while user32.PeekMessageA(lpmsg, 0, 0, 0, PM_REMOVE):
            if (msg.message == WM_NCLBUTTONDOWN and msg.wParam == HTCLOSE): sys.exit()
            user32.DispatchMessageA(lpmsg)
        if(user32.GetAsyncKeyState(VK_ESCAPE)): sys.exit()
        winmm.waveOutGetPosition(hwo, byref(mmt), sizeof(mmt))
        glUniform1f(glGetUniformLocation(program, "time"), mmt.u.sample / SAMPLE_RATE)
        glRects(1, 1, -1, -1)
        SwapBuffers(hdc)
        cnt += 1
        if (mmt.u.sample - s0 > SAMPLE_RATE):
            fps = cnt      
            cnt = 0
            s0 = mmt.u.sample
        sys.stdout.write("\r FPS : %d TIME : %f" %(fps, mmt.u.sample / SAMPLE_RATE))
        sys.stdout.flush()
        if (mmt.u.sample == SND_NUMSAMPLES): sys.exit()

if __name__ == '__main__':
    main()

webGLに移植に関しては、ちょっと面倒です。同様の関数が無かったりすることがあるからです。手順に関しては、全く同じ良いです。