テラByteの時代にキロByte

shader又はdemosceneに関係する事

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で作ることを目指しているようだ。