Prozedurale 3D-Wohnungsvisualisierung im Browser: Next.js, Three.js und WebSocket-Multiplayer
Im Prikolnya-Projekt werden pixelige 3D-Wohnungen für Multiplayer-Arcades ohne externe Modelle gerendert. Es nutzt Next.js 16 mit React Three Fiber, prozedurale Möbelgenerierung aus Primitiven, Canvas-Texturen für Inhalte und Socket.io für Positionssynchronisation. Szenen mit 300–800 Objekten laufen flüssig auf modernen Geräten.
Szenenarchitektur und Wohnungsvorlagen
Wohnungen werden aus Zonen (Räumen) mit gemeinsamen Wänden und Türen aufgebaut. Vorlagen werden durch Daten ohne fest kodierte Logik definiert:
type RoomDef = {
id: string;
name: string;
x: number; z: number;
width: number; depth: number;
isSecret?: boolean;
};
type WallDef = {
x1: number; z1: number;
x2: number; z2: number;
hasDoor?: boolean;
doorOffset?: number;
};
const LOFT: ApartmentTemplate = {
id: "loft",
rooms: [
{ id: "lounge", name: "Lounge", x: 0, z: 0, width: 12, depth: 8 },
{ id: "gaming", name: "Gaming Room", x: 12, z: 0, width: 10, depth: 8 },
{ id: "bar", name: "Bar", x: 0, z: 8, width: 10, depth: 6 },
{ id: "studio", name: "Studio", x: 10, z: 8, width: 12, depth: 6 },
],
walls: [
{ x1: 0, z1: 0, x2: 22, z2: 0 },
{ x1: 22, z1: 0, x2: 22, z2: 14 },
{ x1: 12, z1: 0, x2: 12, z2: 8, hasDoor: true },
],
};
Vorlagen reichen von einem Studio (12x10) bis zu einer Penthouse-Wohnung (24x22). Das Rendering ist deklarativ: Daten werden in Three.js-Objekte transformiert.
Beleuchtung: Ebenen und Stimmungsvorgaben
Die Szene wird mit drei Ebenen beleuchtet:
- Ambient – Basisebene, abhängig von der Tageszeit (morgens: #ffd4a0, Intensität 1.1; nachts: #1a2244, 0.4).
- Directional – Hauptlicht mit Schatten + Fülllicht.
- Punktlichter – eines pro Raum in einer Höhe von 2,3 Einheiten.
Acht Stimmungsvorgaben sind hinzugefügt. Party: Decke #ff2288 (1.8), ambient #110022 (0.2), Akzent #00ffff (3.0). Komplette Transformation ohne Geometrieänderung.
Prozedurale Möbel aus Primitiven
Möbel werden aus boxGeometry und cylinderGeometry zusammengesetzt. Vorteile: keine Downloads, sofortige Generierung, Einfachheit für KI-Beschreibungen.
Beispiel-Arcade-Automat:
function ArcadeModel({ color, accent }: { color: string; accent: string }) {
return (
<group>
<Block args={[0.7, 1.6, 0.7]} position={[0, 0.8, 0]} color={color} />
<Block args={[0.5, 0.35, 0.05]} position={[0, 1.2, 0.33]}
color="#0a0a2a" emissive={accent} emissiveIntensity={0.3} />
<Block args={[0.5, 0.05, 0.25]} position={[0, 0.85, 0.25]}
color="#1a1a2e" rotation={[-0.3, 0, 0]} />
<mesh position={[-0.1, 0.92, 0.25]}>
<cylinderGeometry args={[0.02, 0.02, 0.12]} />
<meshStandardMaterial color="#ff2e63" />
</mesh>
{[0.05, 0.15].map((x) => (
<mesh key={x} position={[x, 0.9, 0.2]}>
<cylinderGeometry args={[0.025, 0.025, 0.02]} />
<meshStandardMaterial color={accent} emissive={accent}
emissiveIntensity={0.5} />
</mesh>
))}
</group>
);
}
Szene: 300–800 Objekte. KI generiert JSON mit Primitiven für Markenpräsentationen.
Wandinhalt via CanvasTexture
Poster werden dynamisch gerendert:
- Canvas 256x170.
- Hintergrund #1a1a2e, Rahmen in Autorfarbe, umgebrochener Text.
THREE.CanvasTexturemitNearestFilter.meshStandardMaterialmitemissiveIntensity: 0.05.
const canvas = document.createElement("canvas");
canvas.width = 256;
canvas.height = 170;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = "#1a1a2e";
ctx.strokeStyle = authorColor;
// Textumbruch, Zeichnen
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.NearestFilter;
3 Slots pro Wand (0.25, 0.5, 0.75), Höhe 1,3 Einheiten. Unterstützt Bilder, Video, Audio.
Pixel-Avatare und Haustiere
Avatare sind Canvas-Texturen auf einem Billboard. Ebenen: Körper (5 Töne), Augen, Hose (6), Kleidung (10), Haare (12), Accessoires (10). Skalierung 16x24 → 128x192, NearestFilter.
Haustiere werden mit lerp 0.05 (nah) / 0.15 (fern) interpoliert.
Multiplayer mit Interpolation
Socket.io sendet Positionen (~20 FPS):
useFrame(() => {
const now = Date.now();
if (now - lastRef.current.t < 50) return;
const dx = Math.abs(pos.x - lastRef.current.x);
const dz = Math.abs(pos.z - lastRef.current.z);
if (dx < 0.05 && dz < 0.05) return;
socketRef.current?.volatile.emit("position", { x: pos.x, z: pos.z });
});
Interpolation: position.x += (target.x - position.x) * 0.15 (15% pro Frame).
volatile.emit verwirft veraltete Pakete.
Wetter und Effekte
Partikelsysteme auf BufferGeometry:
- Regen: 600 Partikel,
lineSegments. - Schnee: 400 Partikel,
pointsmit Drift. - Sterne: 200 Partikel, flackernd auf Y.
useFrame(() => {
for (let i = 0; i < count; i++) {
positions[i * 3 + 1] -= velocities[i];
positions[i * 3] += Math.sin(Date.now() * 0.0008 + i * 0.7) * 0.004;
if (positions[i * 3 + 1] < -0.5) {
positions[i * 3 + 1] = 7 + Math.random() * 2;
}
}
posAttr.needsUpdate = true;
});
Türen und Animationen
Öffnung auf 81° (π*0.45) basierend auf Skalarprodukt der Wandnormale:
const wallNormal = { x: -(z2 - z1), z: x2 - x1 };
const toPlayer = { x: playerX - doorX, z: playerZ - doorZ };
const dot = wallNormal.x * toPlayer.x + wallNormal.z * toPlayer.z;
const direction = dot > 0 ? 1 : -1;
currentAngle += (targetAngle * direction - currentAngle) * 0.1;
Leistungsoptimierungen
| Lösung | Effekt |
|---------|--------|
| dynamic(() => import("./Apartment3D"), { ssr: false }) | Three.js lazy-loaded, ~150 KB gzip |
| antialias: false | -15-20% Fillrate |
| dpr: Math.min(devicePixelRatio, 2) | Spart 2-4x auf HiDPI |
| NearestFilter | Kein trilineares Filtern |
| castShadow nur auf Möbeln | Schatten selektiv |
| receiveShadow nur auf Boden | Ein Empfänger |
3D-Bundle: ~600 KB roh, tree-shaken Three.js.
Gamification und Pläne
- Wirtschaft: Münzen für Beiträge, Limit 100/Tag.
- Level: 10, bis zu 3500 XP.
- Wachsende Gebäude: 1-6 Stockwerke basierend auf Inhalten.
- Events: 8/Jahr, x3 XP.
- Geheime Räume: 10 via Codes.
Nächstes: KI-Möbel, Trophäen, Instancing/LOD.
Wichtigste Erkenntnisse
- Prozedurale Möbel aus 4-15 Primitiven eliminieren glTF-Downloads.
- Socket.io mit
volatile.emitund 15% lerp sorgt für flüssigen Multiplayer. - CanvasTexture + NearestFilter für Pixel-Stil-Inhalte und Avatare.
- Optimierungen reduzieren Bundle auf 150 KB gzip mit 300-800 Objekten.
- Deklarative Vorlagen ermöglichen 5 Wohnungsvarianten ohne Code.
— Editorial Team
Noch keine Kommentare.