ModernGLでCubemap(キューブマップ)を使ってみる.
キューブマップテクスチャを用意して,それを立方体の6面にマッピングする.単なる動作確認が目的なので,できるだけ簡単に実装する方針.
実装方針
- Framebuffer Object (FBO) を用意(line 47参照)
- キューブマップテクスチャを用意(各面はFBOと同サイズ)(line 50参照)
- FBOをクリアすることで特定の色で塗りつぶす(例えばlines 77 & 78参照)
- この結果をキューブマップの1面にコピー(例えばline 79参照)
- 3-4を6面分繰り返す(lines 75 - 99参照)
尚,モデルのcube.obj
はここから拝借.
コード (Python)
from pathlib import Path import moderngl as mgl from moderngl_window.conf import settings from moderngl_window import resources from moderngl_window.resources import programs from moderngl_window.meta import ProgramDescription from moderngl_window.scene.camera import OrbitCamera import numpy as np from pathlib import Path import numpy as np import moderngl as mgl import moderngl_window as mglw from moderngl_window.conf import settings from moderngl_window import resources from moderngl_window.resources import programs, scenes from moderngl_window.meta import ProgramDescription, SceneDescription from moderngl_window.scene.camera import OrbitCamera class App: def __init__(self, width=512, height=512) -> None: # Window # create a gl window: https://moderngl-window.readthedocs.io/en/latest/reference/settings.conf.settings.html#moderngl_window.conf.Settings.WINDOW settings.WINDOW["class"] = "moderngl_window.context.glfw.Window" settings.WINDOW["gl_version"] = (4, 1) settings.WINDOW["size"] = (width, height) settings.WINDOW["aspect_ratio"] = width / height self.window = mglw.create_window_from_settings() self.window.print_context_info() # Resources self.resource_dir = Path(__file__).parent.resolve() / "resources" ## shaders resources.register_program_dir((self.resource_dir / "shaders").resolve()) self.program = programs.load( ProgramDescription( vertex_shader="cubemap_vs.glsl", fragment_shader="cubemap_fs.glsl", ) ) ## meshes resources.register_scene_dir((self.resource_dir / "models").resolve()) self.scene = scenes.load(SceneDescription(path="cube.obj")) for mesh in self.scene.meshes: print(">> mesh.attributes:", mesh.attributes) print(">> len(self.scene.meshes):", len(self.scene.meshes)) # FBO self.fbo = self.window.ctx.simple_framebuffer((256, 256), dtype="f4") # Cubemap self.cubemap = self.window.ctx.texture_cube((256, 256), components=4, dtype="f4") # Camera self.camera = OrbitCamera( aspect_ratio=width / height, near=0.01, far=100.0) self.camera.mouse_sensitivity = 0.75 # Set mouse callback ## Ref: https://github.com/moderngl/moderngl-window/blob/master/examples/custom_config_class.py self.window.mouse_position_event_func = self.mouse_position_event self.window.mouse_scroll_event_func = self.mouse_scroll_event self.window.mouse_exclusivity = True def mouse_position_event(self, x, y, dx, dy) -> None: #print("Mouse position pos={} {} delta={} {}".format(x, y, dx, dy)) self.camera.rot_state(dx, dy) def mouse_scroll_event(self, x_offset, y_offset) -> None: #print("mouse_scroll_event", x_offset, y_offset) self.camera.zoom_state(y_offset) def render_to_cubemap(self): self.window.ctx.enable(mgl.DEPTH_TEST | mgl.CULL_FACE | mgl.BLEND) # Render six different colors for each cubemap face # 0: right (R) self.fbo.use() self.fbo.clear(1.0, 0.0, 0.0, 1.0) self.cubemap.write(0, self.fbo.read(components=4, dtype="f4")) # 1: left (G) self.fbo.use() self.fbo.clear(0.0, 1.0, 0.0, 1.0) self.cubemap.write(1, self.fbo.read(components=4, dtype="f4")) # 2: up (B) self.fbo.use() self.fbo.clear(0.0, 0.0, 1.0, 1.0) self.cubemap.write(2, self.fbo.read(components=4, dtype="f4")) # 3: down (Y) self.fbo.use() self.fbo.clear(1.0, 1.0, 0.0, 1.0) self.cubemap.write(3, self.fbo.read(components=4, dtype="f4")) # 4: back (B) self.fbo.use() self.fbo.clear(0.0, 0.0, 0.0, 1.0) self.cubemap.write(4, self.fbo.read(components=4, dtype="f4")) # 5: front (W) self.fbo.use() self.fbo.clear(1.0, 1.0, 1.0, 1.0) self.cubemap.write(5, self.fbo.read(components=4, dtype="f4")) self.window.fbo.use() def run(self) -> None: self.render_to_cubemap() while not self.window.is_closing: self.window.ctx.enable_only(mgl.DEPTH_TEST | mgl.CULL_FACE) self.window.ctx.clear(0.96, 0.56, 0.63, 0.0) self.program["M"].write(np.identity(4, dtype="float32")) self.program["V"].write(self.camera.matrix) self.program["P"].write(self.camera.projection.matrix) self.cubemap.use(0) self.scene.root_nodes[0].mesh.vao.render(self.program) self.window.swap_buffers() if __name__ == "__main__": app = App() app.run()
シェーダ (GLSL)
// cubemap_vs.glsl #version 440 in vec3 in_position; uniform mat4 M; // model matrix uniform mat4 V; // view matrix uniform mat4 P; // projection matrix out vec3 vs_dir; void main() { vs_dir = normalize(in_position); gl_Position = P * V * M * vec4(in_position, 1.0); }
// cubemap_fs.glsl #version 440 layout (binding = 0) uniform samplerCube cubemap; in vec3 vs_dir; out vec4 fsColor; void main() { fsColor = texture(cubemap, vs_dir); }