Introduction to OpenGL with GLUT / freeglut / Kohei Tokai / Engineering Co., Ltd. is.
Mainly Linux Mint, secondary Windows. OpenGL It is a glBegin method that is written here and there as "old" or "now".
Start the page OpenGL (2) Install PyOpenGL with Python that appears after searching for "pyglfw tutorial". If you leave only the 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()
However, if you do this while running System Monitor, one of the CPU cores will be 100%. poll_events ()
ends in an instant, but it keeps calling this repeatedly. It's a good idea to take a break. so
import time
time.sleep(1e-3)
Just put in. But do I have to do this myself? It was prepared.
glfw.wait_events_timeout(1e-3)
Is. Therefore, it is based on the previous code poll_events ()
changed to glfw.wait_events_timeout (1e-3)
.
[Postscript]
I thought it was different. Not glfw.wait_events_timeout (1e-3)
, but glfw.wait_events ()
. You have to read the reference properly.
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.
(This sentence is a guide, not a reference). The code on this page has been fixed. I was convinced that the Event Driven loop had to run fast.
glfw.wait_events_timeout ()
is useful in animations.
The loop can be pass
because it does nothing. It doesn't even accept the x event that you press to end the program, and there is no way to end it. If you keep pressing x, you will get "No response from Program".
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()
In my environment, the GLFW version was 3.2.1 for Linux Mint and 3.3.2 for Windows. (2020/4/26)
But. On Windows with two old machines The driver does not appear to support OpenGL. I got the message and crashed. In both cases, the graphics are built into the CPU. One is i3 M380, which is natural because the OpenGL Version section is N / A when viewed with software called OpenGL Extension Viewer. The other is i5-2500, and Viewer says Version 3.1, but when I do Rendering Test of Viewer, it crashes. The graphics system is supposed to support OpenGL, but it doesn't seem to work.
Since it is an event driven, you must first know the event. event.py is a program that detects two important window-related events, window_size and window_refresh. In GLFW, the function that associates a callback with an event is named glfw.set_xxx_callback
. Since the first argument of these functions is window, we will call them after the window is created (after 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()
Now, when you run this on a Windows OS, you can see that the window is created, but no event has occurred. Note that refresh occurs on Linux. It is very troublesome that the operation differs depending on the OS. In order to make it work on both OSs with one code, it is necessary to detect the OS or match it with Windows that does not occur. (Addition: It may be a difference in GLFW version, not a difference in OS.)
Now, when you think about it, refresh is just re-. It is not an expose that is often used in such situations. It may be GLFW's argument that the first drawing is just that. We also specify size when calling glfw.create_window. It may be GLFW's argument that you know the initial size.
For this reason, it is troublesome that the initial operation cannot be left to callback.
Next, if you iconify the window and then open it again, no event will occur on Linux, but it will occur on Windows. No event occurs when a part of the window is hidden by another window and then brought to the front again.
Next, if you gently change the window size slightly by operating the mouse, you can see that the size and refresh events occur in pairs in this order.
I will also post the full version.
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()
This is the basic program for still images that do not move at all. 5.1 in the file name depends on the base, so don't worry.
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()
Since drawing is necessary for the initial stage and window_refresh, it is named display and is independent of the callback function window_refresh.
Since GLFW is double buffering from the beginning, the end of the display function is glfw.swap_buffers.
By the way, what is the function make_context_current doing? Make the context "current" ... The argument is window. A program can have multiple windows. It is make_context_current that determines which window the current operation is for. 2wins.py is a program with two windows. In this case, we can't wait for an event all the time in one window, so we're using glfw.wait_events_timeout ()
instead of 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()
The event is as follows. cursor_pos When the cursor moves cursor_enter When the cursor enters or exits the window mouse_button When the mouse button is pressed scroll When the dial in the center of the mouse is turned (y), when it is tilted left or right (x)
In GLFW, the coordinates x and y cannot be obtained, so if the coordinates are needed, obtain them with 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()
What you get with key is an integer value called key code. But you don't need to know how much the key code for the key XX is. All keys have constants. The program shows examples of A, the up cursor key, and enter.
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()
The pyglfw pages that appear to be project pages doesn't say much. There is a link called homepage on this page, but the link is broken. There is a page called Mirror. I'd like to see a Reference on a page like this, but I can't find it.
So, take a look at the GLFW page. If you go to Documentation> HTML Documentation, you will find Tutorial and Reference.
The question is, the GLFW page is written in C, but what happens in Python?
If you compare the previous ones with functions, glfwCreateWindow → glfw.create_window It looks like this. Camel case → Snake case. The number of arguments may differ between C and Python. Python's glfw.get_cursor_pos (window) has one argument, only window, and returns the coordinates as a tuple, but in C void glfwGetCursorPos(GLFWwindow * window, double * xpos, double * ypos); It is in the form of.
For constants, it looks like GLFW_MOUSE_BUTTON_LEFT → glfw.MOUSE_BUTTON_LEFT.
Let's have a code like this. In the function resize, in Windows, it is necessary to take measures against the window_size event occurring at the time of iconification and the window size becoming 0.
[Postscript 2020/5/19] The function arguments were described in the Libapi section of the pyglfw project page, which said "No big deal" above.
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()
In the loop, if it is running, the following drawing can be done, poll_events, otherwise wait_events_timeout. When it is running, it will be slower if you set wait_events_timeout instead of poll_events. Setting DOUBLE_BUFFERING at the beginning to False results in single buffering, which flickers instead of being fast.
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