Managing z-buffering with transparent textures in Three.js


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: <img alt="screenshot" class="b-lazy" data-src="https://dl.dropboxusercontent.com/u/1142760/static/png/Screenshot%202013-11-30%2021.38.26.png" data-original="https://dl.dropboxusercontent.com/u/1142760/static/png/Screenshot%202013-11-30%2021.38.26.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />

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: <a href="http://jsfiddle.net/UgZxc/" rel="nofollow">http://jsfiddle.net/UgZxc/</a>

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(); } );


Generally, this problem cannot be solved by turning depth testing/writing on/off. This is well explained in this answer: <a href="https://stackoverflow.com/questions/8763603/transparent-textures-behaviour-in-webgl" rel="nofollow">Transparent textures behaviour in WebGL</a>

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

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

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: <a href="http://jsfiddle.net/UgZxc/12/" rel="nofollow">http://jsfiddle.net/UgZxc/12/</a>

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.


  • How to perform PyCUDA 4x4 matrix inversion with same accuracy than numpy linalg “inv” or “pinv” func
  • Various HMAC_SHA256 functions in classic ASP gives different results
  • Error error C3861: 'cvPyrSegmentation': identifier not found
  • Ruby - Sort MongoDB records by Timezone?
  • Get SIM Number programmatically in flex
  • Setting resource paths programatically in spring MVC
  • EF 4.3.1 How to map a sub sub class with table per type
  • iOS simulator reverts to iOS7 when I'm trying to run 6
  • Why doesn't ASM call my ``visitCode``?
  • Create a QPaintDevice from HDC handle
  • Call same +load implementation on all subclasses
  • Calling a Generic Interface Method does not work
  • Reflection to find nested class?
  • Instance initialization block and subclasses
  • Android ContactsContract class: How to ignore non-primary ACCOUNT_TYPES?
  • Authorize.net payment gateway on wordpress e-commerce plugin
  • Working on custom component: subclass UIView or UIViewController?
  • Use a different port with RequireHttps filter in MVC2
  • type casting self
  • Pylint warnings on inherited nested class members
  • UITextField — observe changes to selectedTextRange?
  • Grid-lines on a GridView
  • How can I convert this tuple of tuples into a count of its elements?
  • Is it OK to write code after [super dealloc]?
  • Using same constraints in multiple classes
  • LiveData is abstract android
  • Caching attributes in superclass
  • Is calc() supported in html email?
  • req.body is undefined - nodejs
  • Symfony2: How to get request parameter
  • Akka Routing: Reply's send to router ends up as dead letters
  • Is there a mandatory requirement to switch app.yaml?
  • Load html files in TinyMce
  • Change div Background jquery
  • How does Linux kernel interrupt the application?
  • Busy indicator not showing up in wpf window [duplicate]
  • Why do underscore prefixed variables exist?
  • Net Present Value in Excel for Grouped Recurring CF
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize
  • How to load view controller without button in storyboard?