shared_ptr<UniversalMaterial> UniversalMaterial::create(const std::string& name, const Specification& specification) {
    MaterialCache& cache = _internal::materialCache();
    shared_ptr<UniversalMaterial> value = cache[specification];

    if (isNull(value)) {
        // Construct the appropriate material
        value.reset(new UniversalMaterial());

        value->m_bsdf =
            UniversalBSDF::create(
                specification.loadLambertian(),
                specification.loadGlossy(),
                specification.loadTransmissive(),
                specification.m_etaTransmit,
                specification.m_extinctionTransmit,
                specification.m_etaReflect,
                specification.m_extinctionReflect);

        value->m_depthWriteHintDistance = specification.m_depthWriteHintDistance;
        value->m_customShaderPrefix     = specification.m_customShaderPrefix;
        value->m_refractionHint         = specification.m_refractionHint;
        value->m_mirrorHint             = specification.m_mirrorHint;

        // load emission map
        value->m_emissive = specification.loadEmissive();

        // load bump map
        if (! specification.m_bump.texture.filename.empty()) {
            value->m_bump = BumpMap::create(specification.m_bump);
        }

        value->m_name = name;

        value->m_numLightMapDirections = specification.m_numLightMapDirections;
        for (int i = 0; i < specification.m_numLightMapDirections; ++i) {
            value->m_lightMap[i] = Component3(specification.m_lightMapConstant, specification.m_lightMap[i]);
        }

        // Update the cache
        cache.set(specification, value);

        value->computeDefines(value->m_macros);
    }

    return value;
}