Rendu 3D d'appartements procéduraux dans le navigateur : Next.js, Three.js et multijoueur WebSocket
Dans le projet Prikolnya, des appartements 3D pixélisés sont rendus pour des arcades multijoueurs sans modèles externes. Il utilise Next.js 16 avec React Three Fiber, une génération procédurale de meubles à partir de primitives, des textures Canvas pour le contenu et Socket.io pour la synchronisation des positions. Les scènes avec 300 à 800 objets fonctionnent parfaitement sur les appareils modernes.
Architecture de scène et modèles d'appartements
Les appartements sont construits à partir de zones (pièces) avec des murs et des portes partagés. Les modèles sont définis par des données sans logique codée en dur :
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: "Salon", x: 0, z: 0, width: 12, depth: 8 },
{ id: "gaming", name: "Salle de jeu", 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 },
],
};
Les modèles vont d'un studio (12x10) à un penthouse (24x22). Le rendu est déclaratif : les données sont transformées en objets Three.js.
Éclairage : couches et préréglages d'ambiance
La scène est éclairée avec trois couches :
- Ambient — couche de base, dépend de l'heure de la journée (matin : #ffd4a0, intensité 1.1 ; nuit : #1a2244, 0.4).
- Directionnel — lumière principale avec ombres + lumière de remplissage.
- Lumières ponctuelles — une par pièce à une hauteur de 2,3 unités.
Huit préréglages d'ambiance sont ajoutés. Fête : plafond #ff2288 (1.8), ambient #110022 (0.2), accent #00ffff (3.0). Transformation complète sans changer la géométrie.
Meubles procéduraux à partir de primitives
Les meubles sont assemblés à partir de boxGeometry et cylinderGeometry. Avantages : aucun téléchargement, génération instantanée, simplicité pour les descriptions IA.
Exemple de borne d'arcade :
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>
);
}
Scène : 300–800 objets. L'IA génère du JSON avec des primitives pour les présentations de marque.
Contenu mural via CanvasTexture
Les affiches sont rendues dynamiquement :
- Canvas 256x170.
- Arrière-plan #1a1a2e, cadre dans la couleur de l'auteur, texte avec retour à la ligne.
THREE.CanvasTextureavecNearestFilter.meshStandardMaterialavecemissiveIntensity: 0.05.
const canvas = document.createElement("canvas");
canvas.width = 256;
canvas.height = 170;
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = "#1a1a2e";
ctx.strokeStyle = authorColor;
// retour à la ligne, dessin
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.NearestFilter;
3 emplacements par mur (0.25, 0.5, 0.75), hauteur 1,3 unités. Prend en charge les images, vidéo, audio.
Avatars et animaux de compagnie pixélisés
Les avatars sont des textures Canvas sur une affiche. Couches : corps (5 tons), yeux, pantalon (6), vêtements (10), cheveux (12), accessoires (10). Échelle 16x24 → 128x192, NearestFilter.
Les animaux de compagnie sont interpolés avec lerp 0.05 (proche) / 0.15 (loin).
Multijoueur avec interpolation
Socket.io diffuse les positions (~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% par image).
volatile.emit supprime les paquets obsolètes.
Météo et effets
Systèmes de particules sur BufferGeometry :
- Pluie : 600 particules,
lineSegments. - Neige : 400 particules,
pointsavec dérive. - Étoiles : 200 particules, scintillement sur 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;
});
Portes et animations
Ouverture à 81° (π*0.45) basée sur le produit scalaire de la normale du mur :
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;
Optimisations de performance
| Solution | Effet |
|---------|--------|
| dynamic(() => import("./Apartment3D"), { ssr: false }) | Three.js chargé à la demande, ~150 KB gzip |
| antialias: false | -15-20% de fillrate |
| dpr: Math.min(devicePixelRatio, 2) | Économise 2-4x sur HiDPI |
| NearestFilter | Pas de filtrage trilinéaire |
| castShadow uniquement sur les meubles | Ombres sélectives |
| receiveShadow uniquement sur le sol | Un seul récepteur |
Bundle 3D : ~600 KB brut, Three.js tree-shaken.
Ludification et projets
- Économie : pièces pour les publications, limite 100/jour.
- Niveaux : 10, jusqu'à 3500 XP.
- Bâtiments croissants : 1-6 étages basés sur le contenu.
- Événements : 8/an, x3 XP.
- Pièces secrètes : 10 via codes.
Prochainement : meubles IA, trophées, instanciation/LOD.
Points clés à retenir
- Les meubles procéduraux à partir de 4-15 primitives éliminent les téléchargements glTF.
- Socket.io avec
volatile.emitet lerp 15% assure un multijoueur fluide. - CanvasTexture + NearestFilter pour le contenu et les avatars de style pixel.
- Les optimisations réduisent le bundle à 150 KB gzip avec 300-800 objets.
- Les modèles déclaratifs permettent 5 variantes d'appartements sans code.
— Editorial Team
Aucun commentaire pour le moment.