From fa416adaf6fc990754721527ca5ef77f9ff9ea3b Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Tue, 2 Jul 2019 10:17:05 -0700
Subject: [PATCH] adding unlit property to text, fixing transparency, normals,
 other cleanup

---
 interface/src/graphics/WorldBox.cpp           | 14 ++---
 .../src/avatars-renderer/Avatar.cpp           |  2 +-
 .../src/avatars-renderer/SkeletonModel.cpp    |  7 ++-
 .../src/RenderableEntityItem.cpp              |  2 +-
 .../src/RenderableEntityItem.h                |  2 +-
 .../src/RenderableImageEntityItem.cpp         | 17 ++----
 .../src/RenderableImageEntityItem.h           |  2 -
 .../src/RenderableModelEntityItem.cpp         |  5 +-
 .../src/RenderableModelEntityItem.h           |  2 +-
 .../src/RenderableShapeEntityItem.cpp         |  8 +--
 .../src/RenderableTextEntityItem.cpp          | 53 +++++++++++-----
 .../src/RenderableTextEntityItem.h            |  6 +-
 .../entities-renderer/src/paintStroke.slf     |  4 +-
 .../entities/src/EntityItemProperties.cpp     | 13 ++++
 libraries/entities/src/EntityItemProperties.h |  1 +
 libraries/entities/src/EntityPropertyFlags.h  |  1 +
 libraries/entities/src/TextEntityItem.cpp     | 19 +++++-
 libraries/entities/src/TextEntityItem.h       |  4 ++
 libraries/networking/src/udt/PacketHeaders.h  |  1 +
 libraries/render-utils/src/GeometryCache.cpp  | 61 +++++--------------
 libraries/render-utils/src/GeometryCache.h    | 59 ++++++------------
 libraries/render-utils/src/TextRenderer3D.cpp |  4 +-
 libraries/render-utils/src/TextRenderer3D.h   |  2 +-
 libraries/render-utils/src/forward_simple.slf |  2 +-
 .../src/forward_simple_textured.slf           |  2 +-
 .../forward_simple_textured_transparent.slf   |  2 +-
 ..._sdf_text3D.slp => sdf_text3D_forward.slp} |  0
 .../render-utils/sdf_text3D_translucent.slp   |  1 +
 .../sdf_text3D_translucent_unlit.slp          |  1 +
 .../render-utils/sdf_text3D_transparent.slp   |  1 -
 .../src/render-utils/sdf_text3D_unlit.slp     |  1 +
 ..._sdf_text3D.slf => sdf_text3D_forward.slf} |  4 +-
 ...sparent.slf => sdf_text3D_translucent.slf} |  6 +-
 .../src/sdf_text3D_translucent_unlit.slf      | 38 ++++++++++++
 .../render-utils/src/sdf_text3D_unlit.slf     | 32 ++++++++++
 libraries/render-utils/src/simple.slf         |  2 +-
 .../render-utils/src/simple_textured.slf      |  2 +-
 .../render-utils/src/simple_textured_fade.slf |  4 +-
 .../src/simple_textured_unlit.slf             |  4 +-
 .../src/simple_textured_unlit_fade.slf        |  4 +-
 .../render-utils/src/simple_transparent.slf   |  2 +-
 .../src/simple_transparent_textured.slf       |  2 +-
 .../src/simple_transparent_textured_fade.slf  |  2 +-
 libraries/render-utils/src/text/Font.cpp      | 49 +++++++--------
 libraries/render-utils/src/text/Font.h        |  7 +--
 libraries/render/src/render/Item.h            |  2 +-
 .../system/assets/data/createAppTooltips.json |  6 +-
 .../system/html/js/entityProperties.js        |  5 ++
 .../create/assets/data/createAppTooltips.json |  6 +-
 .../html/js/entityProperties.js               |  5 ++
 50 files changed, 283 insertions(+), 198 deletions(-)
 rename libraries/render-utils/src/render-utils/{forward_sdf_text3D.slp => sdf_text3D_forward.slp} (100%)
 create mode 100644 libraries/render-utils/src/render-utils/sdf_text3D_translucent.slp
 create mode 100644 libraries/render-utils/src/render-utils/sdf_text3D_translucent_unlit.slp
 delete mode 100644 libraries/render-utils/src/render-utils/sdf_text3D_transparent.slp
 create mode 100644 libraries/render-utils/src/render-utils/sdf_text3D_unlit.slp
 rename libraries/render-utils/src/{forward_sdf_text3D.slf => sdf_text3D_forward.slf} (97%)
 rename libraries/render-utils/src/{sdf_text3D_transparent.slf => sdf_text3D_translucent.slf} (92%)
 create mode 100644 libraries/render-utils/src/sdf_text3D_translucent_unlit.slf
 create mode 100644 libraries/render-utils/src/sdf_text3D_unlit.slf

diff --git a/interface/src/graphics/WorldBox.cpp b/interface/src/graphics/WorldBox.cpp
index a28850207f..648d6d3177 100644
--- a/interface/src/graphics/WorldBox.cpp
+++ b/interface/src/graphics/WorldBox.cpp
@@ -108,8 +108,8 @@ void WorldBoxRenderData::renderWorldBox(RenderArgs* args, gpu::Batch& batch) {
         glm::vec3(HALF_TREE_SCALE, 0.0f, HALF_TREE_SCALE), GREY,
         geometryIds[17]);
 
-
-    geometryCache->renderWireCubeInstance(args, batch, GREY4);
+    auto pipeline = geometryCache->getShapePipelinePointer(false, false, args->_renderMethod == render::Args::RenderMethod::FORWARD);
+    geometryCache->renderWireCubeInstance(args, batch, GREY4, pipeline);
 
     //  Draw meter markers along the 3 axis to help with measuring things
     const float MARKER_DISTANCE = 1.0f;
@@ -117,22 +117,22 @@ void WorldBoxRenderData::renderWorldBox(RenderArgs* args, gpu::Batch& batch) {
 
     transform = Transform().setScale(MARKER_RADIUS);
     batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(args, batch, RED);
+    geometryCache->renderSolidSphereInstance(args, batch, RED, pipeline);
 
     transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, 0.0f)).setScale(MARKER_RADIUS);
     batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(args, batch, RED);
+    geometryCache->renderSolidSphereInstance(args, batch, RED, pipeline);
 
     transform = Transform().setTranslation(glm::vec3(0.0f, MARKER_DISTANCE, 0.0f)).setScale(MARKER_RADIUS);
     batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(args, batch, GREEN);
+    geometryCache->renderSolidSphereInstance(args, batch, GREEN, pipeline);
 
     transform = Transform().setTranslation(glm::vec3(0.0f, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS);
     batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(args, batch, BLUE);
+    geometryCache->renderSolidSphereInstance(args, batch, BLUE, pipeline);
 
     transform = Transform().setTranslation(glm::vec3(MARKER_DISTANCE, 0.0f, MARKER_DISTANCE)).setScale(MARKER_RADIUS);
     batch.setModelTransform(transform);
-    geometryCache->renderSolidSphereInstance(args, batch, GREY);
+    geometryCache->renderSolidSphereInstance(args, batch, GREY, pipeline);
 }
 
diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
index fb7eddd582..5af98100dc 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp
@@ -1108,7 +1108,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
         batch.setModelTransform(textTransform);
         {
             PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
-            renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), forward);
+            renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor, glm::vec2(-1.0f), true, forward);
         }
     }
 }
diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp
index d8f24208b0..40b65c54a1 100644
--- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp
+++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp
@@ -331,18 +331,19 @@ void SkeletonModel::renderBoundingCollisionShapes(RenderArgs* args, gpu::Batch&
     // draw a blue sphere at the capsule top point
     glm::vec3 topPoint = _translation + _rotation * (scale * (_boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * Vectors::UNIT_Y));
     batch.setModelTransform(Transform().setTranslation(topPoint).postScale(scale * _boundingCapsuleRadius));
-    geometryCache->renderSolidSphereInstance(args, batch, glm::vec4(0.6f, 0.6f, 0.8f, alpha));
+    auto pipeline = geometryCache->getShapePipelinePointer(alpha < 1.0f, false, args->_renderMethod == render::Args::RenderMethod::FORWARD);
+    geometryCache->renderSolidSphereInstance(args, batch, glm::vec4(0.6f, 0.6f, 0.8f, alpha), pipeline);
 
     // draw a yellow sphere at the capsule bottom point
     glm::vec3 bottomPoint = topPoint - _rotation * glm::vec3(0.0f, scale * _boundingCapsuleHeight, 0.0f);
     batch.setModelTransform(Transform().setTranslation(bottomPoint).postScale(scale * _boundingCapsuleRadius));
-    geometryCache->renderSolidSphereInstance(args, batch, glm::vec4(0.8f, 0.8f, 0.6f, alpha));
+    geometryCache->renderSolidSphereInstance(args, batch, glm::vec4(0.8f, 0.8f, 0.6f, alpha), pipeline);
 
     // draw a green cylinder between the two points
     float capsuleDiameter = 2.0f * _boundingCapsuleRadius;
     glm::vec3 cylinderDimensions = glm::vec3(capsuleDiameter, _boundingCapsuleHeight, capsuleDiameter);
     batch.setModelTransform(Transform().setScale(scale * cylinderDimensions).setRotation(_rotation).setTranslation(0.5f * (topPoint + bottomPoint)));
-    geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Shape::Cylinder, glm::vec4(0.6f, 0.8f, 0.6f, alpha));
+    geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Shape::Cylinder, glm::vec4(0.6f, 0.8f, 0.6f, alpha), pipeline);
 }
 
 bool SkeletonModel::hasSkeleton() {
diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp
index 01d31856e0..f8bd6f8ce1 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp
@@ -185,7 +185,7 @@ ItemKey EntityRenderer::getKey() {
     }
 }
 
-uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
+uint32_t EntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const {
     if (Item::isValidID(_renderItemID)) {
         subItems.emplace_back(_renderItemID);
         return 1;
diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h
index 39f9ad091e..1db4cfdf53 100644
--- a/libraries/entities-renderer/src/RenderableEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableEntityItem.h
@@ -62,7 +62,7 @@ public:
 
     static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start);
 
-    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
+    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
     virtual Item::Bound getBound() override;
 
 protected:
diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp
index 20d10c6fd4..cfe6039ab3 100644
--- a/libraries/entities-renderer/src/RenderableImageEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableImageEntityItem.cpp
@@ -111,9 +111,9 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
     void* key = (void*)this;
     AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
         withWriteLock([&] {
-            _dimensions = entity->getScaledDimensions();
             updateModelTransformAndBound();
             _renderTransform = getModelTransform();
+            _renderTransform.postScale(entity->getScaledDimensions());
         });
     });
 }
@@ -152,14 +152,12 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
     NetworkTexturePointer texture;
     QRect subImage;
     glm::vec4 color;
-    glm::vec3 dimensions;
     Transform transform;
     withReadLock([&] {
         texture = _texture;
         subImage = _subImage;
         color = glm::vec4(toGlm(_color), _alpha);
         color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
-        dimensions = _dimensions;
         transform = _renderTransform;
     });
 
@@ -171,7 +169,6 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
     gpu::Batch* batch = args->_batch;
 
     transform.setRotation(EntityItem::getBillboardRotation(transform.getTranslation(), transform.getRotation(), _billboardMode, args->getViewFrustum().getPosition()));
-    transform.postScale(dimensions);
 
     batch->setModelTransform(transform);
     batch->setResourceTexture(0, texture->getGPUTexture());
@@ -200,16 +197,14 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
 
     float maxSize = glm::max(fromImage.width(), fromImage.height());
     float x = _keepAspectRatio ? fromImage.width() / (2.0f * maxSize) : 0.5f;
-    float y = _keepAspectRatio ? -fromImage.height() / (2.0f * maxSize) : -0.5f;
+    float y = _keepAspectRatio ? fromImage.height() / (2.0f * maxSize) : 0.5f;
 
-    glm::vec2 topLeft(-x, -y);
-    glm::vec2 bottomRight(x, y);
-    glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight);
-    glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
-                                  (fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
+    glm::vec2 texCoordBottomLeft((fromImage.x() + 0.5f) / imageWidth, -(fromImage.y() + 0.5f) / imageHeight);
+    glm::vec2 texCoordTopRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
+                                  -(fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
 
     DependencyManager::get<GeometryCache>()->renderQuad(
-        *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
+        *batch, glm::vec2(-x, -y), glm::vec2(x, y), texCoordBottomLeft, texCoordTopRight,
         color, _geometryId
     );
 
diff --git a/libraries/entities-renderer/src/RenderableImageEntityItem.h b/libraries/entities-renderer/src/RenderableImageEntityItem.h
index d60b38fe65..e686452fa5 100644
--- a/libraries/entities-renderer/src/RenderableImageEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableImageEntityItem.h
@@ -47,8 +47,6 @@ private:
     PulsePropertyGroup _pulseProperties;
     BillboardMode _billboardMode;
 
-    glm::vec3 _dimensions;
-
     int _geometryId { 0 };
 };
 
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
index 54edd3543c..cfc94ad92c 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp
@@ -1073,7 +1073,7 @@ ItemKey ModelEntityRenderer::getKey() {
     return _itemKey;
 }
 
-uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { 
+uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const {
     if (_model) {
         auto metaSubItems = _model->fetchRenderItemIDs();
         subItems.insert(subItems.end(), metaSubItems.begin(), metaSubItems.end());
@@ -1519,7 +1519,8 @@ void ModelEntityRenderer::doRender(RenderArgs* args) {
     static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f);
     gpu::Batch& batch = *args->_batch;
     batch.setModelTransform(getModelTransform()); // we want to include the scale as well
-    DependencyManager::get<GeometryCache>()->renderWireCubeInstance(args, batch, greenColor);
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    geometryCache->renderWireCubeInstance(args, batch, greenColor, geometryCache->getShapePipelinePointer(false, false, args->_renderMethod == Args::RenderMethod::FORWARD));
 
 #if WANT_EXTRA_DEBUGGING
     ModelPointer model;
diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h
index ee6e7d0b04..7f84b3ae62 100644
--- a/libraries/entities-renderer/src/RenderableModelEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h
@@ -154,7 +154,7 @@ protected:
 
     void setKey(bool didVisualGeometryRequestSucceed);
     virtual ItemKey getKey() override;
-    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
+    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
 
     virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
     virtual bool needsRenderUpdate() const override;
diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index ec1c890583..ceedf5be6e 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -283,12 +283,8 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
     } else if (!useMaterialPipeline(materials)) {
         // FIXME, support instanced multi-shape rendering using multidraw indirect
         outColor.a *= _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
-        render::ShapePipelinePointer pipeline;
-        if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) {
-            pipeline = outColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
-        } else {
-            pipeline = outColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
-        }
+        render::ShapePipelinePointer pipeline = geometryCache->getShapePipelinePointer(outColor.a < 1.0f, false,
+            renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD);
         if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) {
             geometryCache->renderWireShapeInstance(args, batch, geometryShape, outColor, pipeline);
         } else {
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
index d871df78d5..118ff4d6f1 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.cpp
@@ -31,6 +31,17 @@ const float LINE_SCALE_RATIO = 1.2f;
 TextEntityRenderer::TextEntityRenderer(const EntityItemPointer& entity) :
     Parent(entity),
     _textRenderer(TextRenderer3D::getInstance(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE / 2.0f)) {
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    if (geometryCache) {
+        _geometryID = geometryCache->allocateID();
+    }
+}
+
+TextEntityRenderer::~TextEntityRenderer() {
+    auto geometryCache = DependencyManager::get<GeometryCache>();
+    if (_geometryID && geometryCache) {
+        geometryCache->releaseID(_geometryID);
+    }
 }
 
 bool TextEntityRenderer::isTransparent() const {
@@ -59,17 +70,20 @@ ItemKey TextEntityRenderer::getKey() {
 }
 
 ShapeKey TextEntityRenderer::getShapeKey() {
-    auto builder = render::ShapeKey::Builder();
+    auto builder = render::ShapeKey::Builder().withoutCullFace();
     if (isTransparent()) {
         builder.withTranslucent();
     }
+    if (_unlit) {
+        builder.withUnlit();
+    }
     if (_primitiveMode == PrimitiveMode::LINES) {
         builder.withWireframe();
     }
     return builder.build();
 }
 
-uint32_t TextEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
+uint32_t TextEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) const {
     auto parentSubs = Parent::metaFetchMetaSubItems(subItems);
     if (Item::isValidID(_textRenderID)) {
         subItems.emplace_back(_textRenderID);
@@ -127,6 +141,10 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
         return true;
     }
 
+    if (_unlit != entity->getUnlit()) {
+        return true;
+    }
+
     if (_pulseProperties != entity->getPulseProperties()) {
         return true;
     }
@@ -160,6 +178,7 @@ void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
         _rightMargin = entity->getRightMargin();
         _topMargin = entity->getTopMargin();
         _bottomMargin = entity->getBottomMargin();
+        _unlit = entity->getUnlit();
         updateTextRenderItem();
     });
 }
@@ -193,17 +212,19 @@ void TextEntityRenderer::doRender(RenderArgs* args) {
     batch.setModelTransform(modelTransform);
 
     auto geometryCache = DependencyManager::get<GeometryCache>();
-    render::ShapePipelinePointer pipeline;
-    if (renderLayer == RenderLayer::WORLD && args->_renderMethod != Args::RenderMethod::FORWARD) {
-        pipeline = backgroundColor.a < 1.0f ? geometryCache->getTransparentShapePipeline() : geometryCache->getOpaqueShapePipeline();
-    } else {
-        pipeline = backgroundColor.a < 1.0f ? geometryCache->getForwardTransparentShapePipeline() : geometryCache->getForwardOpaqueShapePipeline();
-    }
-    if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) {
-        geometryCache->renderWireShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
-    } else {
-        geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
-    }
+    // FIXME: we want to use instanced rendering here, but if textAlpha < 1 and backgroundAlpha < 1, the transparency sorting will be wrong
+    //render::ShapePipelinePointer pipeline = geometryCache->getShapePipelinePointer(backgroundColor.a < 1.0f, _unlit,
+    //    renderLayer != RenderLayer::WORLD || args->_renderMethod == Args::RenderMethod::FORWARD);
+    //if (render::ShapeKey(args->_globalShapeKey).isWireframe() || primitiveMode == PrimitiveMode::LINES) {
+    //    geometryCache->renderWireShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
+    //} else {
+    //    geometryCache->renderSolidShapeInstance(args, batch, GeometryCache::Quad, backgroundColor, pipeline);
+    //}
+
+    geometryCache->renderQuad(batch, glm::vec2(-0.5), glm::vec2(0.5), backgroundColor, _geometryID);
+
+    const int TRIANBLES_PER_QUAD = 2;
+    args->_details._trianglesRendered += TRIANBLES_PER_QUAD;
 }
 
 QSizeF TextEntityRenderer::textSize(const QString& text) const {
@@ -224,7 +245,6 @@ void TextEntityRenderer::onAddToSceneTyped(const TypedEntityPointer& entity) {
     render::Transaction transaction;
     transaction.resetItem(_textRenderID, renderPayload);
     AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction);
-    updateTextRenderItem();
 }
 
 void TextEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
@@ -295,6 +315,9 @@ ShapeKey entities::TextPayload::getShapeKey() const {
         if (textRenderable->isTextTransparent()) {
             builder.withTranslucent();
         }
+        if (textRenderable->_unlit) {
+            builder.withUnlit();
+        }
         if (textRenderable->_primitiveMode == PrimitiveMode::LINES) {
             builder.withWireframe();
         }
@@ -355,7 +378,7 @@ void entities::TextPayload::render(RenderArgs* args) {
     batch.setModelTransform(modelTransform);
 
     glm::vec2 bounds = glm::vec2(dimensions.x - (leftMargin + rightMargin), dimensions.y - (topMargin + bottomMargin));
-    textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, forward);
+    textRenderer->draw(batch, leftMargin / scale, -topMargin / scale, text, textColor, bounds / scale, textRenderable->_unlit, forward);
 }
 
 namespace render {
diff --git a/libraries/entities-renderer/src/RenderableTextEntityItem.h b/libraries/entities-renderer/src/RenderableTextEntityItem.h
index d5e36a4622..c62851a876 100644
--- a/libraries/entities-renderer/src/RenderableTextEntityItem.h
+++ b/libraries/entities-renderer/src/RenderableTextEntityItem.h
@@ -26,6 +26,7 @@ class TextEntityRenderer : public TypedEntityRenderer<TextEntityItem> {
     using Pointer = std::shared_ptr<TextEntityRenderer>;
 public:
     TextEntityRenderer(const EntityItemPointer& entity);
+    ~TextEntityRenderer();
 
     QSizeF textSize(const QString& text) const;
 
@@ -35,7 +36,7 @@ protected:
     Item::Bound getBound() override;
     ShapeKey getShapeKey() override;
     ItemKey getKey() override;
-    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
+    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const override;
 
     void onAddToSceneTyped(const TypedEntityPointer& entity) override;
     void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override;
@@ -56,6 +57,7 @@ private:
     float _textAlpha;
     glm::vec3 _backgroundColor;
     float _backgroundAlpha;
+    bool _unlit;
 
     float _leftMargin;
     float _rightMargin;
@@ -65,6 +67,8 @@ private:
     BillboardMode _billboardMode;
     glm::vec3 _dimensions;
 
+    int _geometryID { 0 };
+
     std::shared_ptr<TextPayload> _textPayload;
     render::ItemID _textRenderID;
     void updateTextRenderItem() const;
diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf
index 6ea088751f..4eb27ca200 100644
--- a/libraries/entities-renderer/src/paintStroke.slf
+++ b/libraries/entities-renderer/src/paintStroke.slf
@@ -26,14 +26,14 @@ layout(location=3) in float _distanceFromCenter;
 
 void main(void) {
     vec4 texel = texture(_texture, _texCoord);
-    int frontCondition = 1 - 2 * int(gl_FrontFacing);
+    float frontCondition = 2.0 * float(gl_FrontFacing) - 1.0;
     vec3 color = _color.rgb * texel.rgb;
     float alpha = texel.a * _color.a;
 
     alpha *= mix(1.0, pow(1.0 - abs(_distanceFromCenter), 10.0), _polylineData.faceCameraGlow.y);
 
     packDeferredFragmentTranslucent(
-        float(frontCondition) * _normalWS,
+        _normalWS * frontCondition,
         alpha,
         color,
         DEFAULT_ROUGHNESS);
diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp
index 064fe2e3b1..878d220c22 100644
--- a/libraries/entities/src/EntityItemProperties.cpp
+++ b/libraries/entities/src/EntityItemProperties.cpp
@@ -527,6 +527,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
     CHECK_PROPERTY_CHANGE(PROP_RIGHT_MARGIN, rightMargin);
     CHECK_PROPERTY_CHANGE(PROP_TOP_MARGIN, topMargin);
     CHECK_PROPERTY_CHANGE(PROP_BOTTOM_MARGIN, bottomMargin);
+    CHECK_PROPERTY_CHANGE(PROP_UNLIT, unlit);
 
     // Zone
     changedProperties += _keyLight.getChangedProperties();
@@ -1284,6 +1285,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
  * @property {number} rightMargin=0.0 - The right margin, in meters.
  * @property {number} topMargin=0.0 - The top margin, in meters.
  * @property {number} bottomMargin=0.0 - The bottom margin, in meters.
+ * @property {boolean} unlit=false - <code>true</code> if the entity should be unaffected by lighting.  Otherwise, the text
+ *     is lit by the keylight and local lights.
  * @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera.
  * @property {boolean} faceCamera - <code>true</code> if <code>billboardMode</code> is <code>"yaw"</code>, <code>false</code> 
  *     if it isn't. Setting this property to <code>false</code> sets the <code>billboardMode</code> to <code>"none"</code>.
@@ -1723,6 +1726,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RIGHT_MARGIN, rightMargin);
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TOP_MARGIN, topMargin);
         COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BOTTOM_MARGIN, bottomMargin);
+        COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_UNLIT, unlit);
     }
 
     // Zones only
@@ -2098,6 +2102,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
     COPY_PROPERTY_FROM_QSCRIPTVALUE(rightMargin, float, setRightMargin);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(topMargin, float, setTopMargin);
     COPY_PROPERTY_FROM_QSCRIPTVALUE(bottomMargin, float, setBottomMargin);
+    COPY_PROPERTY_FROM_QSCRIPTVALUE(unlit, bool, setUnlit);
 
     // Zone
     _keyLight.copyFromScriptValue(object, _defaultSettings);
@@ -2381,6 +2386,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
     COPY_PROPERTY_IF_CHANGED(rightMargin);
     COPY_PROPERTY_IF_CHANGED(topMargin);
     COPY_PROPERTY_IF_CHANGED(bottomMargin);
