From 2acb6b6857f46c45cb6ae33fe61eeadf442de40e Mon Sep 17 00:00:00 2001
From: SamGondelman <samuel_gondelman@alumni.brown.edu>
Date: Wed, 28 Nov 2018 16:00:29 -0800
Subject: [PATCH] v3 procedurals and shader fixes

---
 .../src/RenderableShapeEntityItem.cpp         |  19 +--
 libraries/gpu/src/gpu/Pipeline.h              |   2 -
 .../procedural/src/procedural/Procedural.cpp  | 122 +++++++++---------
 .../procedural/src/procedural/Procedural.h    |  64 +++++----
 .../src/procedural/ProceduralCommon.slh       |  18 ++-
 .../src/procedural/ProceduralSkybox.cpp       |   1 -
 libraries/render-utils/src/forward_simple.slf |   4 +-
 libraries/render-utils/src/simple.slf         |  78 ++++++++---
 .../render-utils/src/simple_transparent.slf   |  81 +++++++++---
 9 files changed, 249 insertions(+), 140 deletions(-)

diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
index a705b61cd3..1b5b8bccaf 100644
--- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
+++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
@@ -38,18 +38,10 @@ ShapeEntityRenderer::ShapeEntityRenderer(const EntityItemPointer& entity) : Pare
     // FIXME: Setup proper uniform slots and use correct pipelines for forward rendering
     _procedural._opaqueFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple);
     _procedural._transparentFragmentSource = gpu::Shader::Source::get(shader::render_utils::fragment::simple_transparent);
-    _procedural._opaqueState->setCullMode(gpu::State::CULL_NONE);
-    _procedural._opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
+
+    // TODO: move into Procedural.cpp
     PrepareStencil::testMaskDrawShape(*_procedural._opaqueState);
-    _procedural._opaqueState->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);
-    _procedural._transparentState->setCullMode(gpu::State::CULL_BACK);
-    _procedural._transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
     PrepareStencil::testMask(*_procedural._transparentState);
