commit ec5411694eab95bec510223f985ea3e36976e4b1
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Mon Dec 1 19:52:02 2025 +0100

    Move the shaders from resource files directly to the code.

diff --git a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
index f95a2d9..9c38196 100644
--- a/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
+++ b/src/main/java/org/distorted/library/effectqueue/EffectQueuePostprocess.kt
@@ -67,7 +67,7 @@ class EffectQueuePostprocess : EffectQueue
 
         ///////////////////////////////////////////////////////////////////////////////////////////
         
-        fun createPrograms(vert: ByteArray, frag: ByteArray, GLSL: Int)
+        fun createPrograms(vert: String, frag: String, GLSL: Int)
         {
             val numV = VertexEffect.getNumEnabled()
 
diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.kt b/src/main/java/org/distorted/library/main/DistortedLibrary.kt
index a260358..fb38b0d 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.kt
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.kt
@@ -47,6 +47,8 @@ import org.distorted.library.message.EffectMessageSender
 import org.distorted.library.program.DistortedProgram
 import org.distorted.library.program.VertexCompilationException
 import org.distorted.library.type.Dynamic
+import org.distorted.library.shaders.*
+
 import java.nio.ByteBuffer
 import java.nio.ByteOrder
 import java.nio.FloatBuffer
@@ -278,7 +280,6 @@ object DistortedLibrary
     interface LibraryUser
     {
         fun distortedException(ex: Exception)
-        fun localFile(fileID: Int): ByteArray
         fun logMessage(message: String)
     }
     private lateinit var mUser: LibraryUser
@@ -287,8 +288,8 @@ object DistortedLibrary
     private fun createMainProgram()
     {
         // MAIN PROGRAM ////////////////////////////////////
-        val mainVertStream = mUser.localFile(R.raw.main_vertex_shader)
-        val mainFragStream = mUser.localFile(R.raw.main_fragment_shader)
+        val mainVertStream = Main.vertex()
+        val mainFragStream = Main.fragment()
 
         val numF = FragmentEffect.getNumEnabled()
         val numV = VertexEffect.getNumEnabled()
@@ -324,8 +325,8 @@ object DistortedLibrary
         mTransformFeedbackH = GLES30.glGetUniformLocation(mMainProgramH, "u_TransformFeedback")
 
         // BLIT PROGRAM ////////////////////////////////////
-        val blitVertStream = mUser.localFile(R.raw.blit_vertex_shader)
-        val blitFragStream = mUser.localFile(R.raw.blit_fragment_shader)
+        val blitVertStream = Blit.vertex()
+        val blitFragStream = Blit.fragment()
 
         try
         {
@@ -342,8 +343,8 @@ object DistortedLibrary
         mBlitDepthH = GLES30.glGetUniformLocation(blitProgramH, "u_Depth")
 
         // BLIT DEPTH PROGRAM ////////////////////////////////////
-        val blitDepthVertStream = mUser.localFile(R.raw.blit_depth_vertex_shader)
-        val blitDepthFragStream = mUser.localFile(R.raw.blit_depth_fragment_shader)
+        val blitDepthVertStream = BlitDepth.vertex()
+        val blitDepthFragStream = BlitDepth.fragment()
 
         try
         {
@@ -364,8 +365,8 @@ object DistortedLibrary
     ///////////////////////////////////////////////////////////////////////////////////////////////////
     private fun createNormalProgram()
     {
-        val normalVertexStream = mUser.localFile(R.raw.normal_vertex_shader)
-        val normalFragmentStream = mUser.localFile(R.raw.normal_fragment_shader)
+        val normalVertexStream   = Normal.vertex()
+        val normalFragmentStream = Normal.fragment()
 
         try
         {
@@ -384,8 +385,8 @@ object DistortedLibrary
     ///////////////////////////////////////////////////////////////////////////////////////////////////
     private fun createFullProgram()
     {
-        val fullVertStream = mUser.localFile(R.raw.main_vertex_shader)
-        val fullFragStream = mUser.localFile(R.raw.main_fragment_shader)
+        val fullVertStream = Main.vertex()
+        val fullFragStream = Main.fragment()
 
         val numV = allEnabled
 
@@ -423,8 +424,8 @@ object DistortedLibrary
     private fun createOITProgram()
     {
         // MAIN OIT PROGRAM ////////////////////////////////
-        val mainVertStream = mUser.localFile(R.raw.main_vertex_shader)
-        val mainFragStream = mUser.localFile(R.raw.main_fragment_shader)
+        val mainVertStream = Main.vertex()
+        val mainFragStream = Main.fragment()
 
         val numF = FragmentEffect.getNumEnabled()
         val numV = VertexEffect.getNumEnabled()
@@ -458,8 +459,8 @@ object DistortedLibrary
         mMainOITNumRecordsH = GLES30.glGetUniformLocation(mMainOITProgramH, "u_numRecords")
 
         // OIT CLEAR PROGRAM ////////////////////////////////////
-        val oitClearVertStream = mUser.localFile(R.raw.oit_vertex_shader)
-        val oitClearFragStream = mUser.localFile(R.raw.oit_clear_fragment_shader)
+        val oitClearVertStream = Oit.vertex()
+        val oitClearFragStream = Oit.fragment_clear()
 
         try
         {
@@ -477,8 +478,8 @@ object DistortedLibrary
         mOITClearSizeH = GLES30.glGetUniformLocation(oitClearProgramH, "u_Size")
 
         // OIT BUILD PROGRAM ////////////////////////////////////
-        val oitBuildVertStream = mUser.localFile(R.raw.oit_vertex_shader)
-        val oitBuildFragStream = mUser.localFile(R.raw.oit_build_fragment_shader)
+        val oitBuildVertStream = Oit.vertex()
+        val oitBuildFragStream = Oit.fragment_build()
 
         try
         {
@@ -499,8 +500,8 @@ object DistortedLibrary
         mOITBuildNumRecordsH   = GLES30.glGetUniformLocation(oitBuildProgramH, "u_numRecords")
 
         // OIT COLLAPSE PROGRAM ///////////////////////////
-        val oitCollapseVertStream = mUser.localFile(R.raw.oit_vertex_shader)
-        val oitCollapseFragStream = mUser.localFile(R.raw.oit_collapse_fragment_shader)
+        val oitCollapseVertStream = Oit.vertex()
+        val oitCollapseFragStream = Oit.fragment_collapse()
 
         try
         {
@@ -519,8 +520,8 @@ object DistortedLibrary
         mOITCollapseSizeH         = GLES30.glGetUniformLocation(oitCollapseProgramH, "u_Size")
 
         // OIT RENDER PROGRAM ///////////////////////////
-        val oitRenderVertStream = mUser.localFile(R.raw.oit_vertex_shader)
-        val oitRenderFragStream = mUser.localFile(R.raw.oit_render_fragment_shader)
+        val oitRenderVertStream = Oit.vertex()
+        val oitRenderFragStream = Oit.fragment_render()
 
         try
         {
@@ -1050,8 +1051,8 @@ object DistortedLibrary
 
         try
         {
-            val vertStream = mUser.localFile(R.raw.main_vertex_shader)
-            val fragStream = mUser.localFile(R.raw.preprocess_fragment_shader)
+            val vertStream = Main.vertex()
+            val fragStream = Postprocess.fragment()
             createPrograms(vertStream, fragStream, gLSL)
         }
         catch (ex: Exception)
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.kt b/src/main/java/org/distorted/library/program/DistortedProgram.kt
index 9d1646a..b2f9593 100644
--- a/src/main/java/org/distorted/library/program/DistortedProgram.kt
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.kt
@@ -306,14 +306,14 @@ class DistortedProgram
     }
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
-    private fun readTextFileFromRawResource(inputBytes: ByteArray, doAttributes: Boolean): String?
+    private fun readTextFileFromRawResource(inputBytes: String, doAttributes: Boolean): String?
     {
         val body = StringBuilder()
         var attrList: String? = ""
 
         try
         {
-            val lines = inputBytes.toString(Charsets.UTF_8).lines()
+            val lines = inputBytes.lines()
 
             for (nextLine in lines)
             {
@@ -446,7 +446,7 @@ class DistortedProgram
     *
     * @y.exclude
     */
-    constructor(vert: ByteArray, frag: ByteArray, vertHeader: String,
+    constructor(vert: String, frag: String, vertHeader: String,
                 fragHeader: String, glslVersion: Int, feedback: Array<String>?)
     {
         init(glslVersion)
@@ -473,7 +473,7 @@ class DistortedProgram
     *
     * @y.exclude
     */
-    constructor(vert: ByteArray, frag: ByteArray, vertHeader: String, fragHeader: String,
+    constructor(vert: String, frag: String, vertHeader: String, fragHeader: String,
                 enabledVert: String?, enabledFrag: String?, glslVersion: Int, feedback: Array<String>?)
     {
         init(glslVersion)
@@ -503,7 +503,7 @@ class DistortedProgram
     *
     * @y.exclude
     */
-    constructor( vert: ByteArray, frag: ByteArray, vertHeader: String, fragHeader: String, glslVersion: Int) :
+    constructor( vert: String, frag: String, vertHeader: String, fragHeader: String, glslVersion: Int) :
             this(vert, frag, vertHeader, fragHeader, glslVersion, null)
 
     ///////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/shaders/Blit.kt b/src/main/java/org/distorted/library/shaders/Blit.kt
new file mode 100644
index 0000000..6480b6a
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/Blit.kt
@@ -0,0 +1,69 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object Blit
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// a_Position         Per-vertex position.
+// v_TexCoordinate
+// u_Depth            Distance from the near plane to render plane, in clip coords
+
+    fun vertex() : String
+    {
+    return """
+           precision lowp float;
+
+           in vec2 a_Position;
+           out vec2 v_TexCoordinate;
+           uniform float u_Depth;
+
+           void main()
+             {
+             v_TexCoordinate = a_Position + 0.5;
+             gl_Position     = vec4(2.0*a_Position,u_Depth,1.0);
+             }
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// fragColor         The output color
+// v_TexCoordinate   Interpolated texture coordinate per fragment.
+// u_Texture         The input texture.
+
+    fun fragment() : String
+    {
+    return """
+           precision lowp float;
+
+           out vec4 fragColor; 
+           in vec2 v_TexCoordinate;
+           uniform sampler2D u_Texture;
+
+           void main()                    		
+             {
+             fragColor = texture(u_Texture,v_TexCoordinate);
+             }
+           """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/shaders/BlitDepth.kt b/src/main/java/org/distorted/library/shaders/BlitDepth.kt
new file mode 100644
index 0000000..0500804
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/BlitDepth.kt
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object BlitDepth
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// a_Position - Per-vertex position.
+// 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.
+
+    fun vertex() : String
+    {
+    return """
+           precision highp float;
+
+           in vec2 a_Position; 
+           out vec2 v_TexCoordinate;
+           uniform vec2  u_TexCorr;
+
+           void main()
+             {
+             v_TexCoordinate = (a_Position + 0.5) * u_TexCorr;
+             gl_Position     = vec4(2.0*a_Position,0.0,1.0);
+             }
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// fragColor       - The output color
+// v_TexCoordinate - Interpolated texture coordinate per fragment.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////
+// why -0.0003 ? On some drivers (for example Qualcomm's V@331, V@415 on Adreno 308 - also on
+// some Mali devices ) without it the depth test - when postprocessing a flat 2D object - does
+// not pass (for unknown reasons) and we don't see the Glow halo.  See for example the 'Glowing
+// Leaf' test app.
+// Why exactly 0.0003? Testing of the Cube app on the SM-A315F. ( Mali G52, drivr r20p0 )
+// 0.0003 shouldn't hurt anyway.
+
+    fun fragment() : String
+    {
+    return """
+           precision highp float;
+
+           out vec4 fragColor;
+           in vec2 v_TexCoordinate;
+           uniform sampler2D u_Texture;
+           uniform sampler2D u_DepthTexture;
+
+           void main()                    		
+             {
+             gl_FragDepth = texture(u_DepthTexture,v_TexCoordinate).r -0.0003;
+             fragColor    = texture(u_Texture     ,v_TexCoordinate);
+             }
+           """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/shaders/Main.kt b/src/main/java/org/distorted/library/shaders/Main.kt
new file mode 100644
index 0000000..e03d0bc
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/Main.kt
@@ -0,0 +1,336 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object Main
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun vertex() : String
+    {
+    return """
+precision highp float;
+precision highp int;
+
+in vec3 a_Position;                   // Per-vertex position.
+in vec3 a_Normal;                     // Per-vertex normal vector.
+in vec2 a_TexCoordinate;              // Per-vertex texture coordinate.
+in float a_Component;                 // The component a vertex belongs to.
+                                      // to a vertex effect. An effect will only be active on a vertex iff (a_Association & vAssociation[effect]) != 0.
+                                      // ( see VertexEffect.retSection() )
+
+out vec3 v_Position;                  // for Transform Feedback only
+out vec3 v_endPosition;               // for Transform Feedback only
+out vec3 v_Normal;                    //
+out vec2 v_TexCoordinate;             //
+
+uniform mat4 u_MVPMatrix;             // u_MVMatrixP * projection.
+uniform mat4 u_MVMatrixP;             // the combined model/view matrix. (for points)
+uniform mat4 u_MVMatrixV;             // the combined model/view matrix. (for vectors)
+                                      // which need to work differently on points and vectors
+uniform float u_Inflate;              // how much should we inflate (>0.0) or deflate (<0.0) the mesh.
+uniform int u_TransformFeedback;      // are we doing the transform feedback now?
+
+#ifdef COMP_CENTERS
+layout (std140) uniform componentCenter
+  {
+  vec4 vComCenter[MAX_COMPON];        // centers of mesh components. 4 floats: (x,y,z,unused)
+  };
+#endif
+
+#ifdef BUGGY_UBOS
+layout (packed) uniform componentAssociation
+  {
+  ivec2 vComAssoc[MAX_COMPON];        // component 'AND' and 'EQU' Associations
+  };
+#else
+layout (std140) uniform componentAssociation
+  {
+  ivec4 vComAssoc[MAX_COMPON];        // component 'AND' and 'EQU' Associations
+  };
+#endif
+
+#if NUM_VERTEX>0
+uniform int vNumEffects;              // total number of vertex effects
+
+layout (std140) uniform vUniformProperties
+  {
+  ivec4 vProperties[NUM_VERTEX];      // their properties, 4 ints:
+                                      // 1: name of the effect
+                                      // 2: effect's AND association
+                                      // 3: reserved int (probably another AND assoc in the future)
+                                      // 4: effect's EQU association
+  };
+
+layout (std140) uniform vUniformFloats
+  {
+  vec4 vUniforms[3*NUM_VERTEX];       // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
+                                      // The first vec4 is the Interpolated values,
+                                      // second vec4: first float - cache, next 3: Center, the third -  the Region.
+  };
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// HELPER FUNCTIONS
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Return degree of the point as defined by the Region. Currently only supports spherical regions.
+//
+// Let 'PS' be the vector from point P (the current vertex) to point S (the center of the effect).
+// Let region.xyz be the vector from point S to point O (the center point of the region sphere)
+// Let region.w be the radius of the region sphere.
+// (This all should work regardless if S is inside or outside of the sphere).
+//
+// Then, the degree of a point with respect to a given (spherical!) Region is defined by:
+//
+// If P is outside the sphere, return 0.
+// Otherwise, let X be the point where the halfline SP meets the sphere - then return |PX|/|SX|,
+// aka the 'degree' of point P.
+//
+// We solve the triangle OPX.
+// We know the lengths |PO|, |OX| and the angle OPX, because cos(OPX) = cos(180-OPS) = -cos(OPS) = -PS*PO/(|PS|*|PO|)
+// then from the law of cosines PX^2 + PO^2 - 2*PX*PO*cos(OPX) = OX^2 so PX = -a + sqrt(a^2 + OX^2 - PO^2)
+// where a = PS*PO/|PS| but we are really looking for d = |PX|/(|PX|+|PS|) = 1/(1+ (|PS|/|PX|) ) and
+// |PX|/|PS| = -b + sqrt(b^2 + (OX^2-PO^2)/PS^2) where b=PS*PO/|PS|^2 which can be computed with only one sqrt.
+
+float degree(in vec4 region, in vec3 PS)
+  {
+  float ps_sq = dot(PS,PS);
+
+  if( ps_sq==0.0 ) return 1.0;
+
+  vec3 PO = PS + region.xyz;
+  float d = region.w*region.w-dot(PO,PO);
+
+  if( d<=0.0 ) return 0.0;
+
+  float b = dot(PS,PO)/ps_sq;
+
+  return 1.0 / (1.0 + 1.0/(sqrt(b*b + d/ps_sq)-b));
+  }
+
+#endif  // NUM_VERTEX>0
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+void main()
+  {
+  int component = int(a_Component);
+  vec3 n = a_Normal;
+#ifdef COMP_CENTERS
+  vec3 v = a_Position + u_Inflate*(a_Position - vComCenter[component].xyz);
+#else
+  vec3 v = a_Position + u_Inflate*a_Position;
+#endif
+
+#ifdef POSTPROCESS
+  if( (uint(vComAssoc[component].x) & 0x80000000u) != 0u )
+    {
+    v = vec3(0.0, 0.0, 0.0);
+    }
+#endif
+
+#if NUM_VERTEX>0
+  int effect=0;
+  int andC = vComAssoc[component].x & 0x7fffffff;
+  int equC = vComAssoc[component].y;
+
+  for(int i=0; i<vNumEffects; i++)
+    {
+    if( ((andC & vProperties[i].y) != 0) || (equC == vProperties[i].w) )
+      {
+      // ENABLED EFFECTS WILL BE INSERTED HERE
+
+      }
+    effect+=3;
+    }
+#endif
+
+#ifdef PREAPPLY
+  v_Position   = v;
+  v_endPosition= n;
+#else
+  if( u_TransformFeedback == 1 )
+    {
+    vec4 tmp1 =  u_MVMatrixP * vec4(v,1.0);
+    vec4 tmp2 =  normalize(u_MVMatrixV * vec4(n,0.0));
+
+    v_Position    = vec3(tmp1);
+    v_endPosition = vec3(tmp1+100.0*tmp2);
+    }
+  else
+    {
+    v_Position = v;
+    }
+#endif
+
+  v_TexCoordinate = a_TexCoordinate;
+  v_Normal        = normalize(vec3(u_MVMatrixV*vec4(n,0.0)));
+  gl_Position     = u_MVPMatrix*vec4(v,1.0);
+  }                                       
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment() : String
+    {
+    return """
+precision highp float;
+precision highp int;
+
+in vec3 v_Position;                     // Interpolated position for this fragment.
+in vec3 v_Normal;                       // Interpolated normal for this fragment.
+in vec2 v_TexCoordinate;                // Interpolated texture coordinate per fragment.
+
+out vec4 fragColor;                     // The output color
+
+uniform sampler2D u_Texture;            // The input texture.
+
+#ifdef OIT
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform uvec2 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).
+#endif
+
+#if NUM_FRAGMENT>0
+uniform int fNumEffects;                     // total number of fragment effects
+
+uniform ivec4 fProperties[NUM_FRAGMENT];     // their properties, 4 ints:
+                                             // name of the effect, unused, unused, unused
+
+uniform vec4 fUniforms[3*NUM_FRAGMENT];      // i-th effect is 3 consecutive vec4's: [3*i], [3*i+1], [3*i+2].
+                                             // The first vec4 is the Interpolated values,
+                                             // second vec4: first float - cache, next 3: Center, the third - the Region.
+
+#endif    // NUM_FRAGMENT>0
+
+#ifdef OIT
+//////////////////////////////////////////////////////////////////////////////////////////////
+// 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 + u_Size.x*u_Size.y;
+
+    u_Records[ptr+1u] = depth;
+    u_Records[ptr+2u] = rgba;
+
+    memoryBarrier();
+
+    uint prev = uint(ij.x) + uint(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];
+        }
+      }
+    }
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+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);
+  }
+
+#endif   // OIT
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+void main()                    		
+  {  
+  vec4 color = texture(u_Texture,v_TexCoordinate);
+
+#if NUM_FRAGMENT>0
+  vec3 diff;
+  float degree;
+  int effect=0;
+
+  for(int i=0; i<fNumEffects; i++)
+    {
+    diff   = (v_Position - fUniforms[effect+1].yzw)/fUniforms[effect+2].xyz;
+    degree = max(0.0,1.0-dot(diff,diff));
+
+    // ENABLED EFFECTS WILL BE INSERTED HERE
+
+    effect+=3;
+    }
+#endif
+
+  float z = v_Normal.z;
+  if( z<0.0 ) z = -z;
+
+  vec4 converted = vec4(color.rgb * (1.0 + 4.0*z) * 0.200, color.a);
+
+#ifdef OIT
+  if( converted.a > 0.97 )
+    {
+    fragColor= converted;
+    }
+  else
+    {
+    if( converted.a > 0.0 )
+      {
+      const float S= 2147483647.0; // max signed int. Could probably be max unsigned int but this is enough.
+      vec2 pixel = vec2(gl_FragCoord.xy);
+      insert(pixel, uint(S*(1.0-gl_FragCoord.z)/2.0), convert(converted) );
+      }
+    discard;
+    }
+#else
+  fragColor = converted;
+#endif
+  }           
+           """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/shaders/Normal.kt b/src/main/java/org/distorted/library/shaders/Normal.kt
new file mode 100644
index 0000000..03384f9
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/Normal.kt
@@ -0,0 +1,59 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object Normal
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun vertex() : String
+    {
+    return """
+           precision lowp float;
+
+           in vec3 a_Position;
+           uniform mat4 u_Projection;
+
+           void main()
+             {
+             gl_Position = u_Projection * vec4(a_Position, 1.0);
+             }           
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment() : String
+    {
+    return """
+           precision lowp float;
+
+           out vec4 fragColor;
+
+           void main()
+             {
+             fragColor = vec4(1.0,0.0,0.0,1.0);
+             }
+           """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/shaders/Oit.kt b/src/main/java/org/distorted/library/shaders/Oit.kt
new file mode 100644
index 0000000..30338e6
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/Oit.kt
@@ -0,0 +1,323 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object Oit
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// a_Position            Per-vertex position.
+// v_Pixel               2D pixel coords in window space
+// u_Depth               Distance from the near plane to render plane, in clip coords
+// 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.
+
+    fun vertex() : String
+    {
+    return """
+           precision highp float;
+           precision highp int;
+
+           in vec2 a_Position;
+           out vec2 v_TexCoordinate;
+           out vec2 v_Pixel;
+
+           uniform float u_Depth; 
+           uniform vec2  u_TexCorr;
+           uniform uvec2 u_Size; 
+
+           void main()
+             {
+             v_TexCoordinate = (a_Position + 0.5) * u_TexCorr;
+             v_Pixel         = (a_Position + 0.5) * vec2(u_Size);
+             gl_Position     = vec4(2.0*a_Position,u_Depth,1.0);
+             }      
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment_build() : String
+    {
+    return """
+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 uvec2 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 + u_Size.x*u_Size.y;
+
+    u_Records[ptr+1u] = depth;
+    u_Records[ptr+2u] = rgba;
+
+    memoryBarrier();
+
+    uint prev = uint(ij.x) + uint(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];
+        }
+      }
+    }
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+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  color= texture(u_Texture     , v_TexCoordinate);
+  float depth= texture(u_DepthTexture, v_TexCoordinate).r;
+
+  if( color.a > 0.97 )
+    {
+    gl_FragDepth = depth;
+    fragColor    = color;
+    }
+  else
+    {
+    if( color.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(color) );
+      }
+    discard;
+    }
+  }           
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment_clear() : String
+    {
+    return """
+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 uvec2 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) + uint(v_Pixel.y) * u_Size.x;
+  u_Records[index] = 0u;
+  discard;
+  }           
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment_collapse() : String
+    {
+    return """
+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 uvec2 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).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Pass3 of the OIT algorithm - traverse the per-pixel LinkedList and cut occluded fragments.
+
+void main()                    		
+  {
+  uint prev = uint(v_Pixel.x) + uint(v_Pixel.y) * u_Size.x;
+  uint curr = u_Records[prev];
+
+  if (curr != 0u)
+    {
+    const float S= 2147483647.0;
+    float depth = texture(u_DepthTexture, v_TexCoordinate).r;
+    uint texdepth = uint(S*(1.0-depth)/2.0);
+
+    while( curr != 0u )
+      {
+      if( u_Records[curr+1u] <= texdepth )
+        {
+        u_Records[prev] = 0u;
+        break;
+        }
+
+      prev = curr;
+      curr = u_Records[curr];
+      }
+    }
+  else discard;
+  }           
+           """.trimIndent()
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+    fun fragment_render() : String
+    {
+    return """
+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
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform uvec2 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;
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// A over B (https://en.wikipedia.org/wiki/Alpha_compositing)
+
+vec4 blend(vec4 A,vec4 B)
+  {
+  float b = B.a * (1.0-A.a);
+  float a = A.a + b;
+
+  return vec4( (A.rgb*A.a + B.rgb*b)/a , a );
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Pass4 of the OIT algorithm - keep traversing the linked list, build the final color and blend it.
+
+void main()                    		
+  {
+  uint prev = uint(v_Pixel.x) + uint(v_Pixel.y) * u_Size.x;
+  uint curr = u_Records[prev];
+
+  if (curr != 0u)
+    {
+    const float S= 2147483647.0;
+    gl_FragDepth = 1.0 - 2.0*float(u_Records[curr+1u])/S;
+    vec4 color   = convert(u_Records[curr+2u]);
+    curr = u_Records[curr];
+
+    while (curr != 0u)
+      {
+      color = blend( color , convert(u_Records[curr+2u]) );
+      curr = u_Records[curr];
+      }
+
+    fragColor = color;
+    }
+  else discard;
+  }           
+           """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/distorted/library/shaders/Postprocess.kt b/src/main/java/org/distorted/library/shaders/Postprocess.kt
new file mode 100644
index 0000000..e815d7a
--- /dev/null
+++ b/src/main/java/org/distorted/library/shaders/Postprocess.kt
@@ -0,0 +1,48 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2025 Leszek Koltunski  leszek@koltunski.pl                                          //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// This library is free software; you can redistribute it and/or                                 //
+// modify it under the terms of the GNU Lesser General Public                                    //
+// License as published by the Free Software Foundation; either                                  //
+// version 2.1 of the License, or (at your option) any later version.                            //
+//                                                                                               //
+// This library 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                             //
+// Lesser General Public License for more details.                                               //
+//                                                                                               //
+// You should have received a copy of the GNU Lesser General Public                              //
+// License along with this library; if not, write to the Free Software                           //
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.shaders
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+object Postprocess
+{
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// v_TexCoordinate   Interpolated texture coordinate per fragment.
+// u_Texture         The input texture.
+
+    fun fragment() : String
+    {
+    return """
+           precision highp float;
+
+           in vec2 v_TexCoordinate; 
+           out vec4 fragColor;
+           uniform vec4 u_Color;
+           uniform sampler2D u_Texture;
+           
+           void main()
+             {
+             vec4 color = texture(u_Texture,v_TexCoordinate);
+             fragColor  = vec4(u_Color.rgb, sign(color.a)*u_Color.a);
+             }
+           """.trimIndent()
+    }
+}
\ No newline at end of file
