In my project, I'm implementing a shader to combine up to 4 textures in a terrain using a splatmap texture (https://en.wikipedia.org/wiki/Texture_splatting).
I want to combine the splatmap blending with the height blending technique, described in: http://untitledgam.es/2017/01/height-blending-shader/. This technique basically defines a function to interpolate between two textures more realistically based on height information.
The problem is, in order to use height blending, I need to know which pair of textures I'll be interpolating, while in the splatmap I actually have four different texture sets (channels, R G B and A).
So what I want to achieve is a shader that will select the pair of textures for which the splatmap channel information is strongest, and interpolate between the two while discarding others. My "naïve" attempt was something along the lines of (simplified version, pseudo-code and only for illustrative purposes):
uniform sampler2D tex1;
uniform sampler2D tex2;
uniform sampler2D splat;
void fragment_shader() {
sampler2D sampler;
if (splat.r > splat.g) {
sampler = tex1;
} else {
sampler = tex2;
}
}
The problem, I realized, is that I can't store samplers inside a variable like this due to limitations in GLSL. So I'm looking for a soultion that would be similar to this. Particularly, I would want to avoid a solution where I'm sampling all four textures for every texture lookup. That is because I will be using some expensive techniques inside the shader which require several texture lookups, and I don't want to increase the computation cost 2x.
EDIT My current attempt to do this consists of computing two vectors to select the maximum and "second maximum" channels. A channel vector would be something like (1.0, 0.0, 0.0, 0.0)
to indicate the red channel should be used. The implementation is currently looking something like this:
vec4 c1, c2;
float m = max(max(max(splat_val.r, splat_val.g), splat_val.b), splat_val.a);
if (m == 0.0) {
c1 = vec4(0.0);
c2 = vec4(0.0);
} else {
c1 = vec4(float(splat_val.x == m), float(splat_val.y == m), float(splat_val.z == m), float(splat_val.a == m));
vec4 splat_val_2 = splat_val * (vec4(1.0) - c1);
float m2 = max(max(max(splat_val_2.r, splat_val_2.g), splat_val_2.b), splat_val_2.a);
c2 = vec4(float(splat_val_2.x == m2), float(splat_val_2.y == m2), float(splat_val_2.z == m2), float(splat_val_2.a == m2));
}
Where c1
and c2
end up being the channel vectors for the largest and second largest splatmap channels. I'm still facing some issues when there are multiple elements with the same value though, and I keep thinking there should be a better way to do this.
After I compute the channel vectors, I'd still need a nasty static if structure like the following, so all in all I'm not sure my current attempted solution is a very good one:
if (dot(c1, red_constant) == 1.0) {
if (dot c2, green_constant) == 1.0) {
do_shader(tex_sampler_R, tex_sampler_G);
}
else if (dot c2, blue_constant) == 1.0) {
do_shader(tex_sampler_R, tex_sampler_B);
}
} else if (dot(c2, green_constant) == 1.0) {
// ...
}
// ...