diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
index cb9cc4958ad126392980af4892f9bab25fd5c3e0..15e02c9551c012714594fae00453f4100de83599 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueF.glsl
@@ -25,69 +25,81 @@
 
 /*[EXTRA_CODE_HERE]*/
 
-#define DEBUG_BASIC         1
-#define DEBUG_COLOR         0
+#define DEBUG_BASIC         0
+#define DEBUG_VERTEX        0
 #define DEBUG_NORMAL        0
 #define DEBUG_POSITION      0
-#define DEBUG_REFLECT_VEC   0
-#define DEBUG_REFLECT_COLOR 0
+
+uniform sampler2D diffuseMap;  //always in sRGB space
+
+#ifdef HAS_NORMAL_MAP
+    uniform sampler2D bumpMap;
+#endif
 
 #ifdef HAS_SPECULAR_MAP
-uniform sampler2D specularMap;
+    uniform sampler2D specularMap; // Packed: Occlusion, Metal, Roughness
 #endif
+
 uniform samplerCube environmentMap;
 uniform mat3        env_mat;
 
 #ifdef DEFINE_GL_FRAGCOLOR
-out vec4 frag_data[3];
+out vec4 frag_data[4];
 #else
 #define frag_data gl_FragData
 #endif
 
 VARYING vec3 vary_position;
-VARYING vec3 vary_normal;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
+#ifdef HAS_NORMAL_MAP
+    VARYING vec3 vary_normal;
+    VARYING vec2 vary_texcoord1;
+#endif
+
 #ifdef HAS_SPECULAR_MAP
-VARYING vec2 vary_texcoord2;
+    VARYING vec2 vary_texcoord2;
 #endif
 
 vec2 encode_normal(vec3 n);
 vec3 linear_to_srgb(vec3 c);
 
-struct PBR
-{
-    float LdotH; // Light and Half
-    float NdotL; // Normal and Light
-    float NdotH; // Normal and Half
-    float VdotH; // View and Half
-};
-
 const float M_PI = 3.141592653589793;
 
 void main()
 {
-    vec3 col = vertex_color.rgb * diffuseLookup(vary_texcoord0.xy).rgb;
+// IF .mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
+//    vec3 col = vertex_color.rgb * diffuseLookup(vary_texcoord0.xy).rgb;
+// else
+    vec3 col = vertex_color.rgb * texture2D(diffuseMap, vary_texcoord0.xy).rgb;
 
-//#ifdef HAS_SPECULAR_MAP
-//#else
-    vec4 norm  = vec4(0,0,0,1.0);
-    vec3 tnorm = vary_normal;
-//#endif
-    norm.xyz = normalize(tnorm.xyz);
+    vec3 emissive = vec3(0);
 
-    vec3 spec;
-    spec.rgb = vec3(vertex_color.a);
+#ifdef HAS_NORMAL_MAP
+    vec4 norm = texture2D(bumpMap, vary_texcoord1.xy);
+    vec3 tnorm = norm.xyz * 2 - 1;
+#else
+    vec4 norm = vec4(0,0,0,1.0);
+//    vec3 tnorm = vary_normal;
+    vec3 tnorm = vec3(0,0,1);
+#endif
 
-    vec3 pos = vary_position;
-    vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz));
-    vec3 env_vec = env_mat * refnormpersp;
-    vec3 reflected_color = textureCube(environmentMap, env_vec).rgb;
+    // RGB = Occlusion, Roughness, Metal
+    // default values
+    //   occlusion ?
+    //   roughness 1.0
+    //   metal     1.0
+#ifdef HAS_SPECULAR_MAP
+    vec3 spec = texture2D(specularMap, vary_texcoord0.xy).rgb; // TODO: FIXME: vary_texcoord2
+#else
+    vec3 spec = vec3(0,1,1);
+#endif
+    norm.xyz = normalize(tnorm.xyz);
 
 #if DEBUG_BASIC
-    col.rgb = vec3( 1, 0, 1 ); // DEBUG
+    col.rgb = vec3( 1, 0, 1 );
 #endif
-#if DEBUG_COLOR
+#if DEBUG_VERTEX
     col.rgb = vertex_color.rgb;
 #endif
 #if DEBUG_NORMAL
