I am trying to change the perspective of my scene in a PyQt5 OpenGL widget. I know I have to override some methods, but I do not know which one should I use.
def initializeGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_DEPTH_TEST)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glBegin(GL_LINES)
# More code
Where should I use the glOrtho function? And where can I found information about overriding this methods?
When I go to the declaration of this methods, they have a pass statement and nothing else, how and when are they executed? Should I use QPainter instead of OpenGL?
def __init__(self, parent=None):
super().__init__(parent)
self._x = 0
self._y = -0.3
self._z = 0.5
self._rz = 0
self._ry = -0.5
self.vertices_vertical = [[1000, 1000, 000], [1000, -1000, 000],
[-1000, -1000, 000], [-1000, 1000, 000]]
self.vertices_horizontal = [[1000, 000, -1000], [1000, 000, 1000],
[-1000, 000, 1000], [-1000, 000, -1000]]
def initializeGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, 1000, 750, 0, -1, 1)
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glBegin(GL_LINES)
glColor3d(1, 0, 0)
glVertex3d(0, 0, 0)
glVertex3d(1, 0, 0)
glColor3d(0, 1, 0)
glVertex3d(0, 0, 0)
glVertex3d(0, 1, 0)
glColor3d(0, 0, 1)
glVertex3d(0, 0, 0)
glVertex3d(0, 0, 1)
glEnd()
# glLoadIdentity()
glTranslate(self._x, self._y, self._z)
glRotate(self._ry, 0, 1, 0)
glRotate(self._rz, 0, 0, 1)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_DST_COLOR)
glBegin(GL_QUADS)
glColor4fv((0, 1, 0, 0.6))
for vertex in range(4):
glVertex3fv(self.vertices_vertical[vertex])
glColor4fv((1, 0, 0, 0.6))
for vertex in range(4):
glVertex3fv(self.vertices_horizontal[vertex])
glEnd()
glDisable(GL_BLEND)
In Legacy OpenGL the current matrix is a global state. There are different kind of matrices, for each kind of matrix exists a matrix stack. The top of the matrix stack is the current matrix. The matrix stack which is the target for subsequent matrix operations like glOrtho, glPushMatrix/glPopMatrix, glLoadIdentity,etc. can be chosen by glMatrixMode.
Each vertex coordinate (glVertex) is transformed by the model view matrix (GL_MODELVIEW) and the projection matrix (GL_PROJECTION).
Chose the projection matrix mode in initializeGL. Set the Identity matrix and set the orthographic projection by glOrtho. Note, glOrtho dose not only set a matrix, it defines a Orthographic projection matrix and multiplies the current matrix by the new matrix.
e.g. Set an orthographic projection, which transforms "window" coordinates to the normalized device space. In the following (windowdWidth, windowHeight) is the size of the window:
def initializeGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_DEPTH_TEST)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, windowdWidth, windowHeight, 0, -1, 1)
Use the model view matrix mode to set the model view transformations, before you draw the model:
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
# model view transformations
# [...]
glBegin(GL_LINES)
# [...]
glEnd()
Related
Suppose I have a point in pixel (200,200), and I would like to just shift it to (400,400) pixel. Is there any way to do it without glClear() as I don't want my other objects to get erased?
Here is the code that I am using for drawing a single point:
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
def draw_points(x0,y0):
glPointSize(5)
glBegin(GL_POINTS)
glVertex2f(x0,y0)
glEnd()
def iterate():
glViewport(0, 0, 1000, 1000)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0.0, 1000, 0.0, 1000, 0.0, 1.0)
glMatrixMode (GL_MODELVIEW)
glLoadIdentity()
def showScreen():
elapesed_ms = glutGet(GLUT_ELAPSED_TIME)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
iterate()
glColor3f(255, 255, 255)
draw_points(200,200)
glutSwapBuffers()
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(1000, 1000)
glutInitWindowPosition(0, 0)
wind = glutCreateWindow(b"")
glutDisplayFunc(showScreen)
glutMainLoop()
I want to draw a house with Python OpenGL.
It should look like this:
filled:
unfilled:
Further information: The house should rotate via key input around the x- and y-axis. By pressing the F-key, it should switch between filled and unfilled mode.
My Problem: The window opens up, but I don't see anything on it. I'm not sure what exactly is wrong here or what I'm missing for drawing. Can somebody explain it?
This is my code:
import pygame
from pygame.locals import *
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
# from OpenGL.GL import shaders
# from OpenGL.arrays import vbo
# import numpy as np
# general all vertices needed
vertices = (
# front wall
(2, 0, -1, 2, 1, -1, -2, 1, -1, -2, 0, -1),
# back wall
(2, 0, 1, 2, 1, 1, -2, 1, 1, -2, 0, 1, 2, 2, 0, -2, 2, 0),
# roof ridge
(2.2, 2, 0, -2.2, 2, 0),
# roof edges
(2.2, 0.9, -1.1, -2.2, 0.9, -1.1, 2.2, 0.9, 1.1, -2.2, 0.9, 1.1),
# chimney
(1, 0, 0.5, 1.25, 0, 0.5, 1.25, 0, 0.25, 1, 0, 0.25,
1, 2.5, 0.5, 1.25, 2.5, 0.5, 1.25, 2.5, 0.25, 1, 2.5, 0.25)
)
def DrawHouse():
# house
# back wall
glBegin(GL_LINES)
glColor3f(0.5, 0.5, 0.5)
glVertex3f(2, 0, -1) # top left
glVertex3f(2, 1, -1) # top right
glVertex3f(-2, 1, -1) # bottom right
glVertex3f(-2, 0, -1) # bottom left
glEnd()
# front wall
glBegin(GL_LINES)
glColor3f(0.5, 0.5, 0.5)
glVertex3f(2, 0, 1) # top left
glVertex3f(2, 1, 1) # top right
glVertex3f(-2, 1, 1) # bottom right
glVertex3f(-2, 0, 1) # bottom right
glVertex3f(2, 2, 0) # rooftop
glVertex3f(-2, 2, 0) # rooftop
glEnd()
# roof
glBegin(GL_LINES)
# roof ridge
glColor3f(0, 0, 0)
glVertex3f(2.2, 2, 0)
glVertex3f(-2.2, 2, 0)
glEnd()
# roof edges
glBegin(GL_TRIANGLES)
glColor3f(0, 0, 0)
glVertex3f(2.2, 0.9, -1.1)
glVertex3f(-2.2, 0.9, -1.1)
glVertex3f(2.2, 0.9, 1.1)
glVertex3f(-2.2, 0.9, 1.1)
glEnd()
# chimney
glBegin(GL_POLYGON)
glColor3f(1, 1, 0)
glVertex3f(1, 0, 0.5)
glVertex3f(1.25, 0, 0.5)
glVertex3f(1.25, 0, 0.25)
glVertex3f(1, 0, 0.25)
glVertex3f(1, 2.5, 0.5)
glVertex3f(1.25, 2.5, 0.5)
glVertex3f(1.25, 2.5, 0.25)
glVertex3f(1, 2.5, 0.25)
glEnd()
def init():
# Switch on z-buffer for calculation of hidden surfaces
glEnable(GL_DEPTH_TEST)
# Display front and back of polygons as border lines only
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
def reshape():
# initialize projection matrix, to 60 degrees horizontal field of view
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(60.0, 1.0, 1.0, 200.0) # angle, aspect, near and far clip
# make modelview matrix the current matrix again
glMatrixMode(GL_MODELVIEW)
def main():
pygame.init()
window = (800, 800)
display = pygame.display.set_mode(window, DOUBLEBUF | OPENGL)
pygame.display.set_caption('Haus')
clock = pygame.time.Clock()
# GLUT.glutInitDisplayMode(GLUT_DEPTH | GLUT_RGB | GLUT_DOUBLE) # request render context with z-buffer, doublebuffer for rgb mode
gluOrtho2D(0, 800, 0, 800)
rotX = 0.0
rotY = 0.0
polygonMode = GL_LINE
while True:
clock.tick(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
keys = pygame.key.get_pressed() # checking pressed keys
if keys[pygame.K_d]: # The keys 'a' and 'd' should rotate the house around the y-axis
rotX -= 5
if keys[pygame.K_a]:
rotX += 5
if keys[pygame.K_w]: # the keys 'w' and 'd' should rotate the house around the x-axis
rotY -= 5
if keys[pygame.K_d]:
rotY += 5
if keys[pygame.K_f]: # Key 'f' is to switch between wireframe and filled surfaces
if polygonMode is GL_FILL:
polygonMode = GL_LINE
else:
polygonMode = GL_FILL
# Switch polygon display between outline and filled
glPolygonMode(GL_FRONT_AND_BACK, polygonMode)
# GLUT.glutPostRedisplay() # render image again
# remove Framebuffer and Z-Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
# initialise Modelview Matrix
glLoadIdentity()
# execute Modeltransformation
glTranslatef(0, 0, -3) # shift by -3 in z-direction
glRotatef(rotY, 0, 1, 0) # rotate y-achse
glRotatef(rotX, 1, 0, 0) # rotate x-achse
glTranslatef(-0.5, -0.5, -0.5) # shift by -0.5 in all directions
# copy Back-Buffer in Front-Buffer
# swapBuffers()
DrawHouse()
# Show the screen
pygame.display.flip()
# callback functions
# glutReshapeFunc(reshape)
if __name__ == "__main__":
main()
The coordinates of your geometry are in range [-2.2, 2.2]. However you set an orthographic projection in range [0, 800]:
gluOrtho2D(0, 800, 0, 800)
Actually there are 3 reasons why you don't see anything
That results in only a few pixels being drawn on the bottom left of the screen.
gluOrtho2D creates an orthographic projection with a near plane of -1 and a far plane of 1. This will clip your geometry.
You need to choose the GL_PROJECTION for the current matrix. Later in your code, the model view matrix is set to the identity matrix with glLoadIdentity.
def main():
pygame.init()
window = (800, 800)
display = pygame.display.set_mode(window, DOUBLEBUF | OPENGL)
pygame.display.set_caption('Haus')
clock = pygame.time.Clock()
# gluOrtho2D(0, 800, 0, 800)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(-5, 5, -5, 5, -5, 5)
glMatrixMode(GL_MODELVIEW)
# [...]
Alternatively you can use the perspective projection from the reshape function:
def main():
pygame.init()
window = (800, 800)
display = pygame.display.set_mode(window, DOUBLEBUF | OPENGL)
pygame.display.set_caption('Haus')
clock = pygame.time.Clock()
# gluOrtho2D(0, 800, 0, 800)
reshape()
# [...]
When using gluPerspective in glutReshapeFunc function, the square image flashes while resizing and is gone after a few moments.
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
width = 500
height = 500
def cube():
glBegin(GL_QUADS)
glColor3f(0, 1, 0)
glVertex3f(10, 0, 0)
glVertex3f(10, 10, 0)
glVertex3f(10, 0, 0)
glVertex3f(0, 0, 0)
glVertex3f(10, 0, 0)
glVertex3f(10, 0, 10)
glVertex3f(0, 10, 0)
glVertex3f(10, 10, 0)
glVertex3f(0, 10, 0)
glVertex3f(0, 0, 0)
glVertex3f(0, 10, 0)
glVertex3f(0, 10, 10)
glVertex3f(0, 0, 10)
glVertex3f(0, 0, 0)
glVertex3f(0, 0, 10)
glVertex3f(10, 0, 10)
glVertex3f(0, 0, 10)
glVertex3f(0, 10, 10)
glVertex3f(10, 10, 10)
glVertex3f(10, 10, 0)
glVertex3f(10, 10, 10)
glVertex3f(10, 0, 10)
glVertex3f(10, 10, 10)
glVertex3f(0, 10, 10)
glEnd()
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
glutSwapBuffers()
def mouseTracker(mousex, mousey):
print(f"Mouse pos: {mousex}, {mousey}")
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
gluPerspective(45, (width / height), 0.0001, 1000)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMotionFunc(mouseTracker)
glutPassiveMotionFunc(mouseTracker)
glutReshapeFunc(reshapeWindow)
gluPerspective(45, (width / height), 0.0001, 1000)
glTranslatef(0, 0, -5)
while True:
glutMainLoopEvent()
glutPostRedisplay()
If I put the gluPerspective into the showScreen function like so:
def showScreen():
global width, height
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
gluPerspective(45, (width / height), 0.0001, 1000)
glutSwapBuffers()
The square flashes without resizing but it is gone after a few moments. If I remove the gluPerspective entirely, the image turns into a triangle. Is there any way to change gluPerspective variables without making the image flash?
You have to call gluPerspective before drawing the cube. The matrix operations not only set the current matrix, but define a new matrix and multiply the current matrix by the new matrix. Therefore you must load the Identity matrix with glLoadIdentity before modifying the matrix. The legacy OpenGL provides different current matrices for the model view matrix and the projection matrix. Before changing a matrix, select the matrix mode with glMatrixMode:
Change the projection matrix in the reshape callback:
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glMatrixMode(GL_MODELVIEW)
Set the model view matrix before the application loop or in the application loop:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
gluPerspective defines a Viewing frustum. The center of the view is (0, 0). Hence you need to change the vertex coordinates.
I suggest enabling the Depth Test when drawing 3D meshes.
Minimale example based on your code:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import time
width = 500
height = 500
vertices = [(-1,-1,-1), ( 1,-1,-1), ( 1, 1,-1), (-1, 1,-1), (-1,-1, 1), ( 1,-1, 1), ( 1, 1, 1), (-1, 1, 1)]
faces = [(4,0,3,7), (1,0,4,5), (0,1,2,3), (1,5,6,2), (3,2,6,7), (5,4,7,6)]
colors = [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (1, 0, 1), (0, 1, 1)]
def cube():
glRotatef(1, 3, 1, 1)
glBegin(GL_QUADS)
for i, face in enumerate(faces):
glColor3fv(colors[i])
for vertex in face:
glVertex3fv(vertices[vertex])
glEnd()
def showScreen():
glClearColor(0, 0, 0, 1)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
cube()
glutSwapBuffers()
def mouseTracker(mousex, mousey):
print(f"Mouse pos: {mousex}, {mousey}")
def reshapeWindow(x, y):
global width, height
width = x
height = y
print(x, y)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, (width / height), 0.0001, 1000)
glMatrixMode(GL_MODELVIEW)
glutInit()
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(500, 500)
wind = glutCreateWindow("OpenGL")
glutDisplayFunc(showScreen)
glutIdleFunc(showScreen)
glutMotionFunc(mouseTracker)
glutPassiveMotionFunc(mouseTracker)
glutReshapeFunc(reshapeWindow)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -5)
glEnable(GL_DEPTH_TEST)
while True:
glutMainLoopEvent()
glutPostRedisplay()
time.sleep(0.01)
I used Glut for making window and wanted to draw triangle, then on click button redraw it, but after clearing window I can't draw again. Does something wrong with Buffer depth and buffer bit? If I cleared it do I need to setup both again in my draw function?
def drawSomething():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
glTranslatef(-2.5, 0.5, -6.0)
glColor3f( 1.0, 1.5, 0.0 )
glPolygonMode(GL_FRONT, GL_FILL)
glBegin(GL_TRIANGLES)
glVertex3f(2.0,-1.2,0.0)
glVertex3f(2.6,0.0,0.0)
glVertex3f(2.9,-1.2,0.0)
glEnd()
glFlush()
def funcForUpdate():
glClearColor(0.0, 0.0, 0.0, 0)
glClear(GL_COLOR_BUFFER_BIT)
glEnable(GL_TEXTURE_2D)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0)
glVertex2f(0.0, 0.0)
glTexCoord2f(0.0, 1.0)
glVertex2f(0.0, width)
glTexCoord2f(1.0, 1.0)
glVertex2f(height, width)
glTexCoord2f(1.0, 0.0)
glVertex2f(height, 0.0)
glEnd()
glFlush()
def resizeUpdateFunc(width, height):
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluOrtho2D(0.0, width, 0.0, height)
def handleKey(bkey,x,y):
key = bkey.decode("utf-8")
if key == "a":
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
drawSomething()
glFlush()
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(width, height)
glutInitWindowPosition(0, 0)
glutCreateWindow("test")
drawSomething()
glutDisplayFunc(funcForUpdate)
glutReshapeFunc(resizeUpdateFunc)
glutKeyboardFunc(handleKey)
glutMainLoop()
See glutDisplayFunc:
[...] sets the display callback for the current window. [...] The redisplay state for a window can be either set explicitly by calling glutPostRedisplay or implicitly as the result of window damage reported by the window system.
In fact, a triangle is drawn in drawSomething, but it is immediately overdrawn in funcForUpdate.
Add a state drawTriangle and set the state in handleKey:
drawTriangle = False
def handleKey(bkey, x, y):
global drawTriangle
key = bkey.decode("utf-8")
if key == "a":
drawTriangle = not drawTriangle
glutPostRedisplay()
Draw depending on the variable drawTriangle in funcForUpdate:
def funcForUpdate():
glClearColor(0, 0, 0, 0)
glClear(GL_COLOR_BUFFER_BIT)
# [...]
if drawTriangle:
# [...]
glFlush()
A complete example:
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
drawTriangle = False
def funcForUpdate():
glClearColor(0, 0, 0, 0)
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(1, 1, 0)
glBegin(GL_QUADS)
glVertex2f(0.0, 0.0)
glVertex2f(0, height)
glVertex2f(width, height)
glVertex2f(width, 0)
glEnd()
if drawTriangle:
glPushMatrix()
glLoadIdentity()
glTranslate(width/2, height/2, 0)
glColor3f(1, 0, 0)
glBegin(GL_TRIANGLES)
glVertex3f(-width/4, -height/4, 0)
glVertex3f(width/4, -height/4, 0)
glVertex3f(width/4, height/4, 0)
glEnd()
glPopMatrix()
glFlush()
def resizeUpdateFunc(width, height):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0, width, 0.0, height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
def handleKey(bkey, x, y):
global drawTriangle
key = bkey.decode("utf-8")
if key == "a":
drawTriangle = not drawTriangle
glutPostRedisplay()
width, height = 320, 200
glutInit()
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
glutInitWindowSize(width, height)
glutInitWindowPosition(0, 0)
glutCreateWindow("test")
glutDisplayFunc(funcForUpdate)
glutReshapeFunc(resizeUpdateFunc)
glutKeyboardFunc(handleKey)
glutMainLoop()
I m trying to build a gui with Pyqt5. and withing the gui there is an openGLwidget, that should contain a rotating cube. But i cannot figure out how to make the cube rotate.
Thank you.
this is the setup function
def setupUI(self):
self.openGLWidget.initializeGL()
self.openGLWidget.resizeGL(651,551)
self.openGLWidget.paintGL = self.paintGL
self.rotX=10.0
self.rotY=0.0
self.rotZ=0.0
timer = QTimer(self)
timer.timeout.connect(self.Update)
timer.start(1000)
and here is the paintGL and update functions:
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0,1,0)
self.Cube()
gluPerspective(45, 651/551, 1, 100.0)
glTranslatef(0.0,0.0, -5)
glRotate(self.rotX, 1.0, 0.0, 0.0)
def Update(self):
glClear(GL_COLOR_BUFFER_BIT)
self.rotX+=10
self.openGLWidget.paintGL = self.paintGL
There are different current matrices, see glMatrixMode. The projection matrix should be set to the current GL_PROJECTION and the model view matrix to GL_MODELVIEW.
The operations which manipulate the current matrix (like gluPerspective, glTranslate, glRotate), do not just set a matrix, they specify a matrix and multiply the current matrix by the new matrix. Thus you have to set the Identity matrix at the begin of every frame, by glLoadIdentity:
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT)
glColor3f(0,1,0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(45, 651/551, 1, 100.0)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glTranslatef(0, 0, -7)
glRotate(self.rotX, 1, 0, 0)
self.Cube()
Invoke update() to update respectively repaint a QOpenGLWidget:
timer = QTimer(self)
timer.timeout.connect(self.Update)
timer.start(10)
def Update(self):
self.rotX += 1
self.openGLWidget.update()