PBR sahder を動かす

OpenGL チュートリアルの

https://learnopengl.com/PBR/Theory

のサンプルコード

https://github.com/JoeyDeVries/LearnOpenGL/tree/master/src/6.pbr/2.2.2.ibl_specular_textured

を元に、 pbr shader を自前で稼働させるには何が必要かという観点から調べてみた。

![pbr.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/108d3696-7850-7727-913d-81a2bfd147c1.jpeg)

# PBR Material

glTF でもおなじみの最近のフォトリアルマテリアルのデファクトです。

https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#materials

`albedo`, `normal`, `metallic`, `roughness(smoothness)`, `ambient occlusion`の５つのテクスチャーを持つ。

|property|channel|
|-|-|
|albedo|RGB|
|normal|RGB|
|metallic|float|
|roughness|float|
|ambient occlusion|float|

:::note
metallic, roughness, ambient occlusion は grayscale なので１枚のテクスチャーにまとめられる。
:::

# PBR Shader

## vertex shader

https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/6.pbr/2.2.2.ibl_specular_textured/2.2.2.pbr.vs

シンプル。特に変わったことはしていない。

```c
gl_Position =  projection * view * vec4(WorldPos, 1.0);
```

## fragment shader

https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/6.pbr/2.2.2.ibl_specular_textured/2.2.2.pbr.fs

8つのテクスチャーを使う。

```c
// material parameters
uniform sampler2D albedoMap;
uniform sampler2D normalMap;
uniform sampler2D metallicMap;
uniform sampler2D roughnessMap;
uniform sampler2D aoMap;

// IBL
uniform samplerCube irradianceMap;
uniform samplerCube prefilterMap;
uniform sampler2D brdfLUT;
```

最初の5つは前項で触れた、 PbrMaterial のテクスチャー。
残りの3つが IBL(ImageBasedLighting) 向けのテクスチャー。

:::note
tangent uv と 法線から計算している
:::

# ibl_specular_textured.cpp

https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/6.pbr/2.2.2.ibl_specular_textured/ibl_specular_textured.cpp

## IBL向けのテクスチャー事前生成

IBL に使用する3つのテクスチャーは、プログラム開始時に生成する。
irradianceMap と prefilterMap は hdr テクスチャーから生成する。
brdfLUT は材料は不要です。

### hdr 画像から cubemap を生成する: 2.2.2.equirectangular_to_cubemap.fs

stb で `esources/textures/hdr/newport_loft.hdr` をロードしてテクスチャー化します。

`equirectangular`, `GL_RGB16F`

hdrTexture を元に fbo を使って cubemap `envCubemap` を生成します。
この envCubemap は前処理と、毎フレームの skybox 描画の両方で使います。

`GL_TEXTURE_CUBE_MAP`, `GL_RGB16F`, `512x512`

### cubemap から irradianceMap を生成する: 2.2.2.irradiance_convolution.fs

https://learnopengl.com/PBR/IBL/Diffuse-irradiance

envCubemap を元に fbo を使って cubemap を生成します。

`GL_TEXTURE_CUBE_MAP`, `GL_RGB16F`, `32x32`

https://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20150828

### cubemap から prefilterMap を生成する: 2.2.2.prefilter.fs

envCubemap を元に fbo を使って cubemap を生成します。

`GL_TEXTURE_CUBE_MAP`, `GL_RGB16F`, `128x128`, `mipLevel=5`

### bbrdfLUT(LookUpTable) を生成する: 2.2.2.brdf.vs, fs

`GL_TEXTURE_2D`, `GL_RG16F`, `512x512`

### skybox: 2.2.2.background.vs, fs

## main loop
だいたい以下のような感じ。

```cpp
    // then before rendering, configure the viewport to the original framebuffer's screen dimensions
    int scrWidth, scrHeight;
    glfwGetFramebufferSize(window, &scrWidth, &scrHeight);
    glViewport(0, 0, scrWidth, scrHeight);

    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// sphere0
// sphere1
// sphere2
// sphere3
// sphere4
// skybox

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
```

# 前処理 と mainloop を分ければ読みやすい

- pbr 環境ロード、テクスチャー生成(hdr画像からcubemapなどを生成)
- pbr Sceneロード(５つの球、Pbrマテリアルのテクスチャーロード)
- main loop