reactjs - 从 API 获取数据后如何更新 BufferGeometry 中的点?
问题描述
我希望通过首先创建随机 50000 个点的 BufferGeometry 来创建点云,然后在获取数据后使用新数据更新这些点。这是我想出的组件
import React from "react";
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber'
import { useRef, useEffect, useState, useMemo, useCallback } from "react";
import axios from 'axios';
function Particles({points}) {
const [positions, colors] = useMemo(() => {
let positions = [],
colors = []
if(points.length == 0){
console.log(positions.length)
console.log("Creating random points")
for (let i = 0; i < 50000; i++) {
positions.push(5 - Math.random() * 10)
positions.push(5 - Math.random() * 10)
positions.push(5 - Math.random() * 10)
colors.push(1)
colors.push(0.5)
colors.push(0.5)
}
}
else{
console.log("Filling in values")
console.log(positions.length)
for (let i = 0; i < points.length; i++) {
positions.push(points[i][0])
positions.push(points[i][1])
positions.push(points[i][2])
colors.push(1)
colors.push(0.5)
colors.push(0.5)
}
}
return [new Float32Array(positions), new Float32Array(colors)]
}, [points])
return (
<points>
<bufferGeometry attach="geometry">
<bufferAttribute attachObject={["attributes", "position"]} count={positions.length / 3} array={positions} itemSize={3} />
<bufferAttribute attachObject={["attributes", "color"]} count={colors.length / 3} array={colors} itemSize={3} />
</bufferGeometry>
<pointsMaterial attach="material" vertexColors size={10} sizeAttenuation={false} />
</points>
)
}
const PointCloudPanel = () => {
const [points, setPoints] = useState([])
axios.post('http://127.0.0.1:8090/get_point_cloud')
.then(res => {
setPoints(res.data)
console.log(points)
})
return (
<div>
<Canvas style={{width: `80%`, height: `1000px`}} margin-left='auto' margin-right='auto' orthographic camera={{ zoom: 60 }} raycaster={{ params: { Points: { threshold: 0.2 } } }}>
<Particles points={points} />
</Canvas>
</div>
)
}
export default PointCloudPanel
此代码没有达到预期的效果,而是继续循环调用 API。获取的数据只是具有 x,y,z 值的列表列表,例如
[[1,2,3],[4,3,6],[3,6,8]]
.
解决方案
points
不应该这样更新。更改道具会导致重新渲染。
尝试改变顶点位置并使用内部 API( .needsUpdate
)更新它们
// state.js
const state = {
// this can be used as prop in initial render
// but eventually it should be updated manually via ref.
// and most importantly, length of array should not be changed.
points: new Float32Array([/* ... */]),
requireUpdate: false;
};
export default state;
// your component
import useFrame from '@react-three/fiber';
import state from 'state';
const YourComponent = () => {
const refPoints = useRef();
useFrame(() => {
if (refPoints.current && state.requireUpdate) {
// manually inject numbers into property. so that it won't trigger re-render.
const pos = refPoints.current.geometry.getAttribute('position');
for (let i = 0; i <= state.points.length; i ++) {
pos.array[i] = state.points[i]
}
refPoints.current.geometry.setAttribute('position', pos);
refPoints.current.geometry.attributes.position.needsUpdate = true;
state.requireUpdate = false;
}
});
return (<points ref={refPoints}>
{/* ... bufferGeometry and bufferAttributes ... */}
</points>);
};
// inside UI component
import state from 'state';
const UIComponent = () => {
const updatePoints = async () => {
const result = await makeYourOwnRequest();
state.points = new Float32Array([...result]);
state.requireUpdate = true;
}
return <button onClick={updatePoints} />
}
与典型的反应代码不同,更新 r3f 中的状态不应触发重新渲染。在这种情况下,值突变非常方便。要构建更复杂的东西,请尝试使用zustand。它支持浅状态更新,不会触发与值更新相关的事件。
阅读这个three.js 官方文档会有所帮助。
推荐阅读
- python - `pip list --outdated` 没有列出 pip 本身
- sql-server - invoke-sqlcmd 失败在某些类型的查询上显示没有结果
- javascript - 如何使用 JS 或 JQuery 在每次重新加载时保存对象大小和拖动位置
- c# - C# 检查字符串是否包含一个字符但允许包含其他特定字符
- php - PHP 递归似乎并没有遍历所有途径
- mysql - Mysql int 数据类型的空默认值
- autocomplete - Atom自动完成加导航,不起作用
- python - 我想在 django 社交应用登录后添加服装字段
- java - 如何在 Java 8 中检查两个未排序的列表是否相等?
- c++ - 链表显示功能不正常