From c447ba1064ec9b170dfbee30d3b49da4cf8e0ffd Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 6 Jan 2023 09:25:11 -0800 Subject: More spec-compliant rendering of albedo and metal-roughness. --- gfx/shaders/cook_torrance.frag | 24 +++++++++++++--- gfx/src/renderer/renderer.c | 1 + gfx/src/util/scene.c | 64 +++++++++++++++++++++++++++--------------- gltfview/src/game.c | 15 ++++++---- 4 files changed, 71 insertions(+), 33 deletions(-) diff --git a/gfx/shaders/cook_torrance.frag b/gfx/shaders/cook_torrance.frag index 18d9d18..fcedef6 100644 --- a/gfx/shaders/cook_torrance.frag +++ b/gfx/shaders/cook_torrance.frag @@ -5,8 +5,12 @@ uniform float MetallicFactor; uniform float RoughnessFactor; uniform vec3 EmissiveFactor; +#ifdef HAS_ALBEDO_MAP uniform sampler2D BaseColorTexture; +#endif +#ifdef HAS_METALLIC_ROUGHNESS_MAP uniform sampler2D MetallicRoughnessTexture; +#endif #ifdef HAS_EMISSIVE_MAP uniform sampler2D EmissiveTexture; #endif @@ -161,10 +165,22 @@ void main() //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. // TODO: BaseColorFactor and BaseColorTexture are vec4/rgba quantities - // respectively. Handle the alpha channel later. + // respectively. Handle the alpha channel. // TODO: Other factors. +#ifdef HAS_ALBEDO_MAP vec3 albedo = vec3(BaseColorFactor) * texture(BaseColorTexture, Texcoord).rgb; - vec3 metal_roughness = texture(MetallicRoughnessTexture, Texcoord).rgb; +#else + vec3 albedo = vec3(BaseColorFactor); +#endif +#ifdef HAS_METALLIC_ROUGHNESS_MAP + // Spec: "Its green channel contains roughness values and its blue channel + // contains metalness values." + vec2 metal_roughness + = vec2(MetallicFactor, RoughnessFactor) + * texture(MetallicRoughnessTexture, Texcoord).bg; +#else + vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); +#endif #ifdef HAS_EMISSIVE_MAP vec3 emissive = texture(EmissiveTexture, Texcoord).rgb; #else @@ -175,8 +191,8 @@ void main() #else float occlusion = 0.0; #endif - float metallic = MetallicFactor * metal_roughness.b; - float roughness = metal_roughness.g; + float metallic = metal_roughness.x; + float roughness = metal_roughness.y; // For a single light direction: // vec3 brdf = cook_torrance(albedo, metallic, roughness, emissive, NdotL, NdotV, NdotH, HdotV); diff --git a/gfx/src/renderer/renderer.c b/gfx/src/renderer/renderer.c index 6b77ffe..3654546 100644 --- a/gfx/src/renderer/renderer.c +++ b/gfx/src/renderer/renderer.c @@ -29,6 +29,7 @@ bool renderer_make(Renderer* renderer, RenderBackend* render_backend) { assert(renderer); assert(render_backend); + // TODO: Load the IBL stuff lazily. if (!(renderer->ibl = gfx_make_ibl(render_backend))) { renderer_destroy(renderer, render_backend); return false; diff --git a/gfx/src/util/scene.c b/gfx/src/util/scene.c index 52812e9..35d6dd0 100644 --- a/gfx/src/util/scene.c +++ b/gfx/src/util/scene.c @@ -130,12 +130,14 @@ #define UNIFORM_NORMAL_MAP "NormalMap" // Shader compiler defines. Must match the names in shaders. -#define DEFINE_HAS_TEXCOORDS "HAS_TEXCOORDS" -#define DEFINE_HAS_NORMALS "HAS_NORMALS" -#define DEFINE_HAS_TANGENTS "HAS_TANGENTS" -#define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" -#define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" -#define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" +#define DEFINE_HAS_TEXCOORDS "HAS_TEXCOORDS" +#define DEFINE_HAS_NORMALS "HAS_NORMALS" +#define DEFINE_HAS_TANGENTS "HAS_TANGENTS" +#define DEFINE_HAS_ALBEDO_MAP "HAS_ALBEDO_MAP" +#define DEFINE_HAS_METALLIC_ROUGHNESS_MAP "HAS_METALLIC_ROUGHNESS_MAP" +#define DEFINE_HAS_NORMAL_MAP "HAS_NORMAL_MAP" +#define DEFINE_HAS_OCCLUSION_MAP "HAS_OCCLUSION_MAP" +#define DEFINE_HAS_EMISSIVE_MAP "HAS_EMISSIVE_MAP" typedef enum TextureType { BaseColorTexture, @@ -155,9 +157,11 @@ typedef struct MeshPermutation { bool has_normals : 1; bool has_tangents : 1; // Textures. - bool has_normal_map : 1; - bool has_occlusion_texture : 1; - bool has_emissive_texture : 1; + bool has_albedo_map : 1; + bool has_metallic_roughness_map : 1; + bool has_normal_map : 1; + bool has_occlusion_map : 1; + bool has_emissive_map : 1; }; int32_t all; }; @@ -179,9 +183,11 @@ static size_t make_defines( check(has_texcoords, DEFINE_HAS_TEXCOORDS); check(has_normals, DEFINE_HAS_NORMALS); check(has_tangents, DEFINE_HAS_TANGENTS); + check(has_albedo_map, DEFINE_HAS_ALBEDO_MAP); + check(has_metallic_roughness_map, DEFINE_HAS_METALLIC_ROUGHNESS_MAP); check(has_normal_map, DEFINE_HAS_NORMAL_MAP); - check(has_occlusion_texture, DEFINE_HAS_OCCLUSION_MAP); - check(has_emissive_texture, DEFINE_HAS_EMISSIVE_MAP); + check(has_occlusion_map, DEFINE_HAS_OCCLUSION_MAP); + check(has_emissive_map, DEFINE_HAS_EMISSIVE_MAP); return next; } @@ -191,10 +197,11 @@ static ShaderProgram* make_shader_permutation( RenderBackend* render_backend, MeshPermutation perm) { LOGD( "Compiling Cook-Torrance shader permutation: texcoords: %d, normals: " - "%d, tangents: %d, normal map: %d, AO map: %d, emissive map: %d", + "%d, tangents: %d, albedo map: %d, metallic-roughness map: %d, normal " + "map: %d, AO map: %d, emissive map: %d", perm.has_texcoords, perm.has_normals, perm.has_tangents, - perm.has_normal_map, perm.has_occlusion_texture, - perm.has_emissive_texture); + perm.has_albedo_map, perm.has_metallic_roughness_map, perm.has_normal_map, + perm.has_occlusion_map, perm.has_emissive_map); ShaderCompilerDefine defines[GFX_MAX_SHADER_COMPILER_DEFINES]; const size_t num_defines = make_defines(perm, defines); @@ -436,9 +443,11 @@ static bool load_materials( Material** materials) { assert(data); assert(render_backend); - assert(load_texture_cmds); - assert(textures); assert(materials); + if (data->textures_count > 0) { + assert(load_texture_cmds); + assert(textures); + } for (cgltf_size i = 0; i < data->materials_count; ++i) { const cgltf_material* mat = &data->materials[i]; @@ -446,7 +455,7 @@ static bool load_materials( int next_uniform = 0; MaterialDesc desc = {0}; - // TODO: emissive texture/factor and other material parameters. + // TODO: specular/glossiness and other material parameters. if (mat->has_pbr_metallic_roughness) { const cgltf_pbr_metallic_roughness* pbr = &mat->pbr_metallic_roughness; @@ -579,10 +588,17 @@ static bool load_meshes( const cgltf_primitive* prim = &mesh->primitives[p]; const cgltf_material* mat = prim->material; - MeshPermutation perm = {0}; - perm.has_normal_map = mat->normal_texture.texture != 0; - perm.has_occlusion_texture = mat->occlusion_texture.texture != 0; - perm.has_emissive_texture = mat->emissive_texture.texture != 0; + MeshPermutation perm = {0}; + perm.has_normal_map = mat->normal_texture.texture != 0; + perm.has_occlusion_map = mat->occlusion_texture.texture != 0; + perm.has_emissive_map = mat->emissive_texture.texture != 0; + // TODO: specular/glossiness and other material parameters. + if (mat->has_pbr_metallic_roughness) { + const cgltf_pbr_metallic_roughness* pbr = &mat->pbr_metallic_roughness; + perm.has_albedo_map = pbr->base_color_texture.texture != 0; + perm.has_metallic_roughness_map = + pbr->metallic_roughness_texture.texture != 0; + } GeometryDesc geometry_desc = { .type = from_gltf_primitive_type(prim->type)}; @@ -949,8 +965,10 @@ static bool load_scene( goto cleanup; } - load_textures_lazy( - data, render_backend, mstring_cstring(&directory), load_texture_cmds); + if (data->textures_count > 0) { + load_textures_lazy( + data, render_backend, mstring_cstring(&directory), load_texture_cmds); + } if (!load_materials( data, render_backend, load_texture_cmds, textures, materials)) { diff --git a/gltfview/src/game.c b/gltfview/src/game.c index 54e498b..224200d 100644 --- a/gltfview/src/game.c +++ b/gltfview/src/game.c @@ -189,8 +189,8 @@ static bool load_texture_debugger_scene(Game* game) { bool game_new(Game* game, int argc, const char** argv) { // TODO: getopt() to implement proper argument parsing. - const char* view_mode = argc > 1 ? argv[1] : ""; - const char* scene_filepath = argc > 2 ? argv[2] : DEFAULT_SCENE_FILE; + const char* scene_filepath = argc > 1 ? argv[1] : DEFAULT_SCENE_FILE; + const char* view_mode = argc > 2 ? argv[2] : ""; game->gfx = gfx_init(); if (!game->gfx) { @@ -234,12 +234,15 @@ void game_update(Game* game, double t, double dt) { usleep(1000); game->elapsed -= 1.0; } - Camera* camera = gfx_get_camera_camera(game->camera); + // TODO: Compute bounding boxes to then find a good orbit point and radius + // for each scene. + const vec3 orbit_point = vec3_make(0, 2, 0); + Camera* camera = gfx_get_camera_camera(game->camera); spatial3_orbit( - &camera->spatial, vec3_make(0, 0, 0), - /*radius=*/2, + &camera->spatial, orbit_point, + /*radius=*/2.5, /*azimuth=*/t * 0.5, /*zenith=*/0); - spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0)); + spatial3_lookat(&camera->spatial, orbit_point); } void game_render(const Game* game) { -- cgit v1.2.3