I built a gltf-viewer on my own, but the functionality for loading glf was implemented by referencing gltf-viewer-tutorial, the result is not correct. Below is my ouput for ToyCar.gltf .
The ouput of that tutorial code looks like this:
I think maybe I didn't bind the right texture? Here is my main render loop:
for (const auto& model : info.scene->m_models) {
for (const auto& mesh : model->get_meshes()) {
// model ubo
m_model_data.mvp_matrix = m_camera_data.proj_view * mesh.model_matrix;
m_model_data.mv_matrix = m_camera_data.view * mesh.model_matrix;
m_model_data.normal_matrix = glm::transpose(glm::inverse(m_model_data.mv_matrix));
m_model_ubo->set_data(&m_model_data, sizeof(ModelData));
// bind textures
mesh.material.bind_all_textures();
forward_shader.set_uniform("uBaseColorFactor", mesh.material.base_color_factor);
if (mesh.material.has_component(PBRComponent::MetallicRoughness)) {
forward_shader.set_uniform("uRoughnessFactor", (float)mesh.material.roughness_factor);
forward_shader.set_uniform("uMetallicFactor", (float)mesh.material.metallic_factor);
}
if (mesh.material.has_component(PBRComponent::Emissive)) {
forward_shader.set_uniform("uEmissiveFactor", mesh.material.emissive_factor);
}
if (mesh.material.has_component(PBRComponent::Occlusion)) {
forward_shader.set_uniform("uOcclusionStrength", (float)mesh.material.occlusion_strength);
}
RenderAPI::draw_mesh(mesh);
}
}
And here is my texture binding logic:
textures.at(PBRComponent::BaseColor)->bind(3);
if (textures.contains(PBRComponent::MetallicRoughness))
textures.at(PBRComponent::MetallicRoughness)->bind(4);
if (textures.contains(PBRComponent::Emissive))
textures.at(PBRComponent::Emissive)->bind(5);
if (textures.contains(PBRComponent::Occlusion))
textures.at(PBRComponent::Occlusion)->bind(6);
void Texture2D::bind(GLenum slot) const {
glBindTextureUnit(slot, m_id);
}
Finally, here is my fragment shader:
#version 450
// A reference implementation can be found here:
// https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/metallic-roughness.frag
// Here we implement a simpler version handling only one directional light and
// no normal map/opacity map
in vec3 vViewSpacePosition;
in vec3 vViewSpaceNormal;
in vec2 vTexCoords;
// Here we use vTexCoords but we should use vTexCoords1 or vTexCoords2 depending
// on the material because glTF can handle two texture coordinates sets per
// object see
// https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/textures.glsl
// for a reference implementation
uniform vec3 uLightDirection;
uniform vec3 uLightIntensity;
uniform vec4 uBaseColorFactor;
uniform float uMetallicFactor;
uniform float uRoughnessFactor;
uniform vec3 uEmissiveFactor;
uniform float uOcclusionStrength;
layout(binding = 3) uniform sampler2D uBaseColorTexture;
layout(binding = 4) uniform sampler2D uMetallicRoughnessTexture;
layout(binding = 5) uniform sampler2D uEmissiveTexture;
layout(binding = 6) uniform sampler2D uOcclusionTexture;
uniform int uApplyOcclusion;
out vec3 fColor;
// Constants
const float GAMMA = 2.2;
const float INV_GAMMA = 1. / GAMMA;
const float M_PI = 3.141592653589793;
const float M_1_PI = 1.0 / M_PI;
// We need some simple tone mapping functions
// Basic gamma = 2.2 implementation
// Stolen here:
// https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/master/src/shaders/tonemapping.glsl
// linear to sRGB approximation
// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
vec3 linearTosRGB(vec3 color) { return pow(color, vec3(INV_GAMMA)); }
// sRGB to linear approximation
// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
vec4 sRGBToLinear(vec4 srgbIn)
{
return vec4(pow(srgbIn.xyz, vec3(GAMMA)), srgbIn.w);
}
// The model is mathematically described here
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-b-brdf-implementation
// We try to use the same or similar names for variables
// One thing that is not descibed in the documentation is that the BRDF value
// "f" must be multiplied by NdotL at the end.
void main()
{
vec3 N = normalize(vViewSpaceNormal);
vec3 V = normalize(-vViewSpacePosition);
vec3 L = uLightDirection;
vec3 H = normalize(L + V);
vec4 baseColorFromTexture =
sRGBToLinear(texture(uBaseColorTexture, vTexCoords));
vec4 metallicRougnessFromTexture =
texture(uMetallicRoughnessTexture, vTexCoords);
vec4 baseColor = uBaseColorFactor * baseColorFromTexture;
vec3 metallic = vec3(uMetallicFactor * metallicRougnessFromTexture.b);
float roughness = uRoughnessFactor * metallicRougnessFromTexture.g;
// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#pbrmetallicroughnessmetallicroughnesstexture
// "The metallic-roughness texture.The metalness values are sampled from the B
// channel.The roughness values are sampled from the G channel."
vec3 dielectricSpecular = vec3(0.04);
vec3 black = vec3(0.);
vec3 c_diff =
mix(baseColor.rgb * (1 - dielectricSpecular.r), black, metallic);
vec3 F_0 = mix(vec3(dielectricSpecular), baseColor.rgb, metallic);
float alpha = roughness * roughness;
float VdotH = clamp(dot(V, H), 0., 1.);
float baseShlickFactor = 1 - VdotH;
float shlickFactor = baseShlickFactor * baseShlickFactor; // power 2
shlickFactor *= shlickFactor; // power 4
shlickFactor *= baseShlickFactor; // power 5
vec3 F = F_0 + (vec3(1) - F_0) * shlickFactor;
float sqrAlpha = alpha * alpha;
float NdotL = clamp(dot(N, L), 0., 1.);
float NdotV = clamp(dot(N, V), 0., 1.);
float visDenominator =
NdotL * sqrt(NdotV * NdotV * (1 - sqrAlpha) + sqrAlpha) +
NdotV * sqrt(NdotL * NdotL * (1 - sqrAlpha) + sqrAlpha);
float Vis = visDenominator > 0. ? 0.5 / visDenominator : 0.0;
float NdotH = clamp(dot(N, H), 0., 1.);
float baseDenomD = (NdotH * NdotH * (sqrAlpha - 1.) + 1.);
float D = M_1_PI * sqrAlpha / (baseDenomD * baseDenomD);
vec3 f_specular = F * Vis * D;
vec3 diffuse = c_diff * M_1_PI;
vec3 f_diffuse = (1. - F) * diffuse;
vec3 emissive = sRGBToLinear(texture(uEmissiveTexture, vTexCoords)).rgb * uEmissiveFactor;
vec3 color = (f_diffuse + f_specular) * uLightIntensity * NdotL;
color += emissive;
if (1 == uApplyOcclusion) {
float ao = texture(uOcclusionTexture, vTexCoords).r;
color = mix(color, color * ao, uOcclusionStrength);
}
fColor = linearTosRGB(color);
}
I have been struggling with this bug for days and I still couldn't get it right. Here is my github repo for this project: My gltf-viewer