0

I'm attempting to draw a translucent sphere among a cloud of translucent particles. The sphere is translucent while it's in front of the particles, but it turns opaque as soon as it's among the particles (ie, if a particle vertex sits between the camera and the sphere's surface, sphere becomes opaque). Check out the snippet below (click 'Full page', it looks much better).

Newer versions of three removed .sortParticles, which comes into play here, but I'm working around that by copying the sortPoints function from this example.

The depths of every particle and the sphere seem to be accurate, it's just that the opacity is lost. Perhaps blending fails under certain circumstances?

Is there a way to draw a translucent mesh among translucent particles?

var renderer, scene, camera, sphere;

var particleSystem, uniforms, geometry;

var particles = 200;

var WIDTH = window.innerWidth;
var HEIGHT = window.innerHeight;

init();
animate();

function init() {

    camera = new THREE.PerspectiveCamera(40, WIDTH / HEIGHT, 1, 10000);
    camera.position.z = 70;

    scene = new THREE.Scene();

    uniforms = {

        color: {
            type: "c",
            value: new THREE.Color(0xffffff)
        },
        

    };

    var shaderMaterial = new THREE.ShaderMaterial({

        uniforms: uniforms,
        vertexShader: document.getElementById('vertexshader').textContent,
        fragmentShader: document.getElementById('fragmentshader').textContent,

        depthTest: true,
        depthWrite: false,
        transparent: true

    });
  

    var sphere_geometry = new THREE.SphereGeometry(10, 32, 32);
    var sphere_material = new THREE.MeshNormalMaterial();
    sphere_material.transparent = true;
    sphere_material.opacity = 0.6;
    sphere_material.depthTest = true;
    //sphere_material.depthWrite = false;
    sphere = new THREE.Mesh(sphere_geometry, sphere_material);
    //sphere.renderOrder = -1;
    scene.add(sphere);

    camera.lookAt(sphere.position);

    var radius = 30;

    geometry = new THREE.BufferGeometry();

    var positions = new Float32Array(particles * 3);
    var colors = new Float32Array(particles * 3);
    var sizes = new Float32Array(particles);

    var color = new THREE.Color();

    for (var i = 0, i3 = 0; i < particles; i++, i3 += 3) {

        positions[i3 + 0] = i - 50;
        positions[i3 + 1] = i - 50;
        positions[i3 + 2] = 2*i - 100;

        color.setHSL(i / particles, 1.0, 0.5);

        colors[i3 + 0] = color.r;
        colors[i3 + 1] = color.g;
        colors[i3 + 2] = color.b;

        sizes[i] = 3000;

    }

    geometry.addAttribute('position', new THREE.BufferAttribute(positions, 3));
    geometry.addAttribute('customColor', new THREE.BufferAttribute(colors, 3));
    geometry.addAttribute('size', new THREE.BufferAttribute(sizes, 1));

    particleSystem = new THREE.Points(geometry, shaderMaterial);

    scene.add(particleSystem);

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(WIDTH, HEIGHT);

    var container = document.getElementById('container');
    container.appendChild(renderer.domElement);

    //

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

}

function onWindowResize() {

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

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

}

function animate() {

    requestAnimationFrame(animate);

    render();

}

function render() {

    var time = Date.now() * 0.005;

    var n = 30;
    sphere.position.z = n * (1 + Math.sin(0.2 * time)) - n * 1.5;

    sortPoints();

    renderer.render(scene, camera);

}

function sortPoints() {
    var vector = new THREE.Vector3();
    // Model View Projection matrix
    var matrix = new THREE.Matrix4();
    matrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
    // matrix.multiply( particleSystem.matrixWorld );
    //
    var index = geometry.getIndex();
    var positions = geometry.getAttribute('position').array;
    var length = positions.length / 3;
    if (index === null) {
        var array = new Uint16Array(length);
        for (var i = 0; i < length; i++) {
            array[i] = i;
        }
        index = new THREE.BufferAttribute(array, 1);
        geometry.setIndex(index);
    }
    var sortArray = [];
    for (var i = 0; i < length; i++) {
        vector.fromArray(positions, i * 3);
        vector.applyProjection(matrix);
        sortArray.push([vector.z, i]);
    }

    function numericalSort(a, b) {
        return b[0] - a[0];
    }
    sortArray.sort(numericalSort);
    var indices = index.array;
    for (var i = 0; i < length; i++) {
        indices[i] = sortArray[i][1];
    }
    geometry.index.needsUpdate = true;
}
body {
        color: #ffffff;
        font-family:Monospace;
        font-size:13px;
        text-align:center;
        font-weight: bold;
        background-color: #000000;
        margin: 0px;
        overflow: hidden;
    }
    #info {
        color: #fff;
        position: absolute;
        top: 0px;
        width: 100%;
        padding: 5px;
        z-index:100;
    }
<script src="http://threejs.org/build/three.min.js"></script>
<div id="info">translucent mesh amidst translucent particles</div>
<script type="x-shader/x-vertex" id="vertexshader">
    attribute float size;
    attribute vec3 customColor;

    varying vec3 vColor;

    void main() {

        vColor = customColor;

        vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);

        gl_PointSize = size / length(mvPosition.xyz);

        gl_Position = projectionMatrix * mvPosition;

    }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
    uniform vec3 color;

    varying vec3 vColor;

    void main() {

        gl_FragColor = vec4(color * vColor, 0.2);

        

    }
</script>

<div id="container"></div>
mwcz
  • 8,949
  • 10
  • 42
  • 63
  • 2
    Try (1) 200 points so you can see something, (2) `sphere.renderOrder = -1;` so the sphere is rendered first, (3) `particleSystem.material.depthTest = false;` -- or similar experiments. Make the sphere more transparent. – WestLangley Oct 19 '15 at 02:53
  • @WestLangley, thanks! I tried each suggestion. `sphere.renderOrder = -1` causes the sphere to become opaque. `particleSystem.material.depthTest = false` causes particles to appear in the wrong order (depth matters). I've tried many variations over the last few months and still cannot get a scene with a translucent mesh amid translucent particles to exhibit intuitive draw order and transparency. I updated the code snippet to be a little more useful. – mwcz Jan 15 '16 at 22:05
  • (1) Changing the render order cannot make something become opaque. (2) Try rendering the particles first so you are happy with the output, then render the sphere second with `sphere.material.depthTest = false`. – WestLangley Jan 18 '16 at 14:40
  • @WestLangley odd, when I set `sphere.renderOrder = -1` in the snippet above, the sphere is always opaque. When I remove that line, it is translucent when in front of the particles's center, but opaque when behind the particles's center. I'll take your advice and focus on particles for now (transparent particle ordering, which you have fielded many questions on). Thanks. – mwcz Jan 18 '16 at 15:36
  • 1
    It may _appear_ opaque because the depth buffer prevents anything from being drawn that is behind it. But it is not opaque. Familiarize yourself with webgl so you can better understand how to get three.js to do what you want -- well, if it is possible, that is. : - ) – WestLangley Jan 18 '16 at 16:17

0 Answers0