Multiple transparent textures on the same mesh face in Three.js

Timo Kähkönen picture Timo Kähkönen · Apr 29, 2013 · Viewed 20.3k times · Source

Is it possible to lay multiple textures on top of each other on the same face in Three.js so that the alpha blending is done GPU accelerated in webGL?

The textures are (or should be) applied to the same face so that the bottom texture (texture 1) is without alpha channel and the above textures are alpha channeled in a way like texture 2 in the below image example.

This blending can be achieved using HTML5 Canvas as pre-step, but because texture bitmaps can be huge, I prefer to skip Canvas blending operations.

I tested by creating a copy of the mesh and applying one texture per mesh and made other mesh transparent and moved it a little, which succeeded nearly well, but there is some flickering and because objects cannot be exactly in the same position, there is some room between textures which is not the right effect. They should seem like they were blended in eg. Photoshop (as is the below image).

enter image description here

Answer

Abstract Algorithm picture Abstract Algorithm · Jun 3, 2013

Use ShaderMaterial and set both textures as uniforms, and then blend them within shader.

I made this example: http://abstract-algorithm.com/three_sh/ and that really should be enough.

So, you make ShaderMaterial:

var vertShader = document.getElementById('vertex_shh').innerHTML;
var fragShader = document.getElementById('fragment_shh').innerHTML;

var attributes = {}; // custom attributes

var uniforms = {    // custom uniforms (your textures)

  tOne: { type: "t", value: THREE.ImageUtils.loadTexture( "cover.png" ) },
  tSec: { type: "t", value: THREE.ImageUtils.loadTexture( "grass.jpg" ) }

};

var material_shh = new THREE.ShaderMaterial({

  uniforms: uniforms,
  attributes: attributes,
  vertexShader: vertShader,
  fragmentShader: fragShader

});

And create mesh with that material:

var me = new THREE.Mesh( new THREE.CubeGeometry(80,80,80), material_shh );

You can put simplest vertex shader:

varying vec2 vUv;

void main()
{
    vUv = uv;
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;
}

And fragment shader that will actually do the blending:

#ifdef GL_ES
precision highp float;
#endif

uniform sampler2D tOne;
uniform sampler2D tSec;

varying vec2 vUv;

void main(void)
{
    vec3 c;
    vec4 Ca = texture2D(tOne, vUv);
    vec4 Cb = texture2D(tSec, vUv);
    c = Ca.rgb * Ca.a + Cb.rgb * Cb.a * (1.0 - Ca.a);  // blending equation
    gl_FragColor= vec4(c, 1.0);
}

If you need to blend even more textures, you use same equation for blending just multiple times.

So here's the result:

enter image description here