trying to have better support for the materials

This commit is contained in:
samcake 2016-02-18 18:33:32 -08:00
parent 5c877560fe
commit 903824809c
15 changed files with 236 additions and 51 deletions

View file

@ -0,0 +1,51 @@
//
// debug.js
// examples/utilities/tools/render
//
// Sam Gateau created on 2/18/2016.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DDB = Render.RenderDeferredTask.DebugDeferredBuffer;
DDB.enabled = true;
oldConfig = DDB.toJSON();
// Set up the qml ui
var qml = Script.resolvePath('framebuffer.qml');
var window = new OverlayWindow({
title: 'Framebuffer Debug',
source: qml,
width: 400, height: 400,
});
window.setPosition(25, 50);
window.closed.connect(function() { Script.stop(); });
// Debug buffer sizing
var resizing = false;
Controller.mousePressEvent.connect(function (e) {
if (shouldStartResizing(e.x)) {
resizing = true;
}
});
Controller.mouseReleaseEvent.connect(function() { resizing = false; });
Controller.mouseMoveEvent.connect(function (e) { resizing && setDebugBufferSize(e.x); });
function shouldStartResizing(eventX) {
var x = Math.abs(eventX - Window.innerWidth * (1.0 + DDB.size.x) / 2.0);
var mode = DDB.mode;
return mode !== -1 && x < 20;
}
function setDebugBufferSize(x) {
x = (2.0 * (x / Window.innerWidth) - 1.0); // scale
x = Math.min(Math.max(-1, x), 1); // clamp
DDB.size = { x: x, y: -1, z: 1, w: 1 };
}
Script.scriptEnding.connect(function () { DDB.fromJSON(oldConfig); });

View file

@ -0,0 +1,40 @@
//
// main.qml
// examples/utilities/tools/render
//
// Created by Zach Pomerantz on 2/8/2016
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
//
import QtQuick 2.5
import QtQuick.Controls 1.4
Column {
spacing: 8
Column {
id: debug
property var config: Render.getConfig("DebugDeferredBuffer")
function setDebugMode(mode) {
debug.config.enabled = (mode != -1);
debug.config.mode = mode;
}
Label { text: qsTr("Debug Buffer") }
ExclusiveGroup { id: bufferGroup }
Repeater {
model: [
"Off", "Diffuse", "Metallic", "Roughness", "Normal", "Depth",
"Lighting", "Shadow", "Pyramid Depth", "Ambient Occlusion", "Custom Shader"
]
RadioButton {
text: qsTr(modelData)
exclusiveGroup: bufferGroup
checked: index == 0
onCheckedChanged: if (checked) debug.setDebugMode(index - 1);
}
}
}
}

View file

