はじめに
ブラウザで、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でやっているので、いらないかもしれません。このあたりのことは、よくわかりません。
マニアック過ぎて、見る人もいないと思いますが、自分の忘備録を兼ねてます。