Stretched textures on vertical triangles is one of a million possible bugs when texturing terrain with shaders. To fix it we need to recalculate texture coordinates in 3 projections: x, y and z.
The main algorithm to apply triplanar texturing of a 3D terrain with shaders is as following:
Compute the vertex’s normal vector and check what the larger component of the normal is, out of x, y and z. If x is the larger component, we use the geometry z coordinate as the texture coordinate s, and the geometry y coordinate as the texture coordinate t. If z is the larger component, we use the geometry x coordinate as the texture coordinate s, and the geometry y coordinate as the texture coordinate t.
Here’s how terrain looked like before we used shaders:
Here’s a simple example of GLSL vertex and fragment shaders for texturing terrain in 3D:
Vertex shader
varying float v; varying float xcoord,ycoord,zcoord; void main () { //Texture Coordinates xcoord = gl_Vertex.x; zcoord = gl_Vertex.z; ycoord = gl_Vertex.y ; // projection1. y is largest normal component // so use x and z to sample texture gl_TexCoord[0] = vec4(xcoord,zcoord,0,0); //first projection // projection2. x is largest normal component // so use z and y to sample texture gl_TexCoord[1] = vec4(zcoord,ycoord,0,0); //second projection // projection3. z is largest normal component // so use x and y to sample texture gl_TexCoord[2] = vec4(xcoord,ycoord,0,0); //third projection //gl_Normal is the vertex's normal vector. //We compare it's absolute values with each other to find which projection to use float x = abs(gl_Normal.x); float y = abs(gl_Normal.y); float z = abs(gl_Normal.z); if(x > y && x > z) { //v is a variable used in fragment shader to distinguish textures v = 1; } if(y > x && y > z) { v = 0; } if(z > y && z > x) { v = 2; } gl_Position = ftransform(); }
Fragment shader
varying float v; uniform sampler2D myTexture0; uniform sampler2D myTexture1; uniform sampler2D myTexture2; void main (void) { if ( 0 == v ) { gl_FragColor = texture2D( myTexture0, gl_TexCoord[0].st); } if ( 1 == v ) { gl_FragColor = texture2D( myTexture1, gl_TexCoord[1].st); } if ( 2 == v ) { gl_FragColor = texture2D( myTexture2, gl_TexCoord[2].st); } }
Here’s the result (we used 3 textures in shaders to visualize the result):
Senior Software Engineer developing all kinds of stuff.