+    COPY_PROPERTY_IF_CHANGED(unlit);
 
     // Zone
     _keyLight.merge(other._keyLight);
@@ -2739,6 +2745,7 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
         ADD_PROPERTY_TO_MAP(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float);
         ADD_PROPERTY_TO_MAP(PROP_TOP_MARGIN, TopMargin, topMargin, float);
         ADD_PROPERTY_TO_MAP(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float);
+        ADD_PROPERTY_TO_MAP(PROP_UNLIT, Unlit, unlit, bool);
 
         // Zone
         { // Keylight
@@ -3168,6 +3175,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
                 APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, properties.getRightMargin());
                 APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, properties.getTopMargin());
                 APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, properties.getBottomMargin());
+                APPEND_ENTITY_PROPERTY(PROP_UNLIT, properties.getUnlit());
             }
 
             if (properties.getType() == EntityTypes::Zone) {
@@ -3646,6 +3654,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RIGHT_MARGIN, float, setRightMargin);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TOP_MARGIN, float, setTopMargin);
         READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BOTTOM_MARGIN, float, setBottomMargin);
+        READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_UNLIT, bool, setUnlit);
     }
 
     if (properties.getType() == EntityTypes::Zone) {
@@ -4038,6 +4047,7 @@ void EntityItemProperties::markAllChanged() {
     _rightMarginChanged = true;
     _topMarginChanged = true;
     _bottomMarginChanged = true;
+    _unlitChanged = true;
 
     // Zone
     _keyLight.markAllChanged();
@@ -4627,6 +4637,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
     if (bottomMarginChanged()) {
         out += "bottomMargin";
     }
+    if (unlitChanged()) {
+        out += "unlit";
+    }
 
     // Zone
     getKeyLight().listChangedProperties(out);
diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h
index 619af97ee5..456ee3cdec 100644
--- a/libraries/entities/src/EntityItemProperties.h
+++ b/libraries/entities/src/EntityItemProperties.h
@@ -314,6 +314,7 @@ public:
     DEFINE_PROPERTY_REF(PROP_RIGHT_MARGIN, RightMargin, rightMargin, float, TextEntityItem::DEFAULT_MARGIN);
     DEFINE_PROPERTY_REF(PROP_TOP_MARGIN, TopMargin, topMargin, float, TextEntityItem::DEFAULT_MARGIN);
     DEFINE_PROPERTY_REF(PROP_BOTTOM_MARGIN, BottomMargin, bottomMargin, float, TextEntityItem::DEFAULT_MARGIN);
+    DEFINE_PROPERTY_REF(PROP_UNLIT, Unlit, unlit, bool, false);
 
     // Zone
     DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup);
diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h
index e86cccb997..f98ee913a2 100644
--- a/libraries/entities/src/EntityPropertyFlags.h
+++ b/libraries/entities/src/EntityPropertyFlags.h
@@ -241,6 +241,7 @@ enum EntityPropertyList {
     PROP_RIGHT_MARGIN = PROP_DERIVED_7,
     PROP_TOP_MARGIN = PROP_DERIVED_8,
     PROP_BOTTOM_MARGIN = PROP_DERIVED_9,
+    PROP_UNLIT = PROP_DERIVED_10,
 
     // Zone
     // Keylight
diff --git a/libraries/entities/src/TextEntityItem.cpp b/libraries/entities/src/TextEntityItem.cpp
index 5dff645c89..08200084f4 100644
--- a/libraries/entities/src/TextEntityItem.cpp
+++ b/libraries/entities/src/TextEntityItem.cpp
@@ -64,6 +64,7 @@ EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& de
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(rightMargin, getRightMargin);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(topMargin, getTopMargin);
     COPY_ENTITY_PROPERTY_TO_PROPERTIES(bottomMargin, getBottomMargin);
+    COPY_ENTITY_PROPERTY_TO_PROPERTIES(unlit, getUnlit);
     return properties;
 }
 
@@ -87,6 +88,7 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(rightMargin, setRightMargin);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(topMargin, setTopMargin);
     SET_ENTITY_PROPERTY_FROM_PROPERTIES(bottomMargin, setBottomMargin);
+    SET_ENTITY_PROPERTY_FROM_PROPERTIES(unlit, setUnlit);
 
     if (somethingChanged) {
         bool wantDebug = false;
@@ -129,7 +131,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
     READ_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, float, setRightMargin);
     READ_ENTITY_PROPERTY(PROP_TOP_MARGIN, float, setTopMargin);
     READ_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, float, setBottomMargin);
-    
+    READ_ENTITY_PROPERTY(PROP_UNLIT, bool, setUnlit);
+
     return bytesRead;
 }
 
@@ -149,6 +152,7 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
     requestedProperties += PROP_RIGHT_MARGIN;
     requestedProperties += PROP_TOP_MARGIN;
     requestedProperties += PROP_BOTTOM_MARGIN;
+    requestedProperties += PROP_UNLIT;
 
     return requestedProperties;
 }
@@ -179,6 +183,7 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
     APPEND_ENTITY_PROPERTY(PROP_RIGHT_MARGIN, getRightMargin());
     APPEND_ENTITY_PROPERTY(PROP_TOP_MARGIN, getTopMargin());
     APPEND_ENTITY_PROPERTY(PROP_BOTTOM_MARGIN, getBottomMargin());
+    APPEND_ENTITY_PROPERTY(PROP_UNLIT, getUnlit());
 }
 
 glm::vec3 TextEntityItem::getRaycastDimensions() const {
@@ -382,6 +387,18 @@ float TextEntityItem::getBottomMargin() const {
     });
 }
 
