Jean0's picture
améliore je veux un personnage réelle une femme américaine de 35 ans
fa1c13f verified
raw
history blame
12.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FaceMotion Simulator</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body class="bg-indigo-50 min-h-screen flex items-center justify-center p-4">
<div class="max-w-md w-full bg-white rounded-2xl shadow-xl overflow-hidden">
<!-- Header -->
<div class="bg-indigo-600 p-6 text-white">
<div class="flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">FaceMotion Simulator</h1>
<p class="text-indigo-100">Interactive 3D Head Tracking</p>
</div>
<div class="w-12 h-12 rounded-full bg-indigo-500 flex items-center justify-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
<circle cx="9" cy="7" r="4"></circle>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
<path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
</svg>
</div>
</div>
</div>
<!-- 3D Canvas -->
<div id="canvas-container" class="w-full aspect-square bg-indigo-100 relative">
<canvas id="liveness-canvas" class="absolute inset-0 w-full h-full"></canvas>
<div class="absolute bottom-4 left-0 right-0 flex justify-center">
<div class="bg-indigo-600 text-white px-3 py-1 rounded-full text-xs font-medium shadow-md">
Move the sliders below
</div>
</div>
</div>
<!-- Controls -->
<div class="p-6 space-y-6">
<div>
<div class="flex justify-between mb-2">
<label for="rotate-y" class="text-sm font-medium text-gray-700">Horizontal Rotation</label>
<span id="rotate-y-value" class="text-sm text-indigo-600 font-medium"></span>
</div>
<input type="range" id="rotate-y" min="-80" max="80" value="0" class="w-full h-2 bg-indigo-200 rounded-lg appearance-none cursor-pointer">
</div>
<div>
<div class="flex justify-between mb-2">
<label for="rotate-x" class="text-sm font-medium text-gray-700">Vertical Rotation</label>
<span id="rotate-x-value" class="text-sm text-indigo-600 font-medium"></span>
</div>
<input type="range" id="rotate-x" min="-45" max="45" value="0" class="w-full h-2 bg-indigo-200 rounded-lg appearance-none cursor-pointer">
</div>
<button id="reset-btn" class="w-full py-2 px-4 bg-indigo-100 hover:bg-indigo-200 text-indigo-700 font-medium rounded-lg transition duration-200 flex items-center justify-center space-x-2">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="1 4 1 10 7 10"></polyline>
<polyline points="23 20 23 14 17 14"></polyline>
<path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path>
</svg>
<span>Reset Position</span>
</button>
</div>
</div>
<script>
// === 3D Scene Initialization ===
const canvasContainer = document.getElementById('canvas-container');
const canvas = document.getElementById('liveness-canvas');
// Scene
const scene = new THREE.Scene();
scene.background = null;
// Camera
const camera = new THREE.PerspectiveCamera(75, canvasContainer.clientWidth / canvasContainer.clientHeight, 0.1, 1000);
camera.position.z = 5;
// Renderer
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
antialias: true,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvasContainer.clientWidth, canvasContainer.clientHeight);
// === Create Realistic Female Face ===
const headGroup = new THREE.Group();
scene.add(headGroup);
// Head (Ellipsoid for more realistic shape)
const headGeometry = new THREE.SphereGeometry(1.8, 64, 64);
headGeometry.scale(0.8, 1.1, 0.9); // More oval shape
const headMaterial = new THREE.MeshStandardMaterial({
color: 0xF5D0B9,
roughness: 0.4,
metalness: 0.1,
map: new THREE.TextureLoader().load('https://static.photos/people/1024x576/35')
});
const head = new THREE.Mesh(headGeometry, headMaterial);
headGroup.add(head);
// Eyes
const eyeGeometry = new THREE.SphereGeometry(0.25, 32, 32);
const eyeMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
roughness: 0.1,
metalness: 0.1
});
// Left Eye
const eyeLeft = new THREE.Mesh(eyeGeometry, eyeMaterial);
eyeLeft.position.set(-0.7, 0.3, 1.8);
headGroup.add(eyeLeft);
// Right Eye
const eyeRight = new THREE.Mesh(eyeGeometry, eyeMaterial.clone());
eyeRight.position.set(0.7, 0.3, 1.8);
headGroup.add(eyeRight);
// Pupils
const pupilGeometry = new THREE.SphereGeometry(0.12, 32, 32);
const pupilMaterial = new THREE.MeshStandardMaterial({
color: 0x4A2C12,
});
// Left Pupil
const pupilLeft = new THREE.Mesh(pupilGeometry, pupilMaterial);
pupilLeft.position.set(0, 0, 0.25);
eyeLeft.add(pupilLeft);
// Right Pupil
const pupilRight = new THREE.Mesh(pupilGeometry, pupilMaterial.clone());
pupilRight.position.set(0, 0, 0.25);
eyeRight.add(pupilRight);
// Eyelashes
const eyelashGeometry = new THREE.ConeGeometry(0.02, 0.15, 4);
const eyelashMaterial = new THREE.MeshBasicMaterial({ color: 0x000000 });
// Left Eyelashes
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 0.5 - Math.PI * 0.25;
const eyelash = new THREE.Mesh(eyelashGeometry, eyelashMaterial);
eyelash.position.set(-0.7 + Math.cos(angle) * 0.3, 0.3 + Math.sin(angle) * 0.3, 1.85);
eyelash.rotation.z = angle;
headGroup.add(eyelash);
}
// Right Eyelashes
for (let i = 0; i < 8; i++) {
const angle = (i / 8) * Math.PI * 0.5 - Math.PI * 0.25;
const eyelash = new THREE.Mesh(eyelashGeometry, eyelashMaterial.clone());
eyelash.position.set(0.7 + Math.cos(angle) * 0.3, 0.3 + Math.sin(angle) * 0.3, 1.85);
eyelash.rotation.z = angle;
headGroup.add(eyelash);
}
// Nose (more refined)
const noseGeometry = new THREE.ConeGeometry(0.3, 0.6, 32);
const noseMaterial = new THREE.MeshStandardMaterial({
color: 0xE8BBA5,
roughness: 0.4
});
const nose = new THREE.Mesh(noseGeometry, noseMaterial);
nose.position.set(0, 0, 1.9);
nose.rotation.x = Math.PI / 10;
headGroup.add(nose);
// Lips
const lipGeometry = new THREE.TorusGeometry(0.4, 0.08, 16, 32, Math.PI);
const lipMaterial = new THREE.MeshStandardMaterial({
color: 0xC45C6B,
roughness: 0.3,
metalness: 0.2
});
const lips = new THREE.Mesh(lipGeometry, lipMaterial);
lips.position.set(0, -0.3, 1.7);
lips.rotation.x = Math.PI / 8;
headGroup.add(lips);
// Eyebrows
const eyebrowGeometry = new THREE.BoxGeometry(0.5, 0.05, 0.02);
const eyebrowMaterial = new THREE.MeshStandardMaterial({ color: 0x3A2A1A });
// Left Eyebrow
const leftEyebrow = new THREE.Mesh(eyebrowGeometry, eyebrowMaterial);
leftEyebrow.position.set(-0.7, 0.5, 1.8);
leftEyebrow.rotation.z = -0.2;
headGroup.add(leftEyebrow);
// Right Eyebrow
const rightEyebrow = new THREE.Mesh(eyebrowGeometry, eyebrowMaterial.clone());
rightEyebrow.position.set(0.7, 0.5, 1.8);
rightEyebrow.rotation.z = 0.2;
headGroup.add(rightEyebrow);
// Hair (simplified)
const hairGeometry = new THREE.SphereGeometry(2.0, 32, 32);
hairGeometry.scale(0.9, 1.2, 1.0);
const hairMaterial = new THREE.MeshStandardMaterial({
color: 0x4B3A2D,
roughness: 0.8,
metalness: 0.1
});
const hair = new THREE.Mesh(hairGeometry, hairMaterial);
hair.position.set(0, 0.4, 0);
headGroup.add(hair);
// Enhanced Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
directionalLight.position.set(3, 5, 5);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
scene.add(directionalLight);
const fillLight = new THREE.DirectionalLight(0xffffff, 0.5);
fillLight.position.set(-3, 2, 3);
scene.add(fillLight);
const rimLight = new THREE.DirectionalLight(0xffffff, 0.3);
rimLight.position.set(0, 3, -5);
scene.add(rimLight);
// === Controls ===
const sliderY = document.getElementById('rotate-y');
const sliderX = document.getElementById('rotate-x');
const rotateYValue = document.getElementById('rotate-y-value');
const rotateXValue = document.getElementById('rotate-x-value');
const resetBtn = document.getElementById('reset-btn');
sliderY.addEventListener('input', (e) => {
const rotationRadians = (e.target.value * Math.PI) / 180;
headGroup.rotation.y = rotationRadians;
rotateYValue.textContent = `${e.target.value}°`;
});
sliderX.addEventListener('input', (e) => {
const rotationRadians = (e.target.value * Math.PI) / 180;
headGroup.rotation.x = rotationRadians;
rotateXValue.textContent = `${e.target.value}°`;
});
resetBtn.addEventListener('click', () => {
sliderY.value = 0;
sliderX.value = 0;
headGroup.rotation.set(0, 0, 0);
rotateYValue.textContent = '0°';
rotateXValue.textContent = '0°';
});
// Animation Loop with subtle blinking
let blinkTimer = 0;
let blinkState = false;
function animate() {
requestAnimationFrame(animate);
// Blinking animation
blinkTimer += 0.01;
if (blinkTimer > 5 && !blinkState) {
eyeLeft.scale.y = 0.05;
eyeRight.scale.y = 0.05;
blinkState = true;
blinkTimer = 0;
} else if (blinkTimer > 0.1 && blinkState) {
eyeLeft.scale.y = 1;
eyeRight.scale.y = 1;
blinkState = false;
blinkTimer = 0;
}
renderer.render(scene, camera);
}
// Handle Window Resize
function onResize() {
const width = canvasContainer.clientWidth;
const height = canvasContainer.clientHeight;
renderer.setSize(width, height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
window.addEventListener('resize', onResize);
animate();
onResize();
</script>
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
</body>
</html>