WebGl 扫描仪效果

WebGl scanner effect

嘿,我刚看到这个短视频:https://vine.co/v/hxHz5H2Q07q,我想知道如何实现这种扫描效果。

一开始需要 2 组:一组拿着 material 的立方体,另一组拿着相同的立方体,但只是线框化,所以可以得到这些层。

但是如何让有线的出现这样"scanner"的样子呢?是通过着色器还是在 threejs 中有一些遮罩方法用于在渲染中移动遮罩并显示与遮罩链接的给定对象?

你有一堆平行光线,可能有 100 条或你想要的任何数量,它们射向你的场景并与其相交,同时沿 y 方向向上移动。

original demo

的描述中复制

This experiment was inspired by real-world projection mapping onto physical shapes. The field is created by random generation and merged into a single geometry. It is duplicated and the copy is rendered with a ShaderMaterial that looks at a uniform point and makes most pixels transparent other than those near the light point. Moving the point through the field creates the appearance of scanning.

所以,制作一些几何图形,绘制两次,一次是平面阴影,一个是线框。对于线框版本,制作一个采用单点统一的自定义着色器。如果顶点(或像素)靠近该点绘制颜色,如果不透明(或丢弃)。

var camera = new THREE.PerspectiveCamera( 20, 1, 1, 10000 );
camera.position.z = 1800;

var scene = new THREE.Scene();

var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );

var flatMaterial = new THREE.MeshLambertMaterial( { color: 0x606060, shading: THREE.FlatShading } );
var geometry = new THREE.IcosahedronGeometry( 200, 1 );

//var wireMaterial = new THREE.MeshBasicMaterial( { color: 0x00FF00, wireframe:true  } );

var uniforms = {
    color:           { type: "c",  value: new THREE.Color(0x00FF00) },
    lightPos:        { type: "v3", value: new THREE.Vector3(0, 0, 0) },
    range:           { type: "f",  value: 150, },
};


var wireMaterial = new THREE.ShaderMaterial({
    wireframe:      true,
    uniforms:       uniforms,
    attributes:     {
    },
    vertexShader:   document.getElementById('vertexshader').text,
    fragmentShader: document.getElementById('fragmentshader').text,
    depthTest:      true,
    transparent:    true,
  });


var mesh = new THREE.Mesh( geometry, flatMaterial );
scene.add( mesh );

var mesh = new THREE.Mesh( geometry, wireMaterial );
scene.add( mesh );

renderer = new THREE.WebGLRenderer( { antialias: true } );
document.body.appendChild( renderer.domElement );

function resize() {
    var canvas = renderer.context.canvas
    var width = canvas.clientWidth;
    var height = canvas.clientHeight;
    if (width != canvas.width || height != canvas.height) {
      renderer.setSize( width, height, false );
      camera.aspect = width / height;
      camera.updateProjectionMatrix();
    }
}

function render() {
  
    resize();
  
    var time = Date.now() * 0.001;
    uniforms.lightPos.value.x = Math.sin(time) * 200;
    uniforms.lightPos.value.y = Math.cos(time) * 200;
  
    camera.lookAt( scene.position );

    renderer.render( scene, camera );

    requestAnimationFrame( render );
}
render();
html, body {
  margin: 0px;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js"></script>
<body>
</body>

    <script type="not-js" id="vertexshader">
      varying vec4 v_position;

      void main() {

        vec4 pos = vec4(position, 1.0);

        gl_Position = projectionMatrix * modelViewMatrix * pos;
        v_position = modelMatrix * pos;
      }

    </script>

    <script type="not-js" id="fragmentshader">

      uniform vec3 color;
      uniform vec3 lightPos;
      uniform float range;

      varying vec4 v_position;

      void main() {
        float distanceToLight = distance(lightPos, v_position.xyz);
        gl_FragColor = mix(vec4(color, 1), vec4(0,0,0,0), step(range, distanceToLight));

      }

    </script>