diff --git a/RIFI/index.html b/RIFI/index.html index 5eedb7addf4a1b80df05f582818146b7e903d27a..c9f0c096f1926f588001f1d02a7b9c3ce87eab33 100644 --- a/RIFI/index.html +++ b/RIFI/index.html @@ -2,7 +2,7 @@ <html> <head> <meta charset="UTF-8"> - <title>Projet webGL</title> + <title>Nature Morte Vanitas - Projet WebGL</title> <style> body { margin: 0; @@ -26,7 +26,7 @@ h1 { font-size: 32px; margin-bottom: 20px; - color: #d4af37; /* Gold color */ + color: #d4af37; /* Couleur or */ } p { margin-bottom: 20px; @@ -39,25 +39,32 @@ height: 494px; } </style> - </head> - <body> - <!-- API importe du site de Three.js --> - <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script> - <script type="importmap"> - { - "imports": { +</head> +<body> + <div class="container"> + <h1 class="centre">Nature Morte Vanitas - Projet WebGL</h1> + + <p class="centre"> + Une interprétation 3D de la peinture classique de nature morte vanitas, mettant en scène un crâne, une bougie et des objets symboliques représentant la fugacité de la vie. + </p> + + <div id="webGL" class="centre"></div> + + <p> + Cette scène est inspirée des peintures vanitas du XVIIe siècle, qui contiennent des objets symboliques rappelant aux spectateurs le passage du temps, la fragilité de la vie et la vanité des ambitions terrestres. + </p> + </div> + + <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script> + <script type="importmap"> + { + "imports": { "three": "https://threejs.org/build/three.module.js", "three/addons/": "https://threejs.org/examples/jsm/" - } } - </script> - <!-- JQuery pour afficher les erreurs --> - <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script> - <!-- Un titre centre --> - <h1 class="centre"> Projet webGL</h1> - <div id="webGL" class="centre"></div> - <!-- Mon script avec un chemin relatif --> - <script type="module" src="script.js"></script> - + } + </script> + <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script> + <script type="module" src="script.js"></script> </body> </html> \ No newline at end of file diff --git a/RIFI/script.js b/RIFI/script.js index 271f4444e6d6aa96bdc3d53b8f8962d9292a2e6e..4e523e6f6aca18af910d0ba333d1d80eb072f41e 100644 --- a/RIFI/script.js +++ b/RIFI/script.js @@ -8,78 +8,83 @@ var camera, scene, renderer; var cameraControls; var clock = new THREE.Clock(); var skull; +var candle, candleHolder, flame; // Variables pour la bougie, son support et la flamme +var backgroundMesh; // Pour accéder au fond et le modifier avec la lumière function fillScene() { scene = new THREE.Scene(); - // Nous allons remplacer cette ligne par notre arrière-plan dégradé - // scene.background = new THREE.Color(0x000000); - - // Ajout du fond dégradé createGradientBackground(); - // Éclairage dramatique - var mainLight = new THREE.DirectionalLight(0xFFAA55, 0.7); + var mainLight = new THREE.DirectionalLight(0xFFAA55, 0.5); mainLight.position.set(-200, 200, 100); + // Configuration des ombres pour la lumière principale + mainLight.castShadow = true; + mainLight.shadow.mapSize.width = 2048; + mainLight.shadow.mapSize.height = 2048; + mainLight.shadow.camera.near = 0.5; + mainLight.shadow.camera.far = 1000; + mainLight.shadow.camera.left = -500; + mainLight.shadow.camera.right = 500; + mainLight.shadow.camera.top = 500; + mainLight.shadow.camera.bottom = -500; + mainLight.shadow.bias = -0.0005; scene.add(mainLight); + const cubeTextureLoader = new THREE.CubeTextureLoader(); + const envMap = cubeTextureLoader.load([ + 'textures/env/px.jpg', 'textures/env/nx.jpg', + 'textures/env/py.jpg', 'textures/env/ny.jpg', + 'textures/env/pz.jpg', 'textures/env/nz.jpg' + ]); + scene.environment = envMap; + var ambientLight = new THREE.AmbientLight(0x222222); scene.add(ambientLight); - // Table createTable(); loadSkull(); + createEnhancedCandle(); + createRealisticRose(); // Ajout de la rose } -// Nouvelle fonction pour créer l'arrière-plan dégradé function createGradientBackground() { - // Création d'un grand plan pour servir d'arrière-plan const bgGeometry = new THREE.PlaneGeometry(2000, 1000); - // Création d'une texture avec un dégradé radial const canvas = document.createElement('canvas'); canvas.width = 512; canvas.height = 512; const context = canvas.getContext('2d'); - // Création du dégradé radial (circulaire) - // Centre du dégradé légèrement décalé vers le haut gauche pour simuler la source de lumière - const centerX = 180; - const centerY = 180; - - const gradient = context.createRadialGradient( - centerX, centerY, 0, // Point de départ du dégradé - centerX, centerY, 400 // Point d'arrivée du dégradé - ); - - // Couleurs du dégradé - gradient.addColorStop(0, '#111111'); // Gris très sombre au centre (légère illumination) - gradient.addColorStop(0.3, '#050505'); // Transition vers le noir - gradient.addColorStop(1, '#000000'); // Noir pur aux bords - - // Remplir le canvas avec le dégradé - context.fillStyle = gradient; + // Création d'un fond très sombre, presque noir + context.fillStyle = '#000000'; context.fillRect(0, 0, 512, 512); const texture = new THREE.CanvasTexture(canvas); - // Création du matériau avec la texture de dégradé const bgMaterial = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide }); - // Création du mesh d'arrière-plan et positionnement - const background = new THREE.Mesh(bgGeometry, bgMaterial); - background.position.z = -400; // Derrière la scène - background.position.y = 50; // Centré verticalement + backgroundMesh = new THREE.Mesh(bgGeometry, bgMaterial); + backgroundMesh.position.z = -400; + backgroundMesh.position.y = 50; - scene.add(background); + scene.add(backgroundMesh); } function createTable() { - var tableGeometry = new THREE.BoxGeometry(600, 20, 300); - - // Chargement d'une texture bois foncé + // Groupe pour contenir toutes les parties de la table + const tableGroup = new THREE.Group(); + + // Dimensions de la table (comme dans l'original) + const tableWidth = 600; + const tableDepth = 300; + const tableThickness = 20; + const sideHeight = 100; // Hauteur des panneaux latéraux + const panelThickness = 10; // Épaisseur des panneaux + + // Chargeur de texture const textureLoader = new THREE.TextureLoader(); const woodTexture = textureLoader.load( 'textures/dark_wood.jpg', @@ -90,33 +95,73 @@ function createTable() { } ); + // Matériau pour toute la table var tableMaterial = new THREE.MeshStandardMaterial({ map: woodTexture, - color: 0x2A1B0A, // Brun foncé - roughness: 0.6, // Un peu de rugosité pour un effet plus réaliste - metalness: 0.2 // Légèrement métallique pour capturer la lumière + roughness: 0.6, + metalness: 0.2 }); - var table = new THREE.Mesh(tableGeometry, tableMaterial); - table.position.y = 0; - scene.add(table); + // Plateau de la table (position inchangée) + const tableTopGeometry = new THREE.BoxGeometry(tableWidth, tableThickness, tableDepth); + const tableTop = new THREE.Mesh(tableTopGeometry, tableMaterial); + tableTop.position.y = 0; // Garder la même position y=0 + tableTop.receiveShadow = true; // Le plateau reçoit des ombres + tableGroup.add(tableTop); + + // Panneau latéral gauche (placé sous le plateau) + const leftPanelGeometry = new THREE.BoxGeometry(panelThickness, sideHeight, tableDepth); + const leftPanel = new THREE.Mesh(leftPanelGeometry, tableMaterial); + leftPanel.position.set(-(tableWidth/2 - panelThickness/2), -(sideHeight/2 + tableThickness/2), 0); + leftPanel.receiveShadow = true; + leftPanel.castShadow = true; + tableGroup.add(leftPanel); + + // Panneau latéral droit + const rightPanelGeometry = new THREE.BoxGeometry(panelThickness, sideHeight, tableDepth); + const rightPanel = new THREE.Mesh(rightPanelGeometry, tableMaterial); + rightPanel.position.set((tableWidth/2 - panelThickness/2), -(sideHeight/2 + tableThickness/2), 0); + rightPanel.receiveShadow = true; + rightPanel.castShadow = true; + tableGroup.add(rightPanel); + + // Panneau avant + const frontPanelGeometry = new THREE.BoxGeometry(tableWidth - panelThickness*2, sideHeight, panelThickness); + const frontPanel = new THREE.Mesh(frontPanelGeometry, tableMaterial); + frontPanel.position.set(0, -(sideHeight/2 + tableThickness/2), (tableDepth/2 - panelThickness/2)); + frontPanel.receiveShadow = true; + frontPanel.castShadow = true; + tableGroup.add(frontPanel); + + // Panneau arrière + const backPanelGeometry = new THREE.BoxGeometry(tableWidth - panelThickness*2, sideHeight, panelThickness); + const backPanel = new THREE.Mesh(backPanelGeometry, tableMaterial); + backPanel.position.set(0, -(sideHeight/2 + tableThickness/2), -(tableDepth/2 - panelThickness/2)); + backPanel.receiveShadow = true; + backPanel.castShadow = true; + tableGroup.add(backPanel); + + // Panneau inférieur (base/fond du bureau) + const bottomPanelGeometry = new THREE.BoxGeometry(tableWidth - panelThickness*2, panelThickness, tableDepth - panelThickness*2); + const bottomPanel = new THREE.Mesh(bottomPanelGeometry, tableMaterial); + bottomPanel.position.set(0, -(sideHeight + tableThickness/2), 0); + bottomPanel.receiveShadow = true; + bottomPanel.castShadow = true; + tableGroup.add(bottomPanel); + + scene.add(tableGroup); } - - function loadSkull() { var loader = new OBJLoader(); loader.load( - 'OBJET3D/skul.obj', + 'skul.obj', function(object) { skull = object; skull.scale.set(6, 6, 6); skull.position.set(120, -35, 0); - skull.rotation.x = Math.PI /50; - skull.rotation.y = Math.PI / 200; skull.rotation.set(-Math.PI / 2, 0, -Math.PI / 5); - // Création d'un matériau doré et brillant pour le crâne const skullMaterial = new THREE.MeshPhongMaterial({ color: 0xFFF8F0, specular: 0xFFF8F0, @@ -125,10 +170,11 @@ function loadSkull() { emissiveIntensity: 0.1 }); - skull.traverse(function(child) { if (child.isMesh) { child.material = skullMaterial; + child.castShadow = true; // Le crâne projette des ombres + child.receiveShadow = true; // Le crâne peut aussi recevoir des ombres } }); scene.add(skull); @@ -136,35 +182,733 @@ function loadSkull() { ); } -function init() { - renderer = new THREE.WebGLRenderer({ antialias: true }); - renderer.setSize(846, 494); +function createRealisticRose() { + // Groupe principal pour la fleur + const roseGroup = new THREE.Group(); + + // Chargeur de textures + const textureLoader = new THREE.TextureLoader(); + // Texture pour les pétales (simulation de texture rose) + const petalTexture = textureLoader.load( + 'textures/rose_petal.jpg', + undefined, + undefined, + function(error) { + console.error('Error loading texture:', error); + } + ); + + // Définir les matériaux avec une couleur rose réaliste pour les pétales + const petalMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xde5d83, // Rose moyen + roughness: 0.3, // Légèrement lisse + metalness: 0.0, // Pas métallique + map: petalTexture, // Texture de base (si disponible) + clearcoat: 0.3, // Léger revêtement pour imiter l'aspect lustré + clearcoatRoughness: 0.4, + transmission: 0.2, // Légère translucidité pour un effet réaliste + side: THREE.DoubleSide + }); + // Matériau plus foncé pour les pétales intérieurs + const innerPetalMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xc94c7c, // Rose plus foncé pour l'intérieur + roughness: 0.4, + metalness: 0.0, + map: petalTexture, // Même texture de base + clearcoat: 0.3, + clearcoatRoughness: 0.5, + transmission: 0.15, + side: THREE.DoubleSide + }); + + // Matériau pour la tige et les feuilles + const stemMaterial = new THREE.MeshPhysicalMaterial({ + color: 0x2d5d3a, // Vert foncé pour la tige + roughness: 0.6, + metalness: 0.0 + }); + + const leafMaterial = new THREE.MeshPhysicalMaterial({ + color: 0x3a8c54, // Vert pour les feuilles + roughness: 0.5, + metalness: 0.0, + side: THREE.DoubleSide + }); + + // TIGE + const stemHeight = 70; + const stemRadius = 1.2; + const stemGeometry = new THREE.CylinderGeometry(stemRadius, stemRadius * 1.2, stemHeight, 8); + const stem = new THREE.Mesh(stemGeometry, stemMaterial); + stem.position.y = stemHeight / 2; + stem.castShadow = true; + stem.receiveShadow = true; + + // Légère courbure de la tige + stem.rotation.x = Math.PI * 0.05; + roseGroup.add(stem); + + // FEUILLES (3 feuilles) + const createLeaf = (posY, rotY, scale) => { + // Forme de goutte pour la feuille + const leafShape = new THREE.Shape(); + leafShape.moveTo(0, 0); + leafShape.bezierCurveTo(5, 10, 10, 15, 0, 20); + leafShape.bezierCurveTo(-10, 15, -5, 10, 0, 0); + + // Créer une géométrie extrudée pour la feuille + const leafExtrudeSettings = { + steps: 1, + depth: 0.5, + bevelEnabled: true, + bevelThickness: 0.2, + bevelSize: 0.2, + bevelSegments: 2 + }; + + const leafGeometry = new THREE.ExtrudeGeometry(leafShape, leafExtrudeSettings); + const leaf = new THREE.Mesh(leafGeometry, leafMaterial); + + // Ajuster la position et l'orientation + leaf.scale.set(scale, scale, scale); + leaf.rotation.set(-Math.PI / 2, 0, rotY); + leaf.position.set(0, posY, 0); + + leaf.castShadow = true; + leaf.receiveShadow = true; + return leaf; + }; + + // Ajouter trois feuilles à différentes hauteurs et orientations + roseGroup.add(createLeaf(stemHeight * 0.2, Math.PI / 4, 0.35)); + roseGroup.add(createLeaf(stemHeight * 0.4, -Math.PI / 3, 0.4)); + roseGroup.add(createLeaf(stemHeight * 0.6, Math.PI / 6, 0.3)); + + // CENTRE DE LA FLEUR + const centerRadius = 5; + const centerHeight = 4; + const centerGeometry = new THREE.SphereGeometry(centerRadius, 16, 16); + const centerMaterial = new THREE.MeshPhysicalMaterial({ + color: 0xb3405c, // Rose foncé pour le centre + roughness: 0.7, + metalness: 0.0 + }); + + const flowerCenter = new THREE.Mesh(centerGeometry, centerMaterial); + flowerCenter.position.y = stemHeight + centerRadius * 0.8; + flowerCenter.castShadow = true; + flowerCenter.receiveShadow = true; + roseGroup.add(flowerCenter); + + // CRÉATION DES PÉTALES + const petalCount = 25; // Nombre total de pétales + const layerCount = 5; // Nombre de couches concentriques + + for (let layer = 0; layer < layerCount; layer++) { + const layerPetals = Math.floor(5 + layer * 2); // Plus de pétales dans les couches extérieures + const radiusOffset = 3 + layer * 2.5; // Plus grand rayon pour les couches extérieures + const heightOffset = 1 + layer * 1.2; // Position en hauteur + + for (let i = 0; i < layerPetals; i++) { + // Créer une forme arrondie pour le pétale + const petalShape = new THREE.Shape(); + + // Dessiner une forme de pétale avec des courbes de Bézier + petalShape.moveTo(0, 0); + petalShape.bezierCurveTo(2, 2, 3, 5, 2, 8); + petalShape.bezierCurveTo(0, 10, -2, 8, -2, 8); + petalShape.bezierCurveTo(-3, 5, -2, 2, 0, 0); + + // Réglages d'extrusion pour donner un peu de volume + const extrudeSettings = { + steps: 1, + depth: 0.5, + bevelEnabled: true, + bevelThickness: 0.2, + bevelSize: 0.1, + bevelSegments: 2 + }; + + // Créer la géométrie du pétale + const petalGeometry = new THREE.ExtrudeGeometry(petalShape, extrudeSettings); + + // Utiliser un matériau différent selon la couche + const material = layer < 2 ? innerPetalMaterial : petalMaterial; + const petal = new THREE.Mesh(petalGeometry, material); + + // Calculer la position autour du centre + const angle = (i / layerPetals) * Math.PI * 2; + + // Facteur pour inclinaison plus prononcée des pétales extérieurs + const tiltFactor = 0.2 + layer * 0.15; + + // Facteur d'échelle pour les pétales (plus grands vers l'extérieur) + const scaleFactor = 0.6 + layer * 0.15; + + // Positionner et orienter chaque pétale + petal.scale.set(scaleFactor, scaleFactor, scaleFactor * 0.5); + + // Faire pivoter le pétale pour l'orienter correctement + petal.rotation.set( + -Math.PI / 2 + Math.PI * tiltFactor, // Inclinaison par rapport au centre + 0, + angle + ); + + // Positionner le pétale autour du centre + petal.position.set( + Math.cos(angle) * radiusOffset, + stemHeight + heightOffset, + Math.sin(angle) * radiusOffset + ); + + // Ajouter de légères variations aléatoires pour un aspect plus naturel + petal.rotation.x += (Math.random() - 0.5) * 0.2; + petal.rotation.y += (Math.random() - 0.5) * 0.1; + petal.rotation.z += (Math.random() - 0.5) * 0.1; + + petal.castShadow = true; + petal.receiveShadow = true; + + roseGroup.add(petal); + } + } + + roseGroup.position.set(120, -40, 40); + roseGroup.rotation.set(0, Math.PI * 0.2, 0); + roseGroup.scale.set(0.7, 0.7, 0.7); - renderer.setClearColor(0x000000, 1.0); - var container = document.getElementById('webGL'); - container.appendChild(renderer.domElement); + const roseLight = new THREE.PointLight(0xffccd5, 0.5, 50); + roseLight.position.set(0, stemHeight + 10, 0); + roseGroup.add(roseLight); + + scene.add(roseGroup); + return roseGroup; +} - camera = new THREE.PerspectiveCamera(35, 846/494, 1, 8000); - camera.position.set(0, 200, 600); - cameraControls = new OrbitControls(camera, renderer.domElement); - cameraControls.target.set(0, 50, 0); +function createEnhancedCandle() { + // Groupe pour contenir tous les éléments de la bougie + const candleGroup = new THREE.Group(); + + // Modification: Porte-bougie plus grand et plus élaboré + const holderBaseRadius = 22; // Augmenté + const holderBaseHeight = 8; // Augmenté + const holderBaseGeometry = new THREE.CylinderGeometry(holderBaseRadius, holderBaseRadius * 1.3, holderBaseHeight, 32); + + // Pied central du chandelier plus grand et plus décoratif + const holderStemRadius = 4.5; // Augmenté + const holderStemHeight = 25; // Beaucoup plus haut + const holderStemGeometry = new THREE.CylinderGeometry(holderStemRadius * 0.8, holderStemRadius * 1.2, holderStemHeight, 16); + + // Coupelle supérieure du chandelier plus grande + const holderTopRadius = 16; // Augmenté + const holderTopHeight = 4; // Augmenté + const holderTopGeometry = new THREE.CylinderGeometry(holderTopRadius, holderTopRadius * 0.85, holderTopHeight, 32); + + // Décoration plus élaborée pour le chandelier + const decorRadius = 18; + const decorHeight = 1.5; + const decorGeometry = new THREE.TorusGeometry(decorRadius * 0.7, 1.5, 12, 48); + + // Ajout de décorations supplémentaires + const smallDecorRadius = 10; + const smallDecorGeometry = new THREE.TorusGeometry(smallDecorRadius * 0.7, 1, 8, 32); + + // Matériau bronze vieilli pour le porte-bougie avec texture améliorée + const holderMaterial = new THREE.MeshStandardMaterial({ + color: 0xFFD700, // Couleur dorée + roughness: 0.2, // Réduire la rugosité pour plus de brillance + metalness: 0.8, // Augmenter la métallicité + envMapIntensity: 1 // Augmenter l'intensité des reflets + }); + + // Variante plus foncée et plus détaillée + const holderDarkMaterial = new THREE.MeshStandardMaterial({ + color: 0xFFD700, // Couleur dorée légèrement différente pour le contraste + roughness: 0.25, // Légèrement plus rugueux que le matériau principal + metalness: 0.8, // Presque aussi métallique + envMapIntensity: 1 + }); + + // Assemblage du chandelier + const holderBase = new THREE.Mesh(holderBaseGeometry, holderMaterial); + holderBase.position.y = holderBaseHeight / 2; + holderBase.castShadow = true; // Permet au support de projeter une ombre + holderBase.receiveShadow = true; // Permet au support de recevoir une ombre + + const holderStem = new THREE.Mesh(holderStemGeometry, holderDarkMaterial); + holderStem.position.y = holderBaseHeight + holderStemHeight / 2; + holderStem.castShadow = true; // Permet à la tige de projeter une ombre + holderStem.receiveShadow = true; // Permet à la tige de recevoir une ombre + + const holderTop = new THREE.Mesh(holderTopGeometry, holderMaterial); + holderTop.position.y = holderBaseHeight + holderStemHeight + holderTopHeight / 2; + holderTop.castShadow = true; // Permet à la coupelle de projeter une ombre + holderTop.receiveShadow = true; // Permet à la coupelle de recevoir une ombre + + const decor = new THREE.Mesh(decorGeometry, holderDarkMaterial); + decor.position.y = holderBaseHeight + 6; + decor.rotation.x = Math.PI / 2; + decor.castShadow = true; // Permet à la décoration de projeter une ombre + decor.receiveShadow = true; // Permet à la décoration de recevoir une ombre + + // Décoration supplémentaire au milieu du stem + const middleDecor = new THREE.Mesh(smallDecorGeometry, holderMaterial); + middleDecor.position.y = holderBaseHeight + holderStemHeight / 2; + middleDecor.rotation.x = Math.PI / 2; + middleDecor.castShadow = true; // Permet à la décoration du milieu de projeter une ombre + middleDecor.receiveShadow = true; // Permet à la décoration du milieu de recevoir une ombre + + // Modification: Bougie plus petite + const candleRadius = 5; // Réduit + const candleHeight = 20; // Réduit + const candleGeometry = new THREE.CylinderGeometry(candleRadius * 0.9, candleRadius, candleHeight, 32); + + // Matériau de cire pour la bougie plus réaliste + const candleMaterial = new THREE.MeshStandardMaterial({ + color: 0xf3e5ab, + roughness: 0.8, + metalness: 0.05, + transparent: true, + opacity: 0.95 // Légèrement translucide + }); + + // Ajout d'un effet de cire fondue + const meltedWaxGeometry = new THREE.SphereGeometry(candleRadius * 1.2, 16, 16, 0, Math.PI * 2, 0, Math.PI / 2); + meltedWaxGeometry.translate(0, -candleHeight/2, 0); + const meltedWaxMaterial = new THREE.MeshStandardMaterial({ + color: 0xf3e5ab, + roughness: 0.6, + metalness: 0.1, + transparent: true, + opacity: 0.9 + }); + + const meltedWax = new THREE.Mesh(meltedWaxGeometry, meltedWaxMaterial); + meltedWax.castShadow = true; // Permet à la cire fondue de projeter une ombre + meltedWax.receiveShadow = true; // Permet à la cire fondue de recevoir une ombre + + candle = new THREE.Mesh(candleGeometry, candleMaterial); + candle.position.y = holderBaseHeight + holderStemHeight + holderTopHeight + candleHeight / 2 - 2; // Ajusté pour s'enfoncer légèrement + candle.castShadow = true; // Permet à la bougie de projeter une ombre + candle.receiveShadow = true; // Permet à la bougie de recevoir une ombre + candle.add(meltedWax); + + // Création d'une mèche plus détaillée + const wickRadius = 0.4; + const wickHeight = 3; + const wickGeometry = new THREE.CylinderGeometry(wickRadius * 0.6, wickRadius, wickHeight, 8); + + const wickMaterial = new THREE.MeshStandardMaterial({ + color: 0x442200, + roughness: 1.0, + metalness: 0.0, + emissive: 0x221100, + emissiveIntensity: 0.2 + }); + + const wick = new THREE.Mesh(wickGeometry, wickMaterial); + wick.position.y = holderBaseHeight + holderStemHeight + holderTopHeight + candleHeight + wickHeight / 2 - 2; + wick.castShadow = true; // Permet à la mèche de projeter une ombre + wick.receiveShadow = true; // Permet à la mèche de recevoir une ombre + + // Améliorations pour la flamme + // Flamme intérieure (plus chaude/blanche) + const innerFlameHeight = 8; + const innerFlameRadius = 1.5; + const innerFlameGeometry = new THREE.ConeGeometry(innerFlameRadius, innerFlameHeight, 16, 5, true); + + const innerFlameMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, // Blanc plus pur au centre + transparent: true, + opacity: 0.95, + side: THREE.DoubleSide, + blending: THREE.AdditiveBlending + }); + + const innerFlame = new THREE.Mesh(innerFlameGeometry, innerFlameMaterial); + innerFlame.position.y = holderBaseHeight + holderStemHeight + holderTopHeight + candleHeight + wickHeight + innerFlameHeight / 2 - 2; + // Les flammes n'ont généralement pas besoin de projeter d'ombres car elles sont des sources de lumière + + // Flamme extérieure améliorée avec une forme plus naturelle + const outerFlameHeight = 12; + const outerFlameRadius = 2.5; + + // Utilisation d'une géométrie personnalisée pour une flamme plus naturelle + const outerFlameGeometry = new THREE.ConeGeometry(outerFlameRadius, outerFlameHeight, 20, 8, true); + // Déformer légèrement la géométrie pour un aspect plus réaliste + const positionAttribute = outerFlameGeometry.getAttribute('position'); + for (let i = 0; i < positionAttribute.count; i++) { + const x = positionAttribute.getX(i); + const y = positionAttribute.getY(i); + const z = positionAttribute.getZ(i); + + // Déformation aléatoire pour donner un aspect plus naturel + if (y > outerFlameHeight * 0.3) { + const distortFactor = (y / outerFlameHeight) * 0.4; + positionAttribute.setX(i, x + (Math.random() - 0.5) * distortFactor); + positionAttribute.setZ(i, z + (Math.random() - 0.5) * distortFactor); + } + } + outerFlameGeometry.attributes.position.needsUpdate = true; + + // Matériau amélioré pour la flamme extérieure + const outerFlameMaterial = new THREE.MeshBasicMaterial({ + color: 0xffaa33, // Orange plus chaud + transparent: true, + opacity: 0.85, + side: THREE.DoubleSide, + blending: THREE.AdditiveBlending + }); + + flame = new THREE.Mesh(outerFlameGeometry, outerFlameMaterial); + flame.position.y = holderBaseHeight + holderStemHeight + holderTopHeight + candleHeight + wickHeight + outerFlameHeight / 2 - 2; + + // Ajout d'une flamme externe très subtile pour un effet de halo + const subtleFlameHeight = 15; + const subtleFlameRadius = 4; + const subtleFlameGeometry = new THREE.ConeGeometry(subtleFlameRadius, subtleFlameHeight, 16, 4, true); + + const subtleFlameMaterial = new THREE.MeshBasicMaterial({ + color: 0xff6600, + transparent: true, + opacity: 0.3, + side: THREE.DoubleSide, + blending: THREE.AdditiveBlending + }); + + const subtleFlame = new THREE.Mesh(subtleFlameGeometry, subtleFlameMaterial); + subtleFlame.position.y = holderBaseHeight + holderStemHeight + holderTopHeight + candleHeight + wickHeight + subtleFlameHeight / 2 - 3; + + // Système de lumières amélioré + // Lumière principale de la flamme - Configuration pour projeter des ombres + const flameLight = new THREE.PointLight(0xff9933, 2.2, 400); + flameLight.position.copy(flame.position); + flameLight.power = 120; // Intensité augmentée pour plus d'impact visuel + flameLight.castShadow = true; // Permet à la lumière de projeter des ombres + + // Configuration des paramètres d'ombre pour une meilleure qualité + flameLight.shadow.mapSize.width = 1024; + flameLight.shadow.mapSize.height = 1024; + flameLight.shadow.camera.near = 0.1; + flameLight.shadow.camera.far = 1000; + flameLight.shadow.bias = -0.002; // Réduire les artifacts d'ombre + flameLight.shadow.radius = 3; // Adoucir les bords de l'ombre + + // Seconde lumière pour effet réaliste + const innerLight = new THREE.PointLight(0xffffcc, 1.2, 150); + innerLight.position.copy(innerFlame.position); + innerLight.power = 70; + innerLight.castShadow = false; // Cette lumière secondaire n'a pas besoin de projeter des ombres + + // Troisième lumière très subtile pour l'ambiance + const ambientFlameLight = new THREE.PointLight(0xff5500, 0.6, 600); + ambientFlameLight.position.set(flame.position.x, flame.position.y - 5, flame.position.z); + ambientFlameLight.power = 40; + ambientFlameLight.castShadow = false; // Cette lumière d'ambiance n'a pas besoin de projeter des ombres + + // Halo lumineux amélioré + const flameHaloGeometry = new THREE.SphereGeometry(8, 24, 24); + const flameHaloMaterial = new THREE.MeshBasicMaterial({ + color: 0xff7700, + transparent: true, + opacity: 0.15, + side: THREE.DoubleSide, + blending: THREE.AdditiveBlending + }); + + const flameHalo = new THREE.Mesh(flameHaloGeometry, flameHaloMaterial); + flameHalo.position.copy(flame.position); + + // Particules de feu/braises (simulées par de petites sphères) + const particlesGroup = new THREE.Group(); + const particleCount = 15; + const particleGeometry = new THREE.SphereGeometry(0.15, 8, 8); + const particleMaterial = new THREE.MeshBasicMaterial({ + color: 0xffaa33, + transparent: true, + opacity: 0.7, + blending: THREE.AdditiveBlending + }); + + for (let i = 0; i < particleCount; i++) { + const particle = new THREE.Mesh(particleGeometry, particleMaterial); + // Position aléatoire autour de la flamme + particle.position.set( + (Math.random() - 0.5) * 2, + flame.position.y + Math.random() * 3 - 2, + (Math.random() - 0.5) * 2 + ); + particle.userData.velocity = { + x: (Math.random() - 0.5) * 0.05, + y: 0.05 + Math.random() * 0.1, + z: (Math.random() - 0.5) * 0.05 + }; + particle.userData.life = Math.random() * 100; + particlesGroup.add(particle); + } + + // Assembler tous les éléments dans le groupe + candleGroup.add(holderBase); + candleGroup.add(holderStem); + candleGroup.add(holderTop); + candleGroup.add(decor); + candleGroup.add(middleDecor); + candleGroup.add(candle); + candleGroup.add(wick); + candleGroup.add(innerFlame); + candleGroup.add(flame); + candleGroup.add(subtleFlame); + candleGroup.add(flameHalo); + candleGroup.add(flameLight); + candleGroup.add(innerLight); + candleGroup.add(ambientFlameLight); + candleGroup.add(particlesGroup); + + candleGroup.position.set(-150, 10, 20); + candleGroup.scale.set(2, 2, 2); + + // Permettre au groupe entier de projeter et recevoir des ombres + candleGroup.traverse(function(object) { + if (object.isMesh) { + object.castShadow = true; + object.receiveShadow = true; + } + }); + + scene.add(candleGroup); + + // Sauvegarder les références pour l'animation + flame.userData.innerFlame = innerFlame; + flame.userData.subtleFlame = subtleFlame; + flame.userData.flameHalo = flameHalo; + flame.userData.flameLight = flameLight; + flame.userData.innerLight = innerLight; + flame.userData.ambientLight = ambientFlameLight; + flame.userData.particles = particlesGroup; +} + +function animateFlame() { + if (flame) { + const time = clock.getElapsedTime(); + + // Paramètres pour un mouvement fluide mais naturel + const flickerSpeed = 2.0; + const flickerIntensity = 0.12; + const swaySpeed = 1.0; + const swayAmount = 0.5; + + // Récupération des éléments liés à la flamme + const innerFlame = flame.userData.innerFlame; + const subtleFlame = flame.userData.subtleFlame; + const flameHalo = flame.userData.flameHalo; + const flameLight = flame.userData.flameLight; + const innerLight = flame.userData.innerLight; + const ambientLight = flame.userData.ambientLight; + const particles = flame.userData.particles; + + // Mouvement de balancement simplifié + const swayX = Math.sin(time * swaySpeed) * swayAmount; + const swayZ = Math.cos(time * swaySpeed * 0.8) * swayAmount; + + // Variation de hauteur simplifiée + const heightVariation = 0.98 + flickerIntensity * Math.sin(time * flickerSpeed); + + // Appliquer les transformations à la flamme principale + flame.scale.set(1, heightVariation, 1); + flame.rotation.x = swayX * 0.1; + flame.rotation.z = swayZ * 0.1; + + // Appliquer des transformations à la flamme intérieure + if (innerFlame) { + innerFlame.scale.set(1, heightVariation * 1.05, 1); + innerFlame.rotation.copy(flame.rotation); + } + + // Animer la flamme subtile externe + if (subtleFlame) { + subtleFlame.scale.set(1.1, heightVariation * 1.1, 1.1); + subtleFlame.rotation.copy(flame.rotation); + subtleFlame.material.opacity = 0.3; + } + + // Animer le halo pour diffuser la lumière + if (flameHalo) { + const haloScale = 1 + 0.15 * Math.sin(time * 0.8); + flameHalo.scale.set(haloScale, haloScale, haloScale); + flameHalo.material.opacity = 0.15 + 0.05 * Math.sin(time * 1.2); + } + + // *** AMÉLIORATION PRINCIPALE: Intensité des lumières pour projection sur le fond *** + if (flameLight) { + // Augmenter la portée et l'intensité pour mieux éclairer l'environnement + flameLight.distance = 600; // Augmenter la portée + const lightIntensity = 2.5 + 0.5 * Math.sin(time * 1.5); + flameLight.intensity = lightIntensity; + flameLight.position.copy(flame.position); + + // Augmenter le pouvoir lumineux pour plus d'impact visuel + flameLight.power = 150 + 30 * Math.sin(time * 1.2); + } + + if (innerLight) { + const innerLightIntensity = 1.2 + 0.2 * Math.sin(time * 1.7); + innerLight.intensity = innerLightIntensity; + innerLight.position.copy(innerFlame.position); + innerLight.distance = 200; // Augmenter légèrement la portée + } + + if (ambientLight) { + // Lumière ambiante plus forte pour illuminer tout l'environnement + ambientLight.intensity = 0.7 + 0.2 * Math.sin(time * 0.9); + ambientLight.distance = 800; // Grande portée pour atteindre le fond + ambientLight.position.set(flame.position.x, flame.position.y - 5, flame.position.z); + ambientLight.power = 60 + 15 * Math.sin(time * 0.8); // Pouvoir lumineux augmenté + } + + // Animation simplifiée des particules + if (particles) { + particles.children.forEach(particle => { + particle.position.y += particle.userData.velocity.y * 0.5; + particle.userData.life -= 0.5; + + const lifeFactor = particle.userData.life / 100; + particle.scale.set(lifeFactor, lifeFactor, lifeFactor); + particle.material.opacity = lifeFactor * 0.7; + + if (particle.userData.life <= 0) { + particle.position.set( + flame.position.x + (Math.random() - 0.5) * 0.5, + flame.position.y - 0.5, + flame.position.z + (Math.random() - 0.5) * 0.5 + ); + particle.userData.velocity = { + x: 0, + y: 0.05 + Math.random() * 0.05, + z: 0 + }; + particle.userData.life = 80 + Math.random() * 20; + particle.scale.set(1, 1, 1); + particle.material.opacity = 0.7; + } + }); + } + + // *** AMÉLIORATION CRUCIALE: Mise à jour améliorée du fond pour mieux refléter la flamme *** + updateBackgroundWithCandleLight(time, heightVariation, swayX, swayZ); + } } +// Fonction améliorée pour que le fond réagisse plus visiblement à la lumière de la flamme +function updateBackgroundWithCandleLight(time, flickerFactor, swayX, swayZ) { + if (backgroundMesh && backgroundMesh.material.map) { + // Exécuter la mise à jour à chaque frame pour un rendu plus fluide + const canvas = document.createElement('canvas'); + canvas.width = 512; + canvas.height = 512; + const context = canvas.getContext('2d'); + + // D'abord, créer un fond complètement noir + context.fillStyle = '#000000'; + context.fillRect(0, 0, 512, 512); + + // Calculer un facteur de pulsation qui dépend de la flamme + const pulseFactor = 0.7 + 0.3 * Math.sin(time * 1.2) * flickerFactor; + + // Faire bouger légèrement le centre de lumière pour imiter le balancement de la flamme + const centerX = 220 + swayX * 40; + const centerY = 160 + swayZ * 30; + + // Rayon du gradient plus petit pour un effet plus concentré + const radius = 300 + 80 * pulseFactor; + + // Créer un dégradé radial avec une transition plus subtile + const gradient = context.createRadialGradient( + centerX, centerY, 0, + centerX, centerY, radius + ); + + // Intensités plus faibles pour un fond généralement plus sombre + const centerIntensity = Math.floor(30 + 20 * pulseFactor); + const midIntensity = Math.floor(15 + 10 * pulseFactor); + const outerIntensity = Math.floor(5 + 3 * pulseFactor); + + // Utiliser des teintes plus chaudes mais moins intenses + gradient.addColorStop(0, `rgb(${centerIntensity}, ${Math.floor(centerIntensity*0.4)}, ${Math.floor(centerIntensity*0.08)})`); + gradient.addColorStop(0.2, `rgb(${midIntensity}, ${Math.floor(midIntensity*0.3)}, ${Math.floor(midIntensity*0.06)})`); + gradient.addColorStop(0.5, `rgb(${outerIntensity}, ${Math.floor(outerIntensity*0.2)}, ${Math.floor(outerIntensity*0.04)})`); + gradient.addColorStop(0.7, 'rgba(3, 1, 0, 0.3)'); + gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); + + // Appliquer le gradient par-dessus le fond noir + context.fillStyle = gradient; + context.fillRect(0, 0, 512, 512); + + // Ajouter un effet de lueur diffuse très subtil + if (pulseFactor > 0.85) { + const glowGradient = context.createRadialGradient( + centerX, centerY, radius * 0.3, + centerX, centerY, radius * 0.9 + ); + + // Lueur encore plus subtile + glowGradient.addColorStop(0, `rgba(255, 140, 50, ${0.02 * (pulseFactor - 0.85) * 10})`); + glowGradient.addColorStop(1, 'rgba(255, 140, 50, 0)'); + + context.fillStyle = glowGradient; + context.fillRect(0, 0, 512, 512); + } + + // Mettre à jour la texture du fond + backgroundMesh.material.map.dispose(); + backgroundMesh.material.map = new THREE.CanvasTexture(canvas); + backgroundMesh.material.needsUpdate = true; + } +} +// Fonction d'animation mise à jour function animate() { requestAnimationFrame(animate); var delta = clock.getDelta(); cameraControls.update(delta); + + // Animer la flamme avec l'effet amélioré + animateFlame(); + renderer.render(scene, camera); } +// Fonction d'initialisation +function init() { + renderer = new THREE.WebGLRenderer({ antialias: true }); + renderer.setSize(846, 494); + renderer.setClearColor(0x000000, 1.0); + renderer.physicallyCorrectLights = true; // Pour un rendu plus réaliste + renderer.toneMapping = THREE.ACESFilmicToneMapping; // Améliore le rendu des zones lumineuses + renderer.toneMappingExposure = 1.2; // Ajuste l'exposition + + // Activation des ombres + renderer.shadowMap.enabled = true; + renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Ombres plus douces + + var container = document.getElementById('webGL'); + container.appendChild(renderer.domElement); + + camera = new THREE.PerspectiveCamera(35, 846 / 494, 1, 8000); + camera.position.set(0, 200, 600); + + cameraControls = new OrbitControls(camera, renderer.domElement); + cameraControls.target.set(0, 50, 0); +} + +// Lancer l'application try { init(); fillScene(); animate(); -} catch(e) { +} catch (e) { console.error(e); } \ No newline at end of file diff --git a/RIFI/OBJET3D/skull.obj b/RIFI/skull.obj similarity index 100% rename from RIFI/OBJET3D/skull.obj rename to RIFI/skull.obj diff --git a/RIFI/textures/dark_wood.jpg b/RIFI/textures/dark_wood.jpg new file mode 100644 index 0000000000000000000000000000000000000000..122c143e1e7c24a83caafc8d3c61efd4cf8052ee Binary files /dev/null and b/RIFI/textures/dark_wood.jpg differ