Python + GLFW + OpenGL

Jusqu'au début

Livre du bas

Introduction à OpenGL avec GLUT / freeglut / Kohei Tokai / Engineering Co., Ltd. est.

environnement

Principalement Linux Mint, Windows secondaire. OpenGL C'est une méthode glBegin qui s'écrit ici et là comme "ancienne" ou "maintenant".

Code de base

Démarrez la page OpenGL (2) Installer PyOpenGL avec Python qui apparaît après avoir recherché "pyglfw tutorial". Si vous ne laissez que l'essence,

#   based on https://maku.blog/p/665rua3/

import glfw
from OpenGL.GL import *

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'Program', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.make_context_current(window)

while not glfw.window_should_close(window):
    glfw.poll_events()

glfw.terminate()

Cependant, si vous faites cela lors de l'exécution du moniteur système, l'un des cœurs du processeur sera à 100%. poll_events () se termine dans un instant, mais il continue à l'appeler à plusieurs reprises. C'est une bonne idée de faire une pause. alors

import time

    time.sleep(1e-3)

Mettez juste dedans. Mais est-ce que je dois le faire moi-même? C'était préparé.

    glfw.wait_events_timeout(1e-3)

Est. Par conséquent, il est basé sur le code précédent poll_events () changé en glfw.wait_events_timeout (1e-3). [Postscript] Je pensais que c'était différent. C'est glfw.wait_events () au lieu de glfw.wait_events_timeout (1e-3). Vous devez lire la référence correctement.

It puts the thread to sleep until at least one event has been received and then processes all received events. This saves a great deal of CPU cycles and is useful for, for example, editing tools.

(Cette phrase est un guide, pas une référence). Le code de cette page a été corrigé. J'étais convaincu que la boucle Event Driven devait fonctionner rapidement. glfw.wait_events_timeout () est utile en animation.

La boucle peut être «pass» car elle ne fait rien. Elle n'accepte même pas l'événement x sur lequel vous appuyez pour terminer le programme et il n'y a aucun moyen de le terminer. Si vous continuez d'appuyer sur x, ce sera "Aucune réponse du programme".

Tout d'abord, obtenez la version

version.py


import glfw
from OpenGL.GL import *

print('GLFW version:', glfw.get_version())

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'prog1 5.1', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.make_context_current(window)

print('Vendor :', glGetString(GL_VENDOR))
print('GPU :', glGetString(GL_RENDERER))
print('OpenGL version :', glGetString(GL_VERSION))

glfw.terminate()

Dans mon environnement, la version GLFW était 3.2.1 pour Linux Mint et 3.3.2 pour Windows. (2020/04/26)

Mais. Sous Windows avec deux anciennes machines The driver does not appear to support OpenGL. Crashed avec le message. Dans les deux cas, les graphiques sont intégrés au processeur. L'un est le i3 M380, ce qui est naturel car la section OpenGL Version est N / A lorsqu'elle est visualisée avec un logiciel appelé OpenGL Extension Viewer. L'autre est i5-2500, et Viewer indique la version 3.1, mais lorsque je fais le test de rendu de Viewer, il plante. Le système graphique est censé prendre en charge OpenGL, mais cela ne semble pas fonctionner.

un événement

Puisqu'il s'agit d'un événement, vous devez d'abord connaître l'événement. event.py est un programme qui détecte deux événements importants liés à la fenêtre, window_size et window_refresh. Dans GLFW, la fonction qui lie le rappel à un événement est nommée glfw.set_xxx_callback. Puisque le premier argument de ces fonctions est window, nous les appellerons après la création de window (après glfw.create_window).

win_event.py


#    mini version

#    size is NOT invoked at start.
#    refresh is NOT invoked at start on Windows.

import glfw
from OpenGL.GL import *

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)

def window_size(window, w, h):
    global n_size
    n_size += 1
    print('Size', n_size, w, h)

def window_refresh(window):
    global n_refresh
    n_refresh += 1
    print('Refresh', n_refresh)

n_size = 0
n_refresh = 0

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'Window events', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_window_size_callback(window, window_size)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

