OpenGL チュートリアルの
のサンプルコード
を元に、 pbr shader を自前で稼働させるには何が必要かという観点から調べてみた。
PBR Material
glTF でもおなじみの最近のフォトリアルマテリアルのデファクトです。
albedo
, normal
, metallic
, roughness(smoothness)
, ambient occlusion
の5つのテクスチャーを持つ。
property | channel |
---|---|
albedo | RGB |
normal | RGB |
metallic | float |
roughness | float |
ambient occlusion | float |
metallic, roughness, ambient occlusion は grayscale なので1枚のテクスチャーにまとめられる。
PBR Shader
vertex shader
シンプル。特に変わったことはしていない。
gl_Position = projection * view * vec4(WorldPos, 1.0);
fragment shader
8つのテクスチャーを使う。
// 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) 向けのテクスチャー。
tangent uv と 法線から計算している
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
envCubemap を元に fbo を使って cubemap を生成します。
GL_TEXTURE_CUBE_MAP
, GL_RGB16F
, 32x32
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
だいたい以下のような感じ。
// 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ロード(5つの球、Pbrマテリアルのテクスチャーロード)
- main loop