Edit in JSRUN

console 命令行工具 X clear

                    
>
console
'use strict';

let scene,
    camera,
    renderer,
    controls,
    cactus;

let width = window.innerWidth,
    height = window.innerHeight;

init();
animate();

function init() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
  camera.lookAt(scene.position);
  camera.position.z = 500;

  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(width, height);
  renderer.setClearColor(0xf3f3f3);
  
  controls = new THREE.OrbitControls(camera, renderer.domElement);
  
  const ambientLight = new THREE.AmbientLight();
  scene.add(ambientLight);

  const light = new THREE.DirectionalLight();
  light.position.set(1000, 1000, 2000);
  light.castShadow = true;
  scene.add(light);
  
  drawCactus();

  document.body.appendChild(renderer.domElement);

  window.addEventListener('resize', onResize);
}

function onResize() {
  width = window.innerWidth;
  height = window.innerHeight;
  camera.aspect = width / height;
  camera.updateProjectionMatrix();
  renderer.setSize(width, height);
}

function animate() {
  requestAnimationFrame(animate);

  render();
}

function render() {
  cactus.rotation.x -= 0.01;
  cactus.rotation.y -= 0.02;
  renderer.render(scene, camera);
}

function drawCactus() {
  const geometry = new THREE.SphereGeometry(100, 5, 5);
  geometry.computeBoundingSphere();

  const scale = 300 / geometry.boundingSphere.radius;
  geometry.scale(scale, scale, scale);

  const originalGeometry = geometry.clone();
  originalGeometry.computeFaceNormals();
  originalGeometry.computeVertexNormals(true);

  geometry.mergeVertices();
  geometry.computeFaceNormals();
  geometry.computeVertexNormals(true);

  cactus = new THREE.Group();
  scene.add(cactus);

  const mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
    color: 0xfefefe,
    wireframe: true,
    opacity: 0.5,
  }));
  cactus.add(mesh);

  const innerGeometry = new THREE.SphereGeometry(220, 5, 5);
  const innerSphere = new THREE.Mesh(innerGeometry,
                                     new THREE.MeshStandardMaterial({
    color: 0x68be83,
    roughness: 0.8,
    shading: THREE.FlatShading,
  }));

  cactus.add(innerSphere);

  for (let f = 0, fl = geometry.faces.length; f < fl; f += 1) {
    const face = geometry.faces[f];
    const centroid = new THREE.Vector3()
    .add(geometry.vertices[face.a])
    .add(geometry.vertices[face.b])
    .add(geometry.vertices[face.c])
    .divideScalar(3);
    const arrow = new THREE.ArrowHelper(face.normal, centroid, 15, 0x3333FF);
    mesh.add(arrow);
  }

  const fvNames = ['a', 'b', 'c', 'd'];

  for (let f = 0, fl = originalGeometry.faces.length; f < fl; f += 1) {
    const face = originalGeometry.faces[f];
    for (let v = 0, vl = face.vertexNormals.length; v < vl; v += 1) {
      const arrow = new THREE.ArrowHelper(
        face.vertexNormals[v],
        originalGeometry.vertices[face[fvNames[v]]],
        15,
        0xFF3333
      );
      mesh.add(arrow);
    }
  }

  for (let f = 0, fl = mesh.geometry.faces.length; f < fl; f += 1) {
    const face = mesh.geometry.faces[f];
    for (let v = 0, vl = face.vertexNormals.length; v < vl; v += 1) {
      const arrow = new THREE.ArrowHelper(
        face.vertexNormals[v],
        mesh.geometry.vertices[face[fvNames[v]]],
        15,
        0x000000
      );
      mesh.add(arrow);
    }
  }
}
body {
  margin: 0;
  overflow: hidden;
}

本项目引用的自定义外部资源