body, canvas {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/104/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/controls/TransformControls.js"></script>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.01, 1000);
camera.position.set(5,5,0);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth,innerHeight);
document.body.appendChild(renderer.domElement);
let orbit = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.GridHelper(500, 100, 0x666666, 0x444444));
let s1 = sphere(3, 2, 0)
let s2 = sphere(3, -2, 1)
let u1 = s1.material.uniforms, u2 = s2.material.uniforms;
requestAnimationFrame( render );
function sphere(radius, position, color){
color = color.toFixed(1)
var geometry = new THREE.SphereGeometry(radius, 50, 50);
var material = new THREE.ShaderMaterial({
transparent: true,
depthWrite: false,
side: THREE.DoubleSide,
uniforms: {c: {type: "3f"}, o: {type: "3f"}},
vertexShader: `
varying vec3 p;
void main() {
// transfer vertex position to fragment shader,
// this value is interpolated by gpu hardware between pixels of triangle,
// containing this vertex
p = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}`,
fragmentShader: `
varying vec3 p; // position of current pixel relative to sphere center
uniform vec3 c; // center of current sphere
uniform vec3 o; // center of opposite sphere
void main() {
vec3 a = abs(p)*50.0;
float opacity = a.x<1. || a.y<1. || a.z<1. ? 0.8 : 0.3;
// here is test of shpere overlapping
opacity = distance(o, p + c) < 3.0 ? 0.0 : opacity;
gl_FragColor = vec4(vec3(${color}, 0.0, 1.0 - ${color}), opacity);
}`
});
let mesh = new THREE.Mesh(geometry, material);
mesh.translateX(position)
scene.add(mesh);
let control = new THREE.TransformControls(camera, renderer.domElement);
control.addEventListener('dragging-changed', e => orbit.enabled = !e.value);
scene.add(control);
control.attach(mesh)
return mesh;
}
function render() {
requestAnimationFrame( render );
let p1 = s1.position, p2 = s2.position;
u2.o.value = u1.c.value = [p1.x, p1.y, p1.z];
u1.o.value = u2.c.value = [p2.x, p2.y, p2.z];
u1.c.needUpdate = u1.o.needUpdate =
u2.c.needUpdate = u2.o.needUpdate = true;
renderer.render( scene, camera );
}
</script>