@ -865,9 +865,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
}
} else if (object.name == "Material") {
FBXMaterial material;
if (object.properties.at(1).toByteArray().contains("StingrayPBS1")) {
material.isPBSMaterial = true;
} else if (object.properties.at(1).toByteArray().contains("StingrayPBS2")) {
if (object.properties.at(1).toByteArray().contains("StingrayPBS")) {
material.isPBSMaterial = true;
}
foreach (const FBXNode& subobject, object.children) {
@ -1079,7 +1077,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("transparentcolor")) { // it should be TransparentColor...
// THis is how Maya assign a texture that affect diffuse color AND transparency ?
diffuseTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
transparentTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("bump")) {
bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("normal")) {
@ -1094,13 +1092,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
shininessTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_roughness_map")) {
roughnessTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (_loadLightmaps && type.contains("emissive")) {
} else if (type.contains("emissive")) {
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type.contains("tex_emissive_map")) {
roughnessTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (_loadLightmaps && type.contains("ambient")) {
} else if (type.contains("ambient")) {
ambientTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (_loadLightmaps && type.contains("tex_ao_map")) {
} else if (type.contains("tex_ao_map")) {
occlusionTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
} else if (type == "lcl rotation") {

View file

@ -135,7 +135,7 @@ public:
diffuseColor(diffuseColor),
specularColor(specularColor),
emissiveColor(emissiveColor),
emissiveParams(emissiveParams),
lightmapParams(emissiveParams),
shininess(shininess),
opacity(opacity) {}
@ -145,7 +145,6 @@ public:
float specularFactor = 1.0f;
glm::vec3 emissiveColor{ 0.0f };
glm::vec2 emissiveParams{ 0.0f, 1.0f };
float shininess = 23.0f;
float opacity = 1.0f;
@ -173,6 +172,8 @@ public:
FBXTexture metallicTexture;
FBXTexture emissiveTexture;
FBXTexture occlusionTexture;
FBXTexture lightmapTexture;
glm::vec2 lightmapParams{ 0.0f, 1.0f };
bool isPBSMaterial{ false };
@ -416,6 +417,7 @@ public:
QHash<QString, QString> diffuseTextures;
QHash<QString, QString> transparentTextures;
QHash<QString, QString> bumpTextures;
QHash<QString, QString> normalTextures;
QHash<QString, QString> specularTextures;

View file

@ -80,10 +80,19 @@ void FBXReader::consolidateFBXMaterials() {
}
material.albedoTexture = diffuseTexture;
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
}
FBXTexture transparentTexture;
QString transparentTextureID = transparentTextures.value(material.materialID);
if (!transparentTextureID.isNull()) {
transparentTexture = getTexture(transparentTextureID);
material.opacityTexture = transparentTexture;
detectDifferentUVs |= (transparentTexture.texcoordSet != 0) || (!transparentTexture.transform.isIdentity());
}
FBXTexture normalTexture;
QString bumpTextureID = bumpTextures.value(material.materialID);
QString normalTextureID = normalTextures.value(material.materialID);
@ -133,25 +142,23 @@ void FBXReader::consolidateFBXMaterials() {
}
FBXTexture emissiveTexture;
glm::vec2 emissiveParams(0.f, 1.f);
emissiveParams.x = _lightmapOffset;
emissiveParams.y = _lightmapLevel;
QString emissiveTextureID = emissiveTextures.value(material.materialID);
QString ambientTextureID = ambientTextures.value(material.materialID);
if (_loadLightmaps && (!emissiveTextureID.isNull() || !ambientTextureID.isNull())) {
if (!emissiveTextureID.isNull()) {
emissiveTexture = getTexture(emissiveTextureID);
emissiveParams.y = 4.0f;
} else if (!ambientTextureID.isNull()) {
emissiveTexture = getTexture(ambientTextureID);
}
material.emissiveParams = emissiveParams;
material.emissiveTexture = emissiveTexture;
if (!emissiveTextureID.isNull()) {
emissiveTexture = getTexture(emissiveTextureID);
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
material.emissiveTexture = emissiveTexture;
}
glm::vec2 lightmapParams(0.f, 1.f);
lightmapParams.x = _lightmapOffset;
lightmapParams.y = _lightmapLevel;
FBXTexture ambientTexture;
QString ambientTextureID = ambientTextures.value(material.materialID);
if (_loadLightmaps && !ambientTextureID.isNull()) {
ambientTexture = getTexture(ambientTextureID);
detectDifferentUVs |= (ambientTexture.texcoordSet != 0) || (!ambientTexture.transform.isIdentity());
material.lightmapTexture = ambientTexture;
material.lightmapParams = lightmapParams;
}
// Finally create the true material representation
@ -168,7 +175,6 @@ void FBXReader::consolidateFBXMaterials() {
material._material->setMetallic(material.metallic);
} else {
material._material->setRoughness(model::Material::shininessToRoughness(material.shininess));
float metallic = std::max(material.specularColor.x, std::max(material.specularColor.y, material.specularColor.z));
// FIXME: Do not use the Specular Factor yet as some FBX models have it set to 0
// metallic *= material.specularFactor;

View file

@ -139,7 +139,8 @@ bool NetworkGeometry::isLoadedWithTextures() const {
if ((material->albedoTexture && !material->albedoTexture->isLoaded()) ||
(material->normalTexture && !material->normalTexture->isLoaded()) ||
(material->specularTexture && !material->specularTexture->isLoaded()) ||
(material->emissiveTexture && !material->emissiveTexture->isLoaded())) {
(material->emissiveTexture && !material->emissiveTexture->isLoaded()) ||
(material->lightmapTexture && !material->lightmapTexture->isLoaded())) {
return false;
}
}
@ -180,6 +181,13 @@ void NetworkGeometry::setTextureWithNameToURL(const QString& name, const QUrl& u
} else if (material->emissiveTextureName == name) {
material->emissiveTexture = textureCache->getTexture(url);
auto emissiveMap = model::TextureMapPointer(new model::TextureMap());
emissiveMap->setTextureSource(material->emissiveTexture->_textureSource);
networkMaterial->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
} else if (material->lightmapTextureName == name) {
material->emissiveTexture = textureCache->getTexture(url);
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
lightmapMap->setTextureSource(material->emissiveTexture->_textureSource);
lightmapMap->setTextureTransform(
@ -219,6 +227,10 @@ QStringList NetworkGeometry::getTextureNames() const {
QString textureURL = material->emissiveTexture->getURL().toString();
result << material->emissiveTextureName + ":\"" + textureURL + "\"";
}
if (!material->lightmapTextureName.isEmpty() && material->lightmapTexture) {
QString textureURL = material->lightmapTexture->getURL().toString();
result << material->lightmapTextureName + ":\"" + textureURL + "\"";
}
}
return result;
@ -333,20 +345,38 @@ static NetworkMaterial* buildNetworkMaterial(const FBXMaterial& material, const
networkMaterial->specularTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.specularTexture.filename)), SPECULAR_TEXTURE, material.specularTexture.content);
networkMaterial->specularTextureName = material.specularTexture.name;
auto glossMap = model::TextureMapPointer(new model::TextureMap());
glossMap->setTextureSource(networkMaterial->specularTexture->_textureSource);
auto specularMap = model::TextureMapPointer(new model::TextureMap());
specularMap->setTextureSource(networkMaterial->specularTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, glossMap);
material._material->setTextureMap(model::MaterialKey::METALLIC_MAP, specularMap);
}
if (!material.roughnessTexture.filename.isEmpty()) {
networkMaterial->roughnessTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.roughnessTexture.filename)), ROUGHNESS_TEXTURE, material.roughnessTexture.content);
networkMaterial->roughnessTextureName = material.roughnessTexture.name;
auto roughnessMap = model::TextureMapPointer(new model::TextureMap());
roughnessMap->setTextureSource(networkMaterial->roughnessTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::GLOSS_MAP, roughnessMap);
}
if (!material.emissiveTexture.filename.isEmpty()) {
networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), LIGHTMAP_TEXTURE, material.emissiveTexture.content);
networkMaterial->emissiveTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.emissiveTexture.filename)), EMISSIVE_TEXTURE, material.emissiveTexture.content);
networkMaterial->emissiveTextureName = material.emissiveTexture.name;
auto emissiveMap = model::TextureMapPointer(new model::TextureMap());
emissiveMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource);
material._material->setTextureMap(model::MaterialKey::EMISSIVE_MAP, emissiveMap);
}
if (!material.lightmapTexture.filename.isEmpty()) {
networkMaterial->lightmapTexture = textureCache->getTexture(textureBaseUrl.resolved(QUrl(material.lightmapTexture.filename)), LIGHTMAP_TEXTURE, material.lightmapTexture.content);
networkMaterial->lightmapTextureName = material.lightmapTexture.name;
auto lightmapMap = model::TextureMapPointer(new model::TextureMap());
lightmapMap->setTextureSource(networkMaterial->emissiveTexture->_textureSource);
lightmapMap->setTextureTransform(material.emissiveTexture.transform);
lightmapMap->setLightmapOffsetScale(material.emissiveParams.x, material.emissiveParams.y);
lightmapMap->setTextureSource(networkMaterial->lightmapTexture->_textureSource);
lightmapMap->setTextureTransform(material.lightmapTexture.transform);
lightmapMap->setLightmapOffsetScale(material.lightmapParams.x, material.lightmapParams.y);
material._material->setTextureMap(model::MaterialKey::LIGHTMAP_MAP, lightmapMap);
}

