commit 56c6ca24708e7b8a785a511d4fe401da25e0b475
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Thu May 10 13:30:58 2018 +0100

    OIT: lots of progress on all fronts. Still a few bugs to solve though!

diff --git a/src/main/java/org/distorted/library/main/DistortedEffects.java b/src/main/java/org/distorted/library/main/DistortedEffects.java
index aa7d379..2710cf4 100644
--- a/src/main/java/org/distorted/library/main/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/main/DistortedEffects.java
@@ -68,14 +68,20 @@ public class DistortedEffects
     mQuadPositions.put(positionData).position(0);
     }
 
-  /// BLIT DEPTH PROGRAM ///
-  private static DistortedProgram mBlitDepthProgram;
-  private static int mBlitDepthTextureH;
-  private static int mBlitDepthDepthTextureH;
-  private static int mBlitDepthDepthH;
-  private static int mBlitDepthTexCorrH;
-  private static int mBlitDepthSizeH;
-  private static int mBlitDepthNumRecordsH;
+  /// OIT CLEAR PROGRAM ///
+  private static DistortedProgram mOITClearProgram;
+  private static int mOITClearDepthH;
+  private static int mOITClearTexCorrH;
+  private static int mOITClearSizeH;
+
+  /// OIT BUILD PROGRAM ///
+  private static DistortedProgram mOITBuildProgram;
+  private static int mOITBuildTextureH;
+  private static int mOITBuildDepthTextureH;
+  private static int mOITBuildDepthH;
+  private static int mOITBuildTexCorrH;
+  private static int mOITBuildSizeH;
+  private static int mOITBuildNumRecordsH;
 
   private static int[] mLinkedListSSBO = new int[1];
   private static int[] mAtomicCounter = new int[1];
@@ -88,12 +94,13 @@ public class DistortedEffects
 
   private static int mBufferSize=(0x1<<23);  // 8 million entries
 
-  /// BLIT DEPTH RENDER PROGRAM ///
-  private static DistortedProgram mBlitDepthRenderProgram;
-  private static int mBlitDepthRenderDepthTextureH;
-  private static int mBlitDepthRenderDepthH;
-  private static int mBlitDepthRenderTexCorrH;
-  private static int mBlitDepthRenderSizeH;
+  /// OIT RENDER PROGRAM ///
+  private static DistortedProgram mOITRenderProgram;
+  private static int mOITRenderTextureH;
+  private static int mOITRenderDepthTextureH;
+  private static int mOITRenderDepthH;
+  private static int mOITRenderTexCorrH;
+  private static int mOITRenderSizeH;
 
   /// NORMAL PROGRAM /////
   private static DistortedProgram mNormalProgram;
@@ -155,12 +162,9 @@ public class DistortedEffects
     final InputStream blitVertStream = resources.openRawResource(R.raw.blit_vertex_shader);
     final InputStream blitFragStream = resources.openRawResource(R.raw.blit_fragment_shader);
 
