andre vega
andre vega

Reputation: 21

Multiple textures overlapping webGL

I have two objects that I need to render in two different textures using WebGLRenderTarget. After mapping both textures to a plane using shaders and then adding that plane to the main scene, I am having issues drawing the closer object.

I have created a jsfiddle to show my issue: https://jsfiddle.net/11qb7ay7/82/ The real magic occurs here:

void main() {
    vec4 color = texture2D(tex_sphere, vUv);
    vec4 color_cube = texture2D(tex_cube, vUv);

    gl_FragColor = vec4(color.rgb * color_cube.rgb, 1.0);
}

The sphere is placed in front of the cube relative to the camera. How can I draw the sphere pixels instead of the cubes when they overlap?

To be clear, I am trying to find a way to compute the distance from camera of each pixel and render the closer one first

Upvotes: 2

Views: 1469

Answers (1)

Rabbid76
Rabbid76

Reputation: 210908

In general if tex_sphere has an alpha channel the you can mix the colors by the alpha channel:

void main()
{
    vec4 color = texture2D(tex_sphere, vUv);
    vec4 color_cube = texture2D(tex_cube, vUv);

    vec3 mixCol  = mix(color_cube.rgb, color.rgb, color.a);
    gl_FragColor = vec4(mixCol.rgb, 1.0);
}

If the tex_sphere has a black background, which should be omitted, the you have to check if the color of tex_sphere is not black:

void main()
{
    vec4 color = texture2D(tex_sphere, vUv);
    vec4 color_cube = texture2D(tex_cube, vUv);

    vec3  test   = step(1.0/512.0, color.rgb);
    float a      = max(max(test.r, test.g), test.b);
    vec3  mixCol = mix(color_cube.rgb, color.rgb, a);
    gl_FragColor = vec4(mixCol.rgb, 1.0);
}


Note, mix interpolates between 2 values according to a floating point interpolation value a in the range [0.0, 1.0]. If the a is equal 0.0 then the 1st value is returned and if the a is equal 1.0 then the 2nd value is returned.

step tests whether a value is less than an edge value. If it is less then 0.0 is returned, else 1.0 is returned.


To get a black background you have to set a black "clear" color when you render the sphere to the render target:

function render() {
    controls.update();

    renderer.setClearColor(0x00);
    renderer.render(sphereScene, camera, renderTargets.sphere, true);

    renderer.setClearColor(0xccccff);
    renderer.render(cubeScene, camera, renderTargets.cube, true);

    renderer.setClearColor(0xccccff);
    renderer.render(scene, camera);
}

If you want to use an alpha channel, the you have to set setClearAlpha before you render to the render target:

function render() {
    controls.update();

    renderer.setClearAlpha(0);
    renderer.render(sphereScene, camera, renderTargets.sphere, true);

    renderer.setClearAlpha(1);
    renderer.render(cubeScene, camera, renderTargets.cube, true);

    renderer.render(scene, camera);
}

var scene, renderer, camera, controls;
var cubeScene, sphereScene;
var renderTargets;

init();
animate();

function init() {
    scene = new THREE.Scene();
    cubeScene = new THREE.Scene();
    sphereScene = new THREE.Scene();
    renderer = new THREE.WebGLRenderer( { antialias: false, alpha: true } );
    renderer.setClearColor(0xccccff);
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 4000 );
    camera.position.set(0, 0, 200);
    
    renderer.setSize( window.innerWidth, window.innerHeight );
    controls = new THREE.OrbitControls(camera, renderer.domElement);
    camera.lookAt( scene.position );

    var light = new THREE.HemisphereLight( 0xffffff, 0x444444 );
    scene.add( light );

    container = document.createElement('div');
    document.body.appendChild(container);
    container.appendChild(renderer.domElement);
    
    
    initObjects();
    initRenderTargets(window.innerWidth, window.innerHeight);
}

function onResize() {
    renderer.setSize( window.innerWidth, window.innerHeight );
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderTargets.sphere.setSize( window.innerWidth, window.innerHeight );
    renderTargets.cube.setSize( window.innerWidth, window.innerHeight );
}

function initObjects() {
var cGeometry = new THREE.BoxGeometry( 100, 100, 100 );
var cMaterial = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
var cube = new THREE.Mesh( cGeometry, cMaterial );
cube.position.z = -210;
cube.position.y = 100;

var sGeometry = new THREE.SphereGeometry( 75, 32, 32 );
var sMaterial = new THREE.MeshBasicMaterial({
        color: 0xff0000
    });
var sphere = new THREE.Mesh( sGeometry, sMaterial );
sphere.position.z = -100;


sphereScene.add( sphere );
cubeScene.add( cube );
}

function initRenderTargets(width, height){
		renderTargets = createRenderTargets(width, height);
    
    var uniforms = {
    	"tex_cube": { type: "t", value: renderTargets.cube.texture },
      "tex_sphere": { type: "t", value: renderTargets.sphere.texture }
    }
    
    material = new THREE.ShaderMaterial({
    	uniforms: uniforms,
      vertexShader: document.getElementById('vs_rt').textContent,
      fragmentShader: document.getElementById('fs_rt').textContent
    });
    
    
    var plane = new THREE.PlaneGeometry(width, height);
    quad = new THREE.Mesh(plane, material);
    scene.add(quad);
}

function createRenderTargets(width, height) {
    var parameters = {
        minFilter: THREE.NearestFilter,
        magFilter: THREE.NearestFilter,
    };

    return {
        cube: new THREE.WebGLRenderTarget( width, height, parameters ),
        sphere: new THREE.WebGLRenderTarget( width, height, parameters )
    };
}

function animate() {
    requestAnimationFrame(animate);

    render();

}


//------------------------------------------
// Main rendering
//------------------------------------------
function render() {
		controls.update();
        
    renderer.setClearAlpha(0);
    renderer.render(sphereScene, camera, renderTargets.sphere, true);
    
    renderer.setClearAlpha(1);
    renderer.render(cubeScene, camera, renderTargets.cube, true);
    
    renderer.render(scene, camera);
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js"></script>
<script type="text/javascript" src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script id="vs_rt" type="x-shader/x-vertex">
    uniform sampler2D tex_cube;
    uniform sampler2D tex_sphere;
    varying vec2 vUv;

    void main() {
        vUv = uv;

        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
</script>

<script id="fs_rt" type="x-shader/x-fragment">
    uniform sampler2D tex_cube;
    uniform sampler2D tex_sphere;
    varying vec2 vUv;

    void main() {
        vec4 color = texture2D(tex_sphere, vUv);
        vec4 color_cube = texture2D(tex_cube, vUv);

        vec3 mixCol  = mix(color_cube.rgb, color.rgb, color.a);
        gl_FragColor = vec4(mixCol.rgb, 1.0);
    }
</script>

Upvotes: 2

Related Questions