Maintenant, lorsque vous l'exécutez sur le système d'exploitation Windows, vous pouvez voir que la fenêtre est créée mais qu'aucun événement n'est déclenché. Notez que l'actualisation se produit sous Linux. Il est très gênant que le fonctionnement diffère selon le système d'exploitation. Afin de le faire fonctionner sur les deux systèmes d'exploitation avec un code, il est nécessaire de détecter le système d'exploitation ou de le faire correspondre avec Windows qui ne se produit pas. (Ajout: cela peut être une différence dans la version GLFW, pas une différence dans le système d'exploitation.)

Maintenant, quand vous y réfléchissez, rafraîchir n'est que re-. Ce n'est pas un exposé qui est souvent utilisé dans de telles situations. C'est peut-être l'argument de GLFW que le premier dessin n'est que cela. De plus, nous spécifions la taille lors de l'appel de glfw.create_window. C'est peut-être l'argument de GLFW que vous connaissez la taille initiale.

Pour cette raison, il est gênant que l'opération initiale ne puisse pas être laissée au rappel.

Ensuite, si vous iconifiez la fenêtre puis l'ouvrez à nouveau, aucun événement ne se produira sous Linux, mais il se produira sous Windows. Aucun événement ne se produit lorsqu'une partie de la fenêtre est masquée par une autre fenêtre puis remise au premier plan.

Ensuite, si vous modifiez légèrement la taille de la fenêtre en utilisant la souris, vous pouvez voir que les événements de taille et d'actualisation se produisent par paires dans cet ordre.

Je publierai également la version complète.

win_event_full.py


#    full version

#    size is NOT invoked at start.
#    refresh is NOT invoked at start on Windows.
#    size is invoked at iconify on Windows.

import glfw
from OpenGL.GL import *

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)

def window_size(window, w, h):
    global n_size
    n_size += 1
    print('Size', n_size, w, h)

def framebuffer_size(window, w, h):
    global n_FB_size
    n_FB_size += 1
    print('Framebuffer Size', n_FB_size, w, h)

def window_pos(window, x, y):
    global n_pos
    n_pos += 1
    print('Position', n_pos, x, y)

def window_iconify(window, iconified):
    global n_icon
    n_icon += 1
    print('Iconify', n_size, iconified)

def window_refresh(window):
    global n_refresh
    n_refresh += 1
    print('Refresh', n_refresh)

n_size = 0
n_FB_size = 0
n_pos = 0
n_icon = 0
n_refresh = 0

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'Window events', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_window_size_callback(window, window_size)
glfw.set_framebuffer_size_callback(window, framebuffer_size)
glfw.set_window_pos_callback(window, window_pos)
glfw.set_window_iconify_callback(window, window_iconify)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

Notions de base sur les images fixes

C'est le programme de base pour les images fixes qui ne bougent pas du tout. 5.1 dans le nom du fichier dépend de la base, alors ne vous inquiétez pas.

Python:prog1_5.1_GLFW.py


import glfw
from OpenGL.GL import *

def display():
    glClear(GL_COLOR_BUFFER_BIT)

    glBegin(GL_LINE_LOOP)
    glVertex2d(-0.9, -0.9)
    glVertex2d( 0.9, -0.9)
    glVertex2d( 0.9,  0.9)
    glVertex2d(-0.9,  0.9)
    glEnd()

    glfw.swap_buffers(window)

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)
    display()   # necessary only on Windows

def window_refresh(window):
    display()

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'prog1 5.1', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

Puisque le dessin est nécessaire pour l'étape initiale et window_refresh, il est nommé display et est indépendant de la fonction de rappel window_refresh.

Puisque GLFW est un double tampon depuis le début, la fin de la fonction d'affichage est glfw.swap_buffers.

Au fait, que fait la fonction make_context_current? Rendre le contexte "courant" ... L'argument est window. Un programme peut avoir plusieurs fenêtres. C'est make_context_current qui détermine à quelle fenêtre l'opération en cours est destinée. 2wins.py est un programme avec deux fenêtres. Dans ce cas, nous ne pouvons pas attendre un événement tout le temps dans une fenêtre, nous utilisons donc glfw.wait_events_timeout () au lieu de glfw.wait_events.

2wins.py


import glfw
from OpenGL.GL import *


class GLwin:
    def __init__(self, title, xpos, ypos, window_refresh_fun, init_fun):
        self.window = glfw.create_window(300, 300, title, None, None)
        if not self.window:
            glfw.terminate()
            raise RuntimeError('Could not create an window')
        glfw.set_window_pos(self.window, xpos, ypos)
        glfw.set_window_refresh_callback(self.window, window_refresh_fun)
        glfw.make_context_current(self.window)
        init_fun(self.window)

    def window_should_close(self):
        return glfw.window_should_close(self.window)

    def wait_events_timeout(self):
        glfw.make_context_current(self.window)
        glfw.wait_events_timeout(1e-3)

