在 Three.js 中创建沙滩伞的最有效资源方式

问题描述 投票:0回答:1

我是 Three.js 的新手,我发现它很棒,因为我可以轻松创建一个简单的场景。 我正在制作海滩场景,我想在场景中添加 10 万把海滩伞。我已经能够轻松地使用圆锥体代表天篷和圆柱体代表杆子来做到这一点。现在,为了使场景更加真实,我想将雨伞的伞面做成条纹,就像您在非常常见的沙滩伞中看到的那样。 我知道有多种方法可以做到这一点:

  • 使用纹理
  • 改变每个面的材质
  • 将每张幻灯片创建为单独的网格

由于每个选项都需要一些工作,考虑到我计划在场景中添加大约 10 万把雨伞,我想了解哪一个最能保持场景的轻量级。

感谢您的帮助

javascript optimization three.js web-frontend
1个回答
0
投票

尝试一下实例:

body{
  overflow: hidden;
  margin: 0;
}
<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/[email protected]/build/three.webgpu.js",
      "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
    }
  }
</script>
<script type="module">
import * as THREE from "three";
import {uv, vec2, vec3, fract, attribute, select} from "three";
import {OrbitControls} from "three/addons/controls/OrbitControls.js";
import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js";

console.clear();

let scene = new THREE.Scene();
scene.background = new THREE.Color("skyblue");
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 1, 1).setLength(10);
let renderer = new THREE.WebGPURenderer({antialias: true});
renderer.setPixelRatio( devicePixelRatio );
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));

let circle = 100;

let sand = new THREE.Mesh(
  new THREE.CircleGeometry(circle, 32).rotateX(-Math.PI * 0.5),
  new THREE.MeshLambertMaterial({color: "#F6DCBD"})
);
scene.add(sand);

let gs = [
  new THREE.CylinderGeometry(0.025, 0.025, 0.9, 3, 1, true).translate(0, 0.45, 0),
  new THREE.LatheGeometry([
    [1, 0.8], [0.66, 0.9], [0.33, 0.96], [0, 1]
  ].map(p => {return new THREE.Vector2(...p)}),
  8)
];
gs.forEach((g, gIdx) => {
  g.setAttribute("color", new THREE.Float32BufferAttribute(new Array(g.attributes.position.count * 3).fill(gIdx), 3));
})
let g = mergeGeometries(gs);
let m = new THREE.MeshLambertNodeMaterial({vertexColors: true});
let amount = 10000;
let io = new THREE.InstancedMesh(g, m, amount);

let dummy = new THREE.Object3D();
let dummyColor = new THREE.Color();
let instColor = [];
for(let i = 0; i < amount; i++){
  dummy.position.setFromCylindricalCoords(Math.sqrt(circle * circle * Math.random()), Math.random() * 2 * Math.PI, 0);
  dummy.rotation.y = Math.PI * 2 * Math.random();
  dummy.rotation.z = Math.PI * 0.05 * Math.sign(Math.random() - 0.5);
  dummy.updateMatrix();
  io.setMatrixAt(i, dummy.matrix);
  dummyColor.setHSL(Math.random(), 0.75, 0.5);
  instColor.push(dummyColor.r, dummyColor.g, dummyColor.b);
}
g.setAttribute("instColor", new THREE.InstancedBufferAttribute(new Float32Array(instColor), 3));

// TLS magic is here
let uvScaled = uv().mul(vec2(1., 3.)).toVar();
let iColor = attribute("instColor").toVar();
let col = select(fract(uvScaled.y).greaterThan(0.5), vec3(1., 1., 1.), iColor).toVar();
m.colorNode = col;

scene.add(io);

renderer.setAnimationLoop(() => {
  controls.update();
  renderer.render(scene, camera);
})
</script>

© www.soinside.com 2019 - 2024. All rights reserved.