SSBOでMeshを作る
ここ何年かvertex shaderでmeshを作っては、挫折しての繰り返し。compute shaderならイケるかなと閃いた。
これから試して行きます。 球のmeshをcompute shaderで作った奴。説明は、もう少し理解したら追記しておきます。とりあえず動いているソースを載せておきます。
from OpenGL.GL import * from OpenGL.WGL import * from ctypes import * from ctypes.wintypes import * import sys vsh = """ #version 430 struct Model{ vec3 pos; vec3 nor; vec2 uv; }; layout(std430, binding=7) buffer particles{ Model p[]; }; uniform vec2 resolution; uniform float time; out vec2 uv; mat4 perspective(float fov, float aspect, float near, float far) { float v = 1./tan(radians(fov/2.)), u = v/aspect, w = near-far; return mat4(u,0,0,0,0,v,0,0,0,0,(near+far)/w,-1,0,0,near*far*2./w,1); } mat4 lookAt(vec3 eye, vec3 center, vec3 up) { vec3 w = normalize(eye - center),u = normalize(cross(up, w)),v=cross(w, u); return transpose(mat4(u,-dot(u,eye),v,-dot(v,eye),w,-dot(w,eye),0,0,0,1)); } vec3 rotate(vec3 p,vec3 n,float a) { n = normalize(n); vec3 v = cross(p, n), u = cross(v, n); return u * cos(a) + v * sin(a) + n * dot(p, n); } void main() { mat4 pMat = perspective(45.0, resolution.x / resolution.y, 0.1, 100.0); mat4 vMat = lookAt(vec3(0,2,-2.5), vec3(0,0,0), vec3(0,1,0)); gl_Position = pMat*vMat*vec4(rotate(p[gl_VertexID].pos,vec3(0,1,0),time), 1); int id = gl_VertexID%3; uv = vec2(id&1, id>>1); } """ fsh = """ #version 430 in vec2 uv; out vec4 fragColor; void wireFrame() { float a = min(min(uv.x,uv.y),abs(uv.x-uv.y)); if (a > 0.05) discard; } void main() { fragColor = vec4(1); wireFrame(); } """ csh = """ #version 430 struct Model{ vec3 pos; vec3 nor; vec2 uv; }; layout(std430, binding=7) buffer particles{ Model p[]; }; layout(local_size_x = 128, local_size_y = 1, local_size_z = 1) in; uniform int split; vec2 getUV(uint i) { int id =int(i/6); vec2 uv = vec2(id%split,id/split); id = abs(3-int(i%6)); return (uv + vec2(id&1, id>>1))/split; } #define PI 3.14159265359 #define PI2 ( PI * 2.0 ) #define PIH ( PI / 2.0 ) vec3 map(vec2 p) { //return vec3(p*2,0); p = p * 2.0 - 1.0; vec3 pos = vec3(vec2(cos(p.y * PIH), sin(p.y * PIH)), 0.0); pos.xz = pos.x * vec2(sin(p.x * PI), cos(p.x * PI)); return pos; } void main(){ uint id = gl_GlobalInvocationID.x; vec2 uv = getUV(id); p[id].pos = p[id].nor = map(uv); p[id].uv=uv; } """ winmm = windll.winmm kernel32 = windll.kernel32 user32 = windll.user32 XRES = 1920 // 4 YRES = 1080 // 4 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 #hWnd = user32.CreateWindowExA(0,0xC018,0,WS_OVERLAPPEDWINDOW|WS_VISIBLE,30,30,XRES,YRES,0,0,0,0) hWnd = user32.CreateWindowExA(0,0xC018,0,WS_POPUP|WS_VISIBLE,100,100,XRES,YRES,0,0,0,0) hdc = user32.GetDC(hWnd) user32.SetForegroundWindow(hWnd) 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) hGLrc = wglCreateContext(hdc) wglMakeCurrent(hdc, hGLrc) split= 20 data_size = split*split*6 glClearColor(0, 0, 0, 1) glEnable(GL_CULL_FACE) glCullFace(GL_BACK) glEnable(GL_DEPTH_TEST) glDepthFunc(GL_LEQUAL) program = glCreateProgram() for s, t in zip((vsh, fsh), (GL_VERTEX_SHADER, GL_FRAGMENT_SHADER)): shader = glCreateShader(t) glShaderSource(shader, s) 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) computeProg = glCreateProgram() shader = glCreateShader(GL_COMPUTE_SHADER) glShaderSource(shader, csh) glCompileShader(shader) if glGetShaderiv(shader, GL_COMPILE_STATUS) != GL_TRUE: raise RuntimeError(glGetShaderInfoLog(shader).decode()) glAttachShader(computeProg, shader) glLinkProgram(computeProg) glUseProgram(computeProg) glUniform1i(glGetUniformLocation(computeProg, "split"), split) ssbo = glGenBuffers(1) glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo) glBufferData(GL_SHADER_STORAGE_BUFFER, 4 *(4+4+4) * data_size, None, GL_STATIC_DRAW) glUseProgram(program); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, ssbo) glUseProgram(computeProg) glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, ssbo) glUseProgram(computeProg); glDispatchCompute(data_size//128+1, 1, 1) duration = 60 msg = MSG() lpmsg = pointer(msg) zero = winmm.timeGetTime() done = False fps, cnt, s0 = 0, 0, 0 while done==False: while user32.PeekMessageA(lpmsg, 0, 0, 0, PM_REMOVE): if (msg.message == WM_NCLBUTTONDOWN and msg.wParam == HTCLOSE): done = True user32.DispatchMessageA(lpmsg) if(user32.GetAsyncKeyState(VK_ESCAPE)): done = True time = (winmm.timeGetTime() - zero)*0.001 glUseProgram(program); glUniform1f(glGetUniformLocation(program, "time"), time) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glDrawArrays(GL_TRIANGLES, 0, data_size ) SwapBuffers(hdc) cnt += 1 if (time - s0 > 1): fps = cnt cnt = 0 s0 = time sys.stdout.write("\r FPS : %d TIME : %f" %(fps,time)) sys.stdout.flush() if (time > duration): done = True wglMakeCurrent(0, 0) wglDeleteContext(hGLrc) user32.ReleaseDC(hWnd, hdc) user32.PostQuitMessage(0) user32.DestroyWindow(hWnd)
どうやら、自分は、データを持たないで、データを持ったopenGLのデモと同等の事をshaderと最低限度のopenGLで作ることを目指しているようだ。