-    _procedural._transparentState->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);
 }
 
 bool ShapeEntityRenderer::needsRenderUpdate() const {
@@ -212,7 +204,10 @@ ShapeKey ShapeEntityRenderer::getShapeKey() {
         return builder.build();
     } else {
         ShapeKey::Builder builder;
-        if (_procedural.isReady()) {
+        bool proceduralReady = resultWithReadLock<bool>([&] {
+            return _procedural.isReady();
+        });
+        if (proceduralReady) {
             builder.withOwnPipeline();
         }
         if (isTransparent()) {
@@ -242,7 +237,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
             if (_procedural.isReady()) {
                 outColor = _procedural.getColor(outColor);
                 outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
-                _procedural.prepare(batch, _position, _dimensions, _orientation, outColor);
+                _procedural.prepare(batch, _position, _dimensions, _orientation, ProceduralProgramKey(outColor.a < 1.0f));
                 proceduralRender = true;
             }
         }
diff --git a/libraries/gpu/src/gpu/Pipeline.h b/libraries/gpu/src/gpu/Pipeline.h
index 28f7fe106e..b46226182a 100755
--- a/libraries/gpu/src/gpu/Pipeline.h
+++ b/libraries/gpu/src/gpu/Pipeline.h
@@ -38,8 +38,6 @@ protected:
     StatePointer _state;
 
     Pipeline();
-    Pipeline(const Pipeline& pipeline); // deep copy of the sysmem shader
-    Pipeline& operator=(const Pipeline& pipeline); // deep copy of the sysmem texture
 };
 
 typedef Pipeline::Pointer PipelinePointer;
diff --git a/libraries/procedural/src/procedural/Procedural.cpp b/libraries/procedural/src/procedural/Procedural.cpp
index 7095732d53..a124d4e8be 100644
--- a/libraries/procedural/src/procedural/Procedural.cpp
+++ b/libraries/procedural/src/procedural/Procedural.cpp
@@ -39,8 +39,10 @@ static const std::string PROCEDURAL_BLOCK = "//PROCEDURAL_BLOCK";
 static const std::string PROCEDURAL_VERSION = "//PROCEDURAL_VERSION";
 
 bool operator==(const ProceduralData& a, const ProceduralData& b) {
-    return ((a.version == b.version) && (a.shaderUrl == b.shaderUrl) && (a.uniforms == b.uniforms) &&
-        (a.channels == b.channels));
+    return ((a.version == b.version) &&
+            (a.shaderUrl == b.shaderUrl) &&
+            (a.uniforms == b.uniforms) &&
+            (a.channels == b.channels));
 }
 
 QJsonValue ProceduralData::getProceduralData(const QString& proceduralJson) {
@@ -57,9 +59,9 @@ QJsonValue ProceduralData::getProceduralData(const QString& proceduralJson) {
     return doc.object()[PROCEDURAL_USER_DATA_KEY];
 }
 
-ProceduralData ProceduralData::parse(const QString& userDataJson) {
+ProceduralData ProceduralData::parse(const QString& proceduralData) {
     ProceduralData result;
-    result.parse(getProceduralData(userDataJson).toObject());
+    result.parse(getProceduralData(proceduralData).toObject());
     return result;
 }
 
@@ -73,7 +75,7 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
         if (versionJson.isDouble()) {
             version = (uint8_t)(floor(versionJson.toDouble()));
             // invalid version
-            if (!(version == 1 || version == 2)) {
+            if (!(version == 1 || version == 2 || version == 3)) {
                 return;
             }
         } else {
@@ -102,20 +104,27 @@ void ProceduralData::parse(const QJsonObject& proceduralData) {
 //}
 
 Procedural::Procedural() {
-    _transparentState->setCullMode(gpu::State::CULL_NONE);
+    _opaqueState->setCullMode(gpu::State::CULL_BACK);
+    _opaqueState->setDepthTest(true, true, gpu::LESS_EQUAL);
+    _opaqueState->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);
+
+    _transparentState->setCullMode(gpu::State::CULL_BACK);
     _transparentState->setDepthTest(true, true, gpu::LESS_EQUAL);
-    _transparentState->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
+    _transparentState->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);
 
     _standardInputsBuffer = std::make_shared<gpu::Buffer>(sizeof(StandardInputs), nullptr);
 }
 
 void Procedural::setProceduralData(const ProceduralData& proceduralData) {
+    std::lock_guard<std::mutex> lock(_mutex);
     if (proceduralData == _data) {
         return;
     }
 
-    _dirty = true;
     _enabled = false;
 
     if (proceduralData.version != _data.version) {
@@ -124,6 +133,10 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
     }
 
     if (proceduralData.uniforms != _data.uniforms) {
+        // If the uniform keys changed, we need to recreate the whole shader to handle the reflection
+        if (proceduralData.uniforms.keys() != _data.uniforms.keys()) {
+            _shaderDirty = true;
+        }
         _data.uniforms = proceduralData.uniforms;
         _uniformsDirty = true;
     }
@@ -147,16 +160,14 @@ void Procedural::setProceduralData(const ProceduralData& proceduralData) {
 
     if (proceduralData.shaderUrl != _data.shaderUrl) {
         _data.shaderUrl = proceduralData.shaderUrl;
-        _shaderDirty = true;
         const auto& shaderUrl = _data.shaderUrl;
+
+        _shaderDirty = true;
         _networkShader.reset();
         _shaderPath.clear();
+        _shaderSource.clear();
 
-        if (shaderUrl.isEmpty()) {
-            return;
-        }
-
-        if (!shaderUrl.isValid()) {
+        if (shaderUrl.isEmpty() || !shaderUrl.isValid()) {
             return;
         }
 
@@ -180,6 +191,8 @@ bool Procedural::isReady() const {
     return false;
 #endif
 
+    std::lock_guard<std::mutex> lock(_mutex);
+
     if (!_enabled) {
         return false;
     }
@@ -209,10 +222,11 @@ bool Procedural::isReady() const {
 }
 
 void Procedural::prepare(gpu::Batch& batch,
-    const glm::vec3& position,
-    const glm::vec3& size,
-    const glm::quat& orientation,
-    const glm::vec4& color) {
+                         const glm::vec3& position,
+                         const glm::vec3& size,
+                         const glm::quat& orientation,
+                         const ProceduralProgramKey key) {
+    std::lock_guard<std::mutex> lock(_mutex);
     _entityDimensions = size;
     _entityPosition = position;
     _entityOrientation = glm::mat3_cast(orientation);
@@ -225,62 +239,56 @@ void Procedural::prepare(gpu::Batch& batch,
             _shaderDirty = true;
             _shaderModified = lastModified;
         }
-    } else if (_networkShader && _networkShader->isLoaded()) {
+    } else if (_shaderSource.isEmpty() && _networkShader && _networkShader->isLoaded()) {
         _shaderSource = _networkShader->_source;
+        _shaderDirty = true;
     }
 
-    if (!_opaquePipeline || !_transparentPipeline || _shaderDirty) {
+    if (_shaderDirty) {
+        _proceduralPipelines.clear();
+    }
+
+    auto& pipeline = _proceduralPipelines.find(key);
+    bool recompiledShader = false;
+    if (pipeline == _proceduralPipelines.end()) {
         if (!_vertexShader) {
             _vertexShader = gpu::Shader::createVertex(_vertexSource);
         }
 
+        gpu::Shader::Source& fragmentSource = (key.isTransparent() && _transparentFragmentSource.valid()) ? _transparentFragmentSource : _opaqueFragmentSource;
+
         // Build the fragment shader
-        _opaqueFragmentSource.replacements.clear();
-        if (_data.version == 1) {
-            _opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V1 1";
-        } else if (_data.version == 2) {
-            _opaqueFragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V2 1";
-        }
-        _opaqueFragmentSource.replacements[PROCEDURAL_BLOCK] = _shaderSource.toStdString();
-        _transparentFragmentSource.replacements = _opaqueFragmentSource.replacements;
+        fragmentSource.replacements.clear();
+        fragmentSource.replacements[PROCEDURAL_VERSION] = "#define PROCEDURAL_V" + std::to_string(_data.version);
+        fragmentSource.replacements[PROCEDURAL_BLOCK] = _shaderSource.toStdString();
 
         // Set any userdata specified uniforms
         int customSlot = procedural::slot::uniform::Custom;
         for (const auto& key : _data.uniforms.keys()) {
             std::string uniformName = key.toLocal8Bit().data();
-            _opaqueFragmentSource.reflection.uniforms[uniformName] = customSlot;
-            _transparentFragmentSource.reflection.uniforms[uniformName] = customSlot;
+            fragmentSource.reflection.uniforms[uniformName] = customSlot;
             ++customSlot;
         }
 
         // Leave this here for debugging
-        // qCDebug(procedural) << "FragmentShader:\n" << fragmentShaderSource.c_str();
+        //qCDebug(proceduralLog) << "FragmentShader:\n" << fragmentSource.getSource(shader::Dialect::glsl450, shader::Variant::Mono).c_str();
+
+        gpu::ShaderPointer fragmentShader = gpu::Shader::createPixel(fragmentSource);
+        gpu::ShaderPointer program = gpu::Shader::createProgram(_vertexShader, fragmentShader);
+
+        _proceduralPipelines[key] = gpu::Pipeline::create(program, key.isTransparent() ? _transparentState : _opaqueState);
 
-        // TODO: THis is a simple fix, we need a cleaner way to provide the "hosting" program for procedural custom shaders to be defined together with the required bindings.
-        _opaqueFragmentShader = gpu::Shader::createPixel(_opaqueFragmentSource);
-        _opaqueShader = gpu::Shader::createProgram(_vertexShader, _opaqueFragmentShader);
-        _opaquePipeline = gpu::Pipeline::create(_opaqueShader, _opaqueState);
-        if (_transparentFragmentSource.valid()) {
-            _transparentFragmentShader = gpu::Shader::createPixel(_transparentFragmentSource);
-            _transparentShader = gpu::Shader::createProgram(_vertexShader, _transparentFragmentShader);
-            _transparentPipeline = gpu::Pipeline::create(_transparentShader, _transparentState);
-        } else {
-            _transparentFragmentShader = _opaqueFragmentShader;
-            _transparentShader = _opaqueShader;
-            _transparentPipeline = _opaquePipeline;
-        }
         _start = usecTimestampNow();
         _frameCount = 0;
+        recompiledShader = true;
     }
 
-    bool transparent = color.a < 1.0f;
-    batch.setPipeline(transparent ? _transparentPipeline : _opaquePipeline);
+    batch.setPipeline(_proceduralPipelines[key]);
 
-    if (_shaderDirty || _uniformsDirty || _prevTransparent != transparent) {
-        setupUniforms(transparent);
+    if (_shaderDirty || _uniformsDirty) {
+        setupUniforms();
     }
 
-    _prevTransparent = transparent;
     _shaderDirty = _uniformsDirty = false;
 
     for (auto lambda : _uniforms) {
@@ -290,8 +298,7 @@ void Procedural::prepare(gpu::Batch& batch,
     static gpu::Sampler sampler;
     static std::once_flag once;
     std::call_once(once, [&] {
-        gpu::Sampler::Desc desc;
-        desc._filter = gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR;
+        sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
     });
 
     for (size_t i = 0; i < MAX_PROCEDURAL_TEXTURE_CHANNELS; ++i) {
@@ -301,19 +308,17 @@ void Procedural::prepare(gpu::Batch& batch,
                 gpuTexture->setSampler(sampler);
                 gpuTexture->setAutoGenerateMips(true);
             }
-            batch.setResourceTexture((gpu::uint32)i, gpuTexture);
+            batch.setResourceTexture(procedural::slot::texture::Channel0 + i, gpuTexture);
         }
     }
 }
 
 
-void Procedural::setupUniforms(bool transparent) {
+void Procedural::setupUniforms() {
     _uniforms.clear();
-    auto customUniformCount = _data.uniforms.keys().size();
     // Set any userdata specified uniforms
-    for (int i = 0; i < customUniformCount; ++i) {
-        int slot = procedural::slot::uniform::Custom + i;
-        QString key = _data.uniforms.keys().at(i);
+    int slot = procedural::slot::uniform::Custom;
+    for (const auto& key : _data.uniforms.keys()) {
         std::string uniformName = key.toLocal8Bit().data();
         QJsonValue value = _data.uniforms[key];
         if (value.isDouble()) {
@@ -360,6 +365,7 @@ void Procedural::setupUniforms(bool transparent) {
             }
             }
         }
+        slot++;
     }
 
     _uniforms.push_back([=](gpu::Batch& batch) {
@@ -398,7 +404,7 @@ void Procedural::setupUniforms(bool transparent) {
     });
 }
 
-glm::vec4 Procedural::getColor(const glm::vec4& entityColor) {
+glm::vec4 Procedural::getColor(const glm::vec4& entityColor) const {
     if (_data.version == 1) {
         return glm::vec4(1);
     }
diff --git a/libraries/procedural/src/procedural/Procedural.h b/libraries/procedural/src/procedural/Procedural.h
index 781ac25249..3e10678ba7 100644
--- a/libraries/procedural/src/procedural/Procedural.h
+++ b/libraries/procedural/src/procedural/Procedural.h
@@ -7,8 +7,6 @@
 //
 
 #pragma once
-#ifndef hifi_RenderableProcedrualItem_h
-#define hifi_RenderableProcedrualItem_h
 
 #include <atomic>
 
@@ -32,7 +30,6 @@ const size_t MAX_PROCEDURAL_TEXTURE_CHANNELS{ 4 };
 struct ProceduralData {
     static QJsonValue getProceduralData(const QString& proceduralJson);
     static ProceduralData parse(const QString& userDataJson);
-    // This should only be called from the render thread, as it shares data with Procedural::prepare
     void parse(const QJsonObject&);
 
     // Rendering object descriptions, from userData
@@ -42,10 +39,40 @@ struct ProceduralData {
     QJsonArray channels;
 };
 
+class ProceduralProgramKey {
+public:
+    enum FlagBit {
+        IS_TRANSPARENT = 0,
+        NUM_FLAGS
+    };
+
+    typedef std::bitset<NUM_FLAGS> Flags;
+
+    Flags _flags;
+
+    bool isTransparent() const { return _flags[IS_TRANSPARENT]; }
+
+    ProceduralProgramKey(bool transparent = false) {
+        if (transparent) {
+            _flags.set(IS_TRANSPARENT);
+        }
+    }
+};
+namespace std {
+    template <>
+    struct hash<ProceduralProgramKey> {
+        size_t operator()(const ProceduralProgramKey& key) const {
+            return std::hash<std::bitset<ProceduralProgramKey::FlagBit::NUM_FLAGS>>()(key._flags);
+        }
+    };
+}
+inline bool operator==(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
+    return a._flags == b._flags;
+}
+inline bool operator!=(const ProceduralProgramKey& a, const ProceduralProgramKey& b) {
+    return a._flags != b._flags;
+}
 
-// WARNING with threaded rendering it is the RESPONSIBILITY OF THE CALLER to ensure that 
-// calls to `setProceduralData` happen on the main thread and that calls to `ready` and `prepare` 
-// are treated atomically, and that they cannot happen concurrently with calls to `setProceduralData`
 // FIXME better encapsulation
 // FIXME better mechanism for extending to things rendered using shaders other than simple.slv
 struct Procedural {
@@ -55,10 +82,9 @@ public:
 
     bool isReady() const;
     bool isEnabled() const { return _enabled; }
-    void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, const glm::vec4& color = glm::vec4(1));
-    const gpu::ShaderPointer& getOpaqueShader() const { return _opaqueShader; }
+    void prepare(gpu::Batch& batch, const glm::vec3& position, const glm::vec3& size, const glm::quat& orientation, const ProceduralProgramKey key = ProceduralProgramKey());
 
-    glm::vec4 getColor(const glm::vec4& entityColor);
+    glm::vec4 getColor(const glm::vec4& entityColor) const;
     quint64 getFadeStartTime() const { return _fadeStartTime; }
     bool isFading() const { return _doesFade && _isFading; }
     void setIsFading(bool isFading) { _isFading = isFading; }
@@ -108,22 +134,19 @@ protected:
     QString _shaderPath;
     quint64 _shaderModified { 0 };
     NetworkShaderPointer _networkShader;
-    bool _dirty { false };
     bool _shaderDirty { true };
     bool _uniformsDirty { true };
 
     // Rendering objects
     UniformLambdas _uniforms;
     NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
-    gpu::PipelinePointer _opaquePipeline;
-    gpu::PipelinePointer _transparentPipeline;
+
+    std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
+
+    gpu::ShaderPointer _vertexShader;
+
     StandardInputs _standardInputs;
     gpu::BufferPointer _standardInputsBuffer;
-    gpu::ShaderPointer _vertexShader;
-    gpu::ShaderPointer _opaqueFragmentShader;
-    gpu::ShaderPointer _transparentFragmentShader;
-    gpu::ShaderPointer _opaqueShader;
-    gpu::ShaderPointer _transparentShader;
 
     // Entity metadata
     glm::vec3 _entityDimensions;
@@ -131,14 +154,11 @@ protected:
     glm::mat3 _entityOrientation;
 
 private:
-    // This should only be called from the render thread, as it shares data with Procedural::prepare
-    void setupUniforms(bool transparent);
+    void setupUniforms();
 
     mutable quint64 _fadeStartTime { 0 };
     mutable bool _hasStartedFade { false };
     mutable bool _isFading { false };
     bool _doesFade { true };
-    bool _prevTransparent { false };
+    mutable std::mutex _mutex;
 };
-
-#endif
diff --git a/libraries/procedural/src/procedural/ProceduralCommon.slh b/libraries/procedural/src/procedural/ProceduralCommon.slh
index d515a79e22..ab8d74bdfa 100644
--- a/libraries/procedural/src/procedural/ProceduralCommon.slh
+++ b/libraries/procedural/src/procedural/ProceduralCommon.slh
@@ -8,11 +8,8 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 //
 
-<@include gpu/Transform.slh@>
 <@include gpu/Noise.slh@>
 <@include procedural/ShaderConstants.h@>
- 
-<$declareStandardCameraTransform()$>
 
 LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL0) uniform sampler2D iChannel0;
 LAYOUT(binding=PROCEDURAL_TEXTURE_CHANNEL1) uniform sampler2D iChannel1;
@@ -59,6 +56,19 @@ LAYOUT_STD140(binding=0) uniform standardInputsBuffer {
 #define iChannelResolution standardInputs.channelResolution
 #define iWorldOrientation standardInputs.worldOrientation
 
+struct ProceduralFragmentData {
+    vec3 position;
+    vec3 normal;
+    vec3 diffuse;
+    vec3 specular;
+    vec3 emissive;
+    float alpha;
+    float roughness;
+    float metallic;
+    float occlusion;
+    float scattering;
+};
+
 // Unimplemented uniforms
 // Resolution doesn't make sense in the VR context
 const vec3 iResolution = vec3(1.0);
@@ -69,8 +79,6 @@ const float iSampleRate = 1.0;
 // No support for video input
 const vec4 iChannelTime = vec4(0.0);
 
-#define PROCEDURAL 1
-
 //PROCEDURAL_VERSION
 
 // hack comment for extra whitespace 
diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
index ea5be23eb8..211f6ca0a2 100644
--- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp
+++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp
@@ -35,7 +35,6 @@ bool ProceduralSkybox::empty() {
 void ProceduralSkybox::clear() {
     // Parse and prepare a procedural with no shaders to release textures
     parse(QString());
-    _procedural.isReady();
 
     Skybox::clear();
 }
diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf
index 9c86f9dff1..677c369033 100644
--- a/libraries/render-utils/src/forward_simple.slf
+++ b/libraries/render-utils/src/forward_simple.slf
@@ -14,9 +14,9 @@
 <@include DefaultMaterials.slh@>
 
 <@include ForwardGlobalLight.slh@>
-<@include gpu/Transform.slh@>
-
 <$declareEvalSkyboxGlobalColor()$>
+
+<@include gpu/Transform.slh@>
 <$declareStandardCameraTransform()$>
 
 // the interpolated normal
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index dac01aac89..c0bb021c2d 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -14,6 +14,9 @@
 
 <@include DeferredBufferWrite.slh@>
 
+<@include gpu/Transform.slh@>
+<$declareStandardCameraTransform()$>
+
 <@include render-utils/ShaderConstants.h@>
 
 // the interpolated normal
@@ -45,27 +48,64 @@ float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float s
     return 1.0;
 }
 
+float getProceduralFragment(inout ProceduralFragmentData proceduralData) {
+    return 1.0;
+}
+
 //PROCEDURAL_BLOCK_END
 
 #line 2030
 void main(void) {
     vec3 normal = normalize(_normalWS.xyz);
     vec3 diffuse = _color.rgb;
+    float roughness = DEFAULT_ROUGHNESS;
+    float metallic = DEFAULT_METALLIC;
+    vec3 emissive = DEFAULT_EMISSIVE;
+    float occlusion = DEFAULT_OCCLUSION;
+    float scattering = DEFAULT_SCATTERING;
+
+    float emissiveAmount = 0.0;
+
+#if defined(PROCEDURAL_V1)
+    diffuse = getProceduralColor().rgb;
+    emissiveAmount = 1.0;
+    emissive = vec3(1.0);
+#elif defined(PROCEDURAL_V2)
     vec3 specular = DEFAULT_SPECULAR;
     float shininess = DEFAULT_SHININESS;
-    float emissiveAmount = 0.0;
-    
-#ifdef PROCEDURAL
-
-#ifdef PROCEDURAL_V1
-    diffuse = getProceduralColor().rgb;
-    // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
-    //diffuse = pow(diffuse, vec3(2.2));
-    emissiveAmount = 1.0;
-#else
     emissiveAmount = getProceduralColors(diffuse, specular, shininess);
-#endif
+    roughness = max(0.0, 1.0 - shininess / 128.0);
+    metallic = length(specular);
+    emissive = vec3(clamp(emissiveAmount, 0.0, 1.0));
+#elif defined(PROCEDURAL_V3)
+    TransformCamera cam = getTransformCamera();
+    vec4 position = cam._viewInverse * _positionES;
+    ProceduralFragmentData proceduralData = {
+        position.xyz,
+        normal,
+        vec3(0.0),
+        DEFAULT_SPECULAR,
+        DEFAULT_EMISSIVE,
+        1.0,
+        DEFAULT_ROUGHNESS,
+        DEFAULT_METALLIC,
+        DEFAULT_OCCLUSION,
+        DEFAULT_SCATTERING
+    };
+    emissiveAmount = getProceduralFragment(proceduralData);
+    normal = proceduralData.normal;
+    diffuse = proceduralData.diffuse;
+    roughness = proceduralData.roughness;
+    metallic = proceduralData.metallic;
+    emissive = proceduralData.emissive;
+    occlusion = proceduralData.occlusion;
+    scattering = proceduralData.scattering;
 
+    position = vec4(proceduralData.position, 1.0);
+    vec4 posClip = cam._projection * (cam._view * position);
+    float far = gl_DepthRange.far;
+    float near = gl_DepthRange.near;
+    gl_FragDepth = 0.5 * ((far - near) * posClip.z / posClip.w + near + far);
 #endif
 
     if (emissiveAmount > 0.0) {
@@ -73,18 +113,18 @@ void main(void) {
             normal,
             1.0,
             diffuse,
-            max(0.0, 1.0 - shininess / 128.0),
-            DEFAULT_METALLIC,
-            vec3(clamp(emissiveAmount, 0.0, 1.0)));
+            roughness,
+            metallic,
+            emissive);
     } else {
         packDeferredFragment(
             normal,
             1.0,
             diffuse,
-            max(0.0, 1.0 - shininess / 128.0),
-            length(specular),
-            DEFAULT_EMISSIVE,
-            DEFAULT_OCCLUSION,
-            DEFAULT_SCATTERING);
+            roughness,
+            metallic,
+            emissive,
+            occlusion,
+            scattering);
     }
 }
diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf
index 0e29ed7470..bafec462db 100644
--- a/libraries/render-utils/src/simple_transparent.slf
+++ b/libraries/render-utils/src/simple_transparent.slf
@@ -16,6 +16,9 @@
 <@include DeferredGlobalLight.slh@>
 <$declareEvalGlobalLightingAlphaBlendedWithHaze()$>
 
+<@include gpu/Transform.slh@>
+<$declareStandardCameraTransform()$>
+
 <@include render-utils/ShaderConstants.h@>
 
 // the interpolated normal
@@ -50,46 +53,86 @@ float getProceduralColors(inout vec3 diffuse, inout vec3 specular, inout float s
     return 1.0;
 }
 
+float getProceduralFragment(inout ProceduralFragmentData proceduralData) {
+    return 1.0;
+}
+
 //PROCEDURAL_BLOCK_END
 
 #line 2030
 void main(void) {
     vec3 normal = normalize(_normalWS.xyz);
     vec3 diffuse = _color.rgb;
-    vec3 specular = DEFAULT_SPECULAR;
-    float shininess = DEFAULT_SHININESS;
+    float alpha = _color.a;
+    float occlusion = DEFAULT_OCCLUSION;
+    vec3 fresnel = DEFAULT_FRESNEL;
+    float metallic = DEFAULT_METALLIC;
+    vec3 emissive = DEFAULT_EMISSIVE;
+    float roughness = DEFAULT_ROUGHNESS;
+
     float emissiveAmount = 0.0;
-    
-#ifdef PROCEDURAL
+
+    TransformCamera cam = getTransformCamera();
+    vec3 posEye = _positionES.xyz;
 
 #ifdef PROCEDURAL_V1
     diffuse = getProceduralColor().rgb;
-    // Procedural Shaders are expected to be Gamma corrected so let's bring back the RGB in linear space for the rest of the pipeline
-    //diffuse = pow(diffuse, vec3(2.2));
     emissiveAmount = 1.0;
-#else
+    emissive = vec3(1.0);
+#elif defined(PROCEDURAL_V2)
+    vec3 specular = DEFAULT_SPECULAR;
+    float shininess = DEFAULT_SHININESS;
     emissiveAmount = getProceduralColors(diffuse, specular, shininess);
-#endif
+    roughness = max(0.0, 1.0 - shininess / 128.0);
+    metallic = length(specular);
+    emissive = vec3(clamp(emissiveAmount, 0.0, 1.0));
+#elif defined(PROCEDURAL_V3)
+    vec4 position = cam._viewInverse * _positionES;
+    ProceduralFragmentData proceduralData = {
+        position.xyz,
+        normal,
+        vec3(0.0),
+        DEFAULT_SPECULAR,
+        DEFAULT_EMISSIVE,
+        1.0,
+        DEFAULT_ROUGHNESS,
+        DEFAULT_METALLIC,
+        DEFAULT_OCCLUSION,
+        DEFAULT_SCATTERING
+    };
+    emissiveAmount = getProceduralFragment(proceduralData);
+    position = vec4(proceduralData.position, 1.0);
+    vec4 posEye4 = cam._view * position;
+    posEye = vec3(posEye4);
+    occlusion = proceduralData.occlusion;
+    normal = proceduralData.normal;
+    diffuse = proceduralData.diffuse;
+    alpha = proceduralData.alpha;
+    fresnel = proceduralData.specular;
+    metallic = proceduralData.metallic;
+    emissive = proceduralData.emissive;
+    roughness = proceduralData.roughness;
 
+    vec4 posClip = cam._projection * posEye4;
+    float far = gl_DepthRange.far;
+    float near = gl_DepthRange.near;
+    gl_FragDepth = 0.5 * ((far - near) * posClip.z / posClip.w + near + far);
 #endif
 
-    TransformCamera cam = getTransformCamera();
-    vec3 fragPosition = _positionES.xyz;
-
     if (emissiveAmount > 0.0) {
-        _fragColor0 = vec4(diffuse, _color.a);
+        _fragColor0 = vec4(diffuse, alpha);
     } else {
         _fragColor0 = vec4(evalGlobalLightingAlphaBlendedWithHaze(
             cam._viewInverse,
             1.0,
-            DEFAULT_OCCLUSION,
-            fragPosition,
+            occlusion,
+            posEye,
             normal,
             diffuse,
-            DEFAULT_FRESNEL,
-            length(specular),
-            DEFAULT_EMISSIVE,
-            max(0.0, 1.0 - shininess / 128.0), _color.a),
-            _color.a);
+            fresnel,
+            metallic,
+            emissive,
+            roughness, alpha),
+            alpha);
     }
 }