3
6

More than 3 years have passed since last update.

pyopenglをブラウザで表示する(python + eel)

Last updated at Posted at 2020-05-19

はじめに

ブラウザで、OpenGLだと既にWebGLがある。だけど、compute shaderと他のshaderが使えない。文法も違う。だけど、GUIが圧倒的にWebGLの方が良い。手軽さも良い。
pyopenglは、ほぼ使ってる人がいないと思う。GUIが、どうにもならないからだと思う。いつかブラウザでpyopenglをやりたいと思っていた。
最近、EELをはじめて、情報を漁っていたら、
【OpenCV】OpenCVの画像をEelに表示する 
を見つけた。これは、やるしかないでしょう。CVとか知識がなかったから、かなりハマったけど、できた。

ソースコード

main.py
import eel
from OpenGL.GL import *
from OpenGL.WGL import *
from ctypes import *
from ctypes.wintypes import *
import numpy as np
import cv2
import base64
import time
import atexit

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

# window init
XRES, YRES = user32.GetSystemMetrics(0), user32.GetSystemMetrics(1)
WS_OVERLAPPEDWINDOW = 0xcf0000
hWnd = user32.CreateWindowExA(0,0xC018,0,WS_OVERLAPPEDWINDOW,0,0,XRES,YRES,0,0,0,0)
hdc = user32.GetDC(hWnd)   
user32.SetForegroundWindow(hWnd)

# GL context init
PFD_SUPPORT_OPENGL = 32
PFD_DOUBLEBUFFER = 1
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)

def finish():
    # GL context finish
    wglMakeCurrent(0, 0)
    wglDeleteContext(hGLrc)
    # windoe finish
    user32.ReleaseDC(hWnd, hdc)
    user32.PostQuitMessage(0)
    user32.DestroyWindow(hWnd)
    #print("finish")
atexit.register(finish)


def main():
    vsh = """
#version 430

layout(location = 0) in vec2 p;

void main(){
    gl_Position = vec4(p, 0, 1);
}
"""

    fsh = """
#version 430

out vec4 fragColor;

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));
    fragColor = vec4(g*.3, g*.5, g, 1)+.3;
}
"""

    width = 640
    height = 350


    # GL init
    glClearColor(0, 0, 0, 1)
    glEnable(GL_CULL_FACE)
    glCullFace(GL_BACK)
    glEnable(GL_DEPTH_TEST)
    glDepthFunc(GL_LEQUAL)
    glViewport(0,0,width,height)

    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"), width,height)
    vertices = np.array([-1,-1,1,-1,-1,1,1,1], np.float32)
    glBindBuffer(GL_ARRAY_BUFFER, glGenBuffers(1))
    glBufferData(GL_ARRAY_BUFFER, vertices.nbytes, (c_float*len(vertices))(*vertices), GL_STATIC_DRAW)
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
    glEnableVertexAttribArray(0)

    pixels = np.zeros((height,width,3), np.uint8)
    st = time.time()
    while True:
        eel.sleep(0.01)
        elapsedTime = time.time()-st
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glUniform1f(glGetUniformLocation(program, "time"), elapsedTime)
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
        glReadPixels(0, 0, width,height, GL_BGR, GL_UNSIGNED_BYTE, pixels)
        _, image = cv2.imencode('.jpg', np.flipud(pixels))
        eel.set_base64image("data:image/jpg;base64," + base64.b64encode(image).decode())
        eel.set_elapsedtime(round(elapsedTime,1))

if __name__ == '__main__':
    eel.init('web')
    eel.start('index.html', block=False)
    main()

index.html
<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <script type="text/javascript" src="/eel.js"></script>

        <script>
            eel.expose(set_elapsedtime);
            function set_elapsedtime(elapsedtime0) {
                document.getElementById("elapsedtime").innerHTML = "elapsedtime:" + elapsedtime0 + "s";
            }
            eel.expose(set_base64image);
            function set_base64image(img) {
                document.getElementById("python_video").src = img;
            }
        </script>
    </head>
    <body>
        <div id="elapsedtime">elapsedtime</div>
        <img id="python_video" >
    </body>
</html>

おわりに

python + eelで実現してます。詳しい事は、python,eelで検索してください。
OpenGLの終了処理も無難に書いておきました。pythonでやっているので、いらないかもしれません。このあたりのことは、よくわかりません。
マニアック過ぎて、見る人もいないと思いますが、自分の忘備録を兼ねてます。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6