Hi all!
Just another small post about problems which I encountered with shaders on mobile devices (here: Android). It seems that I found a solution now (after months). Maybe this is helpful for others.
Ok here's how it started:
I had to do a bigger terrain. A splatmap shader seemed handy for adding some details to it. Implementing it in godot (as well as in blender where I designed much of the terrain) wasn't too hard. And everything was fine... until I exported the game to android:

O..K.. that doesn't look too well. (The water shader is another issue)
The splatmap was applied on a 2km x 2km terrain. The terrain mesh (optimized triangular mesh) itself was cut into pieces of 100x100m. The reason:(Creepy voice:) half-precision floats
Yes, Godot calcs down mesh coordinates (also on PC) to half-precision floats before sending them to the gpu. Big meshes therefore can get quite distorted. Even on 100x100 pieces you might get differences of around 5-10cm. If you encounter strange gaps in your terrain but all coords are 100% correct, then yes, you might have encountered this problem.
Correction: Since Godot 3.x this is only true if you enable "Compression" on mesh/dae import.
Here are some links about this number format:
http://www.codersnotes.com/notes/wrangling-halfs/
https://en.wikipedia.org/wiki/Half-precision_floating-point_format
While the splatshader on the PC had no precision issues the mobile shader obviously ran into some. First, I tried to declare the shader vars as highp(recision) but with no success. It seems that this mobile chipset (I tested on an Adreno 418 / Qualcomm Snapdragon 808) didn't want to go higher than 16 bit. Also: Using highp on a fragment shader might cost much performance.
Baking into UV2:
First, I tried to split the original UV into 2 pieces (hi/lo) and to assemble it in the shader again. But that didn't go too well. Most certainly because interpolating the lo part from vertice UVs to fragment UV wasn't helpful when the hi part changed.
I then decided to keep the UV for the splatmap lookup. You may center the UV-coords around 0 (so -0.5,+0.5 instead of 0,1). It might double the precision. (See the link about half-prec. value ranges above)
I created a little addon that bakes some artificial UV2 to the terrain mesh vertices. Based on its local coordinates. The sole purpose of UV2 was to consistently service the lookup in the repeat terrain textures.
You want to set the scale here as small as possible. If you use a square terrain mesh (-> 2 tris) you might just use mod (or fmod) of x/z in a multiple of your biggest terrain square. In my case the terrain contained optimized but irregular tris inside a square of 100x100 each (terrain was split by a phyton script in blender). So this was my UV2 formula:
mdt.set_vertex_uv2(vix,Vector2((vertex.x/100.0)*1024.0,\
(vertex.z/100.0)*1024.0))
mdt is a MeshDataTool
vix is the vertex index
vertex is retrieved via mdt.get_vertex(vix)
So I used the local vertex coords (here: in range of -50 <-> +50 for each 100x100 tile) to set UV2 in a resulting value range of -512 to 512.
Inside the splat shader, I use UV as usual to look up the rgba value from the splatmap.
Then I calculate the lookup index in the splat textures solely via UV2 (multiplied with an individual texture factor, i.e.: 0.05).
This worked suprisingly well.
The downside:
You can't use the UV2 of this surface for other purposes (i.e. baking lightmaps). But, you could do a splatmap lookup via vertex color instead and then bake to UV.
Maybe too complex use VERTEX instead?
Instead of setting UV2, a simpler approach might be to use VERTEX.xz
instead. Set it to varying vec2 in the vertex shader part (it should use the local vertex coordinates) and use that interpolated varying
in the fragment shader.
This is the splatmap shader:
shader_type spatial;
render_mode cull_disabled;
uniform sampler2D splatmap;
uniform sampler2D tex_base;
uniform sampler2D tex_r;
uniform sampler2D tex_g;
uniform sampler2D tex_b;
uniform sampler2D tex_a;
uniform float base_res = 0.05; //20
uniform float r_res = 0.05; //2
uniform float g_res = 0.05;//10
uniform float b_res = 0.05;//10
uniform float a_res = 0.05;//5
varying vec2 loc_vertex;
void vertex() {
loc_vertex = VERTEX.xz;
}
void fragment() {
vec3 base_col;
vec3 r_col;
vec3 g_col;
vec3 b_col;
vec3 a_col;
vec4 texs=texture(splatmap,UV);
float rval = texs.r;
float gval = texs.g;
float bval = texs.b;
float aval = 1.0-texs.a;
float baseval = max(0.0,1.0-(rval+gval+bval+aval));
base_col = texture(tex_base,loc_vertex*base_res).rgb * baseval;
r_col = texture(tex_r,loc_vertex*r_res).rgb * rval;
g_col = texture(tex_g,loc_vertex*g_res).rgb * gval;
b_col = texture(tex_b,loc_vertex*b_res).rgb * bval;
a_col = texture(tex_a,loc_vertex*a_res).rgb * aval;
ALBEDO = base_col+r_col+g_col+b_col+a_col;
}
Quite basic: 1 Splatmap texture + 5 textures (1 default + 4 textures for the r,g,b,a channels)
Perhaps this kind of simple splatmap shader will not suit everyone but the point is the suitability for mobile. In this case I used the local vertex to create a texture UV (instead of UV2).
Result: (on the same android device)
Still not perfect. But bearable. ;-)