Skip to content

【PyOpenGL】5.自由移动的相机

0.简介

距离上次更新已经两个多月了,这段时间对整个框架做了一些调整。主要有一下几个方面:

  1. 统一的资源管理: 增加了load和unload接口,加载图片时自动创建Texture(但不创建GPU资源),不再使用加载图片和手动创建Texture的两步方式。

  2. 渲染重构: 将Mesh生成的OpenGL VAO/VBO/EBO保存在Mesh组件中,将Texture ID依旧保存在Texture类中,在需要时手动load/unload GPU资源,不再每次Render重新生成,大幅提升了渲染速度。

  3. 场景管理: 增加Scene类,用来管理场景中的Actor。

  4. 相机组件: 增加相机组件以实现场景中的主相机MainCamera。

  5. 相机的Input组件: 暂时是给相机Actor使用的,用来处理用户输入,从而控制相机移动。

    • WASDQE控制相机在场景中移动
    • 按住右键旋转相机视角
  6. 一些临时更改: 尝试使用四元数实现相机的旋转控制,还挺好用。

1.创建Actor

1.1.创建Cube Actor

python
def create_cube(self, i, pos):
    # 1.创建渲染Actor
    new_actor = Actor()
    new_actor.name = new_actor.name + str(i)

    transform_comp = TransformComponent()
    transform_comp.position = pos
    transform_comp.angle = 20.0 * i
    transform_comp.rotation_axis = glm.vec3(1.0, 0.3, 0.5)
    transform_comp.update_matrix()
    new_actor.add_component(transform_comp)

    # 2.创建Mesh组件
    mesh_comp = MeshComponent()
    mesh_comp.use_default_cube()
    new_actor.add_component(mesh_comp)

    # 3.创建Material组件
    mat_comp = MaterialComponent()
    shader_name = 'camera_shader'
    mat_shader = self._asset_manager.get_shader(shader_name)
    mat_comp.set_shader(mat_shader)
    new_tex = self._asset_manager.get_texture('keqing')
    mat_comp.bind_texture('icon_tex', new_tex)
    new_actor.add_component(mat_comp)

    self._scene.add_actor(new_actor)

1.2.创建Camera Actor

python
# 相机Actor
self._cam_actor = Actor()
self._cam_actor.name = 'MainCamera'
self._cam_actor.set_visible(False)

# 相机的Transform
transform_comp = TransformComponent()
transform_comp.position = glm.vec3(0.0,  0.0,  10.0)
transform_comp.update_matrix()
self._cam_actor.add_component(transform_comp)
# 相机组件
camera_comp = CameraComponent()
camera_comp.set_screen_size(800, 600)
self._cam_actor.add_component(camera_comp)
# 输入组件
input_comp = CameraInputComponent()
self._cam_actor.add_component(input_comp)
self._scene.add_actor(self._cam_actor)

self._scene.set_camera(self._cam_actor)

2.Scene场景管理

python
class Scene(object):
    def __init__(self) -> None:
        self._name = 'Default'
        self._actors = []
        self._main_camera = None
        self._renderer = RenderSystem()

    def set_name(self, name):
        self._name = name

    def add_actor(self, actor):
        self._actors.append(actor)

    def remove_actor(self, actor):
        self._actors.remove(actor)

    def set_camera(self, camera):
        self._main_camera = camera
        self._renderer.set_camera(camera)

    def get_camera(self):
        return self._main_camera

    def load(self):
        for actor in self._actors:
            actor.init()
            if actor.visible():
                self._renderer.load_gpu_resources(self, actor)

    def unload(self):
        for actor in self._actors:
            actor.shutdown()
            if actor.visible():
                self._renderer.unload_gpu_resources(self, actor)

    def process_input(self, event):
        for actor in self._actors:
            actor.process_input(event)

    def update(self, frame_time):
        for actor in self._actors:
            actor.update(frame_time)

    def render(self):
        for actor in self._actors:
            if actor.visible():
                self._renderer.render(self, actor)

3.渲染重构

3.1.手动控制GPU资源load/unload

  • class RenderSystem 加载与卸载GPU资源

    python
    def load_gpu_resources(self, scene, actor: Actor):
        if not actor.visible():
            return
    
        mesh_comp: MeshComponent = actor.get_component('MeshComponent')
        mesh_comp.load_gpu_resource()
    
        material: MaterialComponent = actor.get_component('MaterialComponent')
        material.load_gpu_resources()
    
    def unload_gpu_resources(self, scene, actor: Actor):
        mesh_comp: MeshComponent = actor.get_component('MeshComponent')
        mesh_comp.unload_gpu_resource()
    
        material: MaterialComponent = actor.get_component('MaterialComponent')
        material.unload_gpu_resources()

mesh_comp.load_gpu_resource()material.load_gpu_resources() 就是上文提到的渲染重构部分。

3.2.渲染方法

目前渲染功能在RenderSystem类的 def render(self, scene, actor: Actor) 方法中。但是,这部分很明显需要重构。(TODO +1)

python
def render(self, scene, actor: Actor):
    GL.glEnable(GL.GL_DEPTH_TEST)

    GL.glEnable(GL.GL_BLEND)
    GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)
    # ......
    GL.glBindVertexArray(vao)
    GL.glUseProgram(mat_shader.get_program())
    GL.glUniformMatrix4fv(location, 1, GL.GL_FALSE, glm.value_ptr(param_value))
    # ......
    GL.glActiveTexture(MeshRendererComponent.INDEX_TO_GL_TEXTURE_LOCATION[gl_texture_index])
    GL.glBindTexture(GL.GL_TEXTURE_2D, mat_tex.get_texture_id())
    # ......
    GL.glDrawArrays(GL.GL_TRIANGLES, 0, vertex_num)
    # ......
    GL.glBindTexture(GL.GL_TEXTURE_2D, 0)
    GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
    GL.glBindVertexArray(0)
    GL.glUseProgram(0)

4.结果展示

alt text

5.总结

到这里,基本完成了OpenGL的基础使用,接下来会有两个方向可以选择:

  1. OpenGL进阶: 深度测试、模板测试、GPU实例化等。

  2. 真正的图形学入门: 光照、阴影、PBR、后处理等。

原本,以上这两部分都会按部就班进行,但是最近计划被打乱,又刚好OpenGL基础系列结束,所以接下来可能会很久之后才会继续了。伤心一秒钟:(