]> git.lizzy.rs Git - minetest.git/commitdiff
Shaders for Android (GLES 2) (#10506)
authorVitaliy <numzer0@yandex.ru>
Sun, 25 Oct 2020 17:01:03 +0000 (20:01 +0300)
committerGitHub <noreply@github.com>
Sun, 25 Oct 2020 17:01:03 +0000 (18:01 +0100)
Shader support for OpenGL ES 2 devices (Android)

Co-authored-by: sfan5 <sfan5@live.de>
17 files changed:
build/android/app/build.gradle
builtin/mainmenu/tab_settings.lua
builtin/settingtypes.txt
client/shaders/3d_interlaced_merge/opengl_fragment.glsl
client/shaders/3d_interlaced_merge/opengl_vertex.glsl
client/shaders/default_shader/opengl_fragment.glsl
client/shaders/default_shader/opengl_vertex.glsl
client/shaders/minimap_shader/opengl_fragment.glsl
client/shaders/minimap_shader/opengl_vertex.glsl
client/shaders/nodes_shader/opengl_fragment.glsl
client/shaders/nodes_shader/opengl_vertex.glsl
client/shaders/object_shader/opengl_fragment.glsl
client/shaders/object_shader/opengl_vertex.glsl
client/shaders/selection_shader/opengl_fragment.glsl
client/shaders/selection_shader/opengl_vertex.glsl
games/devtest/mods/basenodes/init.lua
src/client/shader.cpp

index 8127260307fdc986ebc01d10c66e096e11b96a31..fccb7b3b4a923c76653b8224758b0307d70386fd 100644 (file)
@@ -64,10 +64,9 @@ task prepareAssets() {
        copy {
                from "${projRoot}/builtin" into "${assetsFolder}/builtin"
        }
-       /*copy {
-               // ToDo: fix Minetest shaders that currently don't work with OpenGL ES
+       copy {
                from "${projRoot}/client/shaders" into "${assetsFolder}/client/shaders"
-       }*/
+       }
        copy {
                from "../native/deps/Android/Irrlicht/shaders" into "${assetsFolder}/client/shaders/Irrlicht"
        }
index 8a7445394aac126577f17da6c9f95bbd7fffe109..29744048aab7ca854f558d2fa4619c20638f2730 100644 (file)
@@ -154,15 +154,18 @@ local function formspec(tabview, name, tabdata)
                "box[8,0;3.75,4.5;#999999]"
 
        local video_driver = core.settings:get("video_driver")
-       local shaders_supported = video_driver == "opengl"
-       local shaders_enabled = false
-       if shaders_supported then
-               shaders_enabled = core.settings:get_bool("enable_shaders")
+       local shaders_enabled = core.settings:get_bool("enable_shaders")
+       if video_driver == "opengl" then
                tab_string = tab_string ..
                        "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders") .. ";"
                                        .. tostring(shaders_enabled) .. "]"
+       elseif video_driver == "ogles2" then
+               tab_string = tab_string ..
+                       "checkbox[8.25,0;cb_shaders;" .. fgettext("Shaders (experimental)") .. ";"
+                                       .. tostring(shaders_enabled) .. "]"
        else
                core.settings:set_bool("enable_shaders", false)
+               shaders_enabled = false
                tab_string = tab_string ..
                        "label[8.38,0.2;" .. core.colorize("#888888",
                                        fgettext("Shaders (unavailable)")) .. "]"
index 27f375693ae256e6ae22e500a6c749bb94063107..36446f8089c50f792989dbeac677e491358673af 100644 (file)
@@ -658,8 +658,8 @@ texture_path (Texture path) path
 #    The rendering back-end for Irrlicht.
 #    A restart is required after changing this.
 #    Note: On Android, stick with OGLES1 if unsure! App may fail to start otherwise.
