Created
October 7, 2025 13:38
-
-
Save ji-podhead/4950c9a6111e4b7c6dde7a73cdd5741e to your computer and use it in GitHub Desktop.
Voxelize a mesh with threejs and sample the texture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as THREE from 'three'; | |
| import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js'; | |
| /** | |
| * Extrahiert Textureddaten in ein format, das einfach zu sampeln ist. | |
| * @param {THREE.Texture} texture - Die zu verarbeitende Texture. | |
| * @returns {ImageData} Die Bilddaten der Texture. | |
| */ | |
| function getTextureImageData(texture) { | |
| const image = texture.image; | |
| if (!image || !image.width || !image.height) { | |
| console.error("Texture image is not loaded or invalid."); | |
| return null; | |
| } | |
| // Optimierung: Canvas nur erstellen, wenn nötig | |
| const canvas = document.createElement('canvas'); | |
| canvas.width = image.width; | |
| canvas.height = image.height; | |
| const context = canvas.getContext('2d', { willReadFrequently: true }); | |
| context.drawImage(image, 0, 0, image.width, image.height); | |
| // Optimierung: Direkter Zugriff auf ImageData | |
| return context.getImageData(0, 0, image.width, image.height); | |
| } | |
| /** | |
| * Voxelisiert ein Mesh durch Oberflächensampling und extrahiert Farbdaten aus der Texture. | |
| * Diese Version ist stark optimiert und verwendet MeshSurfaceSampler. | |
| * @param {THREE.Mesh} mesh - Das zu voxelisierende Mesh. | |
| * @param {number} particleCount - Die Anzahl der zu sampelnden Partikel. | |
| * @param {number} [distanceThreshold=5] - Minimale Entfernung vom Modellzentrum für Partikel. | |
| * @returns {Promise<{positions: Float32Array, colors: Float32Array}>} | |
| * Ein Promise, das mit Positionen und Farben für die Partikel auflöst. | |
| */ | |
| export async function textureVoxelizer(mesh, particleCount, distanceThreshold = 5000) { | |
| // Optimierung: Frühe Validierung | |
| if (!mesh.geometry || !mesh.material) { | |
| console.error("Mesh muss Geometrie und Material haben."); | |
| return null; | |
| } | |
| const material = Array.isArray(mesh.material) ? mesh.material[0] : mesh.material; | |
| const texture = material.map; | |
| // Optimierung: Sofortige Fehlerbehandlung | |
| if (!texture || !texture.isTexture) { | |
| console.error("Das Material muss eine Texture haben."); | |
| return { positions: new Float32Array(), colors: new Float32Array() }; | |
| } | |
| // Optimierung: Direkte Texturextraktion | |
| if (!texture.image) { | |
| console.error("Texture-Bild nicht verfügbar."); | |
| return { positions: new Float32Array(), colors: new Float32Array() }; | |
| } | |
| const imageData = getTextureImageData(texture); | |
| if (!imageData) { | |
| console.error("Fehler beim Abrufen der Textureddaten."); | |
| return { positions: new Float32Array(), colors: new Float32Array() }; | |
| } | |
| // Optimierung: Vorinitialisierte Arrays | |
| const positions = new Float32Array(particleCount * 3); | |
| const colors = new Float32Array(particleCount * 3); | |
| // Optimierung: Effizienter Sampler | |
| const sampler = new MeshSurfaceSampler(mesh).build(); | |
| const _position = new THREE.Vector3(); | |
| const _normal = new THREE.Vector3(); | |
| const raycaster = new THREE.Raycaster(); | |
| // Optimierung: Parallelisierte Partikelgenerierung | |
| const sampleCount = Math.ceil(particleCount / 4); // 4 Partikel pro Iteration | |
| for (let i = 0; i < sampleCount; i++) { | |
| // Optimierung: Mehrere Partikel pro Iteration | |
| for (let j = 0; j < 4 && i * 4 + j < particleCount; j++) { | |
| sampler.sample(_position, _normal); | |
| // Optimierung: Präzise UV-Koordinaten | |
| raycaster.set(_position.clone().addScaledVector(_normal, 0.001), _normal.clone().negate()); | |
| const intersects = raycaster.intersectObject(mesh, false); | |
| if (intersects.length > 0 && intersects[0].uv) { | |
| const uv = intersects[0].uv; | |
| const index = (i * 4 + j) * 3; | |
| // Position setzen | |
| positions[index] = _position.x; | |
| positions[index + 1] = _position.y; | |
| positions[index + 2] = _position.z; | |
| // Farbe extrahieren und optimieren | |
| const tx = Math.floor(uv.x * (imageData.width - 1)); | |
| const ty = Math.floor((1 - uv.y) * (imageData.height - 1)); | |
| const colorIndex = (ty * imageData.width + tx) * 4; | |
| // Sample RGB from texture | |
| const sampledR = imageData.data[colorIndex] / 255; | |
| const sampledG = imageData.data[colorIndex + 1] / 255; | |
| const sampledB = imageData.data[colorIndex + 2] / 255; | |
| // Create a THREE.Color object from sampled RGB | |
| const sampledColor = new THREE.Color(sampledR, sampledG, sampledB); | |
| // Get HSL values | |
| const hsl = sampledColor.getHSL(new THREE.Color()); // getHSL returns the color object itself, so we can chain it or use a temp var | |
| // Apply user's HSL modification | |
| const modifiedH = hsl.h; | |
| const modifiedS = hsl.s * 0.8; | |
| const modifiedL = hsl.l * 0.8 + 0.2; | |
| // Set the color with modified HSL | |
| sampledColor.setHSL(modifiedH, modifiedS, modifiedL); | |
| // Get the new RGB values and scale them by 100 as in the original code | |
| const finalR = sampledColor.r * 250; | |
| const finalG = sampledColor.g * 250; | |
| const finalB = sampledColor.b * 250; | |
| // Assign color values to the Float32Array using the correct index | |
| colors[index] = finalR; | |
| colors[index + 1] = finalG; | |
| colors[index + 2] = finalB; | |
| } | |
| } | |
| } | |
| return { | |
| positions: positions, | |
| colors: colors | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment