from OpenGL.extensions import alternate
from OpenGL.GL import *
from OpenGL.GL.ARB.multitexture import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
class TestTexture():
def __init__(self):
self.window_width = 800
self.window_height = 800
def init(self):
glClearColor(0.0, 0.0, 0.0, 0.0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_TEXTURE_2D)
def display(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glBegin(GL_TRIANGLES)
glVertex3f(-1.0, 0.0, 0.0)
glVertex3f(1.0, 0.0, 0.0)
glVertex3f(0.0, 1.0, 0.0)
glEnd()
glFlush()
glutSwapBuffers()
def reshape(self, w, h):
self.window_width = w
self.window_height = h
glViewport(0, 0, self.window_width, self.window_height)
def animate(self):
glutPostRedisplay()
def visible(self, vis):
if (vis == GLUT_VISIBLE):
glutIdleFunc(self.animate)
else:
glutIdleFunc(0)
def key_pressed(self, *args):
if args[0] == b"\x1b":
sys.exit()
def run(self):
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
glutInitWindowSize(self.window_width, self.window_height)
glutInitWindowPosition(800, 100)
glutCreateWindow(b'MCVE')
glutDisplayFunc(self.display)
glutReshapeFunc(self.reshape)
glutIdleFunc(self.animate)
glutVisibilityFunc(self.visible)
glutKeyboardFunc(self.key_pressed)
self.init()
glutMainLoop()
if __name__ == "__main__":
TestTexture().run()
I've already tried few things in order to resize&refresh properly this window but no luck so far. When the window is resized the scene should be rendered properly on real-time but it's not, instead the scene is updated only when you release the mouse (reshape function is not called) or maybe when it's being resized it gets a little chance to update but the final result is definitely not cool at all.
Worth mentioning that a pyqt opengl app is resizing ok and the show window contents while dragging option is enabled.
The test has been made on windows7, PyOpenGL==3.1.1 and python3.5.1_x86
So the question would be, how can I resize&refresh a glut window properly on realtime?
First things first glViewport does not belong into the reshape handler (if I had a cent for every time I wrote this…).
The problem you're running into is not something you can easily address from the "outside" of GLUT because it essentially boils down to how the innards of the main loop are implemented. For smooth updates of the window's contents during resize technically the main loop has to accommodate for this situation, by smoothly interleaving resize events with calls of the display function. The way glutPostRedisplay flags the main loop for redraw will call the display function every few resize steps, but it may accompanied by flicker and jerky redraws.
Your best bet is to do something that's normally frowned upon: Call the display function (including buffer swap) at the end of the resize handler and do not call glutPostRedisplay. However this might be still prone to flicker, depending on how WSI background erasure is implemented. The problem with that is, that resize events may queue up and resize steps long begone show up lagging behind user action.
For truly smooth resize updates an appropriately written main loop is required, that coalesces input/resize events to avoid queue-lag issues and allows for on-resize drawing operation without automatic background erasure to support smooth updates. Current implementations of GLUT don't do this.
I am new to PYopenGL,
Actually, I’m also not sure if PYopenGL is the right approach for my task.
I have a 3D model in a Wavefront obj file format. I need to take a “printscreen” of the model from a given view. In other words, I would need to render the model and instead of display the model save it as an image (jpg)
My idea was to use PYopenGL for this task. However, googling I could find no suggestion or example how to do this. Therefore, I start to have doubts, if PYopenGL is the right tool for my task.
Did somebody of you already something like this or know an example that I can use to learn about?
Thanks in advance.
Michi
GLUT hidden window method is much simpler, and platform independent, but it leads to window blinking.
To setup it for Django i.e. you can implement renderer as separate web server, which will blinks by window only once on start, and then returning rendered images by http response.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from PIL import ImageOps
import sys
width, height = 300, 300
def init():
glClearColor(0.5, 0.5, 0.5, 1.0)
glColor(0.0, 1.0, 0.0)
gluOrtho2D(-1.0, 1.0, -1.0, 1.0)
glViewport(0, 0, width, height)
def render():
glClear(GL_COLOR_BUFFER_BIT)
# draw xy axis with arrows
glBegin(GL_LINES)
# x
glVertex2d(-1, 0)
glVertex2d(1, 0)
glVertex2d(1, 0)
glVertex2d(0.95, 0.05)
glVertex2d(1, 0)
glVertex2d(0.95, -0.05)
# y
glVertex2d(0, -1)
glVertex2d(0, 1)
glVertex2d(0, 1)
glVertex2d(0.05, 0.95)
glVertex2d(0, 1)
glVertex2d(-0.05, 0.95)
glEnd()
glFlush()
def draw():
render()
glutSwapBuffers()
def main():
glutInit(sys.argv)
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
glutInitWindowSize(300, 300)
glutCreateWindow(b"OpenGL Offscreen")
glutHideWindow()
init()
render()
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.frombytes("RGBA", (width, height), data)
image = ImageOps.flip(image) # in my case image is flipped top-bottom for some reason
image.save('glutout.png', 'PNG')
#glutDisplayFunc(draw)
#glutMainLoop()
main()
My answer (partially based on CodeSurgeon's answer) is for the second part of the question.
Off-screen rendering (means render something to internal buffer instead of visible window and save rendered image to file or transfer as http response to display on web page) in PyOpenGL (so as in OpenGL itself) is little tricky because everything done by GLUT so far (create window, init opengl context etc) you need to do by hands now, because you don't need the standard GLUT window to pop up or even blink.
So there are 3 methods for off-screen rendering in OpenGL:
1) Use GLUT for initialization, but hide glut window and render to it. This method is totally platform independent, but GLUT window appears for short time during initialization, so it's not so suitable for web server i.e. But you still can setup it as separate web server, which do initialization only on startup and use some interface to communicate with it.
2) Manually create everything: hidden window, OpenGL context, and Framebuffer Object to render to. This method is good, cause you control everything and no window appears, but creation of context is platform specific (below is example for Win64)
3) 3rd method is like method 2, but use default Framebuffer created by WGL, instead of creating FBO by hands. Same effects as method 2, but simpler. If you don't need FBO for some other reason, may be the preferable one.
Now I'll describe method 2, hardcore one. More samples are in my GitHub repository.
So off-screen rendering algorithm consists of following steps:
Create hidden window, because you need the window, even hidden to
create OpenGL context
Create OpenGL context
Create Framebuffer object (FBO)
Create Rendering buffers (Color and Depth) and attach them to FBO (see FBO manual for details)
Bind FBO to OpenGL context for rendering
Render something. In this example I use only 2D primitives for simplification, but buffers are ready for 3D rendering with depth-test
Setup buffers for reading, in our case there is only one FBO, so no need to choose one to read from
Read rendered data from Color render buffer with glReadPixels()
Do whatever you want with received data, i.e. create PIL image from it and save it to file. Also you can render with double resolution and resize PIL image for anti-aliasing effect.
So there is full example below.
Important! 3.1.1 PyOpenGL implementation have a BUG! When you just import WGL, glReadPixels() starts to crash with
ctypes.ArgumentError: argument 7: : wrong type
To avoid this go to your packages dir\OpenGL\raw\WGL_types.py , find the following lines
HANDLE = POINTER(None) # /home/mcfletch/pylive/OpenGL-ctypes/src/wgl.h:60
# TODO: figure out how to make the handle not appear as a void_p within the code...
HANDLE.final = True
and replace it with (for x64 of course, for x86 UINT32 suppose)
HANDLE = UINT64
HANDLE.final = True
So there is example
from win32api import *
from win32con import *
from win32gui import *
from OpenGL.WGL import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from PIL import ImageOps
import uuid
# =========================================
# I left here only necessary constants, it's easy to search for the rest
PFD_TYPE_RGBA = 0
PFD_MAIN_PLANE = 0
PFD_DOUBLEBUFFER = 0x00000001
PFD_DRAW_TO_WINDOW = 0x00000004
PFD_SUPPORT_OPENGL = 0x00000020
# =========================================
# OpenGL context creation helpers
def mywglCreateContext(hWnd):
pfd = PIXELFORMATDESCRIPTOR()
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL
pfd.iPixelType = PFD_TYPE_RGBA
pfd.cColorBits = 32
pfd.cDepthBits = 24
pfd.iLayerType = PFD_MAIN_PLANE
hdc = GetDC(hWnd)
pixelformat = ChoosePixelFormat(hdc, pfd)
SetPixelFormat(hdc, pixelformat, pfd)
oglrc = wglCreateContext(hdc)
wglMakeCurrent(hdc, oglrc)
# check is context created succesfully
# print "OpenGL version:", glGetString(GL_VERSION)
def mywglDeleteContext():
hrc = wglGetCurrentContext()
wglMakeCurrent(0, 0)
if hrc: wglDeleteContext(hrc)
# =========================================
# OpenGL Framebuffer Objects helpers
def myglCreateBuffers(width, height):
fbo = glGenFramebuffers(1)
color_buf = glGenRenderbuffers(1)
depth_buf = glGenRenderbuffers(1)
# binds created FBO to context both for read and draw
glBindFramebuffer(GL_FRAMEBUFFER, fbo)
# bind color render buffer
glBindRenderbuffer(GL_RENDERBUFFER, color_buf)
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color_buf)
# bind depth render buffer - no need for 2D, but necessary for real 3D rendering
glBindRenderbuffer(GL_RENDERBUFFER, depth_buf)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth_buf)
return fbo, color_buf, depth_buf, width, height
def myglDeleteBuffers(buffers):
fbo, color_buf, depth_buf, width, height = buffers
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glDeleteRenderbuffers(1, color_buf)
glDeleteRenderbuffers(1, depth_buf)
glDeleteFramebuffers(1, fbo)
def myglReadColorBuffer(buffers):
fbo, color_buf, depth_buf, width, height = buffers
glPixelStorei(GL_PACK_ALIGNMENT, 1)
glReadBuffer(GL_COLOR_ATTACHMENT0)
data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE)
return data, width, height
# =========================================
# Scene rendering
def renderInit(width, height):
glClearColor(0.5, 0.5, 0.5, 1.0)
glColor(0.0, 1.0, 0.0)
gluOrtho2D(-1.0, 1.0, -1.0, 1.0)
glViewport(0, 0, width, height)
def render():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# draw xy axis with arrows
glBegin(GL_LINES)
# x
glVertex2d(-1, 0)
glVertex2d(1, 0)
glVertex2d(1, 0)
glVertex2d(0.95, 0.05)
glVertex2d(1, 0)
glVertex2d(0.95, -0.05)
# y
glVertex2d(0, -1)
glVertex2d(0, 1)
glVertex2d(0, 1)
glVertex2d(0.05, 0.95)
glVertex2d(0, 1)
glVertex2d(-0.05, 0.95)
glEnd()
glFlush()
# =========================================
# Windows stuff and main steps
def main():
# Create window first with Win32 API
hInstance = GetModuleHandle(None)
wndClass = WNDCLASS()
wndClass.lpfnWndProc = DefWindowProc
wndClass.hInstance = hInstance
wndClass.hbrBackground = GetStockObject(WHITE_BRUSH)
wndClass.hCursor = LoadCursor(0, IDC_ARROW)
wndClass.lpszClassName = str(uuid.uuid4())
wndClass.style = CS_OWNDC
wndClassAtom = RegisterClass(wndClass)
# don't care about window size, couse we will create independent buffers
hWnd = CreateWindow(wndClassAtom, '', WS_POPUP, 0, 0, 1, 1, 0, 0, hInstance, None)
# Ok, window created, now we can create OpenGL context
mywglCreateContext(hWnd)
# In OpenGL context create Framebuffer Object (FBO) and attach Color and Depth render buffers to it
width, height = 300, 300
buffers = myglCreateBuffers(width, height)
# Init our renderer
renderInit(width, height)
# Now everything is ready for job to be done!
# Render something and save it to file
render()
data, width, height = myglReadColorBuffer(buffers)
image = Image.frombytes("RGBA", (width, height), data)
image = ImageOps.flip(image) # in my case image is flipped top-bottom for some reason
# it's easy to achive antialiasing effect by resizing rendered image
# don't forget to increase initial rendered image resolution and line thikness for 2D
#image = image.resize((width/2, height/2), Image.ANTIALIAS)
image.save("fbo.png", "PNG")
# Shutdown everything
myglDeleteBuffers(buffers)
mywglDeleteContext()
main()
In additional to glutHideWindow(), if you need do it on server, you can use virtual display.
requirements.txt: pyopengl, pillow, pyvirtualdisplay.
packages: freeglut3-dev, xvfb.
from pyvirtualdisplay import Display
# before glutInit create virtual display
display = Display(visible=0, size=(HEIGHT, WIDTH))
display.start()
PyOpenGL can be used for your purposes if all you are interested in is rendering your 3d model/scene from a specific angle to an image as long as you do not mind having an OpenGL window open and running.
There are many sources online that discuss how to write parsers for the .obj format. In addition to looking at the wikipedia article on the format here, I believe you can find an implementation of a fixed function obj loader on the pygame website. If you are making the .obj models yourself, it will be easier as the spec is pretty loose and it can be tough to write a robust parser. Alternatively, you can use libraries like Assimp to load your models and extract their data, which has the python pyAssimp bindings.
As for saving a screenshot, you need to render the 3D scene to a texture. I would strongly recommend taking a look at this answer. You would need to learn about how to use glReadPixels as well as how to use FBOs (Frame Buffer Objects) if you want to do offscreen rendering.
I don't understand why the following code will not construct a graphics window with the circle. It does construct an object, but not graphically, when I run SomeObject = Tracker()
Why is that? This is a simple snippet of code, just for me to understand why I'm not getting a graphics window.
# tracker.py
from graphics import *
class Tracker:
def __inti__(self):
self.win = GraphWin('tracker', 500, 500)
self.circle = Circle(Point(0, 0), 0.5)
self.circle.draw(self.win)
You cannot see your circle because:
The radius of your circle (0.5) is too small
You placed the center of this small circle at (0, 0), which is at the top left corner
Your class's initializer was mispelled inti, hence the code was not called when you created your object.
Here is code that works:
from graphics import *
class Tracker:
def __init__(self):
# Window of size 500 x 500
self.win = GraphWin('tracker', 500, 500)
# Circle of radius 10 centered at (250, 250)
self.circle = Circle(Point(250, 250), 10)
self.circle.draw(self.win)
self.win.getMouse() # Pause to view result
self.win.close()
def main():
tracker = Tracker()
if __name__ == "__main__":
main()
Where graphics.py is presumably taken from this link.
I modified the sample code from the above link (which is probably also what you did) to call getMouse() to keep the window around.
The result is
I would like to open and close a glut window several times in my program. I came up with this python code:
#!/usr/bin/python
from OpenGL.GLUT import *
from OpenGL.GLU import *
from OpenGL.GL import *
#parameters for viewer
name = 'Viewer'
width, height = 500, 500
spheresize = 0.3
def display():
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
gluLookAt(0,0,10,0,0,0,0,1,0)
glPushMatrix()
color = [1.0,0.0,0.0,1.0]
glMaterialfv(GL_FRONT,GL_DIFFUSE,color)
glTranslatef(0.0,0.0,1.0)
glutSolidSphere(spheresize,20,20)
glPopMatrix()
glutSwapBuffers()
return
def closing():
glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE,GLUT_ACTION_GLUTMAINLOOP_RETURNS) glutLeaveMainLoop()
return
#---------------BEGIN PROGRAM----------------
while 1:
glutInit()
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowSize(width,height)
glutCreateWindow(name)
glClearColor(0.,1.0,0.,1.)
glShadeModel(GL_SMOOTH)
glEnable(GL_CULL_FACE)
glEnable(GL_DEPTH_TEST)
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glMatrixMode(GL_PROJECTION)
gluPerspective(40.,1.,1.,40.)
glMatrixMode(GL_MODELVIEW)
glPushMatrix()
glutDisplayFunc(display)
glutCloseFunc(closing)
glutMainLoop()
print "\n\nMAIN MENUE:"
print "p - print"
print "q - quit"
option = raw_input()
if option == "q":
raise SystemExit
elif option == "p":
print ("Hello World!")
When I try to run this, the window closes as I intended, but it crashes after using option 'p' with
freeglut ERROR: Function glutCreateWindow called without first calling 'glutInit'.
After I found some other posts about a similar problem, I assumed it might be a problem of my library linkage, so I tried to run it in a clean python environment on another computer. It gave the same error.
Does anyone have an idea what might be my mistake?
I'm using Python and OpenGL to make some calculations related to a physical system and then display them and be able to manipulate the image rotating it, translating it, ...
I'm not a professional programmer and I have little experience using OpenGL and I'm having some issues trying to get what I want. I would like to be able to save the window that I get as an output in a video file and to do so I've seen the possibility of using glReadPixels to capture each frame and then put them all together.
I am using right now something that looks like the following code and I want to be able to save the frames to images but, although I've been able to save to save the pixels to an array using glReadPixels, I don't know where nor how to call the function that I've defined as captureScreen. What should I do to get the output that I'm looking for?
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from scipy import *
import numpy as np
import random as rnd
def captureScreen(file_):
glPixelStorei(GL_PACK_ALIGNMENT, 1)
data = glReadPixels(0, 0, 800, 800, GL_RGBA, GL_UNSIGNED_BYTE)
image = Image.fromstring("RGBA", (800, 800), data)
image.save(file_, 'png')
def main():
glutInit(sys.argv)
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH)
glutInitWindowPosition(0, 0)
glutInitWindowSize(800, 800)
glutCreateWindow ("Test")
glEnable(GL_DEPTH_TEST)
glDepthMask(GL_TRUE)
glEnable(GL_BLEND)
glEnable(GL_TEXTURE_2D)
glEnable(GL_COLOR_MATERIAL)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glPointSize( 6.0 )
glLineWidth( 2.0 )
glEnable(GL_POINT_SMOOTH)
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST)
glEnable(GL_POLYGON_SMOOTH)
glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
glClearColor(0.0, 0.0, 0.0, 0.0)
glutFullScreen()
glutKeyboardFunc(keyboard)
glutIdleFunc(dynamics)
glutDisplayFunc(display)
glutMouseFunc(mouse)
glutMainLoop()
if __name__ == '__main__':
main()
keyboard, display, dynamics and mouse are functions previously defined.
You can call glReadPixels() just after glutSwapBuffers():
glutSwapBuffers()
glReadBuffer(GL_FRONT)
glReadPixels(...)
Or just before:
glReadBuffer(GL_BACK)
glReadPixels(...)
glutSwapBuffers()
So captureScreen() should be called from your "display" function which is probably the one which calls glutSwapBuffers()