代码之家  ›  专栏  ›  技术社区  ›  Peter Varo

如何在二维中正确平移和缩放?

  •  5
  • Peter Varo  · 技术社区  · 13 年前

    我所想做的就是通过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()
    
    2 回复  |  直到 4 年前
        1
  •  6
  •   Peter Varo    13 年前

    在又经历了一天的痛苦之后,我终于找到了一个解决方案:在2D中,最简单的方法是使用 glOrtho() 作用

    这是我原始代码的简化版本——如果您使用Pyglet处理大量数据,您应该考虑使用Groups和Batches,但为了更容易理解,我使用了 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()
    
        2
  •  1
  •   Aaron Digulla    13 年前

    函数如 glTranslatef 绝对不要工作。相反,它们以指定的数量移动“世界”。所以如果你说 glTranslatef(100,100) 你最终会从现在的位置上下移动100个单位,而不是 100, 100 .

    他们在后台所做的就是修改当前的视图矩阵。要实现这一点,您需要像这样编写代码:

    glPushMatrix() # save the current matrix somewhere; gives you a new copy to modify
    
    glTranslatef(100,100) # modify your copy; 
                          # you need to do this *every time* before you draw anything
    ... draw ...
    
    glPopMatrix() # undo all and any change you made to the matrix
    
    推荐文章