点击查看html编辑器说明文档

importmap 测试 - 3D表情edit icon

|
|
Fork(复制)
|
|
作者:
lynn-ssk
提交反馈
嵌入
设置
下载
HTML
格式化
支持Emmet,输入 p 后按 Tab键试试吧!
<head> ...
展开
</head>
<body>
            
            <!-- 鼠标拖拽查看3D效果 -->

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three@0.164.1/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.164.1/examples/jsm/"
    }
  }
</script>


        
</body>
CSS
格式化
            
            body {
  overflow: hidden;
  margin: 0;
}
        
JS
格式化
            
            import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

// console.clear();

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(30, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 0, 15);
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

window.addEventListener("resize", (event) => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, Math.PI * 0.5));

// atlas
let atlasSize = new THREE.Vector2(4, 4);
let atlas = ((dim) => {
  let c = document.createElement("canvas");
  let tileSize = 256;
  c.width = tileSize * dim.x;
  c.height = tileSize * dim.y;
  let u = (val) => tileSize * 0.01 * val;
  let ctx = c.getContext("2d");

  // debug
  ctx.fillStyle = "rgba(255, 255, 255, 1)";
  ctx.fillRect(0, 0, c.width, c.height);
  //ctx.clearRect(0, 0, c.width, c.height);

  for (let y = 0; y < dim.y; y++) {
    for (let x = 0; x < dim.x; x++) {
      generateSilly(x, y);
    }
  }

  let tex = new THREE.CanvasTexture(c);
  tex.colorSpace = "srgb";
  tex.anisotropy = renderer.capabilities.getMaxAnisotropy();
  return tex;

  function generateSilly(x, y) {
    ctx.save();
    ctx.translate((x + 0.5) * tileSize, (y + 0.5) * tileSize);
    // eyes
    ctx.lineWidth = u(5);
    ctx.lineCap = "round";
    ctx.strokeStyle = "rgba(127, 127, 127, 1)";
    drawEyes(25, -25, 15);
    drawNose();
    drawMouth();
    ctx.restore();
  }

  function drawMouth() {
    let p1 = [-25, Math.random() * 25];
    let p2 = [-10 + Math.random() * 20, Math.random() * 25];
    let p3 = [25, Math.random() * 25];
    ctx.beginPath();
    let yShift = 20;
    ctx.moveTo(u(p1[0]), u(yShift + p1[1]));
    ctx.quadraticCurveTo(
      u(p2[0]),
      u(yShift + p2[1]),
      u(p3[0]),
      u(yShift + p3[1])
    );
    ctx.stroke();
  }

  function drawNose() {
    ctx.beginPath();
    let arcStart = Math.random() * Math.PI * 2;
    let arcEnd = arcStart + (Math.random() * 0.75 + 0.25) * Math.PI * 2;
    ctx.arc(0, 0, u(Math.random() * 10 + 5), arcStart, arcEnd);
    ctx.stroke();
  }

  function drawEyes(x, y, radius) {
    let eyeSymmX = Math.sign(Math.random() - 0.5);
    let eyeSymmY = Math.sign(Math.random() - 0.5);
    //left
    ctx.fillStyle = "rgba(255, 255, 255, 1)";
    ctx.beginPath();
    ctx.arc(-u(x), u(y), u(radius), 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    // pupil
    let dir = [Math.random() - 0.5, Math.random() - 0.5];
    let dirL = Math.hypot(dir[0], dir[1]);
    let dirN = [dir[0] / dirL, dir[1] / dirL];
    let pupilShift = Math.random() * 5;
    let finalDir = { x: dirN[0] * pupilShift, y: dirN[1] * pupilShift };
    let pupilR = 7;
    let pupilColor = `hsla(${Math.random() * 360}, 100%, 25%, 1)`;
    //console.log(finalDir);
    ctx.fillStyle = pupilColor;
    ctx.beginPath();
    ctx.arc(-u(x + finalDir.x), u(y + finalDir.y), u(pupilR), 0, Math.PI * 2);
    ctx.fill();

    // right
    ctx.fillStyle = "rgba(255, 255, 255, 1)";
    ctx.beginPath();
    ctx.arc(u(x), u(y), u(radius), 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    // pupil
    ctx.fillStyle = pupilColor;
    ctx.beginPath();
    ctx.arc(
      u(x + finalDir.x * eyeSymmX),
      u(y + finalDir.y * eyeSymmY),
      u(pupilR),
      0,
      Math.PI * 2
    );
    ctx.fill();
  }
})(atlasSize);

for(let y = 0; y < atlasSize.y; y++){
  for(let x = 0; x < atlasSize.x; x++){
    let testObj = new THREE.Mesh(
      new THREE.SphereGeometry(0.9, 64, 32),
      new THREE.MeshLambertMaterial({
        color: new THREE.Color().setHSL(Math.random(), 0.75, 0.75),
        map: atlas,
        onBeforeCompile: shader => {
          shader.uniforms.atlasSize = {value: atlasSize};
          shader.uniforms.tile = {value: new THREE.Vector2(x, y)}
          shader.fragmentShader = `
            uniform vec2 atlasSize;
            uniform vec2 tile;
            ${shader.fragmentShader}
          `.replace(
            `#include <map_fragment>`,
            `        
            vec2 mUV = vMapUv;
            vec2 centerUV = ((mUV - 0.5) * vec2(2., 1.) + vec2(0.5, 0.)) * PI;
            mUV = centerUV + 0.5;
            vec2 atlasTile = 1. / atlasSize;

            mUV = clamp((mUV + tile) * atlasTile, vec2(0.), vec2(1.));

            #ifdef USE_MAP

            vec4 sampledDiffuseColor = texture2D( map, mUV );

            #ifdef DECODE_VIDEO_TEXTURE

              // use inline sRGB decode until browsers properly support SRGB8_ALPHA8 with video textures (#26516)

              sampledDiffuseColor = vec4( 
                mix( 
                  pow( 
                    sampledDiffuseColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) 
                  ), 
                  sampledDiffuseColor.rgb * 0.0773993808, 
                  vec3( lessThanEqual( sampledDiffuseColor.rgb, vec3( 0.04045 ) ) ) 
                )
                , sampledDiffuseColor.w 
              );

            #endif

            vec2 absUV = abs(centerUV);
            diffuseColor *= mix(sampledDiffuseColor, vec4(1), step(0.5, max(absUV.x, absUV.y)));

          #endif
            `
          );
          //console.log(shader.fragmentShader)
        }
      })
    );
    testObj.position.set(
      (-(atlasSize.x - 1) * 0.5 + x) * 2,
      (-(atlasSize.y - 1) * 0.5 + y) * 2,
      0
    );
    scene.add(testObj);
  }
}


let clock = new THREE.Clock();
let t = 0;

renderer.setAnimationLoop(() => {
  let dt = clock.getDelta();
  t += dt;
  controls.update();

  renderer.render(scene, camera);
});
        
预览
控制台
清空