使用 THREE.js 渲染大量具有不同 theta 长度的球体
Render a lot of spheres with different theta length with THREE.js
我正在使用
var geometry = new THREE.SphereGeometry( 15, 32, 16, 0, 2*Math.PI, 0, x);
用 x < 2*PI
创建球体的一部分,看起来像这样:
问题是我需要数以万计的那些,它们都具有相同的中心和半径,但具有不同的旋转和不同且非常小的 x 值。
我已经对实例化进行了研究,但找不到按照我想要的方式使用它的方法。有什么建议吗?
使用额外的 InstancedBufferAttribute
在 InstancedMesh
中为每个实例传递 phi
个角度,并使用 .onBeforeCompile()
在顶点着色器中处理这些值以形成您想要的部分方法。
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.136.0/build/three.module.js";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000);
camera.position.set(0, 0, 50);
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 control = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
const MAX_COUNT = 10000;
let g = new THREE.SphereGeometry(1, 20, 10, 0, Math.PI * 2, Math.PI * 0.25, Math.PI * 0.5);
let m = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.vertexShader = `
attribute float instPhi;
// straight from the docs on Vector3.setFromSphericalCoords
vec3 setFromSphericalCoords( float radius, float phi, float theta ) {
float sinPhiRadius = sin( phi ) * radius;
float x = sinPhiRadius * sin( theta );
float y = cos( phi ) * radius;
float z = sinPhiRadius * cos( theta );
return vec3(x, y, z);
}
${shader.vertexShader}
`.replace(
`#include <beginnormal_vertex>`,
`#include <beginnormal_vertex>
vec3 sphPos = setFromSphericalCoords(1., instPhi * (1. - uv.y), PI * 2. * uv.x); // compute position
objectNormal = normalize(sphPos); // normal is just a normalized vector of the computed position
`).replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
transformed = sphPos; // set computed position
`
);
//console.log(shader.vertexShader);
}
});
let im = new THREE.InstancedMesh(g, m, MAX_COUNT);
scene.add(im);
let v3 = new THREE.Vector3();
let c = new THREE.Color();
let instPhi = []; // data for Phi values
let objMats = new Array(MAX_COUNT).fill().map((o, omIdx) => {
let om = new THREE.Object3D();
om.position.random().subScalar(0.5).multiplyScalar(100);
om.rotation.setFromVector3(v3.random().multiplyScalar(Math.PI));
om.updateMatrix();
im.setMatrixAt(omIdx, om.matrix);
im.setColorAt(omIdx, c.set(Math.random() * 0xffffff))
instPhi.push((Math.random() * 0.4 + 0.1) * Math.PI);
return om;
});
g.setAttribute("instPhi", new THREE.InstancedBufferAttribute(new Float32Array(instPhi), 1));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>
PS可以变成InstancedBufferGeometry
的使用。创意由你决定:)
我正在使用
var geometry = new THREE.SphereGeometry( 15, 32, 16, 0, 2*Math.PI, 0, x);
用 x < 2*PI
创建球体的一部分,看起来像这样:
问题是我需要数以万计的那些,它们都具有相同的中心和半径,但具有不同的旋转和不同且非常小的 x 值。
我已经对实例化进行了研究,但找不到按照我想要的方式使用它的方法。有什么建议吗?
使用额外的 InstancedBufferAttribute
在 InstancedMesh
中为每个实例传递 phi
个角度,并使用 .onBeforeCompile()
在顶点着色器中处理这些值以形成您想要的部分方法。
body{
overflow: hidden;
margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/three@0.136.0/build/three.module.js";
import {OrbitControls} from "https://cdn.skypack.dev/three@0.136.0/examples/jsm/controls/OrbitControls.js";
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 5000);
camera.position.set(0, 0, 50);
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 control = new OrbitControls(camera, renderer.domElement);
let light = new THREE.DirectionalLight(0xffffff, 0.5);
light.position.setScalar(1);
scene.add(light, new THREE.AmbientLight(0xffffff, 0.5));
const MAX_COUNT = 10000;
let g = new THREE.SphereGeometry(1, 20, 10, 0, Math.PI * 2, Math.PI * 0.25, Math.PI * 0.5);
let m = new THREE.MeshLambertMaterial({
side: THREE.DoubleSide,
onBeforeCompile: shader => {
shader.vertexShader = `
attribute float instPhi;
// straight from the docs on Vector3.setFromSphericalCoords
vec3 setFromSphericalCoords( float radius, float phi, float theta ) {
float sinPhiRadius = sin( phi ) * radius;
float x = sinPhiRadius * sin( theta );
float y = cos( phi ) * radius;
float z = sinPhiRadius * cos( theta );
return vec3(x, y, z);
}
${shader.vertexShader}
`.replace(
`#include <beginnormal_vertex>`,
`#include <beginnormal_vertex>
vec3 sphPos = setFromSphericalCoords(1., instPhi * (1. - uv.y), PI * 2. * uv.x); // compute position
objectNormal = normalize(sphPos); // normal is just a normalized vector of the computed position
`).replace(
`#include <begin_vertex>`,
`#include <begin_vertex>
transformed = sphPos; // set computed position
`
);
//console.log(shader.vertexShader);
}
});
let im = new THREE.InstancedMesh(g, m, MAX_COUNT);
scene.add(im);
let v3 = new THREE.Vector3();
let c = new THREE.Color();
let instPhi = []; // data for Phi values
let objMats = new Array(MAX_COUNT).fill().map((o, omIdx) => {
let om = new THREE.Object3D();
om.position.random().subScalar(0.5).multiplyScalar(100);
om.rotation.setFromVector3(v3.random().multiplyScalar(Math.PI));
om.updateMatrix();
im.setMatrixAt(omIdx, om.matrix);
im.setColorAt(omIdx, c.set(Math.random() * 0xffffff))
instPhi.push((Math.random() * 0.4 + 0.1) * Math.PI);
return om;
});
g.setAttribute("instPhi", new THREE.InstancedBufferAttribute(new Float32Array(instPhi), 1));
renderer.setAnimationLoop(() => {
renderer.render(scene, camera);
});
</script>
PS可以变成InstancedBufferGeometry
的使用。创意由你决定:)