Search code examples
dictionarysystemthree.jsdepthparticles

Depth map for particle system using a texture map


I'm trying to generate a depth map for a particle system, but if I render the particle system using MeshDepthMaterial, then every particle is only rendered as a single point for each vertex--not covering the entire area the texture mapped particle is displayed.

Do I need to use MeshDepthMaterial to generate a depth map, or are there other options?


Solution

  • Right now there is no way to get the MeshDepthMaterial to respect the size or texture of the ParticleSystem. However, it is not too hard to implement a custom ShaderMaterial that does that. First, you need a vertex shader and fragment shader.

    <script type="x-shader/x-vertex" id="vertexShader">
      uniform float size;
      void main() {
        gl_PointSize = size;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position , 1.0 ); 
      }
    </script>
    
    <script type = "x-shader/x-fragment" id="fragmentShader">
      uniform sampler2D map;
      uniform float near;
      uniform float far;
    
      void main() {
        float depth = gl_FragCoord.z / gl_FragCoord.w;
        float depthColor = 1.0 - smoothstep( near, far, depth );
    
        vec4 texColor = texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) );
        gl_FragColor = vec4( vec3(depthColor), texColor.a);
      }
    </script>
    

    The vertex shader is totally standard, the fragment shader takes the texture (sampler2D map), but instead of using it for color values it just uses the alpha level texColor.a. For the rgb, a grayscale value based on the depth is used, just like the MeshDepthMaterial. Now to use this shader you just need to grab the html and create a THREE.ShaderMaterial like so:

    var material = new THREE.ShaderMaterial({
        uniforms : {
          size : { type: 'f', value : 20.0 },
          near : { type: 'f', value : camera.near },
          far  : { type: 'f', value : camera.far },
          map  : { type: "t", value : THREE.ImageUtils.loadTexture( url ) }
        },
        attributes : {},
        vertexShader: vertShader,
        fragmentShader: fragShader,
        transparent: true
      });
    

    Here you have provided the shader with all the info it needs: the camera's near/far range, the size of the particle and the texture it needs to map.

    You can see a jsFiddle demo of it here.