def display1(window):
    glClear(GL_COLOR_BUFFER_BIT)

    glLineWidth(15)
    glColor3d(1.0, 1.0, 0.0)
    glBegin(GL_LINES)
    glVertex2d(-0.8, 0.0)
    glVertex2d( 0.8, 0.0)
    glEnd()

    glfw.swap_buffers(window)

def display2(window):
    glClear(GL_COLOR_BUFFER_BIT)

    glLineWidth(15)
    glColor3d(1.0, 0.0, 1.0)
    glBegin(GL_LINES)
    glVertex2d(0.0, -0.8)
    glVertex2d(0.0,  0.8)
    glEnd()

    glfw.swap_buffers(window)

def init1(window):
    glClearColor(1.0, 0.0, 0.0, 1.0)
    display1(window)   # necessary only on Windows

def init2(window):
    glClearColor(0.0, 1.0, 0.0, 1.0)
    display2(window)   # necessary only on Windows

def window_refresh1(window):
    display1(window)

def window_refresh2(window):
    display2(window)


if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

glwin1 = GLwin('Window 1', 100, 200, window_refresh1, init1)
glwin2 = GLwin('Window 2', 450, 200, window_refresh2, init2)

while not ( glwin1.window_should_close() or glwin2.window_should_close() ):
    glwin1.wait_events_timeout()
    glwin2.wait_events_timeout()

glfw.terminate()

contribution

Souris

L'événement est le suivant. cursor_pos Lorsque le curseur se déplace cursor_enter Lorsque le curseur entre ou sort de la fenêtre mouse_button Lorsque le bouton de la souris est enfoncé défilement Lorsque la molette au centre de la souris est tournée (y), lorsqu'elle est inclinée à gauche ou à droite (x)

Dans GLFW, les coordonnées x et y ne peuvent pas être obtenues, donc si les coordonnées sont nécessaires, obtenez-les avec glfw.get_cursor_pos.

mouse_event.py


import glfw
from OpenGL.GL import *

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)
    display()   # necessary only on Windows

def display():
    glClear(GL_COLOR_BUFFER_BIT)
    glfw.swap_buffers(window)

def cursor_pos(window, xpos, ypos):
    print('cursor_pos:', xpos, ypos)

def cursor_enter(window, entered):
    print('cursor_enter:', entered)

def mouse_button(window, button, action, mods):
    pos = glfw.get_cursor_pos(window)
    print('mouse:', button, end='')

    if button == glfw.MOUSE_BUTTON_LEFT:
        print('(Left)', end='')
    if button == glfw.MOUSE_BUTTON_RIGHT:
        print('(Right)', end='')
    if button == glfw.MOUSE_BUTTON_MIDDLE:
        print('(Middle)', end='')

    if action == glfw.PRESS:
        print(' press')
    elif action == glfw.RELEASE:
        print(' release')
    else:
        print(' hogehoge')

    x, y = pos
    print(pos, x, y)

def scroll(window, xoffset, yoffset):
    print('scroll:', xoffset, yoffset)

def window_refresh(window):
    display()


if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'mouse on GLFW', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_cursor_pos_callback(window, cursor_pos)
glfw.set_cursor_enter_callback(window, cursor_enter)
glfw.set_mouse_button_callback(window, mouse_button)
glfw.set_scroll_callback(window, scroll)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

clavier

Ce que vous obtenez avec la clé est une valeur entière appelée code clé. Mais vous n'avez pas besoin de savoir à combien s'élève le code clé de la clé XX. Toutes les clés ont des constantes. Le programme montre des exemples de A, des touches de curseur vers le haut et entrée.

keyboard.py


import glfw
from OpenGL.GL import *

def display():
    glClear(GL_COLOR_BUFFER_BIT)
    glfw.swap_buffers(window)

def init():
    glClearColor(0.0, 0.0, 1.0, 1.0)
    display()   # necessary only on Windows

def keyboard(window, key, scancode, action, mods):
    print('KB:', key, chr(key), end=' ')
    if action == glfw.PRESS:
        print('press')
    elif action == glfw.REPEAT:
        print('repeat')
    elif action == glfw.RELEASE:
        print('release')
    if key == glfw.KEY_A:
        print('This is A.')
    elif key == glfw.KEY_UP:
        print('This is Up.')
    elif key == glfw.KEY_ENTER:
        print('This is Enter.')

