0

I've just downloaded the latest Three.js master "mrdoob-three.js-d6384d2" , I've modified the "webgl_nearestneighbour.html" to show a transparent image and this is the result: http://i.share.pho.to/8cccac74_l.jpeg

I can't understand if it's by design , if it's a webgl error or if it's a three.js error but, as you can see in the bigger ball, near sprites are clipped while far sprites aren't. Any information is much appreciated (I'm new to webgl).

edit: here's the code.

<html>
 <head>
  <meta charset="utf-8">
  <title>three.js webgl - nearest neighbour</title>
  <style>
   html, body {
    width: 100%;
    height: 100%;
   }

   body {
    background-color: #ffffff;
    margin: 0;
    overflow: hidden;
    font-family: arial;
   }

   #info {
    text-align: center;
    padding: 5px;
    position: absolute;
    width: 100%;
    color: white;
   }
  </style>
 </head>
 <body>
  
  <div id="info"><a href="http://threejs.org" target="_blank">three.js</a> webgl - typed arrays - nearest neighbour for 500,000 sprites</div>
  
  <script src="../build/three.min.js"></script>
  <script src="js/TypedArrayUtils.js"></script>
  <script src="js/controls/FirstPersonControls.js"></script>
  <script type="x-shader/x-vertex" id="vertexshader">
   
   //uniform float zoom;
  
   attribute float alpha;

   varying float vAlpha;

   void main() {
   
    vAlpha = 1.0 - alpha;
    
    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );

    gl_PointSize = 4.0 * ( 300.0 / length( mvPosition.xyz ) );

    gl_Position = projectionMatrix * mvPosition;

   }

  </script>

  <script type="x-shader/x-fragment" id="fragmentshader">

   uniform sampler2D tex1;

   varying float vAlpha;

   void main() {

    gl_FragColor = texture2D(tex1, gl_PointCoord);
    gl_FragColor.r = (1.0 - gl_FragColor.r) * vAlpha + gl_FragColor.r;

   }

  </script>
  <script>  
  
   var camera, scene, renderer;
   var geometry, material, mesh;
   var controls;

   var objects = [];

   var amountOfParticles = 500000, maxDistance = Math.pow(120, 2);
   var positions, alphas, particles, _particleGeom

   var clock = new THREE.Clock();

   var blocker = document.getElementById( 'blocker' );
   var instructions = document.getElementById( 'instructions' );
   
   
   function init() {

       camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000000);

    scene = new THREE.Scene();

    controls = new THREE.FirstPersonControls( camera );
    controls.movementSpeed = 100;
    controls.lookSpeed = 0.1;

    var materials = [

     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/px.jpg' ) } ), // right
     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/nx.jpg' ) } ), // left
     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/py.jpg' ) } ), // top
     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/ny.jpg' ) } ), // bottom
     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/pz.jpg' ) } ), // back
     new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'textures/cube/skybox/nz.jpg' ) } )  // front

    ];

    mesh = new THREE.Mesh( new THREE.BoxGeometry( 10000, 10000, 10000, 7, 7, 7 ), new THREE.MeshFaceMaterial( materials ) );
    mesh.scale.x = - 1;
    scene.add(mesh);
    
    //

    renderer = new THREE.WebGLRenderer(); // Detector.webgl? new THREE.WebGLRenderer(): new THREE.CanvasRenderer()
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    // create the custom shader
    var imagePreviewTexture = THREE.ImageUtils.loadTexture( 'textures/football.png');
    imagePreviewTexture.minFilter = THREE.LinearMipMapLinearFilter;
    imagePreviewTexture.magFilter = THREE.LinearFilter;
    
    pointShaderMaterial = new THREE.ShaderMaterial( {
     uniforms: {
      tex1: { type: "t", value: imagePreviewTexture },
      zoom: { type: 'f', value: 9.0 },
     },
     attributes: {
      alpha: { type: 'f', value: null },
     },
     vertexShader:   document.getElementById( 'vertexshader' ).textContent,
     fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
     transparent: true
    });
    
    
    //create particles with buffer geometry
    var distanceFunction = function(a, b){
     return Math.pow(a[0] - b[0], 2) +  Math.pow(a[1] - b[1], 2) +  Math.pow(a[2] - b[2], 2);
    };

    positions = new Float32Array( amountOfParticles * 3 );
    alphas = new Float32Array( amountOfParticles );
    
    _particleGeom = new THREE.BufferGeometry();
    _particleGeom.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
    _particleGeom.addAttribute( 'alpha', new THREE.BufferAttribute( alphas, 1 ) );
    
    particles = new THREE.PointCloud( _particleGeom, pointShaderMaterial );
    
    for (var x = 0; x < amountOfParticles; x++) {
     positions[ x * 3 + 0 ] = Math.random() * 1000;
     positions[ x * 3 + 1 ] = Math.random() * 1000;
     positions[ x * 3 + 2 ] = Math.random() * 1000;
     alphas[x] = 1.0;
    }
    
    
    var measureStart = new Date().getTime();
    
    // creating the kdtree takes a lot of time to execute, in turn the nearest neighbour search will be much faster
    kdtree = new THREE.TypedArrayUtils.Kdtree( positions, distanceFunction, 3 );
    
    console.log('TIME building kdtree', new Date().getTime() - measureStart);
    
    // display particles after the kd-tree was generated and the sorting of the positions-array is done
    scene.add(particles);

    window.addEventListener( 'resize', onWindowResize, false );

   }

   function onWindowResize() {

    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize( window.innerWidth, window.innerHeight );

    controls.handleResize();
   }

   function animate() {

    requestAnimationFrame( animate );

    //
    displayNearest(camera.position);

    controls.update( clock.getDelta() )

    renderer.render( scene, camera );

   }
   
   function displayNearest(position) {
    
    // take the nearest 200 around him. distance^2 'cause we use the manhattan distance and no square is applied in the distance function
    var imagePositionsInRange = kdtree.nearest([position.x, position.y, position.z], 100, maxDistance);
        
    // We combine the nearest neighbour with a view frustum. Doesn't make sense if we change the sprites not in our view... well maybe it does. Whatever you want.
    var _frustum = new THREE.Frustum();
    var _projScreenMatrix = new THREE.Matrix4();
    camera.matrixWorldInverse.getInverse( camera.matrixWorld );

    _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
    _frustum.setFromMatrix( _projScreenMatrix );
    
    for ( i = 0, il = imagePositionsInRange.length; i < il; i ++ ) {
     var object = imagePositionsInRange[i];
     var objectPoint = new THREE.Vector3().fromArray( object[ 0 ].obj );
     
     if (_frustum.containsPoint(objectPoint)){
     
      var objectIndex = object[0].pos;
      
      // set the alpha according to distance
      alphas[ objectIndex ] = 1.0 / maxDistance * object[1];
      // update the attribute
      _particleGeom.attributes.alpha.needsUpdate = true;
     }
    }
   }
   
   
   init();
   animate();
  </script>
 </body>
