diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index a705b61cd3..4dd6caf55b 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -228,7 +228,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
 
     gpu::Batch& batch = *args->_batch;
 
-    std::shared_ptr<graphics::Material> mat;
+    graphics::MultiMaterial materials;
     auto geometryCache = DependencyManager::get<GeometryCache>();
     GeometryCache::Shape geometryShape;
     bool proceduralRender = false;
@@ -236,9 +236,11 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
     withReadLock([&] {
         geometryShape = geometryCache->getShapeForEntityShape(_shape);
         batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
-        mat = _materials["0"].top().material;
-        if (mat) {
-            outColor = glm::vec4(mat->getAlbedo(), mat->getOpacity());
+        materials = _materials["0"];
+        auto topMat = materials.top().material;
+        if (topMat) {
+            // FIXME: fallthrough to get proper albedo and opacity?
+            outColor = glm::vec4(topMat->getAlbedo(), topMat->getOpacity());
             if (_procedural.isReady()) {
                 outColor = _procedural.getColor(outColor);
                 outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
@@ -248,10 +250,6 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
         }
     });
 
-    if (!mat) {
-        return;
-    }
-
     if (proceduralRender) {
         if (render::ShapeKey(args->_globalShapeKey).isWireframe()) {
             geometryCache->renderWireShape(batch, geometryShape, outColor);
@@ -269,7 +267,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
         }
     } else {
         if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
-            RenderPipelines::bindMaterial(mat, batch, args->_enableTexturing);
+            RenderPipelines::bindMaterials(materials, batch, args->_enableTexturing);
             args->_details._materialSwitches++;
         }
 
diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h
index 104674eddc..78d34a9dee 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h
+++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h
@@ -40,13 +40,13 @@ namespace scriptable {
      * @typedef {object} Graphics.Material
      * @property {string} name
      * @property {string} model
-     * @property {number} opacity
-     * @property {number} roughness
-     * @property {number} metallic
-     * @property {number} scattering
-     * @property {boolean} unlit
-     * @propety {Vec3} emissive
-     * @propety {Vec3} albedo
+     * @property {number|string} opacity
+     * @property {number|string} roughness
+     * @property {number|string} metallic
+     * @property {number|string} scattering
+     * @property {boolean|string} unlit
+     * @propety {Vec3|string} emissive
+     * @propety {Vec3|string} albedo
      * @property {string} emissiveMap
      * @property {string} albedoMap
      * @property {string} opacityMap
@@ -59,6 +59,7 @@ namespace scriptable {
      * @property {string} occlusionMap
      * @property {string} lightmapMap
      * @property {string} scatteringMap
+     * @property {boolean} defaultFallthrough
      */
     class ScriptableMaterial {
     public:
@@ -88,6 +89,9 @@ namespace scriptable {
         QString occlusionMap;
         QString lightmapMap;
         QString scatteringMap;
+
+        bool defaultFallthrough;
+        std::unordered_map<graphics::MaterialKey::FlagBit, bool> propertyFallthroughs; // not actually exposed to script
     };
 
     /**jsdoc
diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
index 67f79db6b7..f32c4f2e01 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp
@@ -362,25 +362,44 @@ namespace scriptable {
         QScriptValue obj = engine->newObject();
         obj.setProperty("name", material.name);
         obj.setProperty("model", material.model);
-        obj.setProperty("opacity", material.opacity);
-        obj.setProperty("roughness", material.roughness);
-        obj.setProperty("metallic", material.metallic);
-        obj.setProperty("scattering", material.scattering);
-        obj.setProperty("unlit", material.unlit);
-        obj.setProperty("emissive", vec3ColorToScriptValue(engine, material.emissive));
-        obj.setProperty("albedo", vec3ColorToScriptValue(engine, material.albedo));
-        obj.setProperty("emissiveMap", material.emissiveMap);
-        obj.setProperty("albedoMap", material.albedoMap);
+
+        const QScriptValue FALLTHROUGH("fallthrough");
+        obj.setProperty("opacity", material.propertyFallthroughs.at(graphics::MaterialKey::OPACITY_VAL_BIT) ? FALLTHROUGH : material.opacity);
+        obj.setProperty("roughness", material.propertyFallthroughs.at(graphics::MaterialKey::GLOSSY_VAL_BIT) ? FALLTHROUGH : material.roughness);
+        obj.setProperty("metallic", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_VAL_BIT) ? FALLTHROUGH : material.metallic);
+        obj.setProperty("scattering", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_VAL_BIT) ? FALLTHROUGH : material.scattering);
+        obj.setProperty("unlit", material.propertyFallthroughs.at(graphics::MaterialKey::UNLIT_VAL_BIT) ? FALLTHROUGH : material.unlit);
+        obj.setProperty("emissive", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.emissive));
+        obj.setProperty("albedo", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_VAL_BIT) ? FALLTHROUGH : vec3ColorToScriptValue(engine, material.albedo));
+
+        obj.setProperty("emissiveMap", material.propertyFallthroughs.at(graphics::MaterialKey::EMISSIVE_MAP_BIT) ? FALLTHROUGH : material.emissiveMap);
+        obj.setProperty("albedoMap", material.propertyFallthroughs.at(graphics::MaterialKey::ALBEDO_MAP_BIT) ? FALLTHROUGH : material.albedoMap);
         obj.setProperty("opacityMap", material.opacityMap);
-        obj.setProperty("metallicMap", material.metallicMap);
-        obj.setProperty("specularMap", material.specularMap);
-        obj.setProperty("roughnessMap", material.roughnessMap);
-        obj.setProperty("glossMap", material.glossMap);
-        obj.setProperty("normalMap", material.normalMap);
-        obj.setProperty("bumpMap", material.bumpMap);
-        obj.setProperty("occlusionMap", material.occlusionMap);
-        obj.setProperty("lightmapMap", material.lightmapMap);
-        obj.setProperty("scatteringMap", material.scatteringMap);
+        obj.setProperty("occlusionMap", material.propertyFallthroughs.at(graphics::MaterialKey::OCCLUSION_MAP_BIT) ? FALLTHROUGH : material.occlusionMap);
+        obj.setProperty("lightmapMap", material.propertyFallthroughs.at(graphics::MaterialKey::LIGHTMAP_MAP_BIT) ? FALLTHROUGH : material.lightmapMap);
+        obj.setProperty("scatteringMap", material.propertyFallthroughs.at(graphics::MaterialKey::SCATTERING_MAP_BIT) ? FALLTHROUGH : material.scatteringMap);
+
+        // Only set one of each of these
+        if (!material.metallicMap.isEmpty()) {
+            obj.setProperty("metallicMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.metallicMap);
+        } else {
+            obj.setProperty("specularMap", material.propertyFallthroughs.at(graphics::MaterialKey::METALLIC_MAP_BIT) ? FALLTHROUGH : material.specularMap);
+        }
+
+        if (!material.roughnessMap.isEmpty()) {
+            obj.setProperty("roughnessMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.roughnessMap);
+        } else {
+            obj.setProperty("glossMap", material.propertyFallthroughs.at(graphics::MaterialKey::ROUGHNESS_MAP_BIT) ? FALLTHROUGH : material.glossMap);
+        }
+
+        if (!material.normalMap.isEmpty()) {
+            obj.setProperty("normalMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.normalMap);
+        } else {
+            obj.setProperty("bumpMap", material.propertyFallthroughs.at(graphics::MaterialKey::NORMAL_MAP_BIT) ? FALLTHROUGH : material.bumpMap);
+        }
+
+        obj.setProperty("defaultFallthrough", material.defaultFallthrough);
+
         return obj;
     }
 
diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
index 51085561c3..4ff751782c 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
+++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp
@@ -42,6 +42,9 @@ scriptable::ScriptableMaterial& scriptable::ScriptableMaterial::operator=(const
     lightmapMap = material.lightmapMap;
     scatteringMap = material.scatteringMap;
 
+    defaultFallthrough = material.defaultFallthrough;
+    propertyFallthroughs = material.propertyFallthroughs;
+
     return *this;
 }
 
@@ -54,7 +57,9 @@ scriptable::ScriptableMaterial::ScriptableMaterial(const graphics::MaterialPoint
     scattering(material->getScattering()),
     unlit(material->isUnlit()),
     emissive(material->getEmissive()),
-    albedo(material->getAlbedo())
+    albedo(material->getAlbedo()),
+    defaultFallthrough(material->getDefaultFallthrough()),
+    propertyFallthroughs(material->getPropertyFallthroughs())
 {
     auto map = material->getTextureMap(graphics::Material::MapChannel::EMISSIVE_MAP);
     if (map && map->getTextureSource()) {
diff --git a/libraries/graphics/src/graphics/Material.cpp b/libraries/graphics/src/graphics/Material.cpp
index be99cea681..b6b759ec86 100755
--- a/libraries/graphics/src/graphics/Material.cpp
+++ b/libraries/graphics/src/graphics/Material.cpp
@@ -17,125 +17,126 @@
 using namespace graphics;
 using namespace gpu;
 
-Material::Material() :
-    _key(0),
-    _schemaBuffer(),
-    _textureMaps()
-{
-    // created from nothing: create the Buffer to store the properties
-    Schema schema;
-    _schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
+const float Material::DEFAULT_EMISSIVE { 0.0f };
+const float Material::DEFAULT_OPACITY { 1.0f };
+const float Material::DEFAULT_ALBEDO { 0.5f };
+const float Material::DEFAULT_METALLIC { 0.0f };
+const float Material::DEFAULT_ROUGHNESS { 1.0f };
+const float Material::DEFAULT_SCATTERING { 0.0f };
+
+Material::Material() {
+    for (int i = 0; i < graphics::MaterialKey::NUM_FLAGS; i++) {
+        _propertyFallthroughs[graphics::MaterialKey::FlagBit(i)] = false;
+    }
 }
 
 Material::Material(const Material& material) :
     _name(material._name),
+    _model(material._model),
     _key(material._key),
-    _textureMaps(material._textureMaps)
+    _emissive(material._emissive),
+    _opacity(material._opacity),
+    _albedo(material._albedo),
+    _roughness(material._roughness),
+    _metallic(material._metallic),
+    _scattering(material._scattering),
+    _texcoordTransforms(material._texcoordTransforms),
+    _lightmapParams(material._lightmapParams),
+    _materialParams(material._materialParams),
+    _textureMaps(material._textureMaps),
+    _defaultFallthrough(material._defaultFallthrough),
+    _propertyFallthroughs(material._propertyFallthroughs)
 {
-    // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
-    Schema schema;
-    _schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
-    _schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
 }
 
-Material& Material::operator= (const Material& material) {
+Material& Material::operator=(const Material& material) {
     QMutexLocker locker(&_textureMapsMutex);
 
     _name = material._name;
+    _model = material._model;
+    _key = material._key;
+    _emissive = material._emissive;
+    _opacity = material._opacity;
+    _albedo = material._albedo;
+    _roughness = material._roughness;
+    _metallic = material._metallic;
+    _scattering = material._scattering;
+    _texcoordTransforms = material._texcoordTransforms;
+    _lightmapParams = material._lightmapParams;
+    _materialParams = material._materialParams;
+    _textureMaps = material._textureMaps;
 
-    _key = (material._key);
-    _textureMaps = (material._textureMaps);
-    _hasCalculatedTextureInfo = false;
-
-    // copied: create the Buffer to store the properties, avoid holding a ref to the old Buffer
-    Schema schema;
-    _schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
-    _schemaBuffer.edit<Schema>() = material._schemaBuffer.get<Schema>();
+    _defaultFallthrough = material._defaultFallthrough;
+    _propertyFallthroughs = material._propertyFallthroughs;
 
     return (*this);
 }
 
-Material::~Material() {
-}
-
-void Material::setEmissive(const Color& emissive, bool isSRGB) {
-    _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f))));
-    _schemaBuffer.edit<Schema>()._key = (uint32) _key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
+void Material::setEmissive(const glm::vec3& emissive, bool isSRGB) {
+    _key.setEmissive(glm::any(glm::greaterThan(emissive, glm::vec3(0.0f))));
+    _emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive);
 }
 
 void Material::setOpacity(float opacity) {
     _key.setTranslucentFactor((opacity < 1.0f));
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._opacity = opacity;
+    _opacity = opacity;
 }
 
 void Material::setUnlit(bool value) {
     _key.setUnlit(value);
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
 }
 
-void Material::setAlbedo(const Color& albedo, bool isSRGB) {
-    _key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f))));
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
+void Material::setAlbedo(const glm::vec3& albedo, bool isSRGB) {
+    _key.setAlbedo(glm::any(glm::greaterThan(albedo, glm::vec3(0.0f))));
+    _albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo);
 }
 
 void Material::setRoughness(float roughness) {
     roughness = std::min(1.0f, std::max(roughness, 0.0f));
-    _key.setGlossy((roughness < 1.0f));
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._roughness = roughness;
+    _key.setGlossy(roughness < 1.0f);
+    _roughness = roughness;
 }
 
 void Material::setMetallic(float metallic) {
     metallic = glm::clamp(metallic, 0.0f, 1.0f);
     _key.setMetallic(metallic > 0.0f);
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._metallic = metallic;
+    _metallic = metallic;
 }
 
 void Material::setScattering(float scattering) {
     scattering = glm::clamp(scattering, 0.0f, 1.0f);
     _key.setMetallic(scattering > 0.0f);
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-    _schemaBuffer.edit<Schema>()._scattering = scattering;
+    _scattering = scattering;
 }
 
 void Material::setTextureMap(MapChannel channel, const TextureMapPointer& textureMap) {
     QMutexLocker locker(&_textureMapsMutex);
 
     if (textureMap) {
-        _key.setMapChannel(channel, (true));
+        _key.setMapChannel(channel, true);
         _textureMaps[channel] = textureMap;
     } else {
-        _key.setMapChannel(channel, (false));
+        _key.setMapChannel(channel, false);
         _textureMaps.erase(channel);
     }
     _hasCalculatedTextureInfo = false;
 
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
-
     if (channel == MaterialKey::ALBEDO_MAP) {
         resetOpacityMap();
-
-        // update the texcoord0 with albedo
-        _schemaBuffer.edit<Schema>()._texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
+        _texcoordTransforms[0] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
     }
 
     if (channel == MaterialKey::OCCLUSION_MAP) {
-        _schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
+        _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
     }
 
     if (channel == MaterialKey::LIGHTMAP_MAP) {
         // update the texcoord1 with lightmap
-        _schemaBuffer.edit<Schema>()._texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
-        _schemaBuffer.edit<Schema>()._lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0));
+        _texcoordTransforms[1] = (textureMap ? textureMap->getTextureTransform().getMatrix() : glm::mat4());
+        _lightmapParams = (textureMap ? glm::vec2(textureMap->getLightmapOffsetScale()) : glm::vec2(0.0, 1.0));
     }
 
-    _schemaBuffer.edit<Schema>()._materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0));
-
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
+    _materialParams = (textureMap ? glm::vec2(textureMap->getMappingMode(), textureMap->getRepeat()) : glm::vec2(MaterialMappingMode::UV, 1.0));
 
 }
 
@@ -163,11 +164,8 @@ void Material::resetOpacityMap() const {
             }
         }
     }
-
-    _schemaBuffer.edit<Schema>()._key = (uint32)_key._flags.to_ulong();
 }
 
-
 const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
     QMutexLocker locker(&_textureMapsMutex);
 
@@ -179,7 +177,6 @@ const TextureMapPointer Material::getTextureMap(MapChannel channel) const {
     }
 }
 
-
 bool Material::calculateMaterialInfo() const {
     if (!_hasCalculatedTextureInfo) {
         QMutexLocker locker(&_textureMapsMutex);
@@ -222,7 +219,12 @@ void Material::setTextureTransforms(const Transform& transform, MaterialMappingM
         }
     }
     for (int i = 0; i < NUM_TEXCOORD_TRANSFORMS; i++) {
-        _schemaBuffer.edit<Schema>()._texcoordTransforms[i] = transform.getMatrix();
+        _texcoordTransforms[i] = transform.getMatrix();
     }
-    _schemaBuffer.edit<Schema>()._materialParams = glm::vec2(mode, repeat);
+    _materialParams = glm::vec2(mode, repeat);
 }
+
+MultiMaterial::MultiMaterial() {
+    Schema schema;
+    _schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Schema), (const gpu::Byte*) &schema, sizeof(Schema)));
+}
\ No newline at end of file
diff --git a/libraries/graphics/src/graphics/Material.h b/libraries/graphics/src/graphics/Material.h
index 9711bd9000..91453bb259 100755
--- a/libraries/graphics/src/graphics/Material.h
+++ b/libraries/graphics/src/graphics/Material.h
@@ -14,7 +14,7 @@
 #include <QMutex>
 
 #include <bitset>
-#include <map>
+#include <unordered_map>
 #include <queue>
 
 #include <ColorUtils.h>
@@ -266,84 +266,44 @@ public:
 
 class Material {
 public:
-    typedef gpu::BufferView UniformBufferView;
-
-    typedef glm::vec3 Color;
-
-    // Texture Map Array Schema
-    static const int NUM_TEXCOORD_TRANSFORMS{ 2 };
-
     typedef MaterialKey::MapChannel MapChannel;
-    typedef std::map<MapChannel, TextureMapPointer> TextureMaps;
-    typedef std::bitset<MaterialKey::NUM_MAP_CHANNELS> MapFlags;
+    typedef std::unordered_map<MapChannel, TextureMapPointer> TextureMaps;
 
     Material();
     Material(const Material& material);
     Material& operator= (const Material& material);
-    virtual ~Material();
 
     const MaterialKey& getKey() const { return _key; }
 
-    void setEmissive(const Color& emissive, bool isSRGB = true);
-    Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._emissive) : _schemaBuffer.get<Schema>()._emissive); }
+    static const float DEFAULT_EMISSIVE;
+    void setEmissive(const glm::vec3& emissive, bool isSRGB = true);
+    glm::vec3 getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_emissive) : _emissive); }
 
+    static const float DEFAULT_OPACITY;
     void setOpacity(float opacity);
-    float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; }
+    float getOpacity() const { return _opacity; }
 
     void setUnlit(bool value);
     bool isUnlit() const { return _key.isUnlit(); }
 
-    void setAlbedo(const Color& albedo, bool isSRGB = true);
-    Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get<Schema>()._albedo) : _schemaBuffer.get<Schema>()._albedo); }
+    static const float DEFAULT_ALBEDO;
+    void setAlbedo(const glm::vec3& albedo, bool isSRGB = true);
+    glm::vec3 getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_albedo) : _albedo); }
 
+    static const float DEFAULT_METALLIC;
     void setMetallic(float metallic);
-    float getMetallic() const { return _schemaBuffer.get<Schema>()._metallic; }
+    float getMetallic() const { return _metallic; }
 
+    static const float DEFAULT_ROUGHNESS;
     void setRoughness(float roughness);
-    float getRoughness() const { return _schemaBuffer.get<Schema>()._roughness; }
+    float getRoughness() const { return _roughness; }
 
+    static const float DEFAULT_SCATTERING;
     void setScattering(float scattering);
-    float getScattering() const { return _schemaBuffer.get<Schema>()._scattering; }
-
-    // Schema to access the attribute values of the material
-    class Schema {
-    public:
-        glm::vec3 _emissive { 0.0f }; // No Emissive
-        float _opacity { 1.0f }; // Opacity = 1 => Not Transparent
-
-        glm::vec3 _albedo { 0.5f }; // Grey albedo => isAlbedo
-        float _roughness { 1.0f }; // Roughness = 1 => Not Glossy
-
-        float _metallic { 0.0f }; // Not Metallic
-        float _scattering { 0.0f }; // Scattering info
-#if defined(__clang__)
-        __attribute__((unused))
-#endif
-        glm::vec2 _spare { 0.0f }; // Padding
-
-        uint32_t _key { 0 }; // a copy of the materialKey
-#if defined(__clang__)
-        __attribute__((unused))
-#endif
-        glm::vec3 _spare2 { 0.0f };
-
-        // for alignment beauty, Material size == Mat4x4
-
-        // Texture Coord Transform Array
-        glm::mat4 _texcoordTransforms[NUM_TEXCOORD_TRANSFORMS];
-
-        glm::vec2 _lightmapParams { 0.0, 1.0 };
-
-        // x: material mode (0 for UV, 1 for PROJECTED)
-        // y: 1 for texture repeat, 0 for discard outside of 0 - 1
-        glm::vec2 _materialParams { 0.0, 1.0 };
-
-        Schema() {}
-    };
-
-    const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
+    float getScattering() const { return _scattering; }
 
     // The texture map to channel association
+    static const int NUM_TEXCOORD_TRANSFORMS { 2 };
     void setTextureMap(MapChannel channel, const TextureMapPointer& textureMap);
     const TextureMaps& getTextureMaps() const { return _textureMaps; } // FIXME - not thread safe... 
     const TextureMapPointer getTextureMap(MapChannel channel) const;
@@ -366,18 +326,34 @@ public:
     const std::string& getModel() const { return _model; }
     void setModel(const std::string& model) { _model = model; }
 
-    const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
+    bool getDefaultFallthrough() const { return _defaultFallthrough; }
+    void setDefaultFallthrough(bool defaultFallthrough) { _defaultFallthrough = defaultFallthrough; }
+
+    std::unordered_map<MaterialKey::FlagBit, bool> getPropertyFallthroughs() { return _propertyFallthroughs; }
+    bool getPropertyFallthrough(MaterialKey::FlagBit property) { return _propertyFallthroughs[property]; }
+    void setPropertyDoesFallthrough(MaterialKey::FlagBit property) { _propertyFallthroughs[property] = true; }
 
 protected:
     std::string _name { "" };
 
 private:
-    mutable MaterialKey _key;
-    mutable UniformBufferView _schemaBuffer;
-    mutable gpu::TextureTablePointer _textureTable{ std::make_shared<gpu::TextureTable>() };
+    mutable MaterialKey _key { 0 };
 
+    // Material properties
+    glm::vec3 _emissive { DEFAULT_EMISSIVE };
+    float _opacity { DEFAULT_OPACITY };
+    glm::vec3 _albedo { DEFAULT_ALBEDO };
+    float _roughness { DEFAULT_ROUGHNESS };
+    float _metallic { DEFAULT_METALLIC };
+    float _scattering { DEFAULT_SCATTERING };
+    std::array<glm::mat4, NUM_TEXCOORD_TRANSFORMS> _texcoordTransforms;
+    glm::vec2 _lightmapParams { 0.0, 1.0 };
+    glm::vec2 _materialParams { 0.0, 1.0 };
     TextureMaps _textureMaps;
 
+    bool _defaultFallthrough { false };
+    std::unordered_map<MaterialKey::FlagBit, bool> _propertyFallthroughs;
+
     mutable QMutex _textureMapsMutex { QMutex::Recursive };
     mutable size_t _textureSize { 0 };
     mutable int _textureCount { 0 };
@@ -385,7 +361,6 @@ private:
     bool calculateMaterialInfo() const;
 
     std::string _model { "hifi_pbr" };
-
 };
 typedef std::shared_ptr< Material > MaterialPointer;
 
@@ -406,6 +381,8 @@ public:
 
 class MultiMaterial : public std::priority_queue<MaterialLayer, std::vector<MaterialLayer>, MaterialLayerCompare> {
 public:
+    MultiMaterial();
+
     bool remove(const MaterialPointer& value) {
         auto it = c.begin();
         while (it != c.end()) {
@@ -422,6 +399,49 @@ public:
             return false;
         }
     }
+
+    // Schema to access the attribute values of the material
+    class Schema {
+    public:
+        glm::vec3 _emissive { Material::DEFAULT_EMISSIVE }; // No Emissive
+        float _opacity { Material::DEFAULT_OPACITY }; // Opacity = 1 => Not Transparent
+
+        glm::vec3 _albedo { Material::DEFAULT_ALBEDO }; // Grey albedo => isAlbedo
+        float _roughness { Material::DEFAULT_ROUGHNESS }; // Roughness = 1 => Not Glossy
+
+        float _metallic { Material::DEFAULT_METALLIC }; // Not Metallic
+        float _scattering { Material::DEFAULT_SCATTERING }; // Scattering info
+#if defined(__clang__)
+        __attribute__((unused))
+#endif
+        glm::vec2 _spare { 0.0f }; // Padding
+
+        uint32_t _key { 0 }; // a copy of the materialKey
+#if defined(__clang__)
+        __attribute__((unused))
+#endif
+        glm::vec3 _spare2 { 0.0f };
+
+        // for alignment beauty, Material size == Mat4x4
+
+        // Texture Coord Transform Array
+        glm::mat4 _texcoordTransforms[Material::NUM_TEXCOORD_TRANSFORMS];
+
+        glm::vec2 _lightmapParams { 0.0, 1.0 };
+
+        // x: material mode (0 for UV, 1 for PROJECTED)
+        // y: 1 for texture repeat, 0 for discard outside of 0 - 1
+        glm::vec2 _materialParams { 0.0, 1.0 };
+
+        Schema() {}
+    };
+
+    gpu::BufferView& getSchemaBuffer() { return _schemaBuffer; }
+    const gpu::TextureTablePointer& getTextureTable() const { return _textureTable; }
+
+private:
+    gpu::BufferView _schemaBuffer;
+    gpu::TextureTablePointer _textureTable { std::make_shared<gpu::TextureTable>() };
 };
 
 };
diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp
index e6e3b0e812..ecaaf62fa7 100644
--- a/libraries/model-networking/src/model-networking/MaterialCache.cpp
+++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp
@@ -111,146 +111,253 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater
 /**jsdoc
  * A material such as may be used by a {@link Entities.EntityType|Material} entity.
  * @typedef {object} Material
- * @property {string} name="" - A name for the material.
- * @property {string} model="hifi_pbr" - <em>Currently not used.</em>
- * @property {Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value
- *     is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB.
- * @property {number} opacity=1.0 - The opacity, <code>0.0</code> &ndash; <code>1.0</code>.
- * @property {boolean} unlit=false - If <code>true</code>, the material is not lit.
- * @property {Color|RGBS} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can
- *     be either RGB or sRGB.
- * @property {number} roughness - The roughness, <code>0.0</code> &ndash; <code>1.0</code>.
- * @property {number} metallic - The metallicness, <code>0.0</code> &ndash; <code>1.0</code>.
- * @property {number} scattering - The scattering, <code>0.0</code> &ndash; <code>1.0</code>.
- * @property {string} emissiveMap - URL of emissive texture image.
- * @property {string} albedoMap - URL of albedo texture image.
+ * @property {string} model="hifi_pbr" - Different material models support different properties and rendering modes.
+ *     Supported models are: "hifi_pbr"
+ * @property {string} name="" - A name for the material. Supported by all material models.
+ * @property {Color|RGBS|string} emissive - The emissive color, i.e., the color that the material emits. A {@link Color} value
+ *     is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {number|string} opacity=1.0 - The opacity, <code>0.0</code> &ndash; <code>1.0</code>.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {boolean|string} unlit=false - If <code>true</code>, the material is not lit.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {Color|RGBS|string} albedo - The albedo color. A {@link Color} value is treated as sRGB. A {@link RGBS} value can
+ *     be either RGB or sRGB.  Set to <code>"fallthrough"</code> to fallthrough to the material below.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {number|string} roughness - The roughness, <code>0.0</code> &ndash; <code>1.0</code>.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {number|string} metallic - The metallicness, <code>0.0</code> &ndash; <code>1.0</code>.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {number|string} scattering - The scattering, <code>0.0</code> &ndash; <code>1.0</code>.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {string} emissiveMap - URL of emissive texture image.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
+ * @property {string} albedoMap - URL of albedo texture image.  Set to <code>"fallthrough"</code> to fallthrough to
+ *     the material below.  "hifi_pbr" model only.
  * @property {string} opacityMap - URL of opacity texture image. Set value the same as the <code>albedoMap</code> value for 
- *     transparency.
- * @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both.
- * @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both.
- * @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both.
- * @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both.
- * @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both.
- * @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both.
- * @property {string} occlusionMap - URL of occlusion texture image.
+ *     transparency.  "hifi_pbr" model only.
+ * @property {string} roughnessMap - URL of roughness texture image. Can use this or <code>glossMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} glossMap - URL of gloss texture image. Can use this or <code>roughnessMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} metallicMap - URL of metallic texture image. Can use this or <code>specularMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} specularMap - URL of specular texture image. Can use this or <code>metallicMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} normalMap - URL of normal texture image. Can use this or <code>bumpMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} bumpMap - URL of bump texture image. Can use this or <code>normalMap</code>, but not both.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} occlusionMap - URL of occlusion texture image.  Set to <code>"fallthrough"</code> to fallthrough to the material below.  "hifi_pbr" model only.
  * @property {string} scatteringMap - URL of scattering texture image. Only used if <code>normalMap</code> or 
- *     <code>bumpMap</code> is specified.
- * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>
+ *     <code>bumpMap</code> is specified.  Set to <code>"fallthrough"</code> to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {string} lightMap - URL of light map texture image. <em>Currently not used.</em>.  Set to <code>"fallthrough"</code>
+ *     to fallthrough to the material below.  "hifi_pbr" model only.
+ * @property {bool} defaultFallthrough=false - If <code>true</code>, all properties will fallthrough to the material below unless they are set.  If
+ *     <code>false</code>, they will respect the individual properties' fallthrough state.  "hifi_pbr" model only.
  */
 // Note: See MaterialEntityItem.h for default values used in practice.
 std::pair<std::string, std::shared_ptr<NetworkMaterial>> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON, const QUrl& baseUrl) {
     std::string name = "";
     std::shared_ptr<NetworkMaterial> material = std::make_shared<NetworkMaterial>();
-    for (auto& key : materialJSON.keys()) {
-        if (key == "name") {
-            auto nameJSON = materialJSON.value(key);
-            if (nameJSON.isString()) {
-                name = nameJSON.toString().toStdString();
-            }
-        } else if (key == "model") {
-            auto modelJSON = materialJSON.value(key);
-            if (modelJSON.isString()) {
-                material->setModel(modelJSON.toString().toStdString());
-            }
-        } else if (key == "emissive") {
-            glm::vec3 color;
-            bool isSRGB;
-            bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB);
-            if (valid) {
-                material->setEmissive(color, isSRGB);
-            }
-        } else if (key == "opacity") {
-            auto value = materialJSON.value(key);
-            if (value.isDouble()) {
-                material->setOpacity(value.toDouble());
-            }
-        } else if (key == "unlit") {
-            auto value = materialJSON.value(key);
-            if (value.isBool()) {
-                material->setUnlit(value.toBool());
-            }
-        } else if (key == "albedo") {
-            glm::vec3 color;
-            bool isSRGB;
-            bool valid = parseJSONColor(materialJSON.value(key), color, isSRGB);
-            if (valid) {
-                material->setAlbedo(color, isSRGB);
-            }
-        } else if (key == "roughness") {
-            auto value = materialJSON.value(key);
-            if (value.isDouble()) {
-                material->setRoughness(value.toDouble());
-            }
-        } else if (key == "metallic") {
-            auto value = materialJSON.value(key);
-            if (value.isDouble()) {
-                material->setMetallic(value.toDouble());
-            }
-        } else if (key == "scattering") {
-            auto value = materialJSON.value(key);
-            if (value.isDouble()) {
-                material->setScattering(value.toDouble());
-            }
-        } else if (key == "emissiveMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setEmissiveMap(baseUrl.resolved(value.toString()));
-            }
-        } else if (key == "albedoMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                QString urlString = value.toString();
-                bool useAlphaChannel = false;
-                auto opacityMap = materialJSON.find("opacityMap");
-                if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == urlString) {
-                    useAlphaChannel = true;
+    std::string modelString = "hifi_pbr";
+    auto modelJSON = materialJSON.value("model");
+    if (modelJSON.isString()) {
+        modelString = modelJSON.toString().toStdString();
+        material->setModel(modelString);
+    }
+    if (modelString == "hifi_pbr") {
+        const QString FALLTHROUGH("fallthrough");
+        for (auto& key : materialJSON.keys()) {
+            if (key == "name") {
+                auto nameJSON = materialJSON.value(key);
+                if (nameJSON.isString()) {
+                    name = nameJSON.toString().toStdString();
+                }
+            } else if (key == "model") {
+                auto modelJSON = materialJSON.value(key);
+                if (modelJSON.isString()) {
+                    material->setModel(modelJSON.toString().toStdString());
+                }
+            } else if (key == "emissive") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_VAL_BIT);
+                } else {
+                    glm::vec3 color;
+                    bool isSRGB;
+                    bool valid = parseJSONColor(value, color, isSRGB);
+                    if (valid) {
+                        material->setEmissive(color, isSRGB);
+                    }
+                }
+            } else if (key == "opacity") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OPACITY_VAL_BIT);
+                } else if (value.isDouble()) {
+                    material->setOpacity(value.toDouble());
+                }
+            } else if (key == "unlit") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::UNLIT_VAL_BIT);
+                } else if (value.isBool()) {
+                    material->setUnlit(value.toBool());
+                }
+            } else if (key == "albedo") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_VAL_BIT);
+                } else {
+                    glm::vec3 color;
+                    bool isSRGB;
+                    bool valid = parseJSONColor(value, color, isSRGB);
+                    if (valid) {
+                        material->setAlbedo(color, isSRGB);
+                    }
+                }
+            } else if (key == "roughness") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::GLOSSY_VAL_BIT);
+                } else if (value.isDouble()) {
+                    material->setRoughness(value.toDouble());
+                }
+            } else if (key == "metallic") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_VAL_BIT);
+                } else if (value.isDouble()) {
+                    material->setMetallic(value.toDouble());
+                }
+            } else if (key == "scattering") {
+                auto value = materialJSON.value(key);
+                if (value.isString() && value.toString() == FALLTHROUGH) {
+                    material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_VAL_BIT);
+                } else if (value.isDouble()) {
+                    material->setScattering(value.toDouble());
+                }
+            } else if (key == "emissiveMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::EMISSIVE_MAP_BIT);
+                    } else {
+                        material->setEmissiveMap(baseUrl.resolved(valueString));
+                    }
+                }
+            } else if (key == "albedoMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    QString valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ALBEDO_MAP_BIT);
+                    } else {
+                        bool useAlphaChannel = false;
+                        auto opacityMap = materialJSON.find("opacityMap");
+                        if (opacityMap != materialJSON.end() && opacityMap->isString() && opacityMap->toString() == valueString) {
+                            useAlphaChannel = true;
+                        }
+                        material->setAlbedoMap(baseUrl.resolved(valueString), useAlphaChannel);
+                    }
+                }
+            } else if (key == "roughnessMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
+                    } else {
+                        material->setRoughnessMap(baseUrl.resolved(valueString), false);
+                    }
+                }
+            } else if (key == "glossMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::ROUGHNESS_MAP_BIT);
+                    } else {
+                        material->setRoughnessMap(baseUrl.resolved(valueString), true);
+                    }
+                }
+            } else if (key == "metallicMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
+                    } else {
+                        material->setMetallicMap(baseUrl.resolved(valueString), false);
+                    }
+                }
+            } else if (key == "specularMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::METALLIC_MAP_BIT);
+                    } else {
+                        material->setMetallicMap(baseUrl.resolved(valueString), true);
+                    }
+                }
+            } else if (key == "normalMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
+                    } else {
+                        material->setNormalMap(baseUrl.resolved(valueString), false);
+                    }
+                }
+            } else if (key == "bumpMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::NORMAL_MAP_BIT);
+                    } else {
+                        material->setNormalMap(baseUrl.resolved(valueString), true);
+                    }
+                }
+            } else if (key == "occlusionMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::OCCLUSION_MAP_BIT);
+                    } else {
+                        material->setOcclusionMap(baseUrl.resolved(valueString));
+                    }
+                }
+            } else if (key == "scatteringMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::SCATTERING_MAP_BIT);
+                    } else {
+                        material->setScatteringMap(baseUrl.resolved(valueString));
+                    }
+                }
+            } else if (key == "lightMap") {
+                auto value = materialJSON.value(key);
+                if (value.isString()) {
+                    auto valueString = value.toString();
+                    if (valueString == FALLTHROUGH) {
+                        material->setPropertyDoesFallthrough(graphics::MaterialKey::FlagBit::LIGHTMAP_MAP_BIT);
+                    } else {
+                        material->setLightmapMap(baseUrl.resolved(valueString));
+                    }
+                }
+            } else if (key == "defaultFallthrough") {
+                auto value = materialJSON.value(key);
+                if (value.isBool()) {
+                    material->setDefaultFallthrough(value.toBool());
                 }
-                material->setAlbedoMap(baseUrl.resolved(urlString), useAlphaChannel);
-            }
-        } else if (key == "roughnessMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setRoughnessMap(baseUrl.resolved(value.toString()), false);
-            }
-        } else if (key == "glossMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setRoughnessMap(baseUrl.resolved(value.toString()), true);
-            }
-        } else if (key == "metallicMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setMetallicMap(baseUrl.resolved(value.toString()), false);
-            }
-        } else if (key == "specularMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setMetallicMap(baseUrl.resolved(value.toString()), true);
-            }
-        } else if (key == "normalMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setNormalMap(baseUrl.resolved(value.toString()), false);
-            }
-        } else if (key == "bumpMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setNormalMap(baseUrl.resolved(value.toString()), true);
-            }
-        } else if (key == "occlusionMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setOcclusionMap(baseUrl.resolved(value.toString()));
-            }
-        } else if (key == "scatteringMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setScatteringMap(baseUrl.resolved(value.toString()));
-            }
-        } else if (key == "lightMap") {
-            auto value = materialJSON.value(key);
-            if (value.isString()) {
-                material->setLightmapMap(baseUrl.resolved(value.toString()));
             }
         }
     }
diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp
index ca2e56862d..489fc5c331 100644
--- a/libraries/render-utils/src/MeshPartPayload.cpp
+++ b/libraries/render-utils/src/MeshPartPayload.cpp
@@ -49,8 +49,6 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr
 }
 }
 
-const graphics::MaterialPointer MeshPartPayload::DEFAULT_MATERIAL = std::make_shared<graphics::Material>();
-
 MeshPartPayload::MeshPartPayload(const std::shared_ptr<const graphics::Mesh>& mesh, int partIndex, graphics::MaterialPointer material) {
     updateMeshPart(mesh, partIndex);
     addMaterial(graphics::MaterialLayer(material, 0));
@@ -158,7 +156,7 @@ void MeshPartPayload::render(RenderArgs* args) {
 
     // apply material properties
     if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
-        RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
+        RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing);
         args->_details._materialSwitches++;
     }
 
@@ -434,7 +432,7 @@ void ModelMeshPartPayload::render(RenderArgs* args) {
 
     // apply material properties
     if (args->_renderMode != render::Args::RenderMode::SHADOW_RENDER_MODE) {
-        RenderPipelines::bindMaterial(!_drawMaterials.empty() ? _drawMaterials.top().material : DEFAULT_MATERIAL, batch, args->_enableTexturing);
+        RenderPipelines::bindMaterials(_drawMaterials, batch, args->_enableTexturing);
         args->_details._materialSwitches++;
     }
 
diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h
index 29c0091f11..03145c981b 100644
--- a/libraries/render-utils/src/MeshPartPayload.h
+++ b/libraries/render-utils/src/MeshPartPayload.h
@@ -74,7 +74,6 @@ public:
     void removeMaterial(graphics::MaterialPointer material);
 
 protected:
-    static const graphics::MaterialPointer DEFAULT_MATERIAL;
     render::ItemKey _itemKey{ render::ItemKey::Builder::opaqueShape().build() };
 
     bool topMaterialExists() const { return !_drawMaterials.empty() && _drawMaterials.top().material; }
diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp
index cd685a54a1..1af47b4321 100644
--- a/libraries/render-utils/src/RenderPipelines.cpp
+++ b/libraries/render-utils/src/RenderPipelines.cpp
@@ -308,24 +308,33 @@ void addPlumberPipeline(ShapePlumber& plumber,
 
 void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* args) {
     // Set a default albedo map
-    batch.setResourceTexture(gr::Texture::MaterialAlbedo,
-        DependencyManager::get<TextureCache>()->getWhiteTexture());
+    batch.setResourceTexture(gr::Texture::MaterialAlbedo, DependencyManager::get<TextureCache>()->getWhiteTexture());
 
     // Set a default material
     if (pipeline.locations->materialBufferUnit) {
         // Create a default schema
-        static bool isMaterialSet = false;
-        static graphics::Material material;
-        if (!isMaterialSet) {
-            material.setAlbedo(vec3(1.0f));
-            material.setOpacity(1.0f);
-            material.setMetallic(0.1f);
-            material.setRoughness(0.9f);
-            isMaterialSet = true;
-        }
+        static gpu::BufferView schemaBuffer;
+        static std::once_flag once;
+        std::call_once(once, [] {
+            graphics::MultiMaterial::Schema schema;
+            graphics::MaterialKey schemaKey;
 
-        // Set a default schema
-        batch.setUniformBuffer(gr::Buffer::Material, material.getSchemaBuffer());
+            schema._albedo = vec3(1.0f);
+            schema._opacity = 1.0f;
+            schema._metallic = 0.1f;
+            schema._roughness = 0.9f;
+
+            schemaKey.setAlbedo(true);
+            schemaKey.setTranslucentFactor(false);
+            schemaKey.setMetallic(true);
+            schemaKey.setGlossy(true);
+            schema._key = (uint32_t)schemaKey._flags.to_ulong();
+
+            auto schemaSize = sizeof(graphics::MultiMaterial::Schema);
+            schemaBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(schemaSize, (const gpu::Byte*) &schema, schemaSize));
+        });
+
+        batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer);
     }
 }
 