def window_refresh(window):
    display()


if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(300, 300, 'KB on GLFW', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_key_callback(window, keyboard)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

Comment lire la référence

pyglfw Ce qui semble être une page de projet ne dit pas grand-chose. Il y a un lien appelé Page d'accueil sur cette page, mais le lien est rompu. Il existe une page appelée Miroir. J'aimerais voir une référence sur une page comme celle-ci, mais je ne la trouve pas.

Alors, jetez un œil à la page GLFW. Si vous allez dans Documentation> Documentation HTML, vous trouverez à la fois un didacticiel et une référence.

La question est, la page GLFW est écrite en C, mais que se passe-t-il en Python?

Si vous comparez les précédents avec des fonctions, glfwCreateWindow → glfw.create_window Ça ressemble à ça. Etui chameau → Etui serpent. Le nombre d'arguments peut différer entre C et Python. Glfw.get_cursor_pos (window) de Python a un argument, seulement window, et renvoie les coordonnées en tapple, mais en C void glfwGetCursorPos(GLFWwindow * window, double * xpos, double * ypos); C'est sous la forme de.

Pour les constantes, cela ressemble à GLFW_MOUSE_BUTTON_LEFT → glfw.MOUSE_BUTTON_LEFT.

Programmes qui doivent d'abord gérer la taille de la fenêtre

Ayons un code comme celui-ci. Dans la fonction resize, sous Windows, il est nécessaire de prendre des mesures contre l'événement window_size survenant au moment de l'iconification et la taille de la fenêtre devenant 0.

[Postscript 2020/5/19] Les arguments de la fonction ont été décrits dans la section Libapi de la page du projet pyglfw, qui disait "Pas grand chose" ci-dessus.

Python:8.4_GLFW.py


import glfw
from OpenGL.GL import *
from OpenGL.GLU import *

W_INIT = 360
H_INIT = 300

VERTEX = [
    [0.0, 0.0, 0.0],   # A
    [1.0, 0.0, 0.0],   # B
    [1.0, 1.0, 0.0],   # C
    [0.0, 1.0, 0.0],   # D
    [0.0, 0.0, 1.0],   # E
    [1.0, 0.0, 1.0],   # F
    [1.0, 1.0, 1.0],   # G
    [0.0, 1.0, 1.0]    # H
]

EDGE = [
    [0, 1], # a (A-B)
    [1, 2], # i (B-C)
    [2, 3], # u (C-D)
    [3, 0], # e (D-A)
    [4, 5], # o (E-F)
    [5, 6], # ka (F-G)
    [6, 7], # ki (G-H)
    [7, 4], # ku (H-E)
    [0, 4], # ke (A-E)
    [1, 5], # ko (B-F)
    [2, 6], # sa (C-G)
    [3, 7]  # shi (D-H)
]

def display():
    glClear(GL_COLOR_BUFFER_BIT)

    glBegin(GL_LINES)
    for edge1 in EDGE:
        for i in edge1:
            glVertex3dv(VERTEX[i])
    glEnd()

    glfw.swap_buffers(window)

def set_view(w, h):
    glLoadIdentity()
    gluPerspective(35.0, w/h, 1.0, 100.0)
    gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)

def resize(window, w, h):
    # for iconify on Windows
    if h==0:
        return
    glViewport(0, 0, w, h)
    set_view(w, h)

def init():
    gray = 0.6
    glClearColor(gray, gray, gray, 1.0)
    set_view(W_INIT, H_INIT)
    display()   # necessary only on Windows

def window_refresh(window):
    display()

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