+void TextEntityItem::setUnlit(bool value) {
+    withWriteLock([&] {
+        _unlit = value;
+    });
+}
+
+bool TextEntityItem::getUnlit() const {
+    return resultWithReadLock<bool>([&] {
+        return _unlit;
+    });
+}
+
 PulsePropertyGroup TextEntityItem::getPulseProperties() const {
     return resultWithReadLock<PulsePropertyGroup>([&] {
         return _pulseProperties;
diff --git a/libraries/entities/src/TextEntityItem.h b/libraries/entities/src/TextEntityItem.h
index 1ead9d3e15..a962482cde 100644
--- a/libraries/entities/src/TextEntityItem.h
+++ b/libraries/entities/src/TextEntityItem.h
@@ -97,6 +97,9 @@ public:
     float getBottomMargin() const;
     void setBottomMargin(float value);
 
+    bool getUnlit() const;
+    void setUnlit(bool value);
+
     PulsePropertyGroup getPulseProperties() const;
 
 private:
@@ -113,6 +116,7 @@ private:
     float _rightMargin;
     float _topMargin;
     float _bottomMargin;
+    bool _unlit;
 };
 
 #endif // hifi_TextEntityItem_h
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 6230b8b11e..736296a2d6 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -271,6 +271,7 @@ enum class EntityVersion : PacketVersion {
     ParticleShapeType,
     ParticleShapeTypeDeadlockFix,
     PrivateUserData,
+    TextUnlit,
 
     // Add new versions above here
     NUM_PACKET_TYPE,
diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp
index 88cca1693b..a2040b02ba 100644
--- a/libraries/render-utils/src/GeometryCache.cpp
+++ b/libraries/render-utils/src/GeometryCache.cpp
@@ -722,42 +722,12 @@ gpu::ShaderPointer GeometryCache::_forwardUnlitShader;
 gpu::ShaderPointer GeometryCache::_forwardSimpleFadeShader;
 gpu::ShaderPointer GeometryCache::_forwardUnlitFadeShader;
 
-render::ShapePipelinePointer GeometryCache::_simpleOpaquePipeline;
-render::ShapePipelinePointer GeometryCache::_simpleTransparentPipeline;
-render::ShapePipelinePointer GeometryCache::_forwardSimpleOpaquePipeline;
-render::ShapePipelinePointer GeometryCache::_forwardSimpleTransparentPipeline;
-render::ShapePipelinePointer GeometryCache::_simpleOpaqueFadePipeline;
-render::ShapePipelinePointer GeometryCache::_simpleTransparentFadePipeline;
-render::ShapePipelinePointer GeometryCache::_simpleWirePipeline;
-
-uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0;
-
-render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) {
-    initializeShapePipelines();
-
-    if (key.isWireframe()) {
-        return _simpleWirePipeline;
-    }
-
-    if (key.isFaded()) {
-        if (key.isTranslucent()) {
-            return _simpleTransparentFadePipeline;
-        } else {
-            return _simpleOpaqueFadePipeline;
-        }
-    } else {
-        if (key.isTranslucent()) {
-            return _simpleTransparentPipeline;
-        } else {
-            return _simpleOpaquePipeline;
-        }
-    }
-}
+std::map<std::tuple<bool, bool, bool>, render::ShapePipelinePointer> GeometryCache::_shapePipelines;
 
 GeometryCache::GeometryCache() :
 _nextID(0) {
     // Let's register its special shapePipeline factory:
-    registerShapePipeline();
+    initializeShapePipelines();
     buildShapes();
 }
 
@@ -799,16 +769,14 @@ void GeometryCache::releaseID(int id) {
 }
 
 void GeometryCache::initializeShapePipelines() {
-    if (!_simpleOpaquePipeline) {
-        _simpleOpaquePipeline = getShapePipeline(false, false, true, false);
-        _simpleTransparentPipeline = getShapePipeline(false, true, true, false);
-        _forwardSimpleOpaquePipeline = getShapePipeline(false, false, true, false, false, true);
-        _forwardSimpleTransparentPipeline = getShapePipeline(false, true, true, false, false, true);
-
-        // FIXME: these need forward pipelines
-        _simpleOpaqueFadePipeline = getFadingShapePipeline(false, false, false, false, false);
-        _simpleTransparentFadePipeline = getFadingShapePipeline(false, true, false, false, false);
-        _simpleWirePipeline = getShapePipeline(false, false, true, true);
+    if (_shapePipelines.empty()) {
+        const int NUM_PIPELINES = 8;
+        for (int i = 0; i < NUM_PIPELINES; ++i) {
+            bool transparent = i & 1;
+            bool unlit = i & 2;
+            bool forward = i & 4;
+            _shapePipelines[std::make_tuple(transparent, unlit, forward)] = getShapePipeline(false, transparent, true, unlit, false, forward);
+        }
     }
 }
 
@@ -1027,7 +995,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec2>& points, con
     int* colorData = new int[details.vertices];
     int* colorDataAt = colorData;
 
-    const glm::vec3 NORMAL(0.0f, 1.0f, 0.0f);
+    const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f);
     auto pointCount = points.size();
     auto colorCount = colors.size();
     int compactColor = 0;
@@ -1105,7 +1073,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
     int* colorData = new int[details.vertices];
     int* colorDataAt = colorData;
 
-    const glm::vec3 NORMAL(0.0f, 1.0f, 0.0f);
+    const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f);
     auto pointCount = points.size();
     auto colorCount = colors.size();
     for (auto i = 0; i < pointCount; i++) {
@@ -1193,7 +1161,7 @@ void GeometryCache::updateVertices(int id, const QVector<glm::vec3>& points, con
     int* colorData = new int[details.vertices];
     int* colorDataAt = colorData;
 
-    const glm::vec3 NORMAL(0.0f, 1.0f, 0.0f);
+    const glm::vec3 NORMAL(0.0f, -1.0f, 0.0f);
     for (int i = 0; i < points.size(); i++) {
         glm::vec3 point = points[i];
         glm::vec2 texCoord = texCoords[i];
@@ -2281,8 +2249,7 @@ void renderInstances(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color
 
         if (isWire) {
             DependencyManager::get<GeometryCache>()->renderWireShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]);
-        }
-        else {
+        } else {
             DependencyManager::get<GeometryCache>()->renderShapeInstances(batch, shape, data.count(), data.buffers[INSTANCE_COLOR_BUFFER]);
         }
     });
diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h
index a42b059a8c..687e91f3b3 100644
--- a/libraries/render-utils/src/GeometryCache.h
+++ b/libraries/render-utils/src/GeometryCache.h
@@ -157,14 +157,6 @@ public:
 
     static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector<glm::vec3> &outPointList);
 
-    static uint8_t CUSTOM_PIPELINE_NUMBER;
-    static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch);
-    static void registerShapePipeline() {
-        if (!CUSTOM_PIPELINE_NUMBER) {
-            CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory);
-        }
-    }
-
     int allocateID() { return _nextID++; }
     void releaseID(int id);
     static const int UNKNOWN_ID;
@@ -180,11 +172,7 @@ public:
     gpu::PipelinePointer getWebBrowserProgram(bool transparent);
 
     static void initializeShapePipelines();
-
-    render::ShapePipelinePointer getOpaqueShapePipeline() { assert(_simpleOpaquePipeline != nullptr); return _simpleOpaquePipeline; }
-    render::ShapePipelinePointer getTransparentShapePipeline() { assert(_simpleTransparentPipeline != nullptr); return _simpleTransparentPipeline; }
-    render::ShapePipelinePointer getForwardOpaqueShapePipeline() { assert(_forwardSimpleOpaquePipeline != nullptr); return _forwardSimpleOpaquePipeline; }
-    render::ShapePipelinePointer getForwardTransparentShapePipeline() { assert(_forwardSimpleTransparentPipeline != nullptr); return _forwardSimpleTransparentPipeline; }
+    render::ShapePipelinePointer getShapePipelinePointer(bool transparent, bool unlit, bool forward) { return _shapePipelines[std::make_tuple(transparent, unlit, forward)]; }
 
     // Static (instanced) geometry
     void renderShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer);
@@ -195,17 +183,17 @@ public:
     void renderWireFadeShapeInstances(gpu::Batch& batch, Shape shape, size_t count, gpu::BufferPointer& colorBuffer,
         gpu::BufferPointer& fadeBuffer1, gpu::BufferPointer& fadeBuffer2, gpu::BufferPointer& fadeBuffer3);
 
-    void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
+    void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color,
+                                    const render::ShapePipelinePointer& pipeline);
     void renderSolidShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
+                                    const render::ShapePipelinePointer& pipeline) {
         renderSolidShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline);
     }
 
-    void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color = glm::vec4(1),
-        const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
+    void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec4& color,
+        const render::ShapePipelinePointer& pipeline);
     void renderWireShapeInstance(RenderArgs* args, gpu::Batch& batch, Shape shape, const glm::vec3& color,
-        const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
+        const render::ShapePipelinePointer& pipeline) {
         renderWireShapeInstance(args, batch, shape, glm::vec4(color, 1.0f), pipeline);
     }
 
@@ -217,33 +205,33 @@ public:
         const render::ShapePipelinePointer& pipeline);
 
     void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
+                                    const render::ShapePipelinePointer& pipeline);
     void renderSolidSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
+                                    const render::ShapePipelinePointer& pipeline) {
         renderSolidSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
     }
-    
+
     void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline);
+                                    const render::ShapePipelinePointer& pipeline);
     void renderWireSphereInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) {
+                                    const render::ShapePipelinePointer& pipeline) {
         renderWireSphereInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
     }
-    
+
     void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline);
+                                    const render::ShapePipelinePointer& pipeline);
     void renderSolidCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleOpaquePipeline) {
+                                    const render::ShapePipelinePointer& pipeline) {
         renderSolidCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
     }
-    
+
     void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec4& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline);
+                                    const render::ShapePipelinePointer& pipeline);
     void renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, const glm::vec3& color,
-                                    const render::ShapePipelinePointer& pipeline = _simpleWirePipeline) {
+                                    const render::ShapePipelinePointer& pipeline) {
         renderWireCubeInstance(args, batch, glm::vec4(color, 1.0f), pipeline);
     }
-    
+
     // Dynamic geometry
     void renderShape(gpu::Batch& batch, Shape shape);
     void renderWireShape(gpu::Batch& batch, Shape shape);
@@ -467,14 +455,7 @@ private:
     static gpu::ShaderPointer _forwardSimpleFadeShader;
     static gpu::ShaderPointer _forwardUnlitFadeShader;
 
-    static render::ShapePipelinePointer _simpleOpaquePipeline;
-    static render::ShapePipelinePointer _simpleTransparentPipeline;
-    static render::ShapePipelinePointer _forwardSimpleOpaquePipeline;
-    static render::ShapePipelinePointer _forwardSimpleTransparentPipeline;
-    static render::ShapePipelinePointer _simpleOpaqueFadePipeline;
-    static render::ShapePipelinePointer _simpleTransparentFadePipeline;
-    static render::ShapePipelinePointer _simpleWirePipeline;
-
+    static std::map<std::tuple<bool, bool, bool>, render::ShapePipelinePointer> _shapePipelines;
     static QHash<SimpleProgramKey, gpu::PipelinePointer> _simplePrograms;
 
     gpu::ShaderPointer _simpleOpaqueWebBrowserShader;