</html>
Techno.Shaman
  • 1,373
  • 1
  • 9
  • 9
  • 1
    Have you tried to set `depthWrite:false` in the material properties ? – Mouloud85 Jul 01 '15 at 18:50
  • You need to provide the relevant code in your post so we know what you are doing and so others can benefit. – WestLangley Jul 01 '15 at 19:31
  • I've added the code, but I barely changed it from the original; I've only selected another image in this line of code: var imagePreviewTexture = THREE.ImageUtils.loadTexture( 'textures/football.png'); I was giving for granted the alpha capabilities. I see that this user had a similar problem but he didn't say if he has fixed it. http://stackoverflow.com/questions/22901015/three-js-transparency-errors-with-multiple-particle-systems-sorting – Techno.Shaman Jul 01 '15 at 19:57
  • 1
    You are using a point cloud, not a sprite. See: http://stackoverflow.com/questions/31037195/three-js-custom-shader-and-png-texture-with-transparency – WestLangley Jul 01 '15 at 20:00
  • This demo is misleading since the var "material" is declared but never used... – Techno.Shaman Jul 01 '15 at 20:51
  • I've added **pointShaderMaterial.depthWrite = false;** and ,while it fixes the problem, it messes up the z.index completely; the order is reversed, near objects are rendered with far objects overlapping on them. – Techno.Shaman Jul 01 '15 at 21:40
  • 1
    Did you follow the instructions in the answer I linked to and modify your fragment shader by adding this line: `if ( gl_FragColor.a < 0.5 ) discard;` ? – WestLangley Jul 01 '15 at 21:48
  • I was busy trying; by the way; I've added **if ( gl_FragColor.a < 0.5 ) discard;** at the bottom of the shader, after setting the output color , and it fixes the problem. Thanx a lot for the help. – Techno.Shaman Jul 01 '15 at 21:55

1 Answers1

1

To recap what has been said in the comments, the solution to this problem is to disable alpha blending, and in the shader you have to discard the pixel drawn based on the alpha value of the input texture.

So that this...

void main() {
gl_FragColor = texture2D(tex1, gl_PointCoord);
gl_FragColor.r = (1.0 - gl_FragColor.r) * vAlpha + gl_FragColor.r;
}

...becomes this

void main() {
gl_FragColor = texture2D(tex1, gl_PointCoord);
gl_FragColor.r = (1.0 - gl_FragColor.r) * vAlpha + gl_FragColor.r;
if ( gl_FragColor.a < 0.5 ) discard;
}
Techno.Shaman
  • 1,373
  • 1
  • 9
  • 9