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>