window = glfw.create_window(W_INIT, H_INIT, 'Wireframe', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_window_size_callback(window, resize)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    glfw.wait_events()

glfw.terminate()

animation

Dans la boucle, s'il est en cours d'exécution, le dessin suivant peut être fait, et poll_events, sinon wait_events_timeout. Lors de l'exécution, il sera plus lent si vous définissez wait_events_timeout au lieu de poll_events. La définition de DOUBLE_BUFFERING au début sur False entraîne une mise en mémoire tampon unique, qui scintille au lieu d'être rapide.

Python:9.1-2_GLFW.py


import glfw
from OpenGL.GL import *
from OpenGL.GLU import *

DOUBLE_BUFFERING = True

W_INIT = 320
H_INIT = 240

VERTEX = [
    [0.0, 0.0, 0.0],   # A
    [1.0, 0.0, 0.0],   # B
    [1.0, 1.0, 0.0],   # C
    [0.0, 1.0, 0.0],   # D
    [0.0, 0.0, 1.0],   # E
    [1.0, 0.0, 1.0],   # F
    [1.0, 1.0, 1.0],   # G
    [0.0, 1.0, 1.0]    # H
]

EDGE = [
    [0, 1], # a (A-B)
    [1, 2], # i (B-C)
    [2, 3], # u (C-D)
    [3, 0], # e (D-A)
    [4, 5], # o (E-F)
    [5, 6], # ka (F-G)
    [6, 7], # ki (G-H)
    [7, 4], # ku (H-E)
    [0, 4], # ke (A-E)
    [1, 5], # ko (B-F)
    [2, 6], # sa (C-G)
    [3, 7]  # shi (D-H)
]

def display():
    global r

    glClear(GL_COLOR_BUFFER_BIT)

    glLoadIdentity()
    gluLookAt(3.0, 4.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
    glRotated(r, 0.0, 1.0, 0.0)
    
    glColor3d(0.0, 0.0, 0.0)
    glBegin(GL_LINES)
    for edge1 in EDGE:
        for i in edge1:
            glVertex3dv(VERTEX[i])
    glEnd()

    if DOUBLE_BUFFERING:
        glfw.swap_buffers(window)
    else:
        glFlush()
    
    r += 1
    if r==360:
        r = 0

def set_view(w, h):
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(35.0, w/h, 1.0, 100.0)
    glMatrixMode(GL_MODELVIEW)

def resize(window, w, h):
    # for iconify on Windows
    if h==0:
        return
    glViewport(0, 0, w, h)
    set_view(w, h)

def mouse_button(window, button, action, mods):
    global rotation
    if action == glfw.PRESS:
        rotation = ( button == glfw.MOUSE_BUTTON_LEFT )

def init():
    gray = 0.8
    glClearColor(gray, gray, gray, 1.0)
    set_view(W_INIT, H_INIT)
    display()   # necessary only on Windows

def window_refresh(window): # for resize
    display()


r = 0
rotation = False

if not glfw.init():
    raise RuntimeError('Could not initialize GLFW3')

if not DOUBLE_BUFFERING:
    glfw.window_hint(glfw.DOUBLEBUFFER, glfw.FALSE)

window = glfw.create_window(W_INIT, H_INIT, 'Animation on GLFW', None, None)
if not window:
    glfw.terminate()
    raise RuntimeError('Could not create an window')

glfw.set_mouse_button_callback(window, mouse_button)
glfw.set_window_size_callback(window, resize)
glfw.set_window_refresh_callback(window, window_refresh)
glfw.make_context_current(window)

init()

while not glfw.window_should_close(window):
    if rotation:
        display()
        glfw.poll_events()
#        glfw.wait_events_timeout(1e-2)
    else:
        glfw.wait_events_timeout(1e-3)

glfw.terminate()

Recommended Posts

Python + GLFW + OpenGL
Python
Raspeye + Python + Mémo OpenGL
python kafka
Les bases de Python ⑤
Résumé Python
Python intégré
Notation d'inclusion Python
Technique Python
Compte à rebours Python 2.7
Mémorandum Python
Python FlowFishMaster
Service Python
astuces python
fonction python ①
Les bases de Python
Mémo Python
ufo-> python (3)
Notation d'inclusion Python
Installer python
Python Singleton
Les bases de Python ④
Mémorandum Python 2
mémo python
Python Jinja2
Incrément Python
atCoder 173 Python
[Python] fonction
Installation de Python
Installer Python 3.4.3.
Essayez Python
Mémo Python
Itératif Python
Algorithme Python
Python2 + mot2vec
[Python] Variables
Fonctions Python
Python sys.intern ()
Tutoriel Python
Fraction Python
underbar python C'est ce que
Résumé Python
Démarrer python
[Python] Trier
Remarque: Python
Les bases de Python ③
Sortie du journal python
Les bases de Python
[Scraping] Scraping Python
Mise à jour Python (2.6-> 2.7)
mémo python
Mémorandum Python
Python #sort
ufo-> python
Python nslookup
apprentissage de python
[Rpmbuild] Python 3.7.3.
Python au prorata (1)
mémorandum python