Search code examples
pythonopenglviewmousepyopengl

How do I control the camera in PyOpenGL using a mouse?


We need to make a 3D game in python so in my research I discovered PyOpenGL. After a bit of playing around with I became stumped on how to move the camera around. Give me some sample code and an explanation.


Solution

  • PyOpenGL is a binding for OpenGL. You need to use some framework to create windows and handle windowing events. In the past I've used pyglet for window creation / input handling / sound. Other common options here include SDL and pySFML but there really are quite a few.

    After that its a matter of deciding how the camera should work and using the input to modify your camera properties. Once you have, say, well defined position, forward, and up vectors for the camera you can turn these things into a matrix that transforms your scene into the point of view of the camera.

    Using OpenGL directly is very low level. It's left up to you to define exactly how this should work.

    Here's some code of mine that uses numpy. You can manipulate the camera using the interface functions in spatial.py (tie these to the mouse input events)

    Once those work you can get the relevant matrices (as numpy arrays) with

    Camera.get_projection_matrix()
    

    and

    Camera.get_camera_matrix()
    

    For instance in one of my projects I initialize the camera with

    def setupView(self):
        self.camera = Camera()
        self.camera.set_position(v3(3, 0, 10))
        self.camera.look_at(v3(0, 0, 0))
    

    and then sync the camera to the player (just another Spatial) with

    def updateCamera(self):
        up = v3(*self.player.get_world_up())
        position = v3(*self.player.get_position())
        position += (v3(*self.player.get_world_forward()) * -10.0)
        position += (up * 3.0)
    
        self.camera.set_position(position)
        self.camera.set_orientation(self.player.get_orientation())
    

    The mouse events affect the player

    def on_mouse_motion(self, x, y, dx, dy):
        self.player.yaw(-dx * 0.005)
        self.player.pitch(dy * 0.005)
    

    On draw then passes the camera matrix to a vertex shader

    def on_draw(self, renderer, t, dt):
        ...
        matC = camera.get_camera_matrix()
        renderer.set_shader_mat44('matC', matC)
        ...
    

    And the vertex shader takes care of the actual vertex transformations by left multiplying C with the current modelview matrix.

    This is not for faint of heart :)