En 2013 j'avais réalisé une petite démo avec Three.js. À l'époque j'avais pas mal galéré, car je n'avais pas beaucoup de connaissance en JavaScript. Quand est-il en 2024 avec toutes les IA disponibles? J'ai donc décidé de refaire la démo avec chatGPT 3.5 (parce qu'il est gratuit) et Gemini (de Google). Le moins que l'on puisse dire est qu'elles tuent en partie le métier. Tu n'as quasi besoin d'aucune compétence de développeur et tu pourras t'en sortir avec quelques copiés-collés.
Pour les ressources, il vous faudra un serveur web, npm, pour le modèle je l'ai téléchargé ici (chez cgtrader): https://www.cgtrader.com/search?free=1&file_types%5B%5D=21&keywords=bag (il vous faudra créer un compte après c'est gratuit)
La démo ci-dessous m'a pris une petite heure pour chaque exemple. Le plus long a été de trouver un modèle 3D gratuit sur le net. Blender est toujours très bien, mais c'est nettement plus rapide de chercher un modèle déjà tout fait.
Cependant, les IA ont leurs limites. Par exemple, j'ai demandé à ChatGPT de bouger l'éclairage. Malheureusement, il faisait référence à un objet JavaScript qui n'existe pas ! Du coup, il a fallu mon petit cerveau pour pallier à ce problème. Mais ce n'était pas grand-chose comparé à Gemini.
Pour l'IA Gimini de Google, il ne m'a jamais donné un exemple fonctionnel. En cause l'appel de la librairie inconnue. J'ai donc modifié l'appel. Il y a aussi quelques différences. Gemini va faire appel au fichier gltf, il ne faudra pas oublier de mettre les ressources comme .bin à côté tandis que ChatGPT va faire appel au fichier glb. La différence entre les deux fichiers est que le glb embarque tout à l'intérieur (texture, code etc...) tandis que le gltf fait appel à des ressources externes (ce qui permet de l'optimiser pour le poids par exemple). À titre indicatif, il faut 8Mo pour le sac.
Autre différence entre Gemini et ChatGPT sont les méthodes de gestions de la souris ou du doigt. Gemini va faire appel à un plug-in , ChatGPT va préférer les événements du navigateur.
Gemini fait appel à des librairies qui n'existent pas, ça n'a pas l'air de trop le déranger ... Bref, lui il ne tue pas le métier.
Exemple généré avec Gimini : https://geekmps.fr/media/tests/3D/Animation%20Sac%203D%20Gemini.html
Exemple généré avec ChatGPT : https://geekmps.fr/media/tests/3D/animation3D three bag.html
Le source généré par Gemini et adapté vite fait par mes soins (il faudra faire un npm install --save three) pour récupérer les sources de three.js
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Sac 3D avec Three.js</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser. .0/examples/js/loaders/GLTFLoader.js"></script>
<script src="/three/examples/jsm/controls/DragControls.js"></script>
<script src="/dat.gui.min.js"></script>
</head>
</head>
<body>
<script>
// Initialisation de la scène
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 0); // Positionnement de la caméra
camera.lookAt(new THREE.Vector3(0, 0, 0)); // Orientation de la caméra
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xDDDDDD); // Couleur de fond gris
document.body.appendChild(renderer.domElement);
// Chargement du modèle 3D du sac
const loader = new THREE.GLTFLoader();
loader.load('https://geekmps.fr/media/tests/3D/sac.gltf', (gltf) => {
const bag = gltf.scene;
scene.add(bag);
// Positionnement et orientation du sac
bag.position.set(0, 0, -5);
bag.rotation.y = Math.PI / 4;
// Ajout d'une lumière ambiante
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
// Ajout d'une lumière directionnelle
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 10);
scene.add(directionalLight);
// Contrôles de drag pour la souris et le toucher
/*const controls = new THREE.DragControls([bag], camera, renderer.domElement);
controls.addEventListener('change', render); // Met à jour la scène après le drag*/
// Création d'une interface GUI avec dat.GUI.js
const gui = new dat.GUI();
const rotationFolder = gui.addFolder('Rotation');
// Boutons pour la rotation
const rotationXButton = rotationFolder.add(bag.rotation, 'x', -Math.PI / 2, Math.PI / 2).step(0.01);
rotationXButton.onChange(render);
const rotationYButton = rotationFolder.add(bag.rotation, 'y', -Math.PI / 2, Math.PI / 2).step(0.01);
rotationYButton.onChange(render);
const rotationZButton = rotationFolder.add(bag.rotation, 'z', -Math.PI / 2, Math.PI / 2).step(0.01);
rotationZButton.onChange(render);
// Variables pour le suivi de la rotation
let mouseDown = false;
let lastMouseX = 0;
let lastMouseY = 0;
let mouseX = 0;
let mouseY = 0;
let rotationSpeed = 0.01;
// Événement de clic de la souris
renderer.domElement.addEventListener('mousedown', (event) => {
mouseDown = true;
lastMouseX = event.clientX;
lastMouseY = event.clientY;
});
// Événement de mouvement de la souris
renderer.domElement.addEventListener('mousemove', (event) => {
if (mouseDown) {
mouseX = event.clientX;
mouseY = event.clientY;
const deltaX = mouseX - lastMouseX;
const deltaY = mouseY - lastMouseY;
bag.rotation.y += deltaX * rotationSpeed;
bag.rotation.x += deltaY * rotationSpeed;
lastMouseX = mouseX;
lastMouseY = mouseY;
render(); // Forcer le rendu après la rotation
}
});
// Événement de relâchement de la souris
renderer.domElement.addEventListener('mouseup', () => {
mouseDown = false;
});
// Variables pour le suivi de la rotation
let touchDown = false;
let lastTouchX = 0;
let lastTouchY = 0;
let touchX = 0;
let touchY = 0;
// Événement de toucher
renderer.domElement.addEventListener('touchstart', (event) => {
if (event.touches.length === 1) { // Un seul doigt
touchDown = true;
lastTouchX = event.touches[0].clientX;
lastTouchY = event.touches[0].clientY;
}
});
// Événement de mouvement des doigts
renderer.domElement.addEventListener('touchmove', (event) => {
if (touchDown && event.touches.length === 1) { // Un seul doigt
touchX = event.touches[0].clientX;
touchY = event.touches[0].clientY;
const deltaX = touchX - lastTouchX;
const deltaY = touchY - lastTouchY;
bag.rotation.y += deltaX * rotationSpeed;
bag.rotation.x += deltaY * rotationSpeed;
lastTouchX = touchX;
lastTouchY = touchY;
render(); // Forcer le rendu après la rotation
}
});
// Événement de relâchement des doigts
renderer.domElement.addEventListener('touchend', () => {
touchDown = false;
});
// Chargement du modèle du deuxième téléphone
loader.load('https://geekmps.fr/media/tests/3D/phone2.glb', (gltf) => {
const phone2 = gltf.scene;
scene.add(phone2);
phone2.scale.set(10, 10, 10);
// Positionnement du deuxième téléphone
phone2.position.set(5, 0, -2);
phone2.rotation.y = -Math.PI / 4; // Rotation initiale
});
const controls2 = new THREE.DragControls([phone2], camera, renderer.domElement);
// Boucle de rendu
function animate() {
requestAnimationFrame(animate);
//controls.update(); // Met à jour les contrôles de drag
controls2.update();
renderer.render(scene, camera);
}
animate();
});
function render() {
renderer.render(scene, camera);
}
</script>
</body>
</html>
Le source généré par ChatGPT, sans presque aucune adaptation de ma part.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rotation du sac</title>
<style>
button {
margin: 10px;
padding: 5px 10px;
font-size: 16px;
cursor: pointer;
}
body {
margin: 0;
padding: 0;
overflow: hidden; /* Pour éviter les barres de défilement */
background-color: #888; /* Couleur de fond gris */
}
</style>
</head>
<body>
<button id="rotateUp">Haut</button>
<button id="rotateDown">Bas</button>
<button id="rotateLeft">Gauche</button>
<button id="rotateRight">Droite</button>
<button id="moveLight">Déplacer la lumière</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser. .0/examples/js/loaders/GLTFLoader.js"></script>
<script>
let mesh; // Variable pour stocker le mesh du sac
let phone; // Variable pour stocker le mesh du téléphone
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xFFFFFF); // Couleur de fond gris
document.body.appendChild(renderer.domElement);
// Chargement du fichier GLB
const loader = new THREE.GLTFLoader();
loader.load(
'https://geekmps.fr/media/tests/3D/sac.glb',
function (gltf) {
mesh = gltf.scene;
mesh.scale.set(1, 1, 1);
// Ajout d'une lumière ambiante
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); // Couleur, Intensité
ambientLight.position.set(2, 5, 0);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // Couleur, Intensité
directionalLight.position.set(4,11, 6); // Position
scene.add(directionalLight);
// Lumière directionnelle (soleil)
const sun = new THREE.DirectionalLight(0xffffff, 1); // Couleur, Intensité
sun.position.set(5, 5, 5); // Position du soleil
scene.add(sun);
scene.add(mesh);
animate();
},
undefined,
function (error) {
console.error('Erreur de chargement : ', error);
}
);
// Chargement du fichier GLB pour le téléphone
loader.load(
'https://geekmps.fr/media/tests/3D/phone2.glb',
function (gltf) {
phone = gltf.scene;
phone.scale.set(10, 10, 10);
phone.position.x = 2; // Position initiale du téléphone
scene.add(phone);
animate();
},
undefined,
function (error) {
console.error('Erreur de chargement : ', error);
}
);
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
// Rotation du sac vers le haut
document.getElementById('rotateUp').addEventListener('click', function() {
if (mesh) {
mesh.rotation.x -= Math.PI / 180; // Rotation de 1 degré vers le haut
}
});
// Rotation du sac vers le bas
document.getElementById('rotateDown').addEventListener('click', function() {
if (mesh) {
mesh.rotation.x += Math.PI / 180; // Rotation de 1 degré vers le bas
}
});
// Rotation du sac vers la gauche
document.getElementById('rotateLeft').addEventListener('click', function() {
if (mesh) {
mesh.rotation.y += Math.PI / 180; // Rotation de 1 degré vers la gauche
}
});
// Rotation du sac vers la droite
document.getElementById('rotateRight').addEventListener('click', function() {
if (mesh) {
mesh.rotation.y -= Math.PI / 180; // Rotation de 1 degré vers la droite
}
});
// Rotation du sac avec la souris
let isDragging = false;
let previousMousePosition = {
x: 0,
y: 0
};
document.addEventListener('mousedown', function(event) {
isDragging = true;
});
document.addEventListener('mousemove', function(event) {
if (!isDragging) return;
const deltaMove = {
x: event.clientX - previousMousePosition.x,
y: event.clientY - previousMousePosition.y
};
if (mesh) {
mesh.rotation.y += deltaMove.x * Math.PI / 360;
mesh.rotation.x += deltaMove.y * Math.PI / 360;
}
previousMousePosition = {
x: event.clientX,
y: event.clientY
};
});
document.addEventListener('mouseup', function(event) {
isDragging = false;
});
// Gestion des événements tactiles pour la rotation et le zoom du sac
let touchStartX = 0;
let touchStartY = 0;
let touchZoomDistance = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length === 1) {
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
} else if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
touchZoomDistance = Math.sqrt(dx * dx + dy * dy);
}
});
document.addEventListener('touchmove', function(event) {
if (event.touches.length === 1) {
const deltaX = event.touches[0].clientX - touchStartX;
const deltaY = event.touches[0].clientY - touchStartY;
if (mesh) {
mesh.rotation.y -= deltaX * 0.01;
mesh.rotation.x -= deltaY * 0.01;
}
touchStartX = event.touches[0].clientX;
touchStartY = event.touches[0].clientY;
} else if (event.touches.length === 2) {
const dx = event.touches[0].clientX - event.touches[1].clientX;
const dy = event.touches[0].clientY - event.touches[1].clientY;
const newDistance = Math.sqrt(dx * dx + dy * dy);
const zoomDelta = newDistance - touchZoomDistance;
camera.position.z += zoomDelta * 0.1;
touchZoomDistance = newDistance;
}
});
// Déplacer le téléphone avec les gestes tactiles
let touchStartPhoneX = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length === 1 && phone) {
touchStartPhoneX = event.touches[0].clientX;
}
});
document.addEventListener('touchmove', function(event) {
if (event.touches.length === 1 && phone) {
const deltaX = event.touches[0].clientX - touchStartPhoneX;
phone.position.x += deltaX * 0.01; // Réglage de la vitesse de déplacement
touchStartPhoneX = event.touches[0].clientX;
}
});
// Gestion du redimensionnement de la fenêtre
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Déplacement de la lumière
document.getElementById('moveLight').addEventListener('click', function() {
// Changer la position de la lumière
directionalLight.position.set(
Math.random() * 10 - 5, // Valeurs aléatoires pour la position X
Math.random() * 10 - 5, // Valeurs aléatoires pour la position Y
Math.random() * 10 - 5 // Valeurs aléatoires pour la position Z
);
});
</script>
</body>
</html>
C'est bien joli de faire une démo comme celle-là, mais ça ne sert pas à grand-chose. On peut imaginer de pouvoir essayer le sac, l'ouvrir et bien d'autres choses.
Pour le moment, les IA ne peuvent pas complètement remplacer des développeurs. Va falloir se méfier tout de même. En attendant c'est un bonne aide. Je me demande du coup à ça sert à quoi de continuer à écrire des blogs.
Et si tu es plus fort que l'IA tu deviens un super code comme ce jeune home
Comments powered by CComment