-#    On other platforms, OpenGL is recommended, and it’s the only driver with
-#    shader support currently.
+#    On other platforms, OpenGL is recommended.
+#    Shaders are supported by OpenGL (desktop only) and OGLES2 (experimental)
 video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2
 
 #    Radius of cloud area stated in number of 64 node cloud squares.
index 25945ad7f7c9ceaec5529f88edddeb3960c4a752..7cba61b39500da8a53a25ef6c1600a4ffb5f29aa 100644 (file)
@@ -6,9 +6,11 @@ uniform sampler2D textureFlags;
 #define rightImage normalTexture
 #define maskImage textureFlags
 
+varying mediump vec2 varTexCoord;
+
 void main(void)
 {
-       vec2 uv = gl_TexCoord[0].st;
+       vec2 uv = varTexCoord.st;
        vec4 left = texture2D(leftImage, uv).rgba;
        vec4 right = texture2D(rightImage, uv).rgba;
        vec4 mask = texture2D(maskImage, uv).rgba;
index 4e0b2b125c6ce81b0f8f84b3699e6b90b871b124..8600494814ecf0e51405f9a08922c18f129ab0ec 100644 (file)
@@ -1,6 +1,7 @@
+varying mediump vec2 varTexCoord;
+
 void main(void)
 {
-       gl_TexCoord[0] = gl_MultiTexCoord0;
-       gl_Position = gl_Vertex;
-       gl_FrontColor = gl_BackColor = gl_Color;
+       varTexCoord = inTexCoord0;
+       gl_Position = inVertexPosition;
 }
index 925ab6e1dcd4b48c884b5525a9f1dca0faf6da02..5018ac6ea39dc27a831d2a5275fc1021683aa52d 100644 (file)
@@ -1,4 +1,6 @@
+varying lowp vec4 varColor;
+
 void main(void)
 {
-       gl_FragColor = gl_Color;
+       gl_FragColor = varColor;
 }
index d0b16c8b0274b1c08a74d34c1c34c84e13b68ee8..d95a3c2d30e77ec0a3fe2a967ff451402952e84f 100644 (file)
@@ -1,9 +1,7 @@
-uniform mat4 mWorldViewProj;
+varying lowp vec4 varColor;
 
 void main(void)
 {
-       gl_TexCoord[0] = gl_MultiTexCoord0;
-       gl_Position = mWorldViewProj * gl_Vertex;
-
-       gl_FrontColor = gl_BackColor = gl_Color;
+       gl_Position = mWorldViewProj * inVertexPosition;
+       varColor = inVertexColor;
 }
index fa4f9cb1ab3bf451f6d0f5aae1d0b77c98f3003e..cef359e8a71d67e0e978b0472a09e4a56baaef76 100644 (file)
@@ -2,9 +2,12 @@ uniform sampler2D baseTexture;
 uniform sampler2D normalTexture;
 uniform vec3 yawVec;
 
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
 void main (void)
 {
-       vec2 uv = gl_TexCoord[0].st;
+       vec2 uv = varTexCoord.st;
 
        //texture sampling rate
        const float step = 1.0 / 256.0;
@@ -27,6 +30,6 @@ void main (void)
 
        vec3 color = (1.1 * diffuse + 0.05 * height + 0.5 * specular) * base.rgb;
        vec4 col = vec4(color.rgb, base.a);
-       col *= gl_Color;
+       col *= varColor;
        gl_FragColor = vec4(col.rgb, base.a);
 }
index 88f9356d5ad4d8b61ed3857cd2afc72ef5c31a74..1a9491805efd1feeafacad943ec0c38acfdcf7e7 100644 (file)
@@ -1,9 +1,11 @@
-uniform mat4 mWorldViewProj;
 uniform mat4 mWorld;
 
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
 void main(void)
 {
-       gl_TexCoord[0] = gl_MultiTexCoord0;
-       gl_Position = mWorldViewProj * gl_Vertex;
-       gl_FrontColor = gl_BackColor = gl_Color;
+       varTexCoord = inTexCoord0.st;
+       gl_Position = mWorldViewProj * inVertexPosition;
+       varColor = inVertexColor;
 }
index 36d47d1f51505686f6ffa9999c90b8e7f47213cb..82c87073aa2a66b8359cad633d1f3f22e43ee07b 100644 (file)
@@ -15,11 +15,12 @@ varying vec3 vPosition;
 // cameraOffset + worldPosition (for large coordinates the limits of float
 // precision must be considered).
 varying vec3 worldPosition;
-
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
 varying vec3 eyeVec;
 
 const float fogStart = FOG_START;
-const float fogShadingParameter = 1 / ( 1 - fogStart);
+const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
 
 #ifdef ENABLE_TONE_MAPPING
 
@@ -56,13 +57,13 @@ vec4 applyToneMapping(vec4 color)
 void main(void)
 {
        vec3 color;
-       vec2 uv = gl_TexCoord[0].st;
+       vec2 uv = varTexCoord.st;
 
        vec4 base = texture2D(baseTexture, uv).rgba;
-
 #ifdef USE_DISCARD
        // If alpha is zero, we can just discard the pixel. This fixes transparency
-       // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa.
+       // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
+       // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
        if (base.a == 0.0) {
                discard;
        }
@@ -70,7 +71,7 @@ void main(void)
 
        color = base.rgb;
 
-       vec4 col = vec4(color.rgb * gl_Color.rgb, 1.0); 
+       vec4 col = vec4(color.rgb * varColor.rgb, 1.0);
        
 #ifdef ENABLE_TONE_MAPPING
        col = applyToneMapping(col);
index 56bff09a84d2b75df46b23cef03d28f7c6c58668..cb344f6e6e36d3e42e9ca5662e0c343e48c94499 100644 (file)
@@ -1,4 +1,3 @@
-uniform mat4 mWorldViewProj;
 uniform mat4 mWorld;
 
 // Color of the light emitted by the sun.
@@ -16,7 +15,8 @@ varying vec3 vPosition;
 // cameraOffset + worldPosition (for large coordinates the limits of float
 // precision must be considered).
 varying vec3 worldPosition;
-
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
 varying vec3 eyeVec;
 
 // Color of the light emitted by the light sources.
@@ -81,13 +81,13 @@ float snoise(vec3 p)
 
 void main(void)
 {
-       gl_TexCoord[0] = gl_MultiTexCoord0;
+       varTexCoord = inTexCoord0.st;
 
        float disp_x;
        float disp_z;
 // OpenGL < 4.3 does not support continued preprocessor lines
 #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES) || (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS)
-       vec4 pos2 = mWorld * gl_Vertex;
+       vec4 pos2 = mWorld * inVertexPosition;
        float tOffset = (pos2.x + pos2.y) * 0.001 + pos2.z * 0.002;
        disp_x = (smoothTriangleWave(animationTimer * 23.0 + tOffset) +
                smoothTriangleWave(animationTimer * 11.0 + tOffset)) * 0.4;
@@ -96,43 +96,43 @@ void main(void)
                smoothTriangleWave(animationTimer * 13.0 + tOffset)) * 0.5;
 #endif
 
-       worldPosition = (mWorld * gl_Vertex).xyz;
+       worldPosition = (mWorld * inVertexPosition).xyz;
 
 // OpenGL < 4.3 does not support continued preprocessor lines
 #if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) && ENABLE_WAVING_WATER
        // Generate waves with Perlin-type noise.
        // The constants are calibrated such that they roughly
        // correspond to the old sine waves.
-       vec4 pos = gl_Vertex;
+       vec4 pos = inVertexPosition;
        vec3 wavePos = worldPosition + cameraOffset;
        // The waves are slightly compressed along the z-axis to get
        // wave-fronts along the x-axis.
-       wavePos.x /= WATER_WAVE_LENGTH * 3;
-       wavePos.z /= WATER_WAVE_LENGTH * 2;
-       wavePos.z += animationTimer * WATER_WAVE_SPEED * 10;
-       pos.y += (snoise(wavePos) - 1) * WATER_WAVE_HEIGHT * 5;
+       wavePos.x /= WATER_WAVE_LENGTH * 3.0;
+       wavePos.z /= WATER_WAVE_LENGTH * 2.0;
+       wavePos.z += animationTimer * WATER_WAVE_SPEED * 10.0;
+       pos.y += (snoise(wavePos) - 1.0) * WATER_WAVE_HEIGHT * 5.0;
        gl_Position = mWorldViewProj * pos;
 #elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES && ENABLE_WAVING_LEAVES
-       vec4 pos = gl_Vertex;
+       vec4 pos = inVertexPosition;
        pos.x += disp_x;
        pos.y += disp_z * 0.1;
        pos.z += disp_z;
        gl_Position = mWorldViewProj * pos;
 #elif MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS && ENABLE_WAVING_PLANTS
-       vec4 pos = gl_Vertex;
-       if (gl_TexCoord[0].y < 0.05) {
+       vec4 pos = inVertexPosition;
+       if (varTexCoord.y < 0.05) {
                pos.x += disp_x;
                pos.z += disp_z;
        }
        gl_Position = mWorldViewProj * pos;
 #else
-       gl_Position = mWorldViewProj * gl_Vertex;
+       gl_Position = mWorldViewProj * inVertexPosition;
 #endif
 
 
        vPosition = gl_Position.xyz;
 
-       eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz;
+       eyeVec = -(mWorldView * inVertexPosition).xyz;
 
        // Calculate color.
        // Red, green and blue components are pre-multiplied with
@@ -141,16 +141,16 @@ void main(void)
        // The pre-baked colors are halved to prevent overflow.
        vec4 color;
        // The alpha gives the ratio of sunlight in the incoming light.
-       float nightRatio = 1 - gl_Color.a;
-       color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + 
-               nightRatio * artificialLight.rgb) * 2;
-       color.a = 1;
+       float nightRatio = 1.0 - inVertexColor.a;
+       color.rgb = inVertexColor.rgb * (inVertexColor.a * dayLight.rgb + 
+               nightRatio * artificialLight.rgb) * 2.0;
+       color.a = 1.0;
 
        // Emphase blue a bit in darker places
        // See C++ implementation in mapblock_mesh.cpp final_color_blend()
-       float brightness = (color.r + color.g + color.b) / 3;
+       float brightness = (color.r + color.g + color.b) / 3.0;
        color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) +
                0.07 * brightness);
 