@@ -96,14 +108,9 @@ void main()
 #if DEBUG_POSITION
     col.rgb = vary_position.xyz;
 #endif
-#if DEBUG_REFLECT_VEC
-    col.rgb = refnormpersp;
-#endif
-#if DEBUG_REFLECT_COLOR
-    col.rgb = reflected_color;
-#endif
 
     frag_data[0] = vec4(col, 0.0);
-    frag_data[1] = vec4(spec, vertex_color.a); // spec
-    frag_data[2] = vec4(encode_normal(norm.xyz), vertex_color.a, 0.0);
+    frag_data[1] = vec4(spec.rgb, vertex_color.a);                                      // Occlusion, Roughness, Metal
+    frag_data[2] = vec4(encode_normal(norm.xyz), vertex_color.a, GBUFFER_FLAG_HAS_PBR); //
+    frag_data[3] = vec4(emissive,0);                                                    // Emissive
 }
diff --git a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
index 72bae808e02734d4031a618ea9d14b6adb2864e4..58048687a5d13fec991e649bfceb67b45caddf6f 100644
--- a/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
+++ b/indra/newview/app_settings/shaders/class1/deferred/pbropaqueV.glsl
@@ -33,10 +33,24 @@ ATTRIBUTE vec4 diffuse_color;
 ATTRIBUTE vec3 normal;
 ATTRIBUTE vec2 texcoord0;
 
+#ifdef HAS_NORMAL_MAP
+    ATTRIBUTE vec4 tangent;
+    ATTRIBUTE vec2 texcoord1;
+    VARYING   vec3 vary_mat0;
+    VARYING   vec3 vary_mat1;
+    VARYING   vec3 vary_mat2;
+#endif
+
+#if HAS_SPECULAR_MAP
+    ATTRIBUTE vec2 texcoord2;
+#endif
+
 VARYING vec3 vary_position;
 VARYING vec3 vary_normal;
 VARYING vec4 vertex_color;
 VARYING vec2 vary_texcoord0;
+VARYING vec2 vary_texcoord1; // normal map
+VARYING vec2 vary_texcoord2; // specular map
 
 void passTextureIndex();
 
@@ -50,5 +64,18 @@ void main()
     passTextureIndex();
     vary_normal = normalize(normal_matrix * normal);
 
+#ifdef HAS_NORMAL_MAP
+    //vary_texcoord1 = (texture_matrix0 * vec4(texcoord1,0,1)).xy;
+    vary_texcoord1 = texcoord1;
+//  vary_mat0 = vec3(t.x, b.x, n.x);
+//  vary_mat1 = vec3(t.y, b.y, n.y);
+//  vary_mat2 = vec3(t.z, b.z, n.z);
+#endif
+
+#ifdef HAS_SPECULAR_MAP
+    //vary_texcoord2 = (texture_matrix0 * vec4(texcoord2,0,1)).xy;
+    vary_texcoord2 = texcoord2;
+#endif
+
     vertex_color = diffuse_color;
 }
diff --git a/indra/newview/lldrawpoolpbropaque.cpp b/indra/newview/lldrawpoolpbropaque.cpp
index 2478aa0cff1a5a740302be2b6056b612536820f5..f23d2d04a8e19b0960e7b117d5af79a05eb873cf 100644
--- a/indra/newview/lldrawpoolpbropaque.cpp
+++ b/indra/newview/lldrawpoolpbropaque.cpp
@@ -73,6 +73,12 @@ void LLDrawPoolPBROpaque::renderDeferred(S32 pass)
          return;
     }
 
+    const U32 type = LLPipeline::RENDER_TYPE_PASS_PBR_OPAQUE;
+    if (!gPipeline.hasRenderType(type))
+    {
+        return;
+    }
+
     gGL.flush();
 
     LLGLDisable blend(GL_BLEND);
@@ -86,7 +92,37 @@ void LLDrawPoolPBROpaque::renderDeferred(S32 pass)
 
     // TODO: handle under water?
     // if (LLPipeline::sUnderWaterRender)
-    // PASS_SIMPLE or PASS_MATERIAL
-    //pushBatches(LLRenderPass::PASS_SIMPLE, getVertexDataMask() | LLVertexBuffer::MAP_TEXTURE_INDEX, TRUE, TRUE);
-}
+    LLCullResult::drawinfo_iterator begin = gPipeline.beginRenderMap(type);
+    LLCullResult::drawinfo_iterator end = gPipeline.endRenderMap(type);
+
+    for (LLCullResult::drawinfo_iterator i = begin; i != end; ++i)
+    {
+        LLDrawInfo& params = **i;
+
+//gGL.getTexUnit(0)->activate();
 
+        if (mShaderLevel > 1)
+        {
+            if (params.mTexture.notNull())
+            {
+                gGL.getTexUnit(-1)->bindFast(params.mTexture); // diffuse
+            }
+        }
+
+        if (params.mNormalMap)
+        {
+            gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::BUMP_MAP, params.mNormalMap);
+        }
+
+        if (params.mSpecularMap)
+        {
+            gDeferredPBROpaqueProgram.bindTexture(LLShaderMgr::SPECULAR_MAP, params.mSpecularMap); // Packed Occlusion Roughness Metal
+        }
+
+        // Similar to LLDrawPooLMaterials::pushMaterialsBatch(params, getVertexDataMask(), false);
+
+        LLRenderPass::applyModelMatrix(params);
+        params.mVertexBuffer->setBufferFast(getVertexDataMask());
+        params.mVertexBuffer->drawRangeFast(params.mDrawMode, params.mStart, params.mEnd, params.mCount, params.mOffset);
+    }
+}
diff --git a/indra/newview/lldrawpoolpbropaque.h b/indra/newview/lldrawpoolpbropaque.h
index ada806a3bf2bec8f41efc61d8adddc9b185f704e..413bcb800cd58ec85a21f9400df7b2a0e88b2702 100644
--- a/indra/newview/lldrawpoolpbropaque.h
+++ b/indra/newview/lldrawpoolpbropaque.h
@@ -34,11 +34,14 @@ class LLDrawPoolPBROpaque : public LLRenderPass
 public:
     enum
     {
-       VERTEX_DATA_MASK = 0
-                        | LLVertexBuffer::MAP_VERTEX
-                        | LLVertexBuffer::MAP_NORMAL
-                        | LLVertexBuffer::MAP_TEXCOORD0
-                        | LLVertexBuffer::MAP_COLOR
+        // See: DEFERRED_VB_MASK
+        VERTEX_DATA_MASK = 0
+                         | LLVertexBuffer::MAP_VERTEX
+                         | LLVertexBuffer::MAP_NORMAL
+                         | LLVertexBuffer::MAP_TEXCOORD0 // Diffuse
+                         | LLVertexBuffer::MAP_TEXCOORD1 // Normal
+                         | LLVertexBuffer::MAP_TEXCOORD2 // Spec <-- ORM Occlusion Roughness Metal
+                         | LLVertexBuffer::MAP_COLOR
     };
     virtual U32 getVertexDataMask() { return VERTEX_DATA_MASK; }
 
diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp
index c041d2470cacf846331505daf3ab40927998628f..d0f7f70c81ec74579edff7c282a53f2ef1606213 100644
--- a/indra/newview/llviewershadermgr.cpp
+++ b/indra/newview/llviewershadermgr.cpp
@@ -1626,9 +1626,9 @@ BOOL LLViewerShaderMgr::loadShadersDeferred()
             gDeferredPBROpaqueProgram.mShaderFiles.clear();
             gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueV.glsl", GL_VERTEX_SHADER_ARB));
             gDeferredPBROpaqueProgram.mShaderFiles.push_back(make_pair("deferred/pbropaqueF.glsl", GL_FRAGMENT_SHADER_ARB));
-            gDeferredPBROpaqueProgram.mFeatures.mIndexedTextureChannels = LLGLSLShader::sIndexedTextureChannels;
             gDeferredPBROpaqueProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED];
-            //gDeferredPBROpaqueProgram.addPermutation("HAS_NORMAL_MAP", "1");
+            gDeferredPBROpaqueProgram.addPermutation("HAS_NORMAL_MAP", "1");
+            gDeferredPBROpaqueProgram.addPermutation("HAS_SPECULAR_MAP", "1");
             success = gDeferredPBROpaqueProgram.createShader(NULL, NULL);
             llassert(success);
         }