Managing z-buffering with transparent textures in

2019-06-08 03:51发布

I am trying to create a simple map with flat, paper-like trees sticking out of it in WebGL with THREE.js. I cannot grasp how the library handles z-buffering. I've tried playing with renderer.sortObjects parameter as well as with material.depthWrite and object.renderDepth, but no combination seems to be working - I either get the library to display the trees in proper order (those further from the camera are obstructed by those closer to the camera), but with weird transparency glitches, OR I manage to get the transparency right, but the trees further from the screen appear on top on those that are closer. After hours of trying to get this right, this is what I ended up with: screenshot

As you can see, the trees that are more to the right are rendered on top on those to the left.

Please help me before I go completely nuts :)

Here's my code: http://jsfiddle.net/UgZxc/

var map_size = 80;

var MapModel = {};

var types_amount = 2;
var floorMap = [];

for(var i=1; i<=map_size; i++){
    floorMap[i]=[];
    for(var j=1; j<=map_size; j++){
        var ran = Math.ceil(Math.random()*types_amount);
        switch(ran){
            case 1:
                floorMap[i][j]='grass';
                break;
            case 2: 
                floorMap[i][j]='water';
                break;
        }
    }
}

MapModel.floorMap = floorMap;

var objectMap = [];
for(var i = map_size; i>=1; i--){
    objectMap[i] = [];
    for(var j=1; j<=map_size; j++){
        objectMap[i][j] = [];
        var rand = Math.ceil(Math.random()*2);
        switch(rand){
            case 1:
                objectMap[i][j].push('tree');
                break;
        }
    }
}

MapModel.objectMap = objectMap;




block_size=100;


// Constructor
MapApp = function()
{
    Sim.App.call(this);
}


// Subclass Sim.App
MapApp.prototype = new Sim.App();

// Our custom initializer
MapApp.prototype.init = function(param)
{
    Sim.App.prototype.init.call(this, param);

    // Create the Earth and add it to our sim
    for(var i=1; i<=map_size; i++){
        for(var j=map_size; j>=1; j--){
            var square = new Square();
            square.init(i, j);
            this.addObject(square);                    
            var arr = MapModel.objectMap[i][j];
            for(var k in arr){
                var obj = new MapObject();
                obj.init(i, j, arr[k]);
                this.addObject(obj);
            }
        }
    }

    // Let there be light!
    var sun = new Sun();
    sun.init();
    this.addObject(sun);

    this.camera.position.x = 3*block_size;
    this.camera.position.y = 3*block_size;
    this.camera.position.z=5*block_size;
    this.camera.rotation.x = Math.round(45* 100* Math.PI/180)/100;

    this.selfUpdate = function(){
        this.camera.position.x += 0.125 * block_size/10;
        this.camera.position.x += 0.050 * block_size/10;
    }

}

Square = function()
{
    Sim.Object.call(this);
    this.size = block_size;
}

Square.prototype = new Sim.Object();

wrote2 = false;
Square.prototype.init = function(x, y){   
    var type=MapModel.floorMap[x][y];
    var reflectivity = 0;    
    switch(type){
        case "grass":
            var earthmap = "http://dl.dropboxusercontent.com/u/1142760/static/html/webgl/tiles/samatrawa.png";
            break;
        case "water":
            var earthmap = "http://dl.dropboxusercontent.com/u/1142760/static/html/webgl/tiles/samawoda.png";
            reflectivity = 1;
            break;

    }
    //console.log(earthmap);
    var geometry = new THREE.CubeGeometry(this.size, this.size, this.size );
    var texture = THREE.ImageUtils.loadTexture(earthmap);
    var material = new THREE.MeshPhongMaterial( { map: texture, color: 0xffffff, reflectivity: reflectivity } );
    material.depthWrite = true;
    var mesh = new THREE.Mesh( geometry, material ); 

    mesh.translateX(x*this.size);
    mesh.translateY(y*this.size);
    mesh.renderDepth = y*block_size;

    if(!wrote2){
        wrote2=true;
        console.log('square renderDepth:', mesh.renderDepth, 'square mesh.position.y:', mesh.position.y);
        console.log('square material.depthWrite', material.depthWrite);
    }

    this.setObject3D(mesh); 
}


Square.prototype.update = function()
{
    // "I feel the Earth move..."
    //this.object3D.rotation.y += 0.1;

    //Sim.Object.prototype.update.call(this);
}

// Custom Sun class
Sun = function()
{
    Sim.Object.call(this);
}

Sun.prototype = new Sim.Object();

Sun.prototype.init = function()
{
    // Create a point light to show off the earth - set the light out back and to left a bit
    var light = new THREE.DirectionalLight( 0xC5BC98, 2);
    light.position.set(-10, 0, 20);

    // Tell the framework about our object
    this.setObject3D(light);    
}


MapObject = function(){
    Sim.Object.call(this);
}

MapObject.prototype = new Sim.Object();
wrote=false
MapObject.prototype.init = function(x, y, type){
    switch(type){
        case "tree":
            var textureURL = "http://dl.dropboxusercontent.com/u/1142760/static/html/webgl/tiles/samodrzewo.png";
            break;
        case "water":
            var textureURL = "http://dl.dropboxusercontent.com/u/1142760/static/html/webgl/tiles/samawoda.png";
            break;

    }
    //console.log(textureURL);
    var geometry = new THREE.PlaneGeometry(1*block_size, 2*block_size);
    var texture = THREE.ImageUtils.loadTexture(textureURL);
    var material = new THREE.MeshPhongMaterial( { map: texture, transparent:true } );
    material.depthWrite = false;

    var mesh = new THREE.Mesh( geometry, material ); 

    mesh.position.x=x*block_size;
    mesh.position.y=y*block_size;
    mesh.translateZ(2*block_size);

    mesh.rotation.x = Math.round(45 * 100 * Math.PI /180)/100;

    //mesh.renderDepth = y*block_size;
    mesh.renderDepth = -y*1000 ;
    if(!wrote){
        console.log('object renderDepth:', mesh.renderDepth, 'object mesh.position.y:', mesh.position.y);
        console.log('object material.depthWrite', material.depthWrite);
        wrote = true;
    }
    //console.log(mesh.rotation.x);

    this.setObject3D(mesh); 
}

var renderer = null;
            var scene = null;
            var camera = null;
            var mesh = null;

            $(document).ready(
                function() {
                    var container = document.getElementById("container");
                    var app = new MapApp();
                    app.init({ container: container });
                    app.run();
                }
            );

1条回答
beautiful°
2楼-- · 2019-06-08 04:16

Generally, this problem cannot be solved by turning depth testing/writing on/off. This is well explained in this answer: Transparent textures behaviour in WebGL

Therefore it can only be solved by drawing the transparent objects in the correct order. The solution is (mostly the default three.js behavior!):

  • Keep depth testing/writing enabled (you rarely disable this anyway).
  • Enable sorting of objects: app.renderer.sortObjects = true; although i don't see where in your code it is disabled.
  • Set renderDepth manually only if you see artifacts.

However, in your case, it turns out that your three.js version does a bad job at reordering (maybe some unstable sorting, I won't dig into that) so you get seemingly random artifacts. Updating to the latest build fixes that.

Working fiddle: http://jsfiddle.net/UgZxc/12/

As a sidenote: Try reducing your code examples/fiddles next time, and also the number of dependencies. There's a lot of unrelated code in there.

查看更多
登录 后发表回答