-    String blitVertHeader= (Distorted.GLSL_VERSION + "#define NUM_VERTEX 0\n"  );
-    String blitFragHeader= (Distorted.GLSL_VERSION + "#define NUM_FRAGMENT 0\n");
-
     try
       {
-      mBlitProgram = new DistortedProgram(blitVertStream,blitFragStream,blitVertHeader,blitFragHeader, Distorted.GLSL);
+      mBlitProgram = new DistortedProgram(blitVertStream,blitFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
       }
     catch(Exception e)
       {
@@ -172,27 +176,46 @@ public class DistortedEffects
     mBlitTextureH  = GLES31.glGetUniformLocation( blitProgramH, "u_Texture");
     mBlitDepthH    = GLES31.glGetUniformLocation( blitProgramH, "u_Depth");
 
-    // BLIT DEPTH PROGRAM ////////////////////////////////////
-    final InputStream blitDepthVertStream = resources.openRawResource(R.raw.blit_depth_vertex_shader);
-    final InputStream blitDepthFragStream = resources.openRawResource(R.raw.blit_depth_fragment_shader);
+    // OIT CLEAR PROGRAM ////////////////////////////////////
+    final InputStream oitClearVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
+    final InputStream oitClearFragStream = resources.openRawResource(R.raw.oit_clear_fragment_shader);
 
     try
       {
-      mBlitDepthProgram = new DistortedProgram(blitDepthVertStream,blitDepthFragStream,blitVertHeader,blitFragHeader, Distorted.GLSL);
+      mOITClearProgram = new DistortedProgram(oitClearVertStream,oitClearFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT DEPTH program: "+e.getMessage());
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT CLEAR program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
-    int blitDepthProgramH   = mBlitDepthProgram.getProgramHandle();
-    mBlitDepthTextureH      = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Texture");
-    mBlitDepthDepthTextureH = GLES31.glGetUniformLocation( blitDepthProgramH, "u_DepthTexture");
-    mBlitDepthDepthH        = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Depth");
-    mBlitDepthTexCorrH      = GLES31.glGetUniformLocation( blitDepthProgramH, "u_TexCorr");
-    mBlitDepthSizeH         = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Size");
-    mBlitDepthNumRecordsH   = GLES31.glGetUniformLocation( blitDepthProgramH, "u_numRecords");
+    int oitClearProgramH   = mOITClearProgram.getProgramHandle();
+    mOITClearDepthH        = GLES31.glGetUniformLocation( oitClearProgramH, "u_Depth");
+    mOITClearTexCorrH      = GLES31.glGetUniformLocation( oitClearProgramH, "u_TexCorr");
+    mOITClearSizeH         = GLES31.glGetUniformLocation( oitClearProgramH, "u_Size");
+
+    // OIT BUILD PROGRAM ////////////////////////////////////
+    final InputStream oitBuildVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
+    final InputStream oitBuildFragStream = resources.openRawResource(R.raw.oit_build_fragment_shader);
+
+    try
+      {
+      mOITBuildProgram = new DistortedProgram(oitBuildVertStream,oitBuildFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
+      }
+    catch(Exception e)
+      {
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT BUILD program: "+e.getMessage());
+      throw new RuntimeException(e.getMessage());
+      }
+
+    int oitBuildProgramH   = mOITBuildProgram.getProgramHandle();
+    mOITBuildTextureH      = GLES31.glGetUniformLocation( oitBuildProgramH, "u_Texture");
+    mOITBuildDepthTextureH = GLES31.glGetUniformLocation( oitBuildProgramH, "u_DepthTexture");
+    mOITBuildDepthH        = GLES31.glGetUniformLocation( oitBuildProgramH, "u_Depth");
+    mOITBuildTexCorrH      = GLES31.glGetUniformLocation( oitBuildProgramH, "u_TexCorr");
+    mOITBuildSizeH         = GLES31.glGetUniformLocation( oitBuildProgramH, "u_Size");
+    mOITBuildNumRecordsH   = GLES31.glGetUniformLocation( oitBuildProgramH, "u_numRecords");
 
     if( mLinkedListSSBO[0]<0 )
       {
@@ -213,24 +236,25 @@ public class DistortedEffects
       }
 
     // BLIT DEPTH RENDER PROGRAM ///////////////////////////
-    final InputStream blitDepthRenderVertStream = resources.openRawResource(R.raw.blit_depth_vertex_shader);
-    final InputStream blitDepthRenderFragStream = resources.openRawResource(R.raw.blit_depth_render_fragment_shader);
+    final InputStream oitRenderVertStream = resources.openRawResource(R.raw.oit_vertex_shader);
+    final InputStream oitRenderFragStream = resources.openRawResource(R.raw.oit_render_fragment_shader);
 
     try
       {
-      mBlitDepthRenderProgram = new DistortedProgram(blitDepthRenderVertStream,blitDepthRenderFragStream,blitVertHeader,blitFragHeader, Distorted.GLSL);
+      mOITRenderProgram = new DistortedProgram(oitRenderVertStream,oitRenderFragStream,Distorted.GLSL_VERSION,Distorted.GLSL_VERSION, Distorted.GLSL);
       }
     catch(Exception e)
       {
-      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile BLIT DEPTH RENDER program: "+e.getMessage());
+      Log.e("EFFECTS", e.getClass().getSimpleName()+" trying to compile OIT RENDER program: "+e.getMessage());
       throw new RuntimeException(e.getMessage());
       }
 
-    int blitDepthRenderProgramH   = mBlitDepthRenderProgram.getProgramHandle();
-    mBlitDepthRenderDepthTextureH = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_DepthTexture");
-    mBlitDepthRenderDepthH        = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_Depth");
-    mBlitDepthRenderTexCorrH      = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_TexCorr");
-    mBlitDepthRenderSizeH         = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_Size");
+    int oitRenderProgramH   = mOITRenderProgram.getProgramHandle();
+    mOITRenderTextureH      = GLES31.glGetUniformLocation( oitRenderProgramH, "u_Texture");
+    mOITRenderDepthTextureH = GLES31.glGetUniformLocation( oitRenderProgramH, "u_DepthTexture");
+    mOITRenderDepthH        = GLES31.glGetUniformLocation( oitRenderProgramH, "u_Depth");
+    mOITRenderTexCorrH      = GLES31.glGetUniformLocation( oitRenderProgramH, "u_TexCorr");
+    mOITRenderSizeH         = GLES31.glGetUniformLocation( oitRenderProgramH, "u_Size");
 
     // NORMAL PROGRAM //////////////////////////////////////
     final InputStream normalVertexStream   = resources.openRawResource(R.raw.normal_vertex_shader);
@@ -393,7 +417,7 @@ public class DistortedEffects
     GLES31.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, mAtomicCounter[0] );
 
     ByteBuffer atomicBuf = (ByteBuffer)GLES31.glMapBufferRange( GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, 4,
-                                                                GLES31.GL_MAP_READ_BIT|GLES31.GL_MAP_WRITE_BIT);
+                                                                GLES31.GL_MAP_WRITE_BIT|GLES31.GL_MAP_INVALIDATE_BUFFER_BIT);
     if( atomicBuf!=null )
       {
       IntBuffer atomicIntBuf = atomicBuf.order(ByteOrder.nativeOrder()).asIntBuffer();
@@ -413,38 +437,54 @@ public class DistortedEffects
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
+// Pass1 of the OIT algorithm. Clear per-pixel head-poiners.
+
+  static void oitClear(DistortedOutputSurface surface)
+    {
+    mOITClearProgram.useProgram();
+
+    GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
+    GLES31.glUniform2f(mOITClearTexCorrH, 1.0f, 1.0f );   // corrections do not really matter here - only present because of common vertex shader.
+    GLES31.glUniform1f( mOITClearDepthH , 1.0f);          // likewise depth
+    GLES31.glUniform2f(mOITClearSizeH, surface.mWidth, surface.mHeight);
+    GLES31.glVertexAttribPointer(mOITClearProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
+    GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Pass2 of the OIT algorithm - build per-pixel linked lists.
 
-  static void blitDepth(DistortedOutputSurface surface, float corrW, float corrH)
+  static void oitBuild(DistortedOutputSurface surface, float corrW, float corrH)
     {
-    mBlitDepthProgram.useProgram();
+    mOITBuildProgram.useProgram();
 
     GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
-    GLES31.glUniform1i(mBlitDepthTextureH, 0);
-    GLES31.glUniform1i(mBlitDepthDepthTextureH, 1);
-    GLES31.glUniform2f(mBlitDepthTexCorrH, corrW, corrH );
-    GLES31.glUniform2f(mBlitDepthSizeH, surface.mWidth, surface.mHeight);
-    GLES31.glUniform1ui(mBlitDepthNumRecordsH, (mBufferSize-surface.mWidth*surface.mHeight)/3 );  // see the fragment shader
-    GLES31.glUniform1f(mBlitDepthDepthH , 1.0f-surface.mNear);
-    GLES31.glVertexAttribPointer(mBlitDepthProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
+    GLES31.glUniform1i(mOITBuildTextureH, 0);
+    GLES31.glUniform1i(mOITBuildDepthTextureH, 1);
+    GLES31.glUniform2f(mOITBuildTexCorrH, corrW, corrH );
+    GLES31.glUniform2f(mOITBuildSizeH, surface.mWidth, surface.mHeight);
+    GLES31.glUniform1ui(mOITBuildNumRecordsH, (mBufferSize-surface.mWidth*surface.mHeight)/3 );  // see the fragment shader
+    GLES31.glUniform1f(mOITBuildDepthH , 1.0f-surface.mNear);
+    GLES31.glVertexAttribPointer(mOITBuildProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
     GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-// render all the transparent pixels from the per-pixel linked lists. This is in the 'merge
-// postprocessing buckets' stage.
+// Pass3 of the OIT algorithm. Render all the transparent pixels from the per-pixel linked lists.
 
-  static void mergeOIT(DistortedOutputSurface surface, float corrW, float corrH)
+  static void oitRender(DistortedOutputSurface surface, float corrW, float corrH)
     {
-    mBlitDepthRenderProgram.useProgram();
+    mOITRenderProgram.useProgram();
 
     //analyzeBuffer(surface.mWidth, surface.mHeight);
 
     GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
-    GLES31.glUniform1i(mBlitDepthRenderDepthTextureH, 1);
-    GLES31.glUniform2f(mBlitDepthRenderTexCorrH, corrW, corrH );
-    GLES31.glUniform2f(mBlitDepthRenderSizeH, surface.mWidth, surface.mHeight);
-    GLES31.glUniform1f( mBlitDepthRenderDepthH , 1.0f-surface.mNear);
-    GLES31.glVertexAttribPointer(mBlitDepthRenderProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
+    GLES31.glUniform1i(mOITRenderTextureH, 0);
+    GLES31.glUniform1i(mOITRenderDepthTextureH, 1);
+    GLES31.glUniform2f(mOITRenderTexCorrH, corrW, corrH );
+    GLES31.glUniform2f(mOITRenderSizeH, surface.mWidth, surface.mHeight);
+    GLES31.glUniform1f( mOITRenderDepthH , 1.0f-surface.mNear);
+    GLES31.glVertexAttribPointer(mOITRenderProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
     GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
     }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedNode.java b/src/main/java/org/distorted/library/main/DistortedNode.java
index 1b300da..20668b5 100644
--- a/src/main/java/org/distorted/library/main/DistortedNode.java
+++ b/src/main/java/org/distorted/library/main/DistortedNode.java
@@ -283,7 +283,6 @@ public class DistortedNode implements DistortedMaster.Slave
 
     if( input.setAsInput() )
       {
-      surface.setAsOutput(currTime);
       mState.apply();
       GLES31.glDisable(GLES31.GL_BLEND);
       mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime, 0);
@@ -303,7 +302,6 @@ public class DistortedNode implements DistortedMaster.Slave
 
     if( input.setAsInput() )
       {
-      surface.setAsOutput(currTime);
       mState.apply();
       mEffects.drawPriv(mSurface.getWidth()/2.0f, mSurface.getHeight()/2.0f, mMesh, surface, currTime, 0);
       return 1;
diff --git a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
index ac92faf..ff91225 100644
--- a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
@@ -78,6 +78,7 @@ public static final int DEBUG_FPS = 1;
 
   // Global buffers used for postprocessing.
   private static DistortedOutputSurface[] mBuffer = new DistortedOutputSurface[EffectQuality.LENGTH];
+  private static DistortedOutputSurface   mBufferOIT;
 
   private long mTime;
   private float mFOV;
@@ -184,16 +185,16 @@ public static final int DEBUG_FPS = 1;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createBuffers(int width, int height, float near)
+  private static void createPostprocessingBuffers(int width, int height, float near)
     {
     float mipmap=1.0f;
 
-    for(int j=0; j<EffectQuality.LENGTH; j++)
+    for (int j=0; j<EffectQuality.LENGTH; j++)
       {
-      mBuffer[j] = new DistortedFramebuffer(2,BOTH_DEPTH_STENCIL,TYPE_SYST, (int)(width*mipmap), (int)(height*mipmap) );
+      mBuffer[j] = new DistortedFramebuffer(2, BOTH_DEPTH_STENCIL, TYPE_SYST, (int) (width * mipmap), (int) (height * mipmap));
       mBuffer[j].mMipmap = mipmap;
-      mBuffer[j].mNear   = near;  // copy mNear as well (for blitting- see PostprocessEffect.apply() )
-      mBuffer[j].glClearColor(1.0f,1.0f,1.0f,0.0f);
+      mBuffer[j].mNear = near;  // copy mNear as well (for blitting- see PostprocessEffect.apply() )
+      mBuffer[j].glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
 
       mipmap *= EffectQuality.MULTIPLIER;
       }
@@ -202,21 +203,85 @@ public static final int DEBUG_FPS = 1;
 
     GLES31.glStencilMask(0xff);
     GLES31.glDepthMask(true);
-    GLES31.glColorMask(true,true,true,true);
-    GLES31.glClearColor(1.0f,1.0f,1.0f,0.0f);
+    GLES31.glColorMask(true, true, true, true);
+    GLES31.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
     GLES31.glClearDepthf(1.0f);
     GLES31.glClearStencil(0);
 
-    for(int j=0; j<EffectQuality.LENGTH; j++)
+    for (int j=0; j<EffectQuality.LENGTH; j++)
       {
       mBuffer[j].setAsOutput();
       GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[1], 0);
-      GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT|GLES31.GL_DEPTH_BUFFER_BIT|GLES31.GL_STENCIL_BUFFER_BIT);
+      GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT | GLES31.GL_DEPTH_BUFFER_BIT | GLES31.GL_STENCIL_BUFFER_BIT);
       GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBuffer[j].mColorH[0], 0);
       GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void initializeOIT(DistortedOutputSurface surface)
+    {
+    if( mBufferOIT==null )
+      {
+      mBufferOIT = new DistortedFramebuffer(1, BOTH_DEPTH_STENCIL, TYPE_SYST, surface.mWidth, surface.mHeight);
+      mBufferOIT.mMipmap = 1.0f;
+      mBufferOIT.mNear = surface.mNear;  // copy mNear as well (for blitting- see PostprocessEffect.apply() )
+      mBufferOIT.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
+
+      DistortedObject.toDo(); // create the FBOs immediately. This is safe as we must be holding the OpenGL context now.
+
+      mBufferOIT.setAsOutput();
+      GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, mBufferOIT.mColorH[0], 0);
+      GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT | GLES31.GL_DEPTH_BUFFER_BIT | GLES31.GL_STENCIL_BUFFER_BIT);
+      }
+
+    if( mBufferOIT.mWidth != surface.mWidth || mBufferOIT.mHeight != surface.mHeight )
+      {
+      mBufferOIT.mWidth  = (int)(surface.mWidth *mBufferOIT.mMipmap);
+      mBufferOIT.mHeight = (int)(surface.mHeight*mBufferOIT.mMipmap);
+
+      mBufferOIT.mNear   = surface.mNear;  // Near plane is independent of the mipmap level
+
+      //android.util.Log.e("surface", "viewport "+i+" to ("+from.mWidth+"x"+from.mHeight+")");
+
+      mBufferOIT.createProjection();
+
+      int maxw = mBufferOIT.mWidth  > mBufferOIT.mRealWidth  ? mBufferOIT.mWidth  : mBufferOIT.mRealWidth;
+      int maxh = mBufferOIT.mHeight > mBufferOIT.mRealHeight ? mBufferOIT.mHeight : mBufferOIT.mRealHeight;
+
+      if (maxw > mBufferOIT.mRealWidth || maxh > mBufferOIT.mRealHeight)
+        {
+        mBufferOIT.mRealWidth = maxw;
+        mBufferOIT.mRealHeight = maxh;
+
+        mBufferOIT.recreate();
+        mBufferOIT.create();
+        }
+      }
+
+    if( mBufferOIT.mNear != surface.mNear || mBufferOIT.mFOV != surface.mFOV )
+      {
+      mBufferOIT.mNear = surface.mNear;
+      mBufferOIT.mFOV  = surface.mFOV;
+      mBufferOIT.createProjection();
+      }
+
+    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mBufferOIT.mFBOH[0]);
+
+    DistortedRenderState.colorDepthStencilOn();
+    GLES31.glClearColor(surface.mClearR, surface.mClearG, surface.mClearB, surface.mClearA);
+    GLES31.glClearDepthf(surface.mClearDepth);
+    GLES31.glClearStencil(surface.mClearStencil);
+    GLES31.glClear(surface.mClear);
+    DistortedRenderState.colorDepthStencilRestore();
+
+    DistortedEffects.zeroOutAtomic();
+    DistortedEffects.oitClear(surface);
+
+    GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT|GLES31.GL_ATOMIC_COUNTER_BARRIER_BIT);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static synchronized void onDestroy()
@@ -225,6 +290,8 @@ public static final int DEBUG_FPS = 1;
       {
       mBuffer[j] = null;
       }
+
+    mBufferOIT = null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -233,9 +300,9 @@ public static final int DEBUG_FPS = 1;
 // smaller. That takes care of outputting pixels to them. When we use them as input, we have to
 // adjust the texture coords - see the get{Width|Height}Correction functions.
 
-  private static void cloneViewport(DistortedOutputSurface from)
+  private static void clonePostprocessingViewport(DistortedOutputSurface from)
     {
-    if( mBuffer[0].mWidth != from.mWidth )
+    if( mBuffer[0].mWidth != from.mWidth || mBuffer[0].mHeight != from.mHeight )
       {
       DistortedOutputSurface surface;
 
@@ -269,7 +336,7 @@ public static final int DEBUG_FPS = 1;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int blitWithDepth(long currTime, DistortedOutputSurface buffer)
+  private int oitBuild(long currTime, DistortedOutputSurface buffer)
     {
     GLES31.glViewport(0, 0, mWidth, mHeight);
     setAsOutput(currTime);
@@ -278,30 +345,38 @@ public static final int DEBUG_FPS = 1;
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mDepthStencilH[0]);
 
-    GLES31.glDisable(GLES31.GL_STENCIL_TEST);
-    GLES31.glStencilMask(0x00);
+    //GLES31.glDisable(GLES31.GL_STENCIL_TEST);
+    //GLES31.glStencilMask(0x00);
+
+    DistortedRenderState.colorDepthStencilOn();
+    DistortedRenderState.enableDepthTest();
 
-    DistortedEffects.blitDepth(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
+    DistortedEffects.oitBuild(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
     GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);
 
+    DistortedRenderState.colorDepthStencilRestore();
+    DistortedRenderState.restoreDepthTest();
+
     return 1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private int mergeOIT(long currTime, DistortedOutputSurface buffer)
+  private int oitRender(long currTime, DistortedOutputSurface buffer)
     {
     GLES31.glViewport(0, 0, mWidth, mHeight);
     setAsOutput(currTime);
+    GLES31.glActiveTexture(GLES31.GL_TEXTURE0);
+    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mColorH[0]);
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mDepthStencilH[0]);
 
     DistortedRenderState.enableStencil();
 
-    DistortedEffects.mergeOIT(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
+    DistortedEffects.oitRender(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);
 
@@ -331,14 +406,16 @@ public static final int DEBUG_FPS = 1;
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Render all children, one by one. If there are no postprocessing effects, just render to THIS.
 // Otherwise, render to a buffer and on each change of Postprocessing Bucket, apply the postprocessing
-// to a whole buffer (lastQueue.postprocess) and merge it (this.blitWithDepth).
+// to a whole buffer (lastQueue.postprocess) and merge it (this.oitBuild).
 
   int renderChildren(long time, int numChildren, ArrayList<DistortedNode> children)
     {
     int quality=0, internalQuality = 0, numRenders = 0, bucketChange = 0;
     DistortedNode child1, child2;
     EffectQueuePostprocess lastQueue=null, currQueue;
-    long lastBucket=0, currBucket;
+    long lastBucket=0, currBucket=0;
+
+    initializeOIT(this);
 
     for(int i=0; i<numChildren; i++)
       {
@@ -346,16 +423,23 @@ public static final int DEBUG_FPS = 1;
       currQueue = child1.getPostprocessQueue();
       currBucket= currQueue.getID();
 
-      if( currBucket==0 ) numRenders += child1.draw(time,this);
+      if( currBucket==0 )
+        {
+        GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mBufferOIT.mFBOH[0]);
+        numRenders += child1.draw(time, mBufferOIT);
+
+        //setAsOutput(time);
+        //numRenders += child1.draw(time,this);
+        }
       else
         {
-        if( mBuffer[0]==null ) createBuffers(mWidth,mHeight,mNear);
+        if( mBuffer[0]==null ) createPostprocessingBuffers(mWidth,mHeight,mNear);
 
         if( lastBucket!=currBucket )
           {
           if( lastBucket==0 )
             {
-            DistortedEffects.zeroOutAtomic();
+            clonePostprocessingViewport(this);
             }
           else
             {
@@ -366,17 +450,16 @@ public static final int DEBUG_FPS = 1;
               }
 
             numRenders += lastQueue.postprocess(mBuffer);
-            numRenders += blitWithDepth(time, mBuffer[quality]);
+            numRenders += mBufferOIT.oitBuild(time, mBuffer[quality]);
             clearBuffer(mBuffer[quality]);
             }
 
           internalQuality = currQueue.getInternalQuality();
           quality         = currQueue.getQuality();
           bucketChange    = i;
-
-          cloneViewport(this);
           }
 
+        mBuffer[quality].setAsOutput(time);
         child1.drawNoBlend(time,mBuffer[quality]);
 
         if( i==numChildren-1 )
@@ -388,15 +471,20 @@ public static final int DEBUG_FPS = 1;
             }
 
           numRenders += currQueue.postprocess(mBuffer);
-          numRenders += blitWithDepth(time, mBuffer[quality]);
-          GLES31.glMemoryBarrier(GLES31.GL_ALL_BARRIER_BITS);
-          numRenders += mergeOIT(time,mBuffer[quality]);  // merge the OIT linked list
+          numRenders += mBufferOIT.oitBuild(time, mBuffer[quality]);
+          GLES31.glMemoryBarrier(GLES31.GL_SHADER_STORAGE_BARRIER_BIT);
+          numRenders += oitRender(time,mBufferOIT);  // merge the OIT linked list
           clearBuffer(mBuffer[quality]);
           }
-        }
+        } // end else (postprocessed child)
 
       lastQueue = currQueue;
       lastBucket= currBucket;
+      } // end main for loop
+
+    if( currBucket==0 ) // there was no postprocessing - we need to merge the main buffer
+      {
+      numRenders += oitRender(time,mBufferOIT);
       }
 
     return numRenders;
@@ -669,11 +757,6 @@ public static final int DEBUG_FPS = 1;
       mNear=0.99f;
       }
 
-    if( mBuffer[0]!=null )
-      {
-      for(int j=0; j<EffectQuality.LENGTH; j++) mBuffer[j].mNear = mNear;
-      }
-
     createProjection();
     }
 
diff --git a/src/main/java/org/distorted/library/main/DistortedRenderState.java b/src/main/java/org/distorted/library/main/DistortedRenderState.java
index 86a224f..7b191cc 100644
--- a/src/main/java/org/distorted/library/main/DistortedRenderState.java
+++ b/src/main/java/org/distorted/library/main/DistortedRenderState.java
@@ -184,6 +184,38 @@ public class DistortedRenderState
       }
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void enableDepthTest()
+    {
+    sState.depthTest = cState.depthTest;
+
+    if (cState.depthTest != 1)
+      {
+      cState.depthTest = 1;
+      GLES31.glEnable(GLES31.GL_DEPTH_TEST);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void restoreDepthTest()
+    {
+    if (sState.depthTest != cState.depthTest)
+      {
+      cState.depthTest = sState.depthTest;
+
+      if (cState.depthTest == 0)
+        {
+        GLES31.glDisable(GLES31.GL_DEPTH_TEST);
+        }
+      else
+        {
+        GLES31.glEnable(GLES31.GL_DEPTH_TEST);
+        }
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static void switchOffDrawing()
diff --git a/src/main/res/raw/blit_depth_fragment_shader.glsl b/src/main/res/raw/blit_depth_fragment_shader.glsl
deleted file mode 100644
index 3397874..0000000
--- a/src/main/res/raw/blit_depth_fragment_shader.glsl
+++ /dev/null
@@ -1,127 +0,0 @@
-//////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2018 Leszek Koltunski                                                          //
-//                                                                                          //
-// This file is part of Distorted.                                                          //
-//                                                                                          //
-// Distorted is free software: you can redistribute it and/or modify                        //
-// it under the terms of the GNU General Public License as published by                     //
-// the Free Software Foundation, either version 2 of the License, or                        //
-// (at your option) any later version.                                                      //
-//                                                                                          //
-// Distorted is distributed in the hope that it will be useful,                             //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
-// GNU General Public License for more details.                                             //
-//                                                                                          //
-// You should have received a copy of the GNU General Public License                        //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-precision highp float;
-precision highp int;
-
-out vec4 fragColor;
-in vec2 v_TexCoordinate;
-in vec2 v_Pixel;              // location of the current fragment, in pixels
-
-uniform sampler2D u_Texture;
-uniform sampler2D u_DepthTexture;
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-// per-pixel linked list. Order Independent Transparency.
-
-uniform vec2 u_Size;
-uniform uint u_numRecords;
-
-layout (binding=0, offset=0) uniform atomic_uint u_Counter;
-
-layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
-  {                                          // one for each pixel in the Output rectangle.
-  uint u_Records[];                          //
-  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
-                                             // triplets of (pointer,depth,rgba).
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-// Concurrent insert to a linked list. Tim Harris, 'pragmatic implementation of non-blocking
-// linked-lists', 2001.
-// This arranges fragments by decreasing 'depth', so one would think - from back to front, but
-// in main() below the depth is mapped with S*(1-depth)/2, so it is really front to back.
-
-void insert( vec2 ij, uint depth, uint rgba )
-  {
-  uint ptr = atomicCounterIncrement(u_Counter);
-/*
-  if( ptr<u_numRecords )
-    {
-    ptr = 3u*ptr + uint(u_Size.x*u_Size.y);
-
-	u_Records[ptr   ] = 0u;
-    u_Records[ptr+1u] = depth;
-    u_Records[ptr+2u] = rgba;//(255u<<16u) + (255u);//rgba;
-
-    uint index = uint(ij.x + ij.y * u_Size.x);
-
-    u_Records[index] = ptr;
-    discard;
-    }
-*/
-  if( ptr<u_numRecords )
-    {
-    ptr = 3u*ptr + uint(u_Size.x*u_Size.y);
-
-    u_Records[ptr+1u] = depth;
-    u_Records[ptr+2u] = rgba;
-
-    memoryBarrier();
-
-    uint prev = uint(ij.x + ij.y * u_Size.x);
-    uint curr = u_Records[prev];
-
-    while (true)
-      {
-      if ( curr==0u || depth > u_Records[curr+1u] )  // need to insert here
-        {
-        u_Records[ptr] = curr;     // next of new record is curr
-        memoryBarrier();
-        uint res = atomicCompSwap( u_Records[prev], curr, ptr );
-
-        if (res==curr) break;      // done!
-        else           curr = res; // could not insert! retry from same place in list
-        }
-      else                         // advance in list
-        {
-        prev = curr;
-        curr = u_Records[prev];
-        }
-      }
-
-    discard;
-    }
-  }
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-uint convert(vec4 c)
-  {
-  return ((uint(255.0*c.r))<<24u) + ((uint(255.0*c.g))<<16u) + ((uint(255.0*c.b))<<8u) + uint(255.0*c.a);
-  }
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-void main()                    		
-  {
-  vec4 frag  = texture(u_Texture     , v_TexCoordinate);
-  float depth= texture(u_DepthTexture, v_TexCoordinate).r;
-
-  if( frag.a > 0.95 )
-    {
-    gl_FragDepth = depth;
-    fragColor    = frag;
-    }
-  else if( frag.a > 0.0 )
-    {
-    const float S= 2147483647.0; // max signed int. Could probably be max unsigned int but this is enough.
-    insert(v_Pixel, uint(S*(1.0-depth)/2.0), convert(frag) );
-    }
-  else discard;
-  }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_depth_render_fragment_shader.glsl b/src/main/res/raw/blit_depth_render_fragment_shader.glsl
deleted file mode 100644
index 70cbfef..0000000
--- a/src/main/res/raw/blit_depth_render_fragment_shader.glsl
+++ /dev/null
@@ -1,79 +0,0 @@
-//////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2018 Leszek Koltunski                                                          //
-//                                                                                          //
-// This file is part of Distorted.                                                          //
-//                                                                                          //
-// Distorted is free software: you can redistribute it and/or modify                        //
-// it under the terms of the GNU General Public License as published by                     //
-// the Free Software Foundation, either version 2 of the License, or                        //
-// (at your option) any later version.                                                      //
-//                                                                                          //
-// Distorted is distributed in the hope that it will be useful,                             //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
-// GNU General Public License for more details.                                             //
-//                                                                                          //
-// You should have received a copy of the GNU General Public License                        //
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-precision highp float;
-precision highp int;
-
-out vec4 fragColor;           // The output color
-in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
-in vec2 v_Pixel;              // location of the current fragment, in pixels
-
-uniform sampler2D u_DepthTexture;
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-// per-pixel linked list. Order Independent Transparency.
-
-uniform vec2 u_Size;
-
-layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
-  {                                          // one for each pixel in the Output rectangle.
-  uint u_Records[];                          //
-  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
-                                             // triplets of (pointer,depth,rgba).
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-vec4 convert(uint rgba)
-  {
-  return vec4( float((rgba>>24u)&255u),float((rgba>>16u)&255u),float((rgba>>8u)&255u),float(rgba&255u) ) / 255.0;
-  }
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-// https://en.wikipedia.org/wiki/Alpha_compositing (premultiplied)
-
-vec4 blend(vec4 clr,vec4 srf)
-  {
-  return clr + (1.0 - clr.a) * vec4(srf.rgb * srf.a , srf.a);
-  }
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-void main()                    		
-  {
-  uint index = uint(v_Pixel.x + v_Pixel.y * u_Size.x);
-  uint curr = u_Records[index];
-
-  if (curr == 0u) discard;
-  else
-    {
-    u_Records[index] = 0u;
-
-    vec4 color= convert(u_Records[curr+2u]);
-    curr = u_Records[curr];
-
-    while (curr > 0u)
-      {
-      color= blend( color, convert(u_Records[curr+2u]) );  // keep walking the linked list
-      curr = u_Records[curr];                              // and blending the colors in
-      }
-
-    gl_FragDepth = texture(u_DepthTexture, v_TexCoordinate).r;
-    fragColor    = color;
-    }
-  }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_depth_vertex_shader.glsl b/src/main/res/raw/blit_depth_vertex_shader.glsl
deleted file mode 100644
index 00f5196..0000000
--- a/src/main/res/raw/blit_depth_vertex_shader.glsl
+++ /dev/null
@@ -1,42 +0,0 @@
-//////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                          //
-//                                                                                          //
-// This file is part of Distorted.                                                          //
-//                                                                                          //
-// Distorted is free software: you can redistribute it and/or modify                        //
-// it under the terms of the GNU General Public License as published by                     //
-// the Free Software Foundation, either version 2 of the License, or                        //
-// (at your option) any later version.                                                      //
-//                                                                                          //
-// Distorted is distributed in the hope that it will be useful,                             //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
-// GNU General Public License for more details.                                             //
-//                                                                                          //
-// You should have received a copy of the GNU General Public License                        // 
-// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-precision highp float;
-precision highp int;
-
-in vec2 a_Position;           // Per-vertex position.
-out vec2 v_TexCoordinate;     //
-out vec2 v_Pixel;             //
-
-uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
-uniform vec2  u_TexCorr;      // when we blit from postprocessing buffers, the buffers can be
-                              // larger than necessary (there is just one static set being
-                              // reused!) so we need to compensate here by adjusting the texture
-                              // coords.
-
-uniform vec2 u_Size;         // size of the output surface, in pixels.
-
-//////////////////////////////////////////////////////////////////////////////////////////////
-
-void main()
-  {
-  v_TexCoordinate = (a_Position + 0.5) * u_TexCorr;
-  v_Pixel         = v_TexCoordinate * u_Size;
-  gl_Position     = vec4(2.0*a_Position,u_Depth,1.0);
-  }
diff --git a/src/main/res/raw/blit_fragment_shader.glsl b/src/main/res/raw/blit_fragment_shader.glsl
index 5dec392..fa99194 100644
--- a/src/main/res/raw/blit_fragment_shader.glsl
+++ b/src/main/res/raw/blit_fragment_shader.glsl
@@ -19,22 +19,13 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 out vec4 fragColor;           // The output color
 in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
-#define TEXTURE texture
-#define FRAG_COLOR fragColor
-#else
-varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
-#define TEXTURE texture2D
-#define FRAG_COLOR gl_FragColor
-#endif
-
 uniform sampler2D u_Texture;  // The input texture.
 
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()                    		
   {
-  FRAG_COLOR = TEXTURE(u_Texture,v_TexCoordinate);
+  fragColor = texture(u_Texture,v_TexCoordinate);
   }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_vertex_shader.glsl b/src/main/res/raw/blit_vertex_shader.glsl
index 7080267..88b64b1 100644
--- a/src/main/res/raw/blit_vertex_shader.glsl
+++ b/src/main/res/raw/blit_vertex_shader.glsl
@@ -19,14 +19,8 @@
 
 precision lowp float;
 
-#if __VERSION__ != 100
 in vec2 a_Position;           // Per-vertex position.
 out vec2 v_TexCoordinate;     //
-#else
-attribute vec2 a_Position;    // Per-vertex position.
-varying vec2 v_TexCoordinate; //
-#endif
-
 uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
 
 //////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/raw/oit_build_fragment_shader.glsl b/src/main/res/raw/oit_build_fragment_shader.glsl
new file mode 100644
index 0000000..eb65a37
--- /dev/null
+++ b/src/main/res/raw/oit_build_fragment_shader.glsl
@@ -0,0 +1,117 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                          //
+//                                                                                          //
+// This file is part of Distorted.                                                          //
+//                                                                                          //
+// Distorted is free software: you can redistribute it and/or modify                        //
+// it under the terms of the GNU General Public License as published by                     //
+// the Free Software Foundation, either version 2 of the License, or                        //
+// (at your option) any later version.                                                      //
+//                                                                                          //
+// Distorted is distributed in the hope that it will be useful,                             //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
+// GNU General Public License for more details.                                             //
+//                                                                                          //
+// You should have received a copy of the GNU General Public License                        //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+precision highp float;
+precision highp int;
+
+out vec4 fragColor;
+in vec2 v_TexCoordinate;
+in vec2 v_Pixel;              // location of the current fragment, in pixels
+
+uniform sampler2D u_Texture;
+uniform sampler2D u_DepthTexture;
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform vec2 u_Size;
+uniform uint u_numRecords;
+
+layout (binding=0, offset=0) uniform atomic_uint u_Counter;
+
+layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
+  {                                          // one for each pixel in the Output rectangle.
+  uint u_Records[];                          //
+  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
+                                             // triplets of (pointer,depth,rgba).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Concurrent insert to a linked list. Tim Harris, 'pragmatic implementation of non-blocking
+// linked-lists', 2001.
+// This arranges fragments by decreasing 'depth', so one would think - from back to front, but
+// in main() below the depth is mapped with S*(1-depth)/2, so it is really front to back.
+
+void insert( vec2 ij, uint depth, uint rgba )
+  {
+  uint ptr = atomicCounterIncrement(u_Counter);
+
+  if( ptr<u_numRecords )
+    {
+    ptr = 3u*ptr + uint(u_Size.x*u_Size.y);
+
+    u_Records[ptr+1u] = depth;
+    u_Records[ptr+2u] = rgba;
+
+    memoryBarrier();
+
+    uint prev = uint(ij.x + ij.y * u_Size.x);
+    uint curr = u_Records[prev];
+
+    while (true)
+      {
+      if ( curr==0u || depth > u_Records[curr+1u] )  // need to insert here
+        {
+        u_Records[ptr] = curr;     // next of new record is curr
+        memoryBarrier();
+        uint res = atomicCompSwap( u_Records[prev], curr, ptr );
+
+        if (res==curr) break;      // done!
+        else           curr = res; // could not insert! retry from same place in list
+        }
+      else                         // advance in list
+        {
+        prev = curr;
+        curr = u_Records[prev];
+        }
+      }
+
+    discard;
+    }
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+uint convert(vec4 c)
+  {
+  return ((uint(255.0*c.r))<<24u) + ((uint(255.0*c.g))<<16u) + ((uint(255.0*c.b))<<8u) + uint(255.0*c.a);
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Pass2 of the OIT algorithm - build the LinkedList phase.
+
+void main()                    		
+  {
+  vec4 frag  = texture(u_Texture     , v_TexCoordinate);
+  float depth= texture(u_DepthTexture, v_TexCoordinate).r;
+
+  if( frag.a > 0.95 )
+    {
+    gl_FragDepth = depth;
+    fragColor    = frag;
+    }
+  else
+    {
+    if( frag.a > 0.0 )
+      {
+      const float S= 2147483647.0; // max signed int. Could probably be max unsigned int but this is enough.
+      insert(v_Pixel, uint(S*(1.0-depth)/2.0), convert(frag) );
+      }
+    discard;
+    }
+  }
\ No newline at end of file
diff --git a/src/main/res/raw/oit_clear_fragment_shader.glsl b/src/main/res/raw/oit_clear_fragment_shader.glsl
new file mode 100644
index 0000000..701fa42
--- /dev/null
+++ b/src/main/res/raw/oit_clear_fragment_shader.glsl
@@ -0,0 +1,47 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                          //
+//                                                                                          //
+// This file is part of Distorted.                                                          //
+//                                                                                          //
+// Distorted is free software: you can redistribute it and/or modify                        //
+// it under the terms of the GNU General Public License as published by                     //
+// the Free Software Foundation, either version 2 of the License, or                        //
+// (at your option) any later version.                                                      //
+//                                                                                          //
+// Distorted is distributed in the hope that it will be useful,                             //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
+// GNU General Public License for more details.                                             //
+//                                                                                          //
+// You should have received a copy of the GNU General Public License                        //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+precision highp float;
+precision highp int;
+
+in vec2 v_TexCoordinate;
+in vec2 v_Pixel;              // location of the current fragment, in pixels
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform vec2 u_Size;
+
+layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
+  {                                          // one for each pixel in the Output rectangle.
+  uint u_Records[];                          //
+  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
+                                             // triplets of (pointer,depth,rgba).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Pass1 of the OIT algorithm - 'clear the head pointers' phase.
+// No we cannot optimize this out by moving the 'u_Records[index]=0u' to the end of the Pass3,
+// because between passes the size of the surface we render to might change.
+
+void main()                    		
+  {
+  uint index= uint(v_Pixel.x + v_Pixel.y * u_Size.x);
+  u_Records[index] = 0u;
+  discard;
+  }
\ No newline at end of file
diff --git a/src/main/res/raw/oit_render_fragment_shader.glsl b/src/main/res/raw/oit_render_fragment_shader.glsl
new file mode 100644
index 0000000..0a15189
--- /dev/null
+++ b/src/main/res/raw/oit_render_fragment_shader.glsl
@@ -0,0 +1,91 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                          //
+//                                                                                          //
+// This file is part of Distorted.                                                          //
+//                                                                                          //
+// Distorted is free software: you can redistribute it and/or modify                        //
+// it under the terms of the GNU General Public License as published by                     //
+// the Free Software Foundation, either version 2 of the License, or                        //
+// (at your option) any later version.                                                      //
+//                                                                                          //
+// Distorted is distributed in the hope that it will be useful,                             //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
+// GNU General Public License for more details.                                             //
+//                                                                                          //
+// You should have received a copy of the GNU General Public License                        //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+precision highp float;
+precision highp int;
+
+out vec4 fragColor;           // The output color
+in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
+in vec2 v_Pixel;              // location of the current fragment, in pixels
+
+uniform sampler2D u_Texture;
+uniform sampler2D u_DepthTexture;
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform vec2 u_Size;
+
+layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
+  {                                          // one for each pixel in the Output rectangle.
+  uint u_Records[];                          //
+  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
+                                             // triplets of (pointer,depth,rgba).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+vec4 convert(uint rgba)
+  {
+  return vec4( float((rgba>>24u)&255u),float((rgba>>16u)&255u),float((rgba>>8u)&255u),float(rgba&255u) ) / 255.0;
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// https://en.wikipedia.org/wiki/Alpha_compositing (premultiplied)
+
+vec4 blend(vec4 clr,vec4 srf)
+  {
+  return clr + (1.0 - clr.a) * vec4(srf.rgb * srf.a , srf.a);
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Pass3 of the OIT algorithm - traverse the per-pixel LinkedList and build the final color.
+
+void main()                    		
+  {
+  float texdepth = texture(u_DepthTexture, v_TexCoordinate).r;
+  vec4  color    = texture(u_Texture     , v_TexCoordinate);
+  uint  index    = uint(v_Pixel.x + v_Pixel.y * u_Size.x);
+  uint  curr     = u_Records[index];
+
+  if (curr != 0u)
+    {
+    const float S= 2147483647.0;
+    uint depth = u_Records[curr+1u];
+    uint texdepthuint = uint(S*(1.0-texdepth)/2.0);
+
+    if( depth >= texdepthuint )
+      {
+      vec4 clr= convert(u_Records[curr+2u]);
+      curr = u_Records[curr];
+
+      while (curr > 0u)
+        {
+        depth= u_Records[curr+1u];                       // keep walking the linked list
+        if( depth < texdepthuint ) break;                // until we reach scene depth
+        clr= blend( clr, convert(u_Records[curr+2u]) );  // and blending the colors in
+        curr = u_Records[curr];
+        }
+
+      color = blend( clr, color);
+      }
+    }
+
+  gl_FragDepth = texdepth;
+  fragColor    = color;
+  }
\ No newline at end of file
diff --git a/src/main/res/raw/oit_vertex_shader.glsl b/src/main/res/raw/oit_vertex_shader.glsl
new file mode 100644
index 0000000..17228a5
--- /dev/null
+++ b/src/main/res/raw/oit_vertex_shader.glsl
@@ -0,0 +1,42 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                          //
+//                                                                                          //
+// This file is part of Distorted.                                                          //
+//                                                                                          //
+// Distorted is free software: you can redistribute it and/or modify                        //
+// it under the terms of the GNU General Public License as published by                     //
+// the Free Software Foundation, either version 2 of the License, or                        //
+// (at your option) any later version.                                                      //
+//                                                                                          //
+// Distorted is distributed in the hope that it will be useful,                             //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
+// GNU General Public License for more details.                                             //
+//                                                                                          //
+// You should have received a copy of the GNU General Public License                        // 
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+precision highp float;
+precision highp int;
+
+in vec2 a_Position;           // Per-vertex position.
+out vec2 v_TexCoordinate;     //
+out vec2 v_Pixel;             // 2D pixel coords in window space
+
+uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
+uniform vec2  u_TexCorr;      // when we blit from postprocessing buffers, the buffers can be
+                              // larger than necessary (there is just one static set being
+                              // reused!) so we need to compensate here by adjusting the texture
+                              // coords.
+
+uniform vec2 u_Size;          // size of the output surface, in pixels.
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+void main()
+  {
+  v_TexCoordinate = (a_Position + 0.5) * u_TexCorr;
+  v_Pixel         = (a_Position + 0.5) * u_Size;
+  gl_Position     = vec4(2.0*a_Position,u_Depth,1.0);
+  }