-       gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0);
+       varColor = clamp(color, 0.0, 1.0);
 }
index 86d5c1c9248074ed1bd2a313fe40d8c7065dfc63..7ac182a632ea5de38a08b4d0d42174710f9bd99c 100644 (file)
@@ -8,6 +8,8 @@ uniform vec3 eyePosition;
 varying vec3 vNormal;
 varying vec3 vPosition;
 varying vec3 worldPosition;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
 
 varying vec3 eyeVec;
 varying float vIDiff;
@@ -15,7 +17,7 @@ varying float vIDiff;
 const float e = 2.718281828459;
 const float BS = 10.0;
 const float fogStart = FOG_START;
-const float fogShadingParameter = 1 / ( 1 - fogStart);
+const float fogShadingParameter = 1.0 / (1.0 - fogStart);
 
 #ifdef ENABLE_TONE_MAPPING
 
@@ -52,13 +54,14 @@ vec4 applyToneMapping(vec4 color)
 void main(void)
 {
        vec3 color;
-       vec2 uv = gl_TexCoord[0].st;
+       vec2 uv = varTexCoord.st;
 
        vec4 base = texture2D(baseTexture, uv).rgba;
 
 #ifdef USE_DISCARD
        // If alpha is zero, we can just discard the pixel. This fixes transparency
-       // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa.
+       // on GPUs like GC7000L, where GL_ALPHA_TEST is not implemented in mesa,
+       // and also on GLES 2, where GL_ALPHA_TEST is missing entirely.
        if (base.a == 0.0) {
                discard;
        }
@@ -68,7 +71,7 @@ void main(void)
 
        vec4 col = vec4(color.rgb, base.a);
 
-       col.rgb *= gl_Color.rgb;
+       col.rgb *= varColor.rgb;
 
        col.rgb *= emissiveColor.rgb * vIDiff;
 
index f8c1cd93204b55a6330ba15a93e30511b0d1741e..e44984dc89d17689f780d174bac9ee36e17b2bdd 100644 (file)
@@ -1,4 +1,3 @@
-uniform mat4 mWorldViewProj;
 uniform mat4 mWorld;
 
 uniform vec3 eyePosition;
@@ -7,6 +6,8 @@ uniform float animationTimer;
 varying vec3 vNormal;
 varying vec3 vPosition;
 varying vec3 worldPosition;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
 
 varying vec3 eyeVec;
 varying float vIDiff;
@@ -18,31 +19,31 @@ float directional_ambient(vec3 normal)
 {
        vec3 v = normal * normal;
 
-       if (normal.y < 0)
-               return dot(v, vec3(0.670820f, 0.447213f, 0.836660f));
+       if (normal.y < 0.0)
+               return dot(v, vec3(0.670820, 0.447213, 0.836660));
 
-       return dot(v, vec3(0.670820f, 1.000000f, 0.836660f));
+       return dot(v, vec3(0.670820, 1.000000, 0.836660));
 }
 
 void main(void)
 {
-       gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
-       gl_Position = mWorldViewProj * gl_Vertex;
+       varTexCoord = (mTexture * inTexCoord0).st;
+       gl_Position = mWorldViewProj * inVertexPosition;
 
        vPosition = gl_Position.xyz;
-       vNormal = gl_Normal;
-       worldPosition = (mWorld * gl_Vertex).xyz;
-       eyeVec = -(gl_ModelViewMatrix * gl_Vertex).xyz;
+       vNormal = inVertexNormal;
+       worldPosition = (mWorld * inVertexPosition).xyz;
+       eyeVec = -(mWorldView * inVertexPosition).xyz;
 
 #if (MATERIAL_TYPE == TILE_MATERIAL_PLAIN) || (MATERIAL_TYPE == TILE_MATERIAL_PLAIN_ALPHA)
        vIDiff = 1.0;
 #else
        // This is intentional comparison with zero without any margin.
        // If normal is not equal to zero exactly, then we assume it's a valid, just not normalized vector
-       vIDiff = length(gl_Normal) == 0.0
+       vIDiff = length(inVertexNormal) == 0.0
                ? 1.0
-               : directional_ambient(normalize(gl_Normal));
+               : directional_ambient(normalize(inVertexNormal));
 #endif
 
-       gl_FrontColor = gl_BackColor = gl_Color;
+       varColor = inVertexColor;
 }
index c679d0e12cd0be4abd817be8827c8f69c8451239..35b1f8902dc06aa173e5df1dd129098f63fb6f5f 100644 (file)
@@ -1,9 +1,12 @@
 uniform sampler2D baseTexture;
 
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
+
 void main(void)
 {
-       vec2 uv = gl_TexCoord[0].st;
+       vec2 uv = varTexCoord.st;
        vec4 color = texture2D(baseTexture, uv);
-       color.rgb *= gl_Color.rgb;
+       color.rgb *= varColor.rgb;
        gl_FragColor = color;
 }
index d0b16c8b0274b1c08a74d34c1c34c84e13b68ee8..9ca87a9cfb32e9f956ef180c5d476c849ea2660b 100644 (file)
@@ -1,9 +1,10 @@
-uniform mat4 mWorldViewProj;
+varying lowp vec4 varColor;
+varying mediump vec2 varTexCoord;
 
 void main(void)
 {
-       gl_TexCoord[0] = gl_MultiTexCoord0;
-       gl_Position = mWorldViewProj * gl_Vertex;
+       varTexCoord = inTexCoord0.st;
+       gl_Position = mWorldViewProj * inVertexPosition;
 
-       gl_FrontColor = gl_BackColor = gl_Color;
+       varColor = inVertexColor;
 }
index 7ffbadbea406d1e8592afeba432f4664cc6329fd..0cb85d808337c8ca4d97d0cfdde052b3dfc94fd4 100644 (file)
@@ -127,6 +127,7 @@ minetest.register_node("basenodes:water_source", {
        description = "Water Source".."\n"..
                "Drowning damage: 1",
        drawtype = "liquid",
+       waving = 3,
        tiles = {"default_water.png"},
        special_tiles = {
                {name = "default_water.png", backface_culling = false},
@@ -152,6 +153,7 @@ minetest.register_node("basenodes:water_flowing", {
        description = "Flowing Water".."\n"..
                "Drowning damage: 1",
        drawtype = "flowingliquid",
+       waving = 3,
        tiles = {"default_water_flowing.png"},
        special_tiles = {
                {name = "default_water_flowing.png", backface_culling = false},
@@ -178,6 +180,7 @@ minetest.register_node("basenodes:river_water_source", {
        description = "River Water Source".."\n"..
                "Drowning damage: 1",
        drawtype = "liquid",
+       waving = 3,
        tiles = { "default_river_water.png" },
        special_tiles = {
                {name = "default_river_water.png", backface_culling = false},
@@ -205,6 +208,7 @@ minetest.register_node("basenodes:river_water_flowing", {
        description = "Flowing River Water".."\n"..
                "Drowning damage: 1",
        drawtype = "flowingliquid",
+       waving = 3,
        tiles = {"default_river_water_flowing.png"},
        special_tiles = {
                {name = "default_river_water_flowing.png", backface_culling = false},
index e2eeb4ab0eb5d8d2d7bb993e50985e5f2b6f9e8a..f2aa002468574431a5b1f0b5f636680012b5e1bd 100644 (file)
@@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "gamedef.h"
 #include "client/tile.h"
+#include "config.h"
 
 #if ENABLE_GLES
 #ifdef _IRR_COMPILE_WITH_OGLES1_
@@ -230,11 +231,24 @@ class MainShaderConstantSetter : public IShaderConstantSetter
 {
        CachedVertexShaderSetting<float, 16> m_world_view_proj;
        CachedVertexShaderSetting<float, 16> m_world;
+#if ENABLE_GLES
+       // Modelview matrix
+       CachedVertexShaderSetting<float, 16> m_world_view;
+       // Texture matrix
+       CachedVertexShaderSetting<float, 16> m_texture;
+       // Normal matrix
+       CachedVertexShaderSetting<float, 9> m_normal;
+#endif
 
 public:
        MainShaderConstantSetter() :
-               m_world_view_proj("mWorldViewProj"),
-               m_world("mWorld")
+                 m_world_view_proj("mWorldViewProj")
+               , m_world("mWorld")
+#if ENABLE_GLES
+               , m_world_view("mWorldView")
+               , m_texture("mTexture")
+               , m_normal("mNormal")
+#endif
        {}
        ~MainShaderConstantSetter() = default;
 
@@ -244,23 +258,42 @@ class MainShaderConstantSetter : public IShaderConstantSetter
                video::IVideoDriver *driver = services->getVideoDriver();
                sanity_check(driver);
 
+               // Set world matrix
+               core::matrix4 world = driver->getTransform(video::ETS_WORLD);
+               if (is_highlevel)
+                       m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
+               else
+                       services->setVertexShaderConstant(world.pointer(), 4, 4);
+
                // Set clip matrix
+               core::matrix4 worldView;
+               worldView = driver->getTransform(video::ETS_VIEW);
+               worldView *= world;
                core::matrix4 worldViewProj;
                worldViewProj = driver->getTransform(video::ETS_PROJECTION);
-               worldViewProj *= driver->getTransform(video::ETS_VIEW);
-               worldViewProj *= driver->getTransform(video::ETS_WORLD);
+               worldViewProj *= worldView;
                if (is_highlevel)
                        m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
                else
                        services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
 
-               // Set world matrix
-               core::matrix4 world = driver->getTransform(video::ETS_WORLD);
-               if (is_highlevel)
-                       m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
-               else
-                       services->setVertexShaderConstant(world.pointer(), 4, 4);
-
+#if ENABLE_GLES
+               if (is_highlevel) {
+                       core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
+                       m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
+                       m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
+
+                       core::matrix4 normal;
+                       worldView.getTransposed(normal);
+                       sanity_check(normal.makeInverse());
+                       float m[9] = {
+                               normal[0], normal[1], normal[2],
+                               normal[4], normal[5], normal[6],
+                               normal[8], normal[9], normal[10],
+                       };
+                       m_normal.set(m, services);
+               }
+#endif
        }
 };
 
@@ -620,15 +653,62 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
                return shaderinfo;
 
        // Create shaders header
-       std::string shaders_header = "#version 120\n";
-
+       bool use_gles = false;
+#if ENABLE_GLES
+       use_gles = driver->getDriverType() == video::EDT_OGLES2;
+#endif
+       std::string shaders_header, vertex_header, pixel_header; // geometry shaders aren’t supported in GLES<3
+       if (use_gles) {
+               shaders_header =
+                       "#version 100\n"
+                       ;
+               vertex_header = R"(
+                       uniform highp mat4 mWorldView;
+                       uniform highp mat4 mWorldViewProj;
+                       uniform mediump mat4 mTexture;
+                       uniform mediump mat3 mNormal;
+
+                       attribute highp vec4 inVertexPosition;
+                       attribute lowp vec4 inVertexColor;
+                       attribute mediump vec4 inTexCoord0;
+                       attribute mediump vec3 inVertexNormal;
+                       attribute mediump vec4 inVertexTangent;
+                       attribute mediump vec4 inVertexBinormal;
+                       )";
+               pixel_header = R"(
+                       precision mediump float;
+                       )";
+       } else {
+               shaders_header = R"(
+                       #version 120
+                       #define lowp
+                       #define mediump
+                       #define highp
+                       )";
+               vertex_header = R"(
+                       #define mWorldView gl_ModelViewMatrix
+                       #define mWorldViewProj gl_ModelViewProjectionMatrix
+                       #define mTexture (gl_TextureMatrix[0])
+                       #define mNormal gl_NormalMatrix
+
+                       #define inVertexPosition gl_Vertex
+                       #define inVertexColor gl_Color
+                       #define inTexCoord0 gl_MultiTexCoord0
+                       #define inVertexNormal gl_Normal
+                       #define inVertexTangent gl_MultiTexCoord1
+                       #define inVertexBinormal gl_MultiTexCoord2
+                       )";
+       }
+
+       bool use_discard = use_gles;
 #ifdef __unix__
        // For renderers that should use discard instead of GL_ALPHA_TEST
        const char* gl_renderer = (const char*)glGetString(GL_RENDERER);
-       if (strstr(gl_renderer, "GC7000")) {
-               shaders_header += "#define USE_DISCARD\n";
-       }
+       if (strstr(gl_renderer, "GC7000"))
+               use_discard = true;
 #endif
+       if (use_discard && shaderinfo.base_material != video::EMT_SOLID)
+               shaders_header += "#define USE_DISCARD\n";
 
        static const char* drawTypes[] = {
                "NDT_NORMAL",
@@ -654,7 +734,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
                shaders_header += "#define ";
                shaders_header += drawTypes[i];
                shaders_header += " ";
-               shaders_header += itos(i);
+               shaders_header += std::to_string(i);
                shaders_header += "\n";
        }
 
@@ -677,27 +757,27 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
                shaders_header += "#define ";
                shaders_header += materialTypes[i];
                shaders_header += " ";
-               shaders_header += itos(i);
+               shaders_header += std::to_string(i);
                shaders_header += "\n";
        }
 
        shaders_header += "#define MATERIAL_TYPE ";
-       shaders_header += itos(material_type);
+       shaders_header += std::to_string(material_type);
        shaders_header += "\n";
        shaders_header += "#define DRAW_TYPE ";
-       shaders_header += itos(drawtype);
+       shaders_header += std::to_string(drawtype);
        shaders_header += "\n";
 
        if (g_settings->getBool("enable_waving_water")){
                shaders_header += "#define ENABLE_WAVING_WATER 1\n";
                shaders_header += "#define WATER_WAVE_HEIGHT ";
-               shaders_header += ftos(g_settings->getFloat("water_wave_height"));
+               shaders_header += std::to_string(g_settings->getFloat("water_wave_height"));
                shaders_header += "\n";
                shaders_header += "#define WATER_WAVE_LENGTH ";
-               shaders_header += ftos(g_settings->getFloat("water_wave_length"));
+               shaders_header += std::to_string(g_settings->getFloat("water_wave_length"));
                shaders_header += "\n";
                shaders_header += "#define WATER_WAVE_SPEED ";
-               shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
+               shaders_header += std::to_string(g_settings->getFloat("water_wave_speed"));
                shaders_header += "\n";
        } else{
                shaders_header += "#define ENABLE_WAVING_WATER 0\n";
@@ -719,7 +799,7 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
                shaders_header += "#define ENABLE_TONE_MAPPING\n";
 
        shaders_header += "#define FOG_START ";
-       shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
+       shaders_header += std::to_string(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
        shaders_header += "\n";
 
        // Call addHighLevelShaderMaterial() or addShaderMaterial()
@@ -727,11 +807,11 @@ ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtyp
        const c8* pixel_program_ptr = 0;
        const c8* geometry_program_ptr = 0;
        if (!vertex_program.empty()) {
-               vertex_program = shaders_header + vertex_program;
+               vertex_program = shaders_header + vertex_header + vertex_program;
                vertex_program_ptr = vertex_program.c_str();
        }
        if (!pixel_program.empty()) {
-               pixel_program = shaders_header + pixel_program;
+               pixel_program = shaders_header + pixel_header + pixel_program;
                pixel_program_ptr = pixel_program.c_str();
        }
        if (!geometry_program.empty()) {
@@ -813,27 +893,37 @@ void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
        geometry_program = "";
        is_highlevel = false;
 
-       if(enable_shaders){
-               // Look for high level shaders
-               if(drivertype == video::EDT_DIRECT3D9){
-                       // Direct3D 9: HLSL
-                       // (All shaders in one file)
-                       vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
-                       pixel_program = vertex_program;
-                       geometry_program = vertex_program;
-               }
-               else if(drivertype == video::EDT_OPENGL){
-                       // OpenGL: GLSL
-                       vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
-                       pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
-                       geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
-               }
-               if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
-                       is_highlevel = true;
-                       return;
-               }
-       }
+       if (!enable_shaders)
+               return;
+
+       // Look for high level shaders
+       switch (drivertype) {
+       case video::EDT_DIRECT3D9:
+               // Direct3D 9: HLSL
+               // (All shaders in one file)
+               vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
+               pixel_program = vertex_program;
+               geometry_program = vertex_program;
+               break;
+
+       case video::EDT_OPENGL:
+#if ENABLE_GLES
+       case video::EDT_OGLES2:
+#endif
+               // OpenGL: GLSL
+               vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
+               pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
+               geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
+               break;
 
+       default:
+               // e.g. OpenGL ES 1 (with no shader support)
+               break;
+       }
+       if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
+               is_highlevel = true;
+               return;
+       }
 }
 
 void dumpShaderProgram(std::ostream &output_stream,