@@ -364,103 +373,322 @@ void initZPassPipelines(ShapePlumber& shapePlumber, gpu::StatePointer state, con
         gpu::Shader::createProgram(deformed_model_shadow_fade_dq), state, extraBatchSetter, itemSetter);
 }
 
+void RenderPipelines::bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) {
+    graphics::MultiMaterial multiMaterial;
+    multiMaterial.push(graphics::MaterialLayer(material, 0));
+    bindMaterials(multiMaterial, batch, enableTextures);
+}
+
 // FIXME find a better way to setup the default textures
-void RenderPipelines::bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures) {
-    if (!material) {
+void RenderPipelines::bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures) {
+    if (multiMaterial.size() == 0) {
         return;
     }
 
     auto textureCache = DependencyManager::get<TextureCache>();
+    auto& drawMaterialTextures = multiMaterial.getTextureTable();
+    auto& schemaBuffer = multiMaterial.getSchemaBuffer();
 
-    batch.setUniformBuffer(gr::Buffer::Material, material->getSchemaBuffer());
+    // The total list of things we need to look for
+    static std::set<graphics::MaterialKey::FlagBit> allFlagBits;
+    static std::once_flag once;
+    std::call_once(once, [] {
+        for (int i = 0; i < graphics::MaterialKey::NUM_FLAGS; i++) {
+            auto flagBit = graphics::MaterialKey::FlagBit(i);
+            // The opacity mask/map are derived from the albedo map
+            if (flagBit != graphics::MaterialKey::OPACITY_MASK_MAP_BIT &&
+                    flagBit != graphics::MaterialKey::OPACITY_TRANSLUCENT_MAP_BIT) {
+                allFlagBits.insert(flagBit);
+            }
+        }
+    });
 
-    const auto& materialKey = material->getKey();
-    const auto& textureMaps = material->getTextureMaps();
+    graphics::MultiMaterial materials = multiMaterial;
+    graphics::MultiMaterial::Schema schema;
+    graphics::MaterialKey schemaKey;
 
-    int numUnlit = 0;
-    if (materialKey.isUnlit()) {
-        numUnlit++;
+    std::set<graphics::MaterialKey::FlagBit> flagBitsToCheck = allFlagBits;
+    std::set<graphics::MaterialKey::FlagBit> flagBitsToSetDefault;
+
+    auto material = materials.top().material;
+    while (material) {
+        bool defaultFallthrough = material->getDefaultFallthrough();
+        const auto& materialKey = material->getKey();
+        const auto& textureMaps = material->getTextureMaps();
+
+        auto it = flagBitsToCheck.begin();
+        while (it != flagBitsToCheck.end()) {
+            auto flagBit = *it;
+            bool wasSet = false;
+            bool forceDefault = false;
+            switch (flagBit) {
+                case graphics::MaterialKey::EMISSIVE_VAL_BIT:
+                    if (materialKey.isEmissive()) {
+                        schema._emissive = material->getEmissive(false);
+                        schemaKey.setEmissive(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::UNLIT_VAL_BIT:
+                    if (materialKey.isUnlit()) {
+                        schemaKey.setUnlit(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::ALBEDO_VAL_BIT:
+                    if (materialKey.isAlbedo()) {
+                        schema._albedo = material->getAlbedo(false);
+                        schemaKey.setAlbedo(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::METALLIC_VAL_BIT:
+                    if (materialKey.isMetallic()) {
+                        schema._metallic = material->getMetallic();
+                        schemaKey.setMetallic(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::GLOSSY_VAL_BIT:
+                    if (materialKey.isRough() || materialKey.isGlossy()) {
+                        schema._roughness = material->getRoughness();
+                        schemaKey.setGlossy(materialKey.isGlossy());
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::OPACITY_VAL_BIT:
+                    if (materialKey.isTranslucentFactor()) {
+                        schema._opacity = material->getOpacity();
+                        schemaKey.setTranslucentFactor(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::SCATTERING_VAL_BIT:
+                    if (materialKey.isScattering()) {
+                        schema._scattering = material->getScattering();
+                        schemaKey.setScattering(true);
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::ALBEDO_MAP_BIT:
+                    if (materialKey.isAlbedoMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setAlbedoMap(true);
+                        schemaKey.setOpacityMaskMap(materialKey.isOpacityMaskMap());
+                        schemaKey.setTranslucentMap(materialKey.isTranslucentMap());
+                    }
+                    break;
+                case graphics::MaterialKey::METALLIC_MAP_BIT:
+                    if (materialKey.isMetallicMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setMetallicMap(true);
+                    }
+                    break;
+                case graphics::MaterialKey::ROUGHNESS_MAP_BIT:
+                    if (materialKey.isRoughnessMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setRoughnessMap(true);
+                    }
+                    break;
+                case graphics::MaterialKey::NORMAL_MAP_BIT:
+                    if (materialKey.isNormalMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setNormalMap(true);
+                    }
+                    break;
+                case graphics::MaterialKey::OCCLUSION_MAP_BIT:
+                    if (materialKey.isOcclusionMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setOcclusionMap(true);
+                    }
+                    break;
+                case graphics::MaterialKey::SCATTERING_MAP_BIT:
+                    if (materialKey.isScatteringMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setScattering(true);
+                    }
+                    break;
+                case graphics::MaterialKey::EMISSIVE_MAP_BIT:
+                    // Lightmap takes precendence over emissive map for legacy reasons
+                    if (materialKey.isEmissiveMap() && !materialKey.isLightmapMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setEmissiveMap(true);
+                    } else if (materialKey.isLightmapMap()) {
+                        // We'll set this later when we check the lightmap
+                        wasSet = true;
+                    }
+                    break;
+                case graphics::MaterialKey::LIGHTMAP_MAP_BIT:
+                    if (materialKey.isLightmapMap()) {
+                        if (!enableTextures) {
+                            forceDefault = true;
+                        } else {
+                            auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
+                            if (itr != textureMaps.end() && itr->second->isDefined()) {
+                                drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
+                                wasSet = true;
+                            } else {
+                                forceDefault = true;
+                            }
+                        }
+                        schemaKey.setLightmapMap(true);
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+            if (wasSet) {
+                flagBitsToCheck.erase(it++);
+            } else if (forceDefault || !defaultFallthrough || !material->getPropertyFallthrough(flagBit)) {
+                flagBitsToSetDefault.insert(flagBit);
+                flagBitsToCheck.erase(it++);
+            } else {
+                ++it;
+            }
+        }
+
+        if (flagBitsToCheck.empty()) {
+            break;
+        }
+
+        materials.pop();
+        material = materials.top().material;
     }
 
-    const auto& drawMaterialTextures = material->getTextureTable();
+    for (auto flagBit : flagBitsToCheck) {
+        flagBitsToSetDefault.insert(flagBit);
+    }
 
-    // Albedo
-    if (materialKey.isAlbedoMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture());
+    // Handle defaults
+    for (auto flagBit : flagBitsToSetDefault) {
+        switch (flagBit) {
+            case graphics::MaterialKey::EMISSIVE_VAL_BIT:
+            case graphics::MaterialKey::UNLIT_VAL_BIT:
+            case graphics::MaterialKey::ALBEDO_VAL_BIT:
+            case graphics::MaterialKey::METALLIC_VAL_BIT:
+            case graphics::MaterialKey::GLOSSY_VAL_BIT:
+            case graphics::MaterialKey::OPACITY_VAL_BIT:
+            case graphics::MaterialKey::SCATTERING_VAL_BIT:
+                // these are initialized to the correct default values in Schema()
+                break;
+            case graphics::MaterialKey::ALBEDO_MAP_BIT:
+                if (schemaKey.isAlbedoMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialAlbedo, textureCache->getWhiteTexture());
+                }
+                break;
+            case graphics::MaterialKey::METALLIC_MAP_BIT:
+                if (schemaKey.isMetallicMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture());
+                }
+                break;
+            case graphics::MaterialKey::ROUGHNESS_MAP_BIT:
+                if (schemaKey.isRoughnessMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture());
+                }
+                break;
+            case graphics::MaterialKey::NORMAL_MAP_BIT:
+                if (schemaKey.isNormalMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture());
+                }
+                break;
+            case graphics::MaterialKey::OCCLUSION_MAP_BIT:
+                if (schemaKey.isOcclusionMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture());
+                }
+                break;
+            case graphics::MaterialKey::SCATTERING_MAP_BIT:
+                if (schemaKey.isScatteringMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture());
+                }
+                break;
+            case graphics::MaterialKey::EMISSIVE_MAP_BIT:
+                if (schemaKey.isEmissiveMap() && !schemaKey.isLightmapMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture());
+                }
+                break;
+            case graphics::MaterialKey::LIGHTMAP_MAP_BIT:
+                if (schemaKey.isLightmapMap()) {
+                    drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture());
+                }
+                break;
+            default:
+                break;
         }
     }
 
-    // Roughness map
-    if (materialKey.isRoughnessMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialRoughness, textureCache->getWhiteTexture());
-        }
-    }
-
-    // Normal map
-    if (materialKey.isNormalMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialNormal, textureCache->getBlueTexture());
-        }
-    }
-
-    // Metallic map
-    if (materialKey.isMetallicMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialMetallic, textureCache->getBlackTexture());
-        }
-    }
-
-    // Occlusion map
-    if (materialKey.isOcclusionMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialOcclusion, textureCache->getWhiteTexture());
-        }
-    }
-
-    // Scattering map
-    if (materialKey.isScatteringMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialScattering, textureCache->getWhiteTexture());
-        }
-    }
-
-    // Emissive / Lightmap
-    if (materialKey.isLightmapMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP);
-
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getGrayTexture());
-        }
-    } else if (materialKey.isEmissiveMap()) {
-        auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP);
-        if (enableTextures && itr != textureMaps.end() && itr->second->isDefined()) {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, itr->second->getTextureView());
-        } else {
-            drawMaterialTextures->setTexture(gr::Texture::MaterialEmissiveLightmap, textureCache->getBlackTexture());
-        }
-    }
-
-    batch.setResourceTextureTable(material->getTextureTable());
+    schema._key = (uint32_t)schemaKey._flags.to_ulong();
+    schemaBuffer.edit<graphics::MultiMaterial::Schema>() = schema;
+    batch.setUniformBuffer(gr::Buffer::Material, schemaBuffer);
+    batch.setResourceTextureTable(drawMaterialTextures);
 }
diff --git a/libraries/render-utils/src/RenderPipelines.h b/libraries/render-utils/src/RenderPipelines.h
index b7d22bc72d..49abe719c8 100644
--- a/libraries/render-utils/src/RenderPipelines.h
+++ b/libraries/render-utils/src/RenderPipelines.h
@@ -15,7 +15,8 @@
 
 class RenderPipelines {
 public:
-    static void bindMaterial(const graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures);
+    static void bindMaterial(graphics::MaterialPointer& material, gpu::Batch& batch, bool enableTextures);
+    static void bindMaterials(graphics::MultiMaterial& multiMaterial, gpu::Batch& batch, bool enableTextures);
 };