- Add uColorFactor.
In glTF, color factor can be used as a color when there isn't color texture,
but it is also used to be multiplied with base color that is sampled from color texture.
In previous implementation, when there isn't color texture, color factor is converted
as single pixel texture.
If the texture is from color factor, we shouldn't multiply color factor again.
But, we couldn't notify whether the texture is from image file or color factor.
- Add vertex color
- Add uMetallicFactor, uRoughnessFactor
- Use #ifdef instead of single value texture.
- Use pre-computed brdf texture.
- Modify alpha mode
Change-Id: I048b793a4481b65da5ce07d0d3e263ae74b2ab59
Signed-off-by: seungho <sbsh.baek@samsung.com>
"baseColorTexture" : {
"index" : 0
},
- "metallicRoughnessTexture" : {
- "index" : 1
- },
"baseColorFactor": [ 1.000, 0.766, 0.336, 1.0 ],
"metallicFactor": 1.0,
"roughnessFactor": 0.0
DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
- DALI_TEST_EQUAL(0u, ctx.resources.mEnvironmentMaps.size());
+ // Default envmap is used
+ DALI_TEST_EQUAL(1u, ctx.resources.mEnvironmentMaps.size());
auto& materials = ctx.resources.mMaterials;
DALI_TEST_EQUAL(2u, materials.size());
const MaterialDefinition materialGroundTruth[]{
- {MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
- MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
- MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY | MaterialDefinition::GLTF_CHANNELS |
+ {MaterialDefinition::ALBEDO | MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
+ MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY |
(0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
0,
- Vector4(1.f, .766f, .336f, 1.f),
- Vector3(0.2, 0.1, 0.0),
+ Color::WHITE,
1.f,
0.f,
+ Vector4(1.000, 0.766, 0.336, 1.0),
+ 1.f,
1.f,
+ Vector3(0.2, 0.1, 0.0),
+ true,
+ false,
+ true,
{
{MaterialDefinition::ALBEDO,
{"AnimatedCube_BaseColor.png",
SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
- {MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
- {"AnimatedCube_MetallicRoughness.png",
- SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT)}},
{MaterialDefinition::NORMAL,
{"AnimatedCube_BaseColor.png",
SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT)}},
MaterialDefinition::EMISSIVE | MaterialDefinition::OCCLUSION |
MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
0,
- Vector4(1.f, .766f, .336f, 1.f),
- Vector3(0.2, 0.1, 0.0),
+ Color::WHITE,
1.f,
0.f,
+ Vector4(1.000, 0.766, 0.336, 1.0),
1.f,
+ 1.f,
+ Vector3(0.2, 0.1, 0.0),
+ true,
+ true,
+ true,
{
{MaterialDefinition::ALBEDO,
{"AnimatedCube_BaseColor.png",
DALI_TEST_EQUAL(md.mColor, m.mColor);
DALI_TEST_EQUAL(md.mMetallic, m.mMetallic);
DALI_TEST_EQUAL(md.mRoughness, m.mRoughness);
+ DALI_TEST_EQUAL(md.mBaseColorFactor, m.mBaseColorFactor);
+ DALI_TEST_EQUAL(md.mNormalScale, m.mNormalScale);
+ DALI_TEST_EQUAL(md.mOcclusionStrength, m.mOcclusionStrength);
+ DALI_TEST_EQUAL(md.mEmissiveFactor, m.mEmissiveFactor);
+ DALI_TEST_EQUAL(md.mNeedAlbedoTexture, m.mNeedAlbedoTexture);
+ DALI_TEST_EQUAL(md.mNeedMetallicRoughnessTexture, m.mNeedMetallicRoughnessTexture);
+ DALI_TEST_EQUAL(md.mNeedNormalTexture, m.mNeedNormalTexture);
DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
auto iTexture = md.mTextureStages.begin();
Accessor{Blob{0, 0}, {}},
Accessor{Blob{0, 0}, {}},
Accessor{Blob{0, 0}, {}},
+ Accessor{Blob{0, 0}, {}},
},
{
0,
Accessor{Blob{0, 0}, {}},
Accessor{Blob{0, 0}, {}},
Accessor{Blob{0, 0}, {}},
+ Accessor{Blob{0, 0}, {}},
},
};
&MeshDefinition::mPositions,
&MeshDefinition::mNormals,
&MeshDefinition::mTexCoords,
+ &MeshDefinition::mColors,
&MeshDefinition::mTangents,
&MeshDefinition::mJoints0,
&MeshDefinition::mWeights0})
"MorphPrimitivesTest",
"MRendererTest",
"SimpleSparseAccessor",
+ "AnimatedCube",
})
{
Context ctx;
ShaderDefinitionFactory sdf;
sdf.SetResources(ctx.resources);
auto& resources = ctx.resources;
- resources.mEnvironmentMaps.push_back({});
LoadGltfScene(TEST_RESOURCE_DIR "/MRendererTest.gltf", sdf, ctx.loadResult);
}
DALI_TEST_EQUAL(root.GetChildCount(), 1u);
- DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
- DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
+ Actor child = root.GetChildAt(0);
+
+ DALI_TEST_EQUAL(child.GetProperty(Actor::Property::NAME).Get<std::string>(), "RootNode");
+ DALI_TEST_EQUAL(child.GetProperty(Actor::Property::SCALE).Get<Vector3>(), Vector3(1.0f, 1.0f, 1.0f));
+ DALI_TEST_EQUAL(child.GetRendererCount(), 1u);
+ DALI_TEST_EQUAL(child.GetRendererAt(0).GetTextures().GetTextureCount(), 4u);
END_TEST;
}
Permutation permutations[]{
{
[](ShaderParameters& p) {},
- {},
+ {"THREE_TEX"},
RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK,
},
{
for(auto& ps : permSets)
{
- printf("%ld\n", &ps - permSets);
-
auto modelNode = new ModelNode();
modelNode->mMeshIdx = 0;
modelNode->mMaterialIdx = 0;
DALI_TEST_EQUAL(shaderDef.mRendererState, rendererState);
uint32_t definesUnmatched = shaderDef.mDefines.size();
- for(auto& d : shaderDef.mDefines)
+ for(auto& define : shaderDef.mDefines)
{
- auto iFind = defines.find(d);
+ auto iFind = defines.find(define);
if(iFind != defines.end())
{
defines.erase(iFind);
}
else
{
- printf("mismatched: %s\n", d.c_str());
break;
}
}
DALI_TEST_CHECK(defines.empty());
DALI_TEST_EQUAL(0, definesUnmatched);
- printf("defines OK\n");
-
auto uMaxLOD = shaderDef.mUniforms["uMaxLOD"];
DALI_TEST_EQUAL(uMaxLOD.GetType(), Property::FLOAT);
+// Original Code
+// https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/glTF-WebGL-PBR/shaders/pbr-frag.glsl
+// Commit dc84b5e374fb3d23153d2248a338ef88173f9eb6
+//
+// This fragment shader defines a reference implementation for Physically Based Shading of
+// a microfacet surface material defined by a glTF model.For the DamagedHelmet.gltf and its Assets
+//
+// References:
+// [1] Real Shading in Unreal Engine 4
+// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+// [2] Physically Based Shading at Disney
+// http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
+// [3] README.md - Environment Maps
+// https://github.com/KhronosGroup/glTF-Sample-Viewer/#environment-maps
+// [4] \"An Inexpensive BRDF Model for Physically based Rendering\" by Christophe Schlick
+// https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf
+
#version 300 es
#ifdef HIGHP
- precision highp float;
+precision highp float;
#else
- precision mediump float;
+precision mediump float;
#endif
#ifdef THREE_TEX
#ifdef GLTF_CHANNELS
-// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#pbrmetallicroughnessmetallicroughnesstexture
#define METALLIC b
#define ROUGHNESS g
#else //GLTF_CHANNELS
#endif //GLTF_CHANNELS
#endif //THREE_TEX
-#ifdef THREE_TEX
- uniform sampler2D sAlbedoAlpha;
- uniform sampler2D sMetalRoughness;
- uniform sampler2D sNormal;
-
-#ifdef ALPHA_TEST
- uniform float uAlphaThreshold;
-#endif //ALPHA_TEST
+uniform lowp vec4 uColorFactor;
+uniform lowp float uMetallicFactor;
+uniform lowp float uRoughnessFactor;
-#else
- uniform sampler2D sAlbedoMetal;
- uniform sampler2D sNormalRoughness;
+#ifdef THREE_TEX
+#ifdef BASECOLOR_TEX
+uniform sampler2D sAlbedoAlpha;
+#endif // BASECOLOR_TEX
+#ifdef METALLIC_ROUGHNESS_TEX
+uniform sampler2D sMetalRoughness;
+#endif // METALLIC_ROUGHNESS_TEX
+#ifdef NORMAL_TEX
+uniform sampler2D sNormal;
+uniform float uNormalScale;
+#endif // NORMAL_TEX
+#else // THREE_TEX
+uniform sampler2D sAlbedoMetal;
+uniform sampler2D sNormalRoughness;
#endif
#ifdef OCCLUSION
- uniform sampler2D sOcclusion;
- uniform float uOcclusionStrength;
+uniform sampler2D sOcclusion;
+uniform float uOcclusionStrength;
#endif
#ifdef EMISSIVE
- uniform sampler2D sEmissive;
- uniform vec3 uEmissiveFactor;
+uniform sampler2D sEmissive;
+uniform vec3 uEmissiveFactor;
#endif
-uniform samplerCube sDiffuse;
-uniform samplerCube sSpecular;
-
-// Number of mip map levels in the texture
-uniform float uMaxLOD;
-
-// Transformation matrix of the cubemap texture
-uniform mat4 uCubeMatrix;
-
-uniform vec4 uColor;
-uniform float uMetallicFactor;
-uniform float uRoughnessFactor;
-
-//IBL Light intensity
+//// For IBL
+uniform samplerCube sDiffuseEnvSampler;
+uniform samplerCube sSpecularEnvSampler;
+uniform sampler2D sbrdfLUT;
uniform float uIblIntensity;
+// For Alpha Mode.
+uniform lowp float uOpaque;
+uniform lowp float uMask;
+uniform lowp float uAlphaThreshold;
+
// TODO: Multiple texture coordinate will be supported.
-in vec2 vUV;
-in vec3 vNormal;
-in vec3 vTangent;
-in vec3 vViewVec;
+in lowp vec2 vUV;
+in lowp mat3 vTBN;
+in lowp vec4 vColor;
+in highp vec3 vPositionToCamera;
out vec4 FragColor;
-// Functions for BRDF calculation come from
-// https://www.unrealengine.com/blog/physically-based-shading-on-mobile
-// Based on the paper by Dimitar Lazarov
-// http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf
-vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV )
+struct PBRInfo
{
- const vec4 c0 = vec4( -1.0, -0.0275, -0.572, 0.022 );
- const vec4 c1 = vec4( 1.0, 0.0425, 1.04, -0.04 );
- vec4 r = Roughness * c0 + c1;
- float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
- vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
-
- return SpecularColor * AB.x + AB.y;
+ mediump float NdotL; // cos angle between normal and light direction
+ mediump float NdotV; // cos angle between normal and view direction
+ mediump float NdotH; // cos angle between normal and half vector
+ mediump float VdotH; // cos angle between view direction and half vector
+ mediump vec3 reflectance0; // full reflectance color (normal incidence angle)
+ mediump vec3 reflectance90; // reflectance color at grazing angle
+ lowp float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2])
+};
+
+const float M_PI = 3.141592653589793;
+const float c_MinRoughness = 0.04;
+
+vec3 specularReflection(PBRInfo pbrInputs)
+{
+ return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
}
-void main()
+float geometricOcclusion(PBRInfo pbrInputs)
{
- // We get information from the maps (albedo, normal map, roughness, metalness
- // I access the maps in the order they will be used
-#ifdef THREE_TEX
- vec4 albedoAlpha = texture(sAlbedoAlpha, vUV.st);
- float alpha = albedoAlpha.a;
-#ifdef ALPHA_TEST
- if (alpha <= uAlphaThreshold)
- {
- discard;
- }
-#endif //ALPHA_TEST
- vec3 albedoColor = albedoAlpha.rgb * uColor.rgb;
-
- vec4 metalRoughness = texture(sMetalRoughness, vUV.st);
- float metallic = metalRoughness.METALLIC * uMetallicFactor;
- float roughness = metalRoughness.ROUGHNESS * uRoughnessFactor;
-
- vec3 normalMap = texture(sNormal, vUV.st).rgb;
-#else //THREE_TEX
- vec4 albedoMetal = texture(sAlbedoMetal, vUV.st);
- vec3 albedoColor = albedoMetal.rgb * uColor.rgb;
- float metallic = albedoMetal.a * uMetallicFactor;
-
- vec4 normalRoughness = texture(sNormalRoughness, vUV.st);
- vec3 normalMap = normalRoughness.rgb;
- float roughness = normalRoughness.a * uRoughnessFactor;
-#endif
- //Normalize vectors
- vec3 normal = normalize(vNormal);
- vec3 tangent = normalize(vTangent);
-
- // NOTE: normal and tangent have to be orthogonal for the result of the cross()
- // product to be a unit vector. We might find that we need to normalize().
- vec3 bitangent = cross(normal, tangent);
-
- vec3 viewVec = normalize(vViewVec);
-
- // Create Inverse Local to world matrix
- mat3 vInvTBN = mat3(tangent, bitangent, normal);
-
- // Get normal map info in world space
- normalMap = normalize(normalMap - 0.5);
- vec3 newNormal = vInvTBN * normalMap.rgb;
+ mediump float NdotL = pbrInputs.NdotL;
+ mediump float NdotV = pbrInputs.NdotV;
+ lowp float r = pbrInputs.alphaRoughness;
- // Calculate normal dot view vector
- float NoV = max(dot(newNormal, -viewVec), 0.0);
-
- // Reflect vector
- vec3 reflectionVec = reflect(viewVec, newNormal);
-
- //transform it now to environment coordinates (used when the environment rotates)
- vec3 reflecCube = (uCubeMatrix * vec4( reflectionVec, 0.0 ) ).xyz;
- reflecCube = normalize( reflecCube );
-
- //transform it now to environment coordinates
- vec3 normalCube = ( uCubeMatrix * vec4( newNormal, 0.0 ) ).xyz;
- normalCube = normalize( normalCube );
-
- // Get irradiance from diffuse cubemap
- vec3 irradiance = texture( sDiffuse, normalCube ).rgb;
-
- // Access reflection color using roughness value
- float finalLod = mix( 0.0, uMaxLOD - 2.0, roughness);
- vec3 reflectionColor = textureLod(sSpecular, reflecCube, finalLod).rgb;
-
- // We are supposed to be using DielectricColor (0.04) of a plastic (almost everything)
- // http://blog.selfshadow.com/publications/s2014-shading-course/hoffman/s2014_pbs_physics_math_slides.pdf
- // however that seems to prevent achieving very dark tones (i.e. get dark gray blacks).
- vec3 DiffuseColor = albedoColor - albedoColor * metallic; // 1 mad
- vec3 SpecularColor = mix( vec3(0.04), albedoColor, metallic); // 2 mad
-
- // Calculate specular color using Magic Function (takes original roughness and normal dot view).
- vec3 specColor = reflectionColor.rgb * EnvBRDFApprox(SpecularColor, roughness, NoV );
+ lowp float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
+ lowp float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV)));
+ return attenuationL * attenuationV;
+}
- // Multiply the result by albedo texture and do energy conservation
- vec3 diffuseColor = irradiance * DiffuseColor;
+float microfacetDistribution(PBRInfo pbrInputs)
+{
+ mediump float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
+ lowp float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+ return roughnessSq / (M_PI * f * f);
+}
- // Final color is the sum of the diffuse and specular term
- vec3 finalColor = diffuseColor + specColor;
+vec3 linear(vec3 color)
+{
+ return pow(color, vec3(2.2));
+}
- finalColor = sqrt( finalColor ) * uIblIntensity;
+void main()
+{
+ // Metallic and Roughness material properties are packed together
+ // In glTF, these factors can be specified by fixed scalar values
+ // or from a metallic-roughness map
+ // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
+ // This layout intentionally reserves the 'r' channel for (optional) occlusion map data
+ lowp float metallic = uMetallicFactor;
+ lowp float perceptualRoughness = uRoughnessFactor;
+ // If there isn't normal texture, use surface normal
+ mediump vec3 n = normalize(vTBN[2].xyz);
+#ifdef THREE_TEX
+ // The albedo may be defined from a base texture or a flat color
+#ifdef BASECOLOR_TEX
+ lowp vec4 baseColor = texture(sAlbedoAlpha, vUV);
+ baseColor = vec4(linear(baseColor.rgb), baseColor.w) * uColorFactor;
+#else // BASECOLOR_TEX
+ lowp vec4 baseColor = vColor * uColorFactor;
+#endif // BASECOLOR_TEX
+
+#ifdef METALLIC_ROUGHNESS_TEX
+ lowp vec4 metrou = texture(sMetalRoughness, vUV);
+ metallic = metrou.METALLIC * metallic;
+ perceptualRoughness = metrou.ROUGHNESS * perceptualRoughness;
+#endif // METALLIC_ROUGHNESS_TEX
+
+#ifdef NORMAL_TEX
+ n = texture(sNormal, vUV).rgb;
+ n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
+#endif // NORMAL_TEX
+#else // THREE_TEX
+ vec4 albedoMetal = texture(sAlbedoMetal, vUV);
+ lowp vec4 baseColor = vec4(linear(albedoMetal.rgb), 1.0) * vColor * uColorFactor;
+
+ metallic = albedoMetal.METALLIC * metallic;
+
+ vec4 normalRoughness = texture(sNormalRoughness, vUV);
+ perceptualRoughness = normalRoughness.ROUGHNESS * perceptualRoughness;
+
+ n = normalRoughness.rgb;
+ n = normalize(vTBN * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
+#endif // THREE_TEX
+
+ // The value of uOpaque and uMask can be 0.0 or 1.0.
+ // If uOpaque is 1.0, alpha value of final color is 1.0;
+ // If uOpaque is 0.0 and uMask is 1.0, alpha value of final color is 0.0 when input alpha is lower than uAlphaThreshold or
+ // 1.0 when input alpha is larger than uAlphaThreshold.
+ // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_material_alphamode
+ baseColor.a = mix(baseColor.a, 1.0, uOpaque);
+ baseColor.a = min(mix(baseColor.a, floor(baseColor.a - uAlphaThreshold + 1.0), uMask), 1.0);
+
+ metallic = clamp(metallic, 0.0, 1.0);
+ // Roughness is authored as perceptual roughness; as is convention,
+ // convert to material roughness by squaring the perceptual roughness [2].
+ perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
+ lowp float alphaRoughness = perceptualRoughness * perceptualRoughness;
+
+ lowp vec3 f0 = vec3(0.04);
+ lowp vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
+ diffuseColor *= (1.0 - metallic);
+ lowp vec3 specularColor = mix(f0, baseColor.rgb, metallic);
+
+ // Compute reflectance.
+ lowp float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+
+ // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
+ // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+ lowp float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
+ lowp vec3 specularEnvironmentR0 = specularColor.rgb;
+ lowp vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
+
+ mediump vec3 v = normalize(vPositionToCamera); // Vector from surface point to camera
+ mediump float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);
+ mediump vec3 reflection = -normalize(reflect(v, n));
+
+ lowp vec3 color = vec3(0.0);
+ lowp vec3 diffuseLight = linear(texture(sDiffuseEnvSampler, n).rgb);
+ lowp vec3 specularLight = linear(texture(sSpecularEnvSampler, reflection).rgb);
+ // retrieve a scale and bias to F0. See [1], Figure 3
+ lowp vec3 brdf = linear(texture(sbrdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb);
+
+ lowp vec3 diffuse = diffuseLight * diffuseColor;
+ lowp vec3 specular = specularLight * (specularColor * brdf.x + brdf.y);
+ color += (diffuse + specular) * uIblIntensity;
#ifdef OCCLUSION
- float ao = texture(sOcclusion, vUV.st).r;
- finalColor = mix( finalColor, finalColor * ao, uOcclusionStrength );
-#endif
+ lowp float ao = texture(sOcclusion, vUV).r;
+ color = mix(color, color * ao, uOcclusionStrength);
+#endif // OCCLUSION
#ifdef EMISSIVE
- vec3 emissive = texture( sEmissive, vUV.st ).rgb * uEmissiveFactor;
- finalColor += emissive;
-#endif
+ lowp vec3 emissive = linear(texture(sEmissive, vUV).rgb) * uEmissiveFactor;
+ color += emissive;
+#endif // EMISSIVE
-#ifdef THREE_TEX
- FragColor = vec4( finalColor, alpha );
-#else //THREE_TEX
- FragColor = vec4( finalColor, 1.0 );
-#endif //THREE_TEX
+ FragColor = vec4(pow(color, vec3(1.0 / 2.2)), baseColor.a);
}
+// Original Code
+// https://github.com/KhronosGroup/glTF-Sample-Viewer/blob/glTF-WebGL-PBR/shaders/pbr-vert.glsl
+// Commit dc84b5e374fb3d23153d2248a338ef88173f9eb6
+
#version 300 es
#ifdef HIGHP
in vec3 aPosition;
in vec2 aTexCoord;
in vec3 aNormal;
+
+#ifdef VEC4_TANGENT
+in vec4 aTangent;
+#else
in vec3 aTangent;
+#endif
+
+in vec4 aVertexColor;
#ifdef MORPH
uniform sampler2D sBlendShapeGeometry;
#endif
out vec2 vUV;
-out vec3 vNormal;
-out vec3 vTangent;
-out vec3 vViewVec;
+out lowp mat3 vTBN;
+out lowp vec4 vColor;
+out highp vec3 vPositionToCamera;
-uniform highp mat4 uMvpMatrix;
uniform highp mat4 uViewMatrix;
uniform mat3 uNormalMatrix;
uniform mat4 uModelMatrix;
-uniform mat4 uModelView;
uniform mat4 uProjection;
+uniform lowp float uHasVertexColor;
#ifdef SKINNING
in vec4 aJoints;
{
vec4 position = vec4(aPosition, 1.0);
vec3 normal = aNormal;
- vec3 tangent = aTangent;
+ vec3 tangent = aTangent.xyz;
#ifdef MORPH
int width = textureSize( sBlendShapeGeometry, 0 ).x;
tangent = (bone * vec4(tangent, 0.0)).xyz;
#endif
- vec4 vPosition = uModelMatrix * position;
+ vec4 positionW = uModelMatrix * position;
+ vec4 positionV = uViewMatrix * positionW;
- vNormal = normalize(uNormalMatrix * normal);
+ vPositionToCamera = transpose(mat3(uViewMatrix)) * -vec3(positionV.xyz / positionV.w);
- vTangent = normalize(uNormalMatrix * tangent);
-
-
- vec4 viewPosition = uViewMatrix * vPosition;
- gl_Position = uProjection * viewPosition;
+ lowp vec3 bitangent = cross(normal, tangent);
+#ifdef VEC4_TANGENT
+ bitangent *= aTangent.w;
+#endif
+ vTBN = mat3(uModelMatrix) * mat3(tangent, bitangent, normal);
#ifdef FLIP_V
vUV = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
vUV = aTexCoord;
#endif
- vViewVec = viewPosition.xyz;
+ vColor = mix(vec4(1.0f), aVertexColor, uHasVertexColor);
+
+ gl_Position = uProjection * positionV;
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
*/
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/devel-api/adaptor-framework/image-loading.h>
+
// INTERNAL INCLUDES
#include "dali-scene-loader/public-api/environment-definition.h"
#include "dali-scene-loader/public-api/utils.h"
+namespace
+{
+#define TOKEN_STRING(x) #x
+std::string GetDaliImagePath()
+{
+ return (nullptr == DALI_IMAGE_DIR) ? Dali::EnvironmentVariable::GetEnvironmentVariable(TOKEN_STRING(DALI_IMAGE_DIR)) : DALI_IMAGE_DIR;
+}
+} // unnamed namespace
+
namespace Dali
{
namespace SceneLoader
{
+namespace
+{
+const std::string PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME = "brdfLUT.png";
+}
+
EnvironmentDefinition::RawData
EnvironmentDefinition::LoadRaw(const std::string& environmentsPath) const
{
loadFn(mDiffuseMapPath, raw.mDiffuse);
loadFn(mSpecularMapPath, raw.mSpecular);
+
+ if(mUseBrdfTexture)
+ {
+ Devel::PixelBuffer pixelBuffer = LoadImageFromFile(GetDaliImagePath() + PRE_COMPUTED_BRDF_TEXTURE_FILE_NAME);
+ if(pixelBuffer)
+ {
+ raw.mBrdf = Devel::PixelBuffer::Convert(pixelBuffer);
+ }
+ }
return raw;
}
{
textures.mSpecular = raw.mSpecular.CreateTexture();
}
+
+ if(raw.mBrdf)
+ {
+ textures.mBrdf = Texture::New(TextureType::TEXTURE_2D, raw.mBrdf.GetPixelFormat(), raw.mBrdf.GetWidth(), raw.mBrdf.GetHeight());
+ textures.mBrdf.Upload(raw.mBrdf);
+ }
return textures;
}
{
Texture mDiffuse; // irradiance
Texture mSpecular; // radiance
+ Texture mBrdf; // pre-computed brdf
bool IsLoaded() const
{
struct RawData
{
- CubeData mDiffuse;
- CubeData mSpecular;
+ CubeData mDiffuse;
+ CubeData mSpecular;
+ PixelData mBrdf;
};
using EnvironmentData = std::pair<EnvironmentDefinition, Textures>;
std::string mSpecularMapPath;
Quaternion mCubeOrientation = Quaternion::IDENTITY;
float mIblIntensity = 1.0f;
+ bool mUseBrdfTexture = false;
};
} // namespace SceneLoader
const std::string ORIENTATION_PROPERTY("orientation");
const std::string SCALE_PROPERTY("scale");
const std::string BLEND_SHAPE_WEIGHTS_UNIFORM("uBlendShapeWeight");
-
const std::string MRENDERER_MODEL_IDENTIFICATION("M-Renderer");
-
const std::string ROOT_NODE_NAME("RootNode");
const Vector3 SCALE_TO_ADJUST(100.0f, 100.0f, 100.0f);
+constexpr float DEFAULT_INTENSITY = 0.5f;
+
const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
Geometry::POINTS,
Geometry::LINES,
{gt::Attribute::NORMAL, &MeshDefinition::mNormals, sizeof(Vector3)},
{gt::Attribute::TANGENT, &MeshDefinition::mTangents, sizeof(Vector3)},
{gt::Attribute::TEXCOORD_0, &MeshDefinition::mTexCoords, sizeof(Vector2)},
+ {gt::Attribute::COLOR_0, &MeshDefinition::mColors, sizeof(Vector4)},
{gt::Attribute::JOINTS_0, &MeshDefinition::mJoints0, sizeof(Vector4)},
{gt::Attribute::WEIGHTS_0, &MeshDefinition::mWeights0, sizeof(Vector4)},
};
matDef.SetAlphaCutoff(std::min(1.f, std::max(0.f, m.mAlphaCutoff)));
}
- matDef.mColor = pbr.mBaseColorFactor;
+ matDef.mBaseColorFactor = pbr.mBaseColorFactor;
matDef.mTextureStages.reserve(!!pbr.mBaseColorTexture + !!pbr.mMetallicRoughnessTexture + !!m.mNormalTexture + !!m.mOcclusionTexture + !!m.mEmissiveTexture);
if(pbr.mBaseColorTexture)
// TODO: and there had better be one
matDef.mFlags |= semantic;
}
+ else
+ {
+ matDef.mNeedAlbedoTexture = false;
+ }
matDef.mMetallic = pbr.mMetallicFactor;
matDef.mRoughness = pbr.mRoughnessFactor;
// TODO: and there had better be one
matDef.mFlags |= semantic;
}
+ else
+ {
+ matDef.mNeedMetallicRoughnessTexture = false;
+ }
+ matDef.mNormalScale = m.mNormalTexture.mScale;
if(m.mNormalTexture)
{
const auto semantic = MaterialDefinition::NORMAL;
// TODO: and there had better be one
matDef.mFlags |= semantic;
}
+ else
+ {
+ matDef.mNeedNormalTexture = false;
+ }
// TODO: handle doubleSided
if(m.mOcclusionTexture)
auto& accPositions = *attribs.find(gt::Attribute::POSITION)->second;
meshDef.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
+ // glTF2 support vector4 tangent for mesh.
+ // https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview
+ meshDef.mTangentType = Property::VECTOR4;
const bool needNormalsTangents = accPositions.mType == gt::AccessorType::VEC3;
for(auto& am : ATTRIBUTE_MAPPINGS)
auto& accessor = meshDef.*(am.mAccessor);
accessor = ConvertMeshPrimitiveAccessor(*iFind->second);
- // Fixing up -- a few of glTF2 sample models have VEC4 tangents; we need VEC3s.
- if(iFind->first == gt::Attribute::TANGENT && (accessor.mBlob.mElementSizeHint > am.mElementSizeRequired))
- {
- accessor.mBlob.mStride = std::max(static_cast<uint16_t>(accessor.mBlob.mStride + accessor.mBlob.mElementSizeHint - am.mElementSizeRequired),
- accessor.mBlob.mElementSizeHint);
- accessor.mBlob.mElementSizeHint = am.mElementSizeRequired;
- }
-
if(iFind->first == gt::Attribute::JOINTS_0)
{
meshDef.mFlags |= (iFind->second->mComponentType == gt::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
js::SetObjectReader(SCENE_READER);
}
+void SetDefaultEnvironmentMap(const gt::Document& doc, ConversionContext& cctx)
+{
+ EnvironmentDefinition envDef;
+ envDef.mUseBrdfTexture = true;
+ envDef.mIblIntensity = DEFAULT_INTENSITY;
+ cctx.mOutput.mResources.mEnvironmentMaps.push_back({std::move(envDef), EnvironmentDefinition::Textures()});
+}
+
} // namespace
void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactory, LoadResult& params)
ConvertMeshes(doc, cctx);
ConvertNodes(doc, cctx, isMRendererModel);
ConvertAnimations(doc, cctx);
-
ProcessSkins(doc, cctx);
-
ProduceShaders(shaderFactory, params.mScene);
params.mScene.EnsureUniqueSkinningShaderInstances(params.mResources);
+
+ // Set Default Environment map
+ SetDefaultEnvironmentMap(doc, cctx);
}
} // namespace SceneLoader
raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
- else // single value albedo, albedo-alpha or albedo-metallic
+ else if(mNeedAlbedoTexture) // single value albedo, albedo-alpha or albedo-metallic
{
uint32_t bufferSize = 4;
uint8_t* buffer = nullptr;
raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
- else if(createMetallicRoughnessAndNormal)
+ else if(createMetallicRoughnessAndNormal && mNeedMetallicRoughnessTexture)
{
// NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
// glTF2 uses B & G, so we might as well just set all components to 1.0.
raw.mTextures.push_back({SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags});
++iTexture;
}
- else if(createMetallicRoughnessAndNormal)
+ else if(mNeedNormalTexture)
{
- const auto bufferSize = 3;
- uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
- raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
- }
- else // single-value normal-roughness
- {
- const auto bufferSize = 4;
- uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
- raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+ if(createMetallicRoughnessAndNormal)
+ {
+ const auto bufferSize = 3;
+ uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff}; // normal of (0, 0, 1)
+ raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+ }
+ else // single-value normal-roughness
+ {
+ const auto bufferSize = 4;
+ uint8_t* buffer = new uint8_t[bufferSize]{0x7f, 0x7f, 0xff, 0xff}; // normal of (0, 0, 1), roughness of 1.0
+ raw.mTextures.push_back({PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER});
+ }
}
}
textureSet.SetTexture(n, envTextures.mSpecular);
textureSet.SetSampler(n, specularSampler);
+ ++n;
+ }
+
+ // If pre-computed brdf texture is defined, set the texture.
+ if(envTextures.mBrdf)
+ {
+ textureSet.SetTexture(n, envTextures.mBrdf);
}
}
else
public: // DATA
uint32_t mFlags = 0x0;
- Index mEnvironmentIdx = 0;
- Vector4 mColor = Color::WHITE;
- Vector3 mEmissiveFactor = Vector3::ZERO;
- float mMetallic = 1.f;
- float mRoughness = 1.f;
- float mOcclusionStrength = 1.f;
+ Index mEnvironmentIdx = 0;
+ Vector4 mColor = Color::WHITE;
+ float mMetallic = 1.f;
+ float mRoughness = 1.f;
+ Vector4 mBaseColorFactor = Vector4::ONE;
+ float mNormalScale = 1.f;
+ float mOcclusionStrength = 1.f;
+ Vector3 mEmissiveFactor = Vector3::ZERO;
+
+ // For the glTF, each of albedo, metallicRoughness, normal textures are not essential.
+ bool mNeedAlbedoTexture = true;
+ bool mNeedMetallicRoughnessTexture = true;
+ bool mNeedNormalTexture = true;
+
std::vector<TextureStage> mTextureStages;
};
if(mTangents.IsDefined())
{
- DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % sizeof(Vector3) == 0) ||
- mTangents.mBlob.mStride >= sizeof(Vector3)) &&
+ uint32_t propertySize = (mTangentType == Property::VECTOR4) ? sizeof(Vector4) : sizeof(Vector3);
+ DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % propertySize == 0) ||
+ mTangents.mBlob.mStride >= propertySize) &&
"Tangents buffer length not a multiple of element size");
const auto bufferSize = mTangents.mBlob.GetBufferSize();
std::vector<uint8_t> buffer(bufferSize);
{
ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << meshPath << "'.";
}
+ mTangents.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
- mTangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
-
- raw.mAttribs.push_back({"aTangent", Property::VECTOR3, static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer)});
+ raw.mAttribs.push_back({"aTangent", mTangentType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
}
else if(mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
{
hasUvs ? GenerateTangentsWithUvs(raw) : GenerateTangents(raw);
}
+ if(mColors.IsDefined())
+ {
+ uint32_t propertySize = mColors.mBlob.mElementSizeHint;
+ Property::Type propertyType = (propertySize == sizeof(Vector4)) ? Property::VECTOR4 : ((propertySize == sizeof(Vector3)) ? Property::VECTOR3 : Property::NONE);
+ if(propertyType != Property::NONE)
+ {
+ DALI_ASSERT_ALWAYS(((mColors.mBlob.mLength % propertySize == 0) ||
+ mColors.mBlob.mStride >= propertySize) &&
+ "Colors buffer length not a multiple of element size");
+ const auto bufferSize = mColors.mBlob.GetBufferSize();
+ std::vector<uint8_t> buffer(bufferSize);
+ if(!ReadAccessor(mColors, binFile, buffer.data()))
+ {
+ ExceptionFlinger(ASSERT_LOCATION) << "Failed to read colors from '" << meshPath << "'.";
+ }
+ mColors.mBlob.ApplyMinMax(bufferSize / propertySize, reinterpret_cast<float*>(buffer.data()));
+
+ raw.mAttribs.push_back({"aVertexColor", propertyType, static_cast<uint32_t>(bufferSize / propertySize), std::move(buffer)});
+ }
+ }
+
if(IsSkinned())
{
if(MaskMatch(mFlags, U16_JOINT_IDS))
Accessor mPositions;
Accessor mNormals; // data can be generated based on positions
Accessor mTexCoords;
+ Accessor mColors;
Accessor mTangents; // data can be generated based on normals and texCoords (the latter isn't mandatory; the results will be better if available)
Accessor mJoints0;
Accessor mWeights0;
+ Property::Type mTangentType{Property::VECTOR3};
Blob mBlendShapeHeader;
std::vector<BlendShape> mBlendShapes;
actor.SetProperty(Actor::Property::COLOR, mColor);
+ actor.RegisterProperty("uHasVertexColor", static_cast<float>(mesh.first.mColors.IsDefined()));
+
auto& matDef = resources.mMaterials[mMaterialIdx].first;
+ actor.RegisterProperty("uColorFactor", matDef.mBaseColorFactor);
actor.RegisterProperty("uMetallicFactor", matDef.mMetallic);
actor.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
+ actor.RegisterProperty("uNormalScale", matDef.mNormalScale);
if(matDef.mFlags & MaterialDefinition::OCCLUSION)
{
actor.RegisterProperty("uOcclusionStrength", matDef.mOcclusionStrength);
Index envIdx = matDef.mEnvironmentIdx;
actor.RegisterProperty("uIblIntensity", resources.mEnvironmentMaps[envIdx].first.mIblIntensity);
- const auto alphaCutoff = matDef.GetAlphaCutoff();
- if(alphaCutoff > 0.f)
+ float opaque = 0.0f;
+ float mask = 0.0f;
+ float alphaCutoff = matDef.GetAlphaCutoff();
+ if(!MaskMatch(matDef.mFlags, MaterialDefinition::TRANSPARENCY))
+ {
+ opaque = 1.0f;
+ }
+ else
{
- actor.RegisterProperty("uAlphaThreshold", alphaCutoff);
+ if(alphaCutoff > 0.f)
+ {
+ mask = 1.0f;
+ }
}
+ actor.RegisterProperty("uOpaque", opaque);
+ actor.RegisterProperty("uMask", mask);
+ actor.RegisterProperty("uAlphaThreshold", alphaCutoff);
}
void ArcNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
}
if(hasTransparency ||
- materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
- materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
- materialDef.CheckTextures(MaterialDefinition::NORMAL))
+ !materialDef.CheckTextures(MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC) ||
+ !materialDef.CheckTextures(MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS))
+
{
shaderDef.mDefines.push_back("THREE_TEX");
+
+ // For the glTF, each of basecolor, metallic_roughness, normal texture is not essential.
+ if(MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
+ {
+ shaderDef.mDefines.push_back("BASECOLOR_TEX");
+ }
+
+ if(materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS))
+ {
+ shaderDef.mDefines.push_back("METALLIC_ROUGHNESS_TEX");
+ }
+
+ if(MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
+ {
+ shaderDef.mDefines.push_back("NORMAL_TEX");
+ }
}
if(materialDef.GetAlphaCutoff() > 0.f)
}
}
+ if(meshDef.mTangentType == Property::VECTOR4)
+ {
+ shaderDef.mDefines.push_back("VEC4_TANGENT");
+ }
+
shaderDef.mUniforms["uMaxLOD"] = 6.f;
shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;