diff --git a/libraries/render-utils/src/TextRenderer3D.cpp b/libraries/render-utils/src/TextRenderer3D.cpp
index d3ea20273e..8c514df91d 100644
--- a/libraries/render-utils/src/TextRenderer3D.cpp
+++ b/libraries/render-utils/src/TextRenderer3D.cpp
@@ -67,11 +67,11 @@ float TextRenderer3D::getFontSize() const {
 }
 
 void TextRenderer3D::draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
-                         const glm::vec2& bounds, bool forward) {
+                         const glm::vec2& bounds, bool unlit, bool forward) {
     // The font does all the OpenGL work
     if (_font) {
         _color = color;
-        _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, forward);
+        _font->drawString(batch, _drawInfo, str, _color, _effectType, { x, y }, bounds, unlit, forward);
     }
 }
 
diff --git a/libraries/render-utils/src/TextRenderer3D.h b/libraries/render-utils/src/TextRenderer3D.h
index ce4dd9f9e5..8118aa883c 100644
--- a/libraries/render-utils/src/TextRenderer3D.h
+++ b/libraries/render-utils/src/TextRenderer3D.h
@@ -39,7 +39,7 @@ public:
     float getFontSize() const; // Pixel size
     
     void draw(gpu::Batch& batch, float x, float y, const QString& str, const glm::vec4& color,
-              const glm::vec2& bounds, bool forward);
+              const glm::vec2& bounds, bool unlit, bool forward);
 
 private:
     TextRenderer3D(const char* family, float pointSize, int weight = -1, bool italic = false,
diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf
index 677c369033..50e3cdc511 100644
--- a/libraries/render-utils/src/forward_simple.slf
+++ b/libraries/render-utils/src/forward_simple.slf
@@ -52,7 +52,7 @@ void main(void) {
         1.0,
         DEFAULT_OCCLUSION,
         fragPosition,
-        normal,
+        normal * (2.0 * float(gl_FrontFacing) - 1.0),
         diffuse,
         DEFAULT_FRESNEL,
         length(specular),
diff --git a/libraries/render-utils/src/forward_simple_textured.slf b/libraries/render-utils/src/forward_simple_textured.slf
index 373ab13d1a..79e1bf1dbc 100644
--- a/libraries/render-utils/src/forward_simple_textured.slf
+++ b/libraries/render-utils/src/forward_simple_textured.slf
@@ -49,7 +49,7 @@ void main(void) {
         1.0,
         DEFAULT_OCCLUSION,
         fragPosition,
-        normalize(_normalWS),
+        normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
         albedo,
         fresnel,
         metallic,
diff --git a/libraries/render-utils/src/forward_simple_textured_transparent.slf b/libraries/render-utils/src/forward_simple_textured_transparent.slf
index 1b5047507b..6cff37ba9d 100644
--- a/libraries/render-utils/src/forward_simple_textured_transparent.slf
+++ b/libraries/render-utils/src/forward_simple_textured_transparent.slf
@@ -50,7 +50,7 @@ void main(void) {
         1.0,
         DEFAULT_OCCLUSION,
         fragPosition,
-        normalize(_normalWS),
+        normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
         albedo,
         fresnel,
         metallic,
diff --git a/libraries/render-utils/src/render-utils/forward_sdf_text3D.slp b/libraries/render-utils/src/render-utils/sdf_text3D_forward.slp
similarity index 100%
rename from libraries/render-utils/src/render-utils/forward_sdf_text3D.slp
rename to libraries/render-utils/src/render-utils/sdf_text3D_forward.slp
diff --git a/libraries/render-utils/src/render-utils/sdf_text3D_translucent.slp b/libraries/render-utils/src/render-utils/sdf_text3D_translucent.slp
new file mode 100644
index 0000000000..52dfbe4f9c
--- /dev/null
+++ b/libraries/render-utils/src/render-utils/sdf_text3D_translucent.slp
@@ -0,0 +1 @@
+VERTEX sdf_text3D
\ No newline at end of file
diff --git a/libraries/render-utils/src/render-utils/sdf_text3D_translucent_unlit.slp b/libraries/render-utils/src/render-utils/sdf_text3D_translucent_unlit.slp
new file mode 100644
index 0000000000..52dfbe4f9c
--- /dev/null
+++ b/libraries/render-utils/src/render-utils/sdf_text3D_translucent_unlit.slp
@@ -0,0 +1 @@
+VERTEX sdf_text3D
\ No newline at end of file
diff --git a/libraries/render-utils/src/render-utils/sdf_text3D_transparent.slp b/libraries/render-utils/src/render-utils/sdf_text3D_transparent.slp
deleted file mode 100644
index 3eea3a0da0..0000000000
--- a/libraries/render-utils/src/render-utils/sdf_text3D_transparent.slp
+++ /dev/null
@@ -1 +0,0 @@
-VERTEX sdf_text3D
diff --git a/libraries/render-utils/src/render-utils/sdf_text3D_unlit.slp b/libraries/render-utils/src/render-utils/sdf_text3D_unlit.slp
new file mode 100644
index 0000000000..52dfbe4f9c
--- /dev/null
+++ b/libraries/render-utils/src/render-utils/sdf_text3D_unlit.slp
@@ -0,0 +1 @@
+VERTEX sdf_text3D
\ No newline at end of file
diff --git a/libraries/render-utils/src/forward_sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D_forward.slf
similarity index 97%
rename from libraries/render-utils/src/forward_sdf_text3D.slf
rename to libraries/render-utils/src/sdf_text3D_forward.slf
index 09b10c0c42..02ad49fc43 100644
--- a/libraries/render-utils/src/forward_sdf_text3D.slf
+++ b/libraries/render-utils/src/sdf_text3D_forward.slf
@@ -1,7 +1,7 @@
 <@include gpu/Config.slh@>
 <$VERSION_HEADER$>
 //  Generated on <$_SCRIBE_DATE$>
-//  sdf_text3D_transparent.frag
+//  sdf_text3D_forward.frag
 //  fragment shader
 //
 //  Created by Bradley Austin Davis on 2015-02-04
@@ -53,5 +53,5 @@ void main() {
         DEFAULT_FRESNEL,
         DEFAULT_METALLIC,
         DEFAULT_ROUGHNESS),
-        1.0);
+        alpha);
 }
\ No newline at end of file
diff --git a/libraries/render-utils/src/sdf_text3D_transparent.slf b/libraries/render-utils/src/sdf_text3D_translucent.slf
similarity index 92%
rename from libraries/render-utils/src/sdf_text3D_transparent.slf
rename to libraries/render-utils/src/sdf_text3D_translucent.slf
index c4a80091de..bf72345f38 100644
--- a/libraries/render-utils/src/sdf_text3D_transparent.slf
+++ b/libraries/render-utils/src/sdf_text3D_translucent.slf
@@ -1,7 +1,7 @@
 <@include gpu/Config.slh@>
 <$VERSION_HEADER$>
 //  Generated on <$_SCRIBE_DATE$>
-//  sdf_text3D_transparent.frag
+//  sdf_text3D_translucent.frag
 //  fragment shader
 //
 //  Created by Bradley Austin Davis on 2015-02-04
@@ -12,8 +12,8 @@
 
 <@include DefaultMaterials.slh@>
 
-<@include ForwardGlobalLight.slh@>
-<$declareEvalGlobalLightingAlphaBlended()$>
+<@include DeferredGlobalLight.slh@>
+<$declareEvalGlobalLightingAlphaBlendedWithHaze()$>
 
 <@include gpu/Transform.slh@>
 <$declareStandardCameraTransform()$>
diff --git a/libraries/render-utils/src/sdf_text3D_translucent_unlit.slf b/libraries/render-utils/src/sdf_text3D_translucent_unlit.slf
new file mode 100644
index 0000000000..43742b0c53
--- /dev/null
+++ b/libraries/render-utils/src/sdf_text3D_translucent_unlit.slf
@@ -0,0 +1,38 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+//  Generated on <$_SCRIBE_DATE$>
+//  sdf_text3D_translucent.frag
+//  fragment shader
+//
+//  Created by Bradley Austin Davis on 2015-02-04
+//  Based on fragment shader code from 
+//  https://github.com/paulhoux/Cinder-Samples/blob/master/TextRendering/include/text/Text.cpp 
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+<@include DefaultMaterials.slh@>
+
+<@include LightingModel.slh@>
+
+<@include render-utils/ShaderConstants.h@>
+
+<@include sdf_text3D.slh@>
+<$declareEvalSDFSuperSampled()$>
+
+layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color;
+layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
+#define _texCoord0 _texCoord01.xy
+#define _texCoord1 _texCoord01.zw
+
+layout(location=0) out vec4 _fragColor0;
+
+void main() {
+    float a = evalSDFSuperSampled(_texCoord0);
+
+    float alpha = a * _color.a;
+    if (alpha <= 0.0) {
+        discard;
+    }
+
+    _fragColor0 = vec4(_color.rgb * isUnlitEnabled(), alpha);
+}
\ No newline at end of file
diff --git a/libraries/render-utils/src/sdf_text3D_unlit.slf b/libraries/render-utils/src/sdf_text3D_unlit.slf
new file mode 100644
index 0000000000..db8366518e
--- /dev/null
+++ b/libraries/render-utils/src/sdf_text3D_unlit.slf
@@ -0,0 +1,32 @@
+<@include gpu/Config.slh@>
+<$VERSION_HEADER$>
+//  Generated on <$_SCRIBE_DATE$>
+//  sdf_text3D_unlit.frag
+//  fragment shader
+//
+//  Created by Bradley Austin Davis on 2015-02-04
+//  Based on fragment shader code from 
+//  https://github.com/paulhoux/Cinder-Samples/blob/master/TextRendering/include/text/Text.cpp 
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+<@include DeferredBufferWrite.slh@>
+<@include render-utils/ShaderConstants.h@>
+
+<@include sdf_text3D.slh@>
+<$declareEvalSDFSuperSampled()$>
+
+layout(location=RENDER_UTILS_ATTR_NORMAL_WS) in vec3 _normalWS;
+layout(location=RENDER_UTILS_ATTR_COLOR) in vec4 _color;
+layout(location=RENDER_UTILS_ATTR_TEXCOORD01) in vec4 _texCoord01;
+#define _texCoord0 _texCoord01.xy
+#define _texCoord1 _texCoord01.zw
+
+void main() {
+    float a = evalSDFSuperSampled(_texCoord0);
+
+    packDeferredFragmentUnlit(
+        normalize(_normalWS),
+        a,
+        _color.rgb);
+}
\ No newline at end of file
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index 469c0976aa..884c7468bb 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -60,7 +60,7 @@ float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition pro
 
 #line 2030
 void main(void) {
-    vec3 normal = normalize(_normalWS.xyz);
+    vec3 normal = normalize(_normalWS.xyz) * (2.0 * float(gl_FrontFacing) - 1.0);
     vec3 diffuse = _color.rgb;
     float roughness = DEFAULT_ROUGHNESS;
     float metallic = DEFAULT_METALLIC;
diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf
index dbc49fcb5d..c79ff8cea3 100644
--- a/libraries/render-utils/src/simple_textured.slf
+++ b/libraries/render-utils/src/simple_textured.slf
@@ -33,7 +33,7 @@ void main(void) {
     texel.rgb *= _color.rgb;
 
     packDeferredFragment(
-        normalize(_normalWS),
+        normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
         1.0,
         texel.rgb,
         DEFAULT_ROUGHNESS,
diff --git a/libraries/render-utils/src/simple_textured_fade.slf b/libraries/render-utils/src/simple_textured_fade.slf
index 5a9eb0688e..5fa4a70fad 100644
--- a/libraries/render-utils/src/simple_textured_fade.slf
+++ b/libraries/render-utils/src/simple_textured_fade.slf
@@ -48,13 +48,13 @@ void main(void) {
     const float ALPHA_THRESHOLD = 0.999;
     if (texel.a < ALPHA_THRESHOLD) {
         packDeferredFragmentTranslucent(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             texel.a,
             texel.rgb + fadeEmissive,
             DEFAULT_ROUGHNESS);
     } else {
         packDeferredFragment(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             1.0,
             texel.rgb,
             DEFAULT_ROUGHNESS,
diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf
index 475428f0ae..7aa1970aee 100644
--- a/libraries/render-utils/src/simple_textured_unlit.slf
+++ b/libraries/render-utils/src/simple_textured_unlit.slf
@@ -36,13 +36,13 @@ void main(void) {
     const float ALPHA_THRESHOLD = 0.999;
     if (texel.a < ALPHA_THRESHOLD) {
        packDeferredFragmentTranslucent(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             texel.a,
             texel.rgb,
             DEFAULT_ROUGHNESS);
     } else {
         packDeferredFragmentUnlit(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             1.0,
             texel.rgb);
     }
diff --git a/libraries/render-utils/src/simple_textured_unlit_fade.slf b/libraries/render-utils/src/simple_textured_unlit_fade.slf
index d0ba4c13fe..079a046757 100644
--- a/libraries/render-utils/src/simple_textured_unlit_fade.slf
+++ b/libraries/render-utils/src/simple_textured_unlit_fade.slf
@@ -48,13 +48,13 @@ void main(void) {
     const float ALPHA_THRESHOLD = 0.999;
     if (texel.a < ALPHA_THRESHOLD) {
        packDeferredFragmentTranslucent(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             texel.a,
             texel.rgb + fadeEmissive,
             DEFAULT_ROUGHNESS);
     } else {
         packDeferredFragmentUnlit(
-            normalize(_normalWS),
+            normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
             1.0,
             texel.rgb + fadeEmissive);
     }
diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf
index 6d8348f50c..4fc7da60ea 100644
--- a/libraries/render-utils/src/simple_transparent.slf
+++ b/libraries/render-utils/src/simple_transparent.slf
@@ -65,7 +65,7 @@ float getProceduralFragmentWithPosition(inout ProceduralFragmentWithPosition pro
 
 #line 2030
 void main(void) {
-    vec3 normal = normalize(_normalWS.xyz);
+    vec3 normal = normalize(_normalWS.xyz) * (2.0 * float(gl_FrontFacing) - 1.0);
     vec3 diffuse = _color.rgb;
     float alpha = _color.a;
     float occlusion = DEFAULT_OCCLUSION;
diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf
index 9f8a88c7c2..64e052b376 100644
--- a/libraries/render-utils/src/simple_transparent_textured.slf
+++ b/libraries/render-utils/src/simple_transparent_textured.slf
@@ -50,7 +50,7 @@ void main(void) {
         1.0,
         DEFAULT_OCCLUSION,
         fragPosition,
-        normalize(_normalWS),
+        normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0),
         albedo,
         fresnel,
         metallic,
diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf
index d401989f90..44e849be69 100644
--- a/libraries/render-utils/src/simple_transparent_textured_fade.slf
+++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf
@@ -52,7 +52,7 @@ void main(void) {
     texel.a *= abs(_color.a);
 
     vec3 fragPosition = _positionES.xyz;
-    vec3 fragNormal = normalize(_normalWS);
+    vec3 fragNormal = normalize(_normalWS) * (2.0 * float(gl_FrontFacing) - 1.0);
 
     TransformCamera cam = getTransformCamera();
 
diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp
index c69db5e055..eee6a7daea 100644
--- a/libraries/render-utils/src/text/Font.cpp
+++ b/libraries/render-utils/src/text/Font.cpp
@@ -15,9 +15,7 @@
 
 static std::mutex fontMutex;
 
-gpu::PipelinePointer Font::_deferredPipeline;
-gpu::PipelinePointer Font::_forwardPipeline;
-gpu::PipelinePointer Font::_transparentPipeline;
+std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> Font::_pipelines;
 gpu::Stream::FormatPointer Font::_format;
 
 struct TextureVertex {
@@ -221,31 +219,28 @@ void Font::read(QIODevice& in) {
 }
 
 void Font::setupGPU() {
-    if (!_deferredPipeline) {
-        // Setup render pipeline
-        {
-            {
-                auto state = std::make_shared<gpu::State>();
-                state->setCullMode(gpu::State::CULL_BACK);
-                state->setDepthTest(true, true, gpu::LESS_EQUAL);
-                state->setBlendFunction(false,
-                    gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-                    gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
-                PrepareStencil::testMaskDrawShape(*state);
-                _deferredPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D), state);
-                _forwardPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::forward_sdf_text3D), state);
-            }
+    if (_pipelines.empty()) {
+        using namespace shader::render_utils::program;
 
-            {
-                auto state = std::make_shared<gpu::State>();
-                state->setCullMode(gpu::State::CULL_BACK);
-                state->setDepthTest(true, true, gpu::LESS_EQUAL);
-                state->setBlendFunction(true,
-                    gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
-                    gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+        static const std::vector<std::tuple<bool, bool, bool, uint32_t>> keys = {
+            std::make_tuple(false, false, false, sdf_text3D), std::make_tuple(true, false, false, sdf_text3D_translucent),
+            std::make_tuple(false, true, false, sdf_text3D_unlit), std::make_tuple(true, true, false, sdf_text3D_translucent_unlit),
+            std::make_tuple(false, false, true, sdf_text3D_forward), std::make_tuple(true, false, true, sdf_text3D_forward/*sdf_text3D_translucent_forward*/),
+            std::make_tuple(false, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_unlit_forward*/), std::make_tuple(true, true, true, sdf_text3D_translucent_unlit/*sdf_text3D_translucent_unlit_forward*/)
+        };
+        for (auto& key : keys) {
+            auto state = std::make_shared<gpu::State>();
+            state->setCullMode(gpu::State::CULL_BACK);
+            state->setDepthTest(true, true, gpu::LESS_EQUAL);
+            state->setBlendFunction(std::get<0>(key),
+                gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+                gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE);
+            if (std::get<0>(key)) {
                 PrepareStencil::testMask(*state);
-                _transparentPipeline = gpu::Pipeline::create(gpu::Shader::createProgram(shader::render_utils::program::sdf_text3D_transparent), state);
+            } else {
+                PrepareStencil::testMaskDrawShape(*state);
             }
+            _pipelines[std::make_tuple(std::get<0>(key), std::get<1>(key), std::get<2>(key))] = gpu::Pipeline::create(gpu::Shader::createProgram(std::get<3>(key)), state);
         }
 
         // Sanity checks
@@ -349,7 +344,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
 }
 
 void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString& str, const glm::vec4& color,
-                      EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool forward) {
+                      EffectType effectType, const glm::vec2& origin, const glm::vec2& bounds, bool unlit, bool forward) {
     if (str == "") {
         return;
     }
@@ -376,7 +371,7 @@ void Font::drawString(gpu::Batch& batch, Font::DrawInfo& drawInfo, const QString
     }
     // need the gamma corrected color here
 
-    batch.setPipeline(color.a < 1.0f ? _transparentPipeline : (forward ? _forwardPipeline : _deferredPipeline));
+    batch.setPipeline(_pipelines[std::make_tuple(color.a < 1.0f, unlit, forward)]);
     batch.setInputFormat(_format);
     batch.setInputBuffer(0, drawInfo.verticesBuffer, 0, _format->getChannels().at(0)._stride);
     batch.setResourceTexture(render_utils::slot::texture::TextFont, _texture);
diff --git a/libraries/render-utils/src/text/Font.h b/libraries/render-utils/src/text/Font.h
index 893ab59981..be1e890e3d 100644
--- a/libraries/render-utils/src/text/Font.h
+++ b/libraries/render-utils/src/text/Font.h
@@ -46,11 +46,10 @@ public:
     // Render string to batch
     void drawString(gpu::Batch& batch, DrawInfo& drawInfo, const QString& str,
         const glm::vec4& color, EffectType effectType, 
-        const glm::vec2& origin, const glm::vec2& bound, bool forward);
+        const glm::vec2& origin, const glm::vec2& bound, bool unlit, bool forward);
 
     static Pointer load(const QString& family);
 
-
 private:
     static Pointer load(QIODevice& fontFile);
     QStringList tokenizeForWrapping(const QString& str) const;
@@ -80,9 +79,7 @@ private:
     gpu::TexturePointer _texture;
     gpu::BufferStreamPointer _stream;
 
-    static gpu::PipelinePointer _deferredPipeline;
-    static gpu::PipelinePointer _forwardPipeline;
-    static gpu::PipelinePointer _transparentPipeline;
+    static std::map<std::tuple<bool, bool, bool>, gpu::PipelinePointer> _pipelines;
     static gpu::Stream::FormatPointer _format;
 };
 
diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h
index 4736359832..6b66e844c0 100644
--- a/libraries/render/src/render/Item.h
+++ b/libraries/render/src/render/Item.h
@@ -614,7 +614,7 @@ public:
     virtual ShapeKey getShapeKey() = 0;
     virtual Item::Bound getBound() = 0;
     virtual void render(RenderArgs* args) = 0;
-    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) = 0;
+    virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) const = 0;
 };
 
 template <> const ItemKey payloadGetKey(const PayloadProxyInterface::Pointer& payload);
diff --git a/scripts/simplifiedUI/system/assets/data/createAppTooltips.json b/scripts/simplifiedUI/system/assets/data/createAppTooltips.json
index b59797fca7..f9f3ef5e47 100644
--- a/scripts/simplifiedUI/system/assets/data/createAppTooltips.json
+++ b/scripts/simplifiedUI/system/assets/data/createAppTooltips.json
@@ -39,6 +39,10 @@
     "leftMargin": {
         "tooltip": "The left margin, in meters."
     },
+    "unlit": {
+        "tooltip": "If enabled, the entity will not be lit by the keylight or local lights.",
+        "jsPropertyName": "unlit"
+    },
     "zoneShapeType": {
         "tooltip": "The shape of the volume in which the zone's lighting effects and avatar permissions have effect.",
         "jsPropertyName": "shapeType"
@@ -135,7 +139,7 @@
         "tooltip": "The radius of bloom. The higher the value, the larger the bloom."
     },
     "avatarPriority": {
-        "tooltip":  "Alter Avatars' update priorities."
+        "tooltip": "Alter Avatars' update priorities."
     },
     "modelURL": {
         "tooltip": "A mesh model from an FBX or OBJ file."
diff --git a/scripts/simplifiedUI/system/html/js/entityProperties.js b/scripts/simplifiedUI/system/html/js/entityProperties.js
index e64543d41f..40ee1b78bc 100644
--- a/scripts/simplifiedUI/system/html/js/entityProperties.js
+++ b/scripts/simplifiedUI/system/html/js/entityProperties.js
@@ -216,6 +216,11 @@ const GROUPS = [
                 decimals: 2,
                 propertyID: "leftMargin",
             },
+            {
+                label: "Unlit",
+                type: "bool",
+                propertyID: "unlit",
+            },
         ]
     },
     {
diff --git a/scripts/system/create/assets/data/createAppTooltips.json b/scripts/system/create/assets/data/createAppTooltips.json
index b59797fca7..f9f3ef5e47 100644
--- a/scripts/system/create/assets/data/createAppTooltips.json
+++ b/scripts/system/create/assets/data/createAppTooltips.json
@@ -39,6 +39,10 @@
     "leftMargin": {
         "tooltip": "The left margin, in meters."
     },
+    "unlit": {
+        "tooltip": "If enabled, the entity will not be lit by the keylight or local lights.",
+        "jsPropertyName": "unlit"
+    },
     "zoneShapeType": {
         "tooltip": "The shape of the volume in which the zone's lighting effects and avatar permissions have effect.",
         "jsPropertyName": "shapeType"
@@ -135,7 +139,7 @@
         "tooltip": "The radius of bloom. The higher the value, the larger the bloom."
     },
     "avatarPriority": {
-        "tooltip":  "Alter Avatars' update priorities."
+        "tooltip": "Alter Avatars' update priorities."
     },
     "modelURL": {
         "tooltip": "A mesh model from an FBX or OBJ file."
diff --git a/scripts/system/create/entityProperties/html/js/entityProperties.js b/scripts/system/create/entityProperties/html/js/entityProperties.js
index e64543d41f..40ee1b78bc 100644
--- a/scripts/system/create/entityProperties/html/js/entityProperties.js
+++ b/scripts/system/create/entityProperties/html/js/entityProperties.js
@@ -216,6 +216,11 @@ const GROUPS = [
                 decimals: 2,
                 propertyID: "leftMargin",
             },
+            {
+                label: "Unlit",
+                type: "bool",
+                propertyID: "unlit",
+            },
         ]
     },
     {