Out of interest I wanted the precusor to a 3D hull, a convex one. I heavily used chatgpt for the implementation, but came up with the ideas and creativity myself.

So the easiest way to do this is to get each length to the centre of the points.

Once we have all these lengths, we get the top 10th for example. So we get the ones which are furtherest away from the centre.

So we avoid the need for complex algorithms. And can use lengths to describe the outer points.

Once we have the points it was simple enough to construct a wireframe.

Below is the code for this little experiment.

1import React, { useMemo } from 'react';2import { Points, PointMaterial, OrbitControls } from '@react-three/drei';3import * as THREE from 'three';45import { ConvexGeometry } from 'three/examples/jsm/geometries/ConvexGeometry.js'; // Import ConvexGeometry helper67function createRedPointsGeometry(redPoints) {8 const vectors = [];9 for (let i = 0; i < redPoints.length; i += 3) {10 vectors.push(new THREE.Vector3(redPoints[i], redPoints[i + 1], redPoints[i + 2]));11 }1213 // Create a Convex Geometry from the outer points14 const convexGeometry = new ConvexGeometry(vectors);1516 return convexGeometry;17}1819function getRandomPointInSphere(radius) {20 const u = Math.random();21 const v = Math.random();22 const theta = 2 * Math.PI * u;23 const phi = Math.acos(2 * v - 1);24 const r = radius * Math.cbrt(Math.random());2526 const x = r * Math.sin(phi) * Math.cos(theta);27 const y = r * Math.sin(phi) * Math.sin(theta);28 const z = r * Math.cos(phi);2930 return [x, y, z];31 }3233function RandomPoints({ radius = 5, count = 1000 }) {34 const { innerPoints, outerPoints } = useMemo(() => {35 const points = [];36 for (let i = 0; i < count; i++) {37 const point = getRandomPointInSphere(radius);38 const distance = Math.sqrt(point[0] ** 2 + point[1] ** 2 + point[2] ** 2);39 points.push({ point, distance });40 }4142 points.sort((a, b) => b.distance - a.distance);43 const top10PercentIndex = Math.floor(count * 0.1);44 const outerPoints = points.slice(0, top10PercentIndex).map(p => p.point);45 const innerPoints = points.slice(top10PercentIndex).map(p => p.point);4647 return {48 innerPoints: new Float32Array(innerPoints.flat()),49 outerPoints: new Float32Array(outerPoints.flat())50 };51 }, [radius, count]);5253 // Create Convex Geometry and Wireframe Geometry for the outer points54 const convexGeometry = useMemo(() => createRedPointsGeometry(outerPoints), [outerPoints]);55 const wireframeGeometry = useMemo(() => new THREE.WireframeGeometry(convexGeometry), [convexGeometry]);5657 return (58 <>59 {/* Inner Points */}60 <Points positions={innerPoints}>61 <PointMaterial color="white" size={0.5} sizeAttenuation={true} />62 </Points>6364 {/* Outer Points as Convex Hull Wireframe */}65 <lineSegments geometry={wireframeGeometry}>66 <lineBasicMaterial color="red" />67 </lineSegments>68 </>69 );70}7172export default function App() {73 return (74 <>75 <ambientLight />76 <RandomPoints />77 <OrbitControls />78 </>79 );80}

We could use buffergeometries `.setFromPoints`

and use the outer points to construct a geometry.

For uvs you could use something like gl-mesh3d and for normals consider the following:

So we get the central point in the points blob and for each outer vertex we do:

1normalise(outerVertex - centre) = normal