Method A: enable back face culling and use 2 planes, one of which is rotated 180 degrees to face the other way.
Method B: disable back face culling, bind 2 textures, sample both in the shader and use gl_FrontFacing to select one of them in the fragment shader:
fragColor = ( gl_FrontFacing ) ? texture( uTexFront, vertTexCoord0 ) :
texture( uTexBack, vertTexCoord0 );
Method C: disable back face culling, bind 2 textures, sample both in the shader and perform a dot product between the normal and the vector to the camera. If the dot product is <0, use the back facing texture, otherwise use the front facing texture.
In vertex shader (note: only relevant code is shown):
out vec4 vertPosition; // view space position
out vec3 vertNormal; // view space normal
vertPosition = ciModelView * ciPosition;
vertNormal = ciNormalMatrix * ciNormal;
In fragment shader (note: only relevant code is shown):
vec3 toCamera = normalize( -vertPosition.xyz );
float dp = dot( vertNormal, toCamera );
fragColor = ( dp < 0 ) ? texture( uTexBack, vertTexCoord0 ) :
texture( uTexFront, vertTexCoord0 );
(strictly speaking we don’t have to normalize
toCamera, but since it is a direction and you might want to use it for lighting calculations, I figured it wouldn’t hurt to normalize here).
P.S.: regarding the code in your post: please use mix to blend your colors, instead of rolling your own equation. The GLSL compiler might be smart enough to vectorize the calculation (calculate the four components in parallel), but don’t count on it. Using
mix, the compiler has a better understanding of what it is you’re trying to do.