Display pyopengl in a browser (python + eel)

Introduction

In the browser, OpenGL already has WebGL. But I can't use compute shader and other shaders. The grammar is also different. However, the GUI is overwhelmingly better with WebGL. Easy to use. I don't think most people are using pyopengl. I think it's because the GUI doesn't help. Someday I wanted to do pyopengl in a browser. Recently, when I was collecting information for the first time in EEL, [OpenCV] Display OpenCV images on Eel I found. This will have to be done. I didn't have any knowledge about CV, so I was really into it, but I was able to do it.

Source code

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>

in conclusion

It is realized by python + eel. For more information, search for python, eel. I also wrote the closing process of OpenGL safely. I'm doing it in python, so you may not need it. I'm not sure about this. I'm too enthusiastic and I don't think anyone will see it, but it also serves as a memorandum of my own.

Recommended Posts

Display pyopengl in a browser (python + eel)
Display a list of alphabets in Python 3
Display Python 3 in the browser with MAMP
Take a screenshot in Python
Create a function in Python
Create a dictionary in Python
Make a bookmarklet in Python
Display a histogram of image brightness values in python
Draw a heart in Python
[Django3] Display a web page in Django3 + WSL + Python virtual environment
VScode environment construction (on Mac) & graph display in Python (@browser)
Maybe in a python (original title: Maybe in Python)
Write a binary search in Python
[python] Manage functions in a list
Hit a command in Python (Windows)
Draw a scatterplot matrix in python
ABC166 in Python A ~ C problem
Write A * (A-star) algorithm in Python
Create a binary file in Python
Waveform display of audio in Python
Display characters like AA in python
Solve ABC036 A ~ C in Python
Write a vim plugin in Python
Write a depth-first search in Python
Implementing a simple algorithm in Python 2
Solve ABC037 A ~ C in Python
Run a simple algorithm in Python
Draw a CNN diagram in Python
Create a random string in Python
Schedule a Zoom meeting in Python
When writing a program in Python
Generate a first class collection in Python
Display LaTeX notation formulas in Python, matplotlib
[Python, Julia] 3D display in Jupyter-Mayavi library
Spiral book in Python! Python with a spiral book! (Chapter 14 ~)
Solve ABC175 A, B, C in Python
Use print in a Python2 lambda expression
A simple HTTP client implemented in Python
Do a non-recursive Euler Tour in Python
Django ~ Let's display it in the browser ~
I made a payroll program in Python!
Precautions when pickling a function in python
Write the test in a python docstring
Display matplotlib diagrams in a web application
Try sending a SYN packet in Python
Try drawing a simple animation in Python
Create a simple GUI app in Python
Create a JSON object mapper in Python
Write a short property definition in Python
Draw a heart in Python Part 2 (SymPy)
[Python] [Windows] Take a screen capture in Python
Run the Python interpreter in a script
How to get a stacktrace in python
How to display multiplication table in python
Write a Caesar cipher program in Python
Hash in Perl is a dictionary in Python
Scraping a website using JavaScript in Python
Write a simple greedy algorithm in Python
Solve ABC165 A, B, D in Python
[GPS] Create a kml file in Python
Write a simple Vim Plugin in Python 3