我要做的就是通过pyglet在OpenGL中创建一个非常简单的2D平移和缩放功能。如您所见,缩放在第一个跳转之后完美地起作用了:(然后,拖动(平移)也起作用了,但是它也跳了起来(而且跳得很大)。
这是我的简化代码和演示其行为的视频(pyglet_test.mp4):
import pyglet from pyglet.gl import * # Zooming constants ZOOM_IN_FACTOR = 1.2 ZOOM_OUT_FACTOR = 1/ZOOM_IN_FACTOR class App(pyglet.window.Window): def __init__(self, width, height, *args, **kwargs): # Create GL configuration conf = Config( sample_buffers=1, samples=4, depth_size=16, double_buffer=True ) # Initialize parent super().__init__( width, height, config=conf, *args, **kwargs ) # Create Group self.group = group = pyglet.graphics.Group() # Create Batch self.batch = batch = pyglet.graphics.Batch() # Create QUAD for testing and add it to batch batch.add( 4, GL_QUADS, group, ('v2i', ( -50, -50, 50, -50, 50, 50, -50, 50 )), ('c3B', ( 255, 0, 0, 255, 255, 0, 0, 255, 0, 0, 0, 255 )) ) # Initialize OpenGL self.init_gl() # Initialize camera values self.camera_x = 0 self.camera_y = 0 self.camera_zoom = 1 def init_gl(self): # Set clear color glClearColor(0/255, 0/255, 0/255, 0/255) # Set antialiasing glEnable( GL_LINE_SMOOTH ) glEnable( GL_POLYGON_SMOOTH ) glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ) # Set alpha blending glEnable( GL_BLEND ) glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) # Set viewport glViewport( 0, 0, self.width, self.height ) # Initialize Projection matrix glMatrixMode( GL_PROJECTION ) glLoadIdentity() # Set orthographic projection matrix glOrtho( 0, self.width, 0, self.height, 1, -1 ) # Initialize Modelview matrix glMatrixMode( GL_MODELVIEW ) glLoadIdentity() # Save the default modelview matrix glPushMatrix() def on_resize(self, width, height): # Initialize OpenGL for new dimensions self.width = width self.height = height self.init_gl() def camera_matrix(transformations): # Create camera setter function def set_camera(self): # Take saved matrix off the stack and reset it glMatrixMode( GL_MODELVIEW ) glPopMatrix() glLoadIdentity() # Call wrapped function transformations(self) # Save default matrix again with camera translation glPushMatrix() # Return wrapper function return set_camera @camera_matrix def move_camera(self): # Move to camera position glTranslatef( self.camera_x, self.camera_y, 0 ) # Scale camera glScalef( self.camera_zoom, self.camera_zoom, 1 ) @camera_matrix def zoom_camera(self): # Move to camera position glTranslatef( self.camera_x, self.camera_y, 0 ) # Scale camera glScalef( self.camera_zoom, self.camera_zoom, 1 ) # Move back from camera position glTranslatef( -self.camera_x, -self.camera_y, 0 ) def on_mouse_drag(self, x, y, dx, dy, button, modifiers): # Move camera self.camera_x += dx self.camera_y += dy self.move_camera() def on_mouse_scroll(self, x, y, dx, dy): # Get scale factor f = ZOOM_IN_FACTOR if dy < 0 else ZOOM_OUT_FACTOR if dy > 0 else 1 # If zoom_level is in the proper range if .2 < self.camera_zoom*f < 5: # Zoom camera self.camera_x = x self.camera_y = y self.camera_zoom *= f self.zoom_camera() def on_draw(self): # Clear window with ClearColor glClear( GL_COLOR_BUFFER_BIT ) # Pop default matrix onto current matrix glMatrixMode( GL_MODELVIEW ) glPopMatrix() # Save default matrix again glPushMatrix() # Move to center of the screen glTranslatef( self.width/2, self.height/2, 0 ) # Draw objects self.batch.draw() def run(self): pyglet.app.run() # Create instance of app and run it App(500, 500).run()
经过一天的苦难,我终于找到了解决方案:在2D模式下,基于鼠标坐标(枢轴点)的缩放和不带跳转的单击鼠标拖拽平移的最简单方法是使用该glOrtho()功能更改投影矩阵。
glOrtho()
下面是我的原代码的简化版本- 如果你正在使用Pyglet数据的seriuos金额,你应该考虑使用组和批次,但为便于理解我所用glBegin(),glColor(),glVertex(),glEnd()这里的功能来绘制。
glBegin()
glColor()
glVertex()
glEnd()
import pyglet from pyglet.gl import * # Zooming constants ZOOM_IN_FACTOR = 1.2 ZOOM_OUT_FACTOR = 1/ZOOM_IN_FACTOR class App(pyglet.window.Window): def __init__(self, width, height, *args, **kwargs): conf = Config(sample_buffers=1, samples=4, depth_size=16, double_buffer=True) super().__init__(width, height, config=conf, *args, **kwargs) #Initialize camera values self.left = 0 self.right = width self.bottom = 0 self.top = height self.zoom_level = 1 self.zoomed_width = width self.zoomed_height = height def init_gl(self, width, height): # Set clear color glClearColor(0/255, 0/255, 0/255, 0/255) # Set antialiasing glEnable( GL_LINE_SMOOTH ) glEnable( GL_POLYGON_SMOOTH ) glHint( GL_LINE_SMOOTH_HINT, GL_NICEST ) # Set alpha blending glEnable( GL_BLEND ) glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) # Set viewport glViewport( 0, 0, width, height ) def on_resize(self, width, height): # Set window values self.width = width self.height = height # Initialize OpenGL context self.init_gl(width, height) def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): # Move camera self.left -= dx*self.zoom_level self.right -= dx*self.zoom_level self.bottom -= dy*self.zoom_level self.top -= dy*self.zoom_level def on_mouse_scroll(self, x, y, dx, dy): # Get scale factor f = ZOOM_IN_FACTOR if dy > 0 else ZOOM_OUT_FACTOR if dy < 0 else 1 # If zoom_level is in the proper range if .2 < self.zoom_level*f < 5: self.zoom_level *= f mouse_x = x/self.width mouse_y = y/self.height mouse_x_in_world = self.left + mouse_x*self.zoomed_width mouse_y_in_world = self.bottom + mouse_y*self.zoomed_height self.zoomed_width *= f self.zoomed_height *= f self.left = mouse_x_in_world - mouse_x*self.zoomed_width self.right = mouse_x_in_world + (1 - mouse_x)*self.zoomed_width self.bottom = mouse_y_in_world - mouse_y*self.zoomed_height self.top = mouse_y_in_world + (1 - mouse_y)*self.zoomed_height def on_draw(self): # Initialize Projection matrix glMatrixMode( GL_PROJECTION ) glLoadIdentity() # Initialize Modelview matrix glMatrixMode( GL_MODELVIEW ) glLoadIdentity() # Save the default modelview matrix glPushMatrix() # Clear window with ClearColor glClear( GL_COLOR_BUFFER_BIT ) # Set orthographic projection matrix glOrtho( self.left, self.right, self.bottom, self.top, 1, -1 ) # Draw quad glBegin( GL_QUADS ) glColor3ub( 0xFF, 0, 0 ) glVertex2i( 10, 10 ) glColor3ub( 0xFF, 0xFF, 0 ) glVertex2i( 110, 10 ) glColor3ub( 0, 0xFF, 0 ) glVertex2i( 110, 110 ) glColor3ub( 0, 0, 0xFF ) glVertex2i( 10, 110 ) glEnd() # Remove default modelview matrix glPopMatrix() def run(self): pyglet.app.run() App(500, 500).run()