Mugichoko's blog

Mugichoko’s blog

プログラミングを中心としたメモ書き.

ModernGL奮闘記 (3) - GLFWウィンドウ -

Stanford Bunnyを読み込めたので,以下の2つに挑戦する.

  1. 自分で用意したシェーダを使う
  2. それをGLFWウィンドウに表示する
    • やはりフレームは自分で更新したい

フォルダ構造

  • glfw_model_loading.py
  • resources
    • models/bunny/bunny.objなどのファイル
    • shaders
      • test_vs.glsl: バーテクスシェーダ
      • test_fs.glsl: フラグメントシェーダ

コード

# glfw_model_loading.py
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 import Camera

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()

        # 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="test_vs.glsl", fragment_shader="test_fs.glsl",
            )
        )
        ## meshes
        resources.register_scene_dir((self.resource_dir / "models").resolve())
        self.scene = scenes.load(SceneDescription(path="bunny/bunny.obj"))
        for mesh in self.scene.meshes:
            print(">> mesh.attributes:", mesh.attributes)
        print(">> len(self.scene.meshes):", len(self.scene.meshes))
        
        # Camera
        self.camera = Camera(aspect_ratio=width / height, near=0.01, far=100.0)
        self.camera.set_position(0, 0.05, 0.5)
        self.camera.look_at(pos=(0, 0, 0))

    def run(self) -> None:
        while not self.window.is_closing:
            self.window.ctx.enable_only(mgl.DEPTH_TEST)
            self.window.ctx.clear(1, 0, 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.scene.root_nodes[0].mesh.material.mat_texture.texture.use(0)
            self.scene.root_nodes[0].mesh.vao.render(self.program)

            self.window.swap_buffers()

if __name__ == "__main__":
    app = App()
    app.run()
// test_vs.glsl
#version 440

in vec3 in_position;
in vec2 in_texcoord_0;
in vec3 in_normal;

uniform mat4 M; // model matrix
uniform mat4 V; // view matrix
uniform mat4 P; // projection matrix

out vec3 vsNormal;
out vec2 vsTexCoord;

void main()
{
    vsTexCoord = in_texcoord_0;
    vsNormal = in_normal;

    gl_Position = P * V * M * vec4(in_position, 1.0);
}
// test_fs.glsl
#version 440

layout (binding = 0) uniform sampler2D diffuseMap;
layout (binding = 1) uniform sampler2D specularMap;
layout (binding = 2) uniform sampler2D normalMap;
layout (binding = 3) uniform sampler2D heightMap;

in vec3 vsNormal;
in vec2 vsTexCoord;

out vec4 fsColor;

void main()
{
    fsColor = texture2D(diffuseMap, vsTexCoord);
}

解説

  • glfw_model_loading.py
    • line 16: ここでGLFWを指定
    • lines 24 - 30: シェーダの読み込み
    • lines 43 - 54: メインループ
    • line 58: メインループを実行
  • test_vs.glsl & test_fs.glsl

結果

本当に簡単で直観的.ちなみに,バーテクスシェーダとフラグメントシェーダは合わせて一つのファイルに書き込める様子(参考:ModernGL-Window/examples).

f:id:Mugichoko:20211112070318p:plain
ライティングなしのテクスチャマッピングができた!