Galería de Arte 3D - Paisajes Infinitos EN HTML
- samuel gaitan
- 29 ago
- 6 Min. de lectura
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Galería de Arte 3D - Paisajes Infinitos</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
overflow: hidden;
font-family: 'Georgia', serif;
}
position: relative;
width: 100vw;
height: 100vh;
}
#poem-overlay {
position: absolute;
top: 20px;
left: 20px;
color: rgba(255, 255, 255, 0.9);
font-size: 18px;
line-height: 1.6;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
z-index: 100;
max-width: 300px;
padding: 20px;
background: rgba(0, 0, 0, 0.3);
border-radius: 10px;
backdrop-filter: blur(10px);
transition: opacity 1s ease-in-out;
}
position: absolute;
bottom: 20px;
right: 20px;
z-index: 100;
color: white;
background: rgba(0, 0, 0, 0.5);
padding: 15px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
.control-btn {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
padding: 8px 15px;
margin: 3px;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.control-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: translateY(-2px);
}
#title {
position: absolute;
top: 20px;
right: 20px;
color: white;
font-size: 24px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);
z-index: 100;
}
</style>
</head>
<body>
<div id="container">
<div id="title">Paisajes Infinitos</div>
<div id="poem-overlay"></div>
<div id="controls">
<div>Escena Actual: <span id="scene-name">Amanecer</span></div>
<button class="control-btn" onclick="nextScene()">Cambiar Escena</button>
<button class="control-btn" onclick="toggleAnimation()">Pausar/Reanudar</button>
</div>
</div>
<script>
// Variables globales
let scene, camera, renderer, canvas;
let brushStrokes = [];
let currentScene = 0;
let animationRunning = true;
let poems = [];
let currentPoem = 0;
// Banco de poemas (triadas)
const poemBank = [
{
title: "Aurora Dorada",
verses: [
"El sol despierta lento,",
"pintando cielos de oro,",
"la tierra se estremece."
]
},
{
title: "Crepúsculo Ardiente",
verses: [
"Fuego en el horizonte,",
"las nubes se sonrojan,",
"el día se despide."
]
},
{
title: "Luna Plateada",
verses: [
"Reina de la noche,",
"bañas el mundo en plata,",
"susurras secretos."
]
},
{
title: "Pincel del Tiempo",
verses: [
"Cada trazo cuenta",
"la historia del momento,",
"arte que no muere."
]
},
{
title: "Colores Salvajes",
verses: [
"Rojos que gritan,",
"azules que cantan suave,",
"arcoíris del alma."
]
},
{
title: "Viento Creador",
verses: [
"Sopla inspiración,",
"mueve el pincel invisible,",
"crea sin descanso."
]
},
{
title: "Sombras Danzantes",
verses: [
"Bailan con la luz,",
"forman figuras etéreas,",
"poesía visual."
]
},
{
title: "Infinito Lienzo",
verses: [
"Sin límites ni bordes,",
"el espacio es mi papel,",
"pinto con estrellas."
]
},
{
title: "Melodía Cromática",
verses: [
"Cada color suena,",
"sinfonía de matices,",
"música para ojos."
]
},
{
title: "Eterno Renacer",
verses: [
"Cada amanecer",
"es un nuevo comienzo,",
"arte sin final."
]
}
];
// Configuraciones de escenas
const sceneConfigs = [
{
name: "Amanecer",
skyColor: [1.0, 0.8, 0.6],
lightColor: [1.0, 0.9, 0.7],
brushColors: [
[1.0, 0.7, 0.3], [1.0, 0.8, 0.4], [0.9, 0.6, 0.2],
[1.0, 0.9, 0.6], [0.8, 0.5, 0.1]
]
},
{
name: "Atardecer",
skyColor: [0.8, 0.4, 0.6],
lightColor: [1.0, 0.5, 0.3],
brushColors: [
[1.0, 0.3, 0.2], [0.9, 0.4, 0.1], [0.8, 0.2, 0.4],
[1.0, 0.6, 0.2], [0.7, 0.1, 0.3]
]
},
{
name: "Noche de Luna",
skyColor: [0.1, 0.1, 0.3],
lightColor: [0.8, 0.8, 1.0],
brushColors: [
[0.6, 0.7, 1.0], [0.4, 0.5, 0.8], [0.3, 0.3, 0.7],
[0.8, 0.8, 0.9], [0.2, 0.2, 0.5]
]
},
{
name: "Abstracto",
skyColor: [0.2, 0.2, 0.2],
lightColor: [1.0, 1.0, 1.0],
brushColors: [
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()]
]
}
];
// Clase para pinceladas 3D
class BrushStroke {
constructor(scene, config) {
this.scene = scene;
this.config = config;
this.points = [];
this.geometry = new THREE.BufferGeometry();
this.material = new THREE.LineBasicMaterial({
color: new THREE.Color(...this.getRandomColor()),
linewidth: Math.random() * 3 + 1,
transparent: true,
opacity: 0.8
});
this.line = new THREE.Line(this.geometry, this.material);
this.age = 0;
this.maxAge = 200 + Math.random() * 300;
this.initializePath();
scene.add(this.line);
}
getRandomColor() {
const colors = this.config.brushColors;
return colors[Math.floor(Math.random() * colors.length)];
}
initializePath() {
const startX = (Math.random() - 0.5) * 20;
const startY = (Math.random() - 0.5) * 10;
const startZ = (Math.random() - 0.5) * 20;
this.direction = new THREE.Vector3(
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 2,
(Math.random() - 0.5) * 2
).normalize();
for (let i = 0; i < 50; i++) {
const point = new THREE.Vector3(
startX + this.direction.x * i * 0.2 + Math.sin(i * 0.1) * 2,
startY + this.direction.y * i * 0.2 + Math.cos(i * 0.1) * 2,
startZ + this.direction.z * i * 0.2 + Math.sin(i * 0.15) * 1.5
);
this.points.push(point);
}
this.geometry.setFromPoints(this.points);
}
update() {
this.age++;
// Añadir nuevo punto
if (this.points.length < 100) {
const lastPoint = this.points[this.points.length - 1];
const newPoint = lastPoint.clone();
// Movimiento orgánico
newPoint.add(new THREE.Vector3(
(Math.random() - 0.5) * 0.5,
(Math.random() - 0.5) * 0.5,
(Math.random() - 0.5) * 0.5
));
this.points.push(newPoint);
this.geometry.setFromPoints(this.points);
}
// Desvanecimiento
const fadeStart = this.maxAge * 0.7;
if (this.age > fadeStart) {
const fadeProgress = (this.age - fadeStart) / (this.maxAge - fadeStart);
this.material.opacity = 0.8 * (1 - fadeProgress);
}
// Rotación sutil
this.line.rotation.y += 0.001;
this.line.rotation.x += 0.0005;
return this.age < this.maxAge;
}
destroy() {
this.scene.remove(this.line);
this.geometry.dispose();
this.material.dispose();
}
}
// Inicialización
function init() {
// Crear escena
scene = new THREE.Scene();
// Configurar cámara
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 15);
// Configurar renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(new THREE.Color(...sceneConfigs[currentScene].skyColor));
document.getElementById('container').appendChild(renderer.domElement);
// Iluminación
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(
new THREE.Color(...sceneConfigs[currentScene].lightColor), 0.8
);
directionalLight.position.set(10, 10, 5);
scene.add(directionalLight);
// Crear elementos del paisaje base
createLandscape();
// Iniciar ciclos
startPoemCycle();
animate();
}
function createLandscape() {
// Suelo/terreno base
const groundGeometry = new THREE.PlaneGeometry(50, 50, 32, 32);
const groundMaterial = new THREE.MeshLambertMaterial({
color: 0x2d5a3d,
transparent: true,
opacity: 0.3
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.position.y = -5;
scene.add(ground);
// Elementos atmosféricos según la escena
if (currentScene === 2) { // Noche
createMoon();
createStars();
} else if (currentScene === 0) { // Amanecer
createSun(-10, 3, -10);
} else if (currentScene === 1) { // Atardecer
createSun(10, 2, -8);
}
}
function createMoon() {
const moonGeometry = new THREE.SphereGeometry(2, 16, 16);
const moonMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff,
emissive: 0x222222
});
const moon = new THREE.Mesh(moonGeometry, moonMaterial);
moon.position.set(8, 8, -15);
scene.add(moon);
}
function createStars() {
const starGeometry = new THREE.BufferGeometry();
const starPositions = [];
for (let i = 0; i < 1000; i++) {
starPositions.push(
(Math.random() - 0.5) * 200,
Math.random() * 100,
(Math.random() - 0.5) * 200
);
}
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starPositions, 3));
const starMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.5
});
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
}
function createSun(x, y, z) {
const sunGeometry = new THREE.SphereGeometry(3, 16, 16);
const sunMaterial = new THREE.MeshBasicMaterial({
color: currentScene === 0 ? 0xffd700 : 0xff4500,
emissive: currentScene === 0 ? 0x664400 : 0x441100
});
const sun = new THREE.Mesh(sunGeometry, sunMaterial);
sun.position.set(x, y, z);
scene.add(sun);
}
function clearScene() {
// Limpiar pinceladas
brushStrokes.forEach(stroke => stroke.destroy());
brushStrokes = [];
// Limpiar toda la escena
while(scene.children.length > 0) {
const child = scene.children[0];
scene.remove(child);
if (child.geometry) child.geometry.dispose();
if (child.material) child.material.dispose();
}
// Recrear iluminación
const ambientLight = new THREE.AmbientLight(0x404040, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(
new THREE.Color(...sceneConfigs[currentScene].lightColor), 0.8
);
directionalLight.position.set(10, 10, 5);
scene.add(directionalLight);
}
function nextScene() {
currentScene = (currentScene + 1) % sceneConfigs.length;
// Limpiar escena actual
clearScene();
// Actualizar colores de fondo
renderer.setClearColor(new THREE.Color(...sceneConfigs[currentScene].skyColor));
// Recrear paisaje
createLandscape();
// Actualizar UI
document.getElementById('scene-name').textContent = sceneConfigs[currentScene].name;
}
function createBrushStroke() {
if (brushStrokes.length < 20) {
const stroke = new BrushStroke(scene, sceneConfigs[currentScene]);
brushStrokes.push(stroke);
}
}
function updateBrushStrokes() {
for (let i = brushStrokes.length - 1; i >= 0; i--) {
if (!brushStrokes[i].update()) {
brushStrokes[i].destroy();
brushStrokes.splice(i, 1);
}
}
}
function animate() {
if (!animationRunning) {
requestAnimationFrame(animate);
return;
}
// Crear nueva pincelada aleatoriamente
if (Math.random() < 0.1) {
createBrushStroke();
}
// Actualizar pinceladas existentes
updateBrushStrokes();
// Rotación lenta de la cámara
const time = Date.now() * 0.0005;
camera.position.x = Math.cos(time) * 20;
camera.position.z = Math.sin(time) * 20;
camera.lookAt(0, 0, 0);
// Actualizar colores abstractos en escena abstracta
if (currentScene === 3) {
sceneConfigs[3].brushColors = [
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()],
[Math.random(), Math.random(), Math.random()]
];
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function startPoemCycle() {
function showNextPoem() {
const poemElement = document.getElementById('poem-overlay');
const poem = poemBank[currentPoem];
poemElement.innerHTML = `
<h3 style="color: #ffd700; margin-bottom: 10px;">${poem.title}</h3>
${poem.verses.map(verse => `<div>${verse}</div>`).join('')}
`;
currentPoem = (currentPoem + 1) % poemBank.length;
}
showNextPoem();
setInterval(showNextPoem, 10000); // Cambiar cada 10 segundos
}
function toggleAnimation() {
animationRunning = !animationRunning;
}
// Event listeners
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Auto cambio de escena cada 30 segundos
setInterval(() => {
if (animationRunning) {
nextScene();
}
}, 30000);
// Inicializar cuando la página cargue
window.addEventListener('load', init);
// Efectos de partículas adicionales
function createParticleSystem() {
const particleCount = 500;
const particles = new THREE.BufferGeometry();
const positions = [];
const colors = [];
for (let i = 0; i < particleCount; i++) {
positions.push(
(Math.random() - 0.5) * 100,
Math.random() * 50,
(Math.random() - 0.5) * 100
);
const color = sceneConfigs[currentScene].brushColors[
Math.floor(Math.random() * sceneConfigs[currentScene].brushColors.length)
];
colors.push(...color);
}
particles.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
particles.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
const particleMaterial = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
transparent: true,
opacity: 0.6
});
const particleSystem = new THREE.Points(particles, particleMaterial);
scene.add(particleSystem);
return particleSystem;
}
// Añadir sistema de partículas después de 5 segundos
setTimeout(() => {
createParticleSystem();
}, 5000);
</script>
</body>
</html>




Comentarios