View file

@ -180,8 +180,12 @@ public:
QSharedPointer<NetworkTexture> normalTexture;
QString specularTextureName;
QSharedPointer<NetworkTexture> specularTexture;
QString roughnessTextureName;
QSharedPointer<NetworkTexture> roughnessTexture;
QString emissiveTextureName;
QSharedPointer<NetworkTexture> emissiveTexture;
QString lightmapTextureName;
QSharedPointer<NetworkTexture> lightmapTexture;
};

View file

@ -212,6 +212,10 @@ NetworkTexture::TextureLoaderFunc NetworkTexture::getTextureLoader() const {
return TextureLoaderFunc(model::TextureUsage::createNormalTextureFromNormalImage);
break;
}
case ROUGHNESS_TEXTURE: {
return TextureLoaderFunc(model::TextureUsage::createRoughnessTextureFromImage);
break;
}
case CUSTOM_TEXTURE: {
return _textureLoader;
break;

View file

@ -29,7 +29,7 @@ class NetworkTexture;
typedef QSharedPointer<NetworkTexture> NetworkTexturePointer;
enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, LIGHTMAP_TEXTURE, CUSTOM_TEXTURE };
enum TextureType { DEFAULT_TEXTURE, NORMAL_TEXTURE, BUMP_TEXTURE, SPECULAR_TEXTURE, ROUGHNESS_TEXTURE, EMISSIVE_TEXTURE, CUBE_TEXTURE, LIGHTMAP_TEXTURE, CUSTOM_TEXTURE };
/// Stores cached textures, including render-to-texture targets.
class TextureCache : public ResourceCache, public Dependency {

View file

@ -223,6 +223,55 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
return theTexture;
}
gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) {
QImage image = srcImage;
if (!image.hasAlphaChannel()) {
if (image.format() != QImage::Format_RGB888) {
image = image.convertToFormat(QImage::Format_RGB888);
}
} else {
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
}
image = image.convertToFormat(QImage::Format_Grayscale8);
/* gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
// Actual alpha channel?
for (int y = 0; y < image.height(); ++y) {
QRgb* data = reinterpret_cast<QRgb*>(image.scanLine(y));
for (int x = 0; x < image.width(); ++x) {
data[x]auto alpha = q(data[x]);
if (alpha != 255) {
validAlpha = true;
break;
}
}
}
*/
gpu::Texture* theTexture = nullptr;
if ((image.width() > 0) && (image.height() > 0)) {
// bool isLinearRGB = true; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
bool isLinearRGB = false; //(_type == NORMAL_TEXTURE) || (_type == EMISSIVE_TEXTURE);
gpu::Element formatGPU = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
gpu::Element formatMip = gpu::Element(gpu::SCALAR, gpu::NUINT8, gpu::RGB);
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
theTexture->autoGenerateMips(-1);
// FIXME queue for transfer to GPU and block on completion
}
return theTexture;
}
class CubeLayout {
public:
int _widthRatio = 1;

View file

@ -34,6 +34,7 @@ public:
static gpu::Texture* create2DTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createNormalTextureFromNormalImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createNormalTextureFromBumpImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createRoughnessTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createCubeTextureFromImage(const QImage& image, const std::string& srcImageName);
static gpu::Texture* createLightmapTextureFromImage(const QImage& image, const std::string& srcImageName);

View file

@ -105,7 +105,7 @@ DeferredFragment unpackDeferredFragment(DeferredTransform deferredTransform, vec
frag.diffuse = frag.diffuseVal.xyz;
frag.specular = frag.specularVal.xyz;
frag.gloss = frag.specularVal.w;
frag.gloss = (frag.specularVal.w * 125)*(frag.specularVal.w * 125);
return frag;

View file

@ -30,7 +30,7 @@ vec4 evalSkyboxLight(vec3 direction, float lod) {
<@include model/Light.slh@>
<@func declareEvalAmbientGlobalColor()@>
vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 specular, float gloss) {
vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float gloss) {
// Need the light now
Light light = getLight();
@ -41,7 +41,7 @@ vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obsc
vec3 color = albedo * getLightColor(light) * obscurance * getLightAmbientIntensity(light);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, fresnel, gloss);
color += vec3(albedo * shading.w + shading.rgb) * min(shadowAttenuation, obscurance) * getLightColor(light) * getLightIntensity(light);
@ -52,7 +52,7 @@ vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obsc
<@func declareEvalAmbientSphereGlobalColor()@>
vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 specular, float gloss) {
vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float gloss) {
// Need the light now
Light light = getLight();
@ -64,7 +64,7 @@ vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, floa
vec3 color = albedo * evalSphericalLight(getLightAmbientSphere(light), ambientNormal).xyz * obscurance * getLightAmbientIntensity(light);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, fresnel, gloss);
color += vec3(albedo * shading.w + shading.rgb) * min(shadowAttenuation, obscurance) * getLightColor(light) * getLightIntensity(light);
@ -76,7 +76,7 @@ vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, floa
<$declareSkyboxMap()$>
vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 specular, float gloss) {
vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float gloss) {
// Need the light now
Light light = getLight();
@ -87,7 +87,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu
vec3 color = albedo * evalSphericalLight(getLightAmbientSphere(light), fragNormal).xyz * obscurance * getLightAmbientIntensity(light);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, specular, gloss);
vec4 shading = evalFragShading(fragNormal, -getLightDirection(light), fragEyeDir, fresnel, gloss);
color += vec3(albedo * shading.w + shading.rgb) * min(shadowAttenuation, obscurance) * getLightColor(light) * getLightIntensity(light);

View file

@ -23,8 +23,8 @@ vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3 sp
// Specular Lighting depends on the half vector and the gloss
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
float specularPower = pow(max(0.0, dot(halfDir, fragNormal)), gloss * 128.0);
specularPower *= (gloss * 128.0 * 0.125 + 0.25);
float specularPower = pow(max(0.0, dot(halfDir, fragNormal)), gloss);
specularPower *= (gloss * 0.125 + 0.25);
float shlickPower = (1.0 - dot(fragLightDir,halfDir));
float shlickPower2 = shlickPower * shlickPower;
@ -47,7 +47,7 @@ vec4 evalBlinnShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, vec3
// Specular Lighting depends on the half vector and the gloss
vec3 halfDir = normalize(fragEyeDir + fragLightDir);
float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss * 128.0);
float specularPower = pow(facingLight * max(0.0, dot(halfDir, fragNormal)), gloss);
vec3 reflect = specularPower * specular * diffuse;
return vec4(reflect, diffuse);

View file

@ -175,9 +175,9 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat
batch.setResourceTexture(ShapePipeline::Slot::NORMAL_MAP, nullptr);
}
// TODO: For now gloss map is used as the "specular map in the shading, we ll need to fix that
if (materialKey.isGlossMap()) {
auto specularMap = textureMaps[model::MaterialKey::GLOSS_MAP];
// Metallic map
if (materialKey.isMetallicMap()) {
auto specularMap = textureMaps[model::MaterialKey::METALLIC_MAP];
if (specularMap && specularMap->isDefined()) {
batch.setResourceTexture(ShapePipeline::Slot::SPECULAR_MAP, specularMap->getTextureView());