1

I'm using three.js for graphical web project.

I have many circles and it spread out like this.

enter image description here

I wonder that, if distance between object and camera getting far,

Can object's opacity to lower?(or fade out)

Also if distance getting close, set object's opacity to higher(or fade in)

I searched it in docs(https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene), but there is no clear description.

Is there any solution about this?

Thanks.

1

2 Answers 2

14

Changing the opacity when the camera gets closer to an object is not possible with the default materials of three.js. But it's not hard to enhance the fragment shader of a material with the following line of code in order to achieve the desired effect:

gl_FragColor.a *= pow( gl_FragCoord.z, f );

I have enhanced MeshNormalMaterial via onBeforeCompile() in the following demo. The idea is to gradually lower the opacity if objects come closer to the camera. You can control this transition process with the variable f. A high value means objects sooner start to get transparent.

let camera, scene, renderer;

init();
animate();

function init() {

  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 50;

  scene = new THREE.Scene();

  const geometry = new THREE.BoxBufferGeometry(2, 2, 2);
  const material = new THREE.MeshNormalMaterial({
    transparent: true
  });

  material.onBeforeCompile = function(shader) {

    shader.fragmentShader = shader.fragmentShader.replace(
      'gl_FragColor = vec4( packNormalToRGB( normal ), opacity );',
      [
        'gl_FragColor = vec4( packNormalToRGB( normal ), opacity );',
        'gl_FragColor.a *= pow( gl_FragCoord.z, 50.0 );',
      ].join('\n')
    );

  };

  for (let i = 0; i < 1000; i++) {

    const object = new THREE.Mesh(geometry, material);
    object.position.x = Math.random() * 80 - 40;
    object.position.y = Math.random() * 80 - 40;
    object.position.z = Math.random() * 80 - 40;
    object.rotation.x = Math.random() * 2 * Math.PI;
    object.rotation.y = Math.random() * 2 * Math.PI;
    object.rotation.z = Math.random() * 2 * Math.PI;
    object.scale.x = Math.random() + 0.5;
    object.scale.y = Math.random() + 0.5;
    object.scale.z = Math.random() + 0.5;
    scene.add(object);

  }

  renderer = new THREE.WebGLRenderer({
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  new THREE.OrbitControls(camera, renderer.domElement);

}

function animate() {

  requestAnimationFrame(animate);
  renderer.render(scene, camera);

}
body {
      margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>

Sign up to request clarification or add additional context in comments.

4 Comments

Cool solution with the nice visual effect :)
Good example. Thanks for detailed comment :)
@Mugen87 This is nice! How would you do that with a MeshStandardMaterial?
Try it with this updated code: jsfiddle.net/u5tx2ban
1

I made a demo, you can define a function to calculate the opacity with the distance between the camera and mesh, the closer the higher, here is my formula:

opacity = -1/400*distance

You also need to set transparent true, and update opacity every frame. Here is my example.

2 Comments

Working with gl_FragCoord.z in the fragment shader is more accurate since you calculate the opacity value per fragment and not per object. Besides, your demo only works since all objects have an own material.
@Mugen87,Thanks for your advice, very good solution, I should learn how to write a shader.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.