Files
skyview.astronomiemuseum.de/public/mars.php
T

241 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
ini_set('display_errors', '1');
error_reporting(E_ALL);
session_start();
$pageTitle = 'Mars - AstroTools';
$bodyClass = 'mars-page';
$headerIntroPre = 'Planetendarstellung';
$headerIntroTitle = 'Mars';
$headerIntroSub = 'Eine einfache 3D-Ansicht mit Marstextur.';
?>
<?php require __DIR__ . '/header.php'; ?>
<style>
.mars-stage-card {
padding: 0;
overflow: hidden;
}
.mars-stage {
position: relative;
min-height: 70vh;
background:
radial-gradient(circle at top, rgba(255, 153, 102, 0.12), transparent 40%),
linear-gradient(180deg, rgba(14, 10, 10, 0.94), rgba(8, 8, 12, 0.98));
}
#marsViewport {
width: 100%;
height: 70vh;
display: block;
}
.mars-overlay {
position: absolute;
left: 1rem;
bottom: 1rem;
padding: 0.6rem 0.85rem;
border-radius: 999px;
background: rgba(15, 15, 22, 0.72);
color: #f4e7de;
font-size: 0.95rem;
letter-spacing: 0.02em;
backdrop-filter: blur(10px);
}
@media (max-width: 768px) {
.mars-stage,
#marsViewport {
height: 58vh;
min-height: 420px;
}
}
</style>
<section class="card mars-stage-card">
<div class="mars-stage">
<canvas id="marsViewport" aria-label="3D-Ansicht des Mars"></canvas>
<div class="mars-overlay">Ziehen zum Drehen, Mausrad zum Zoomen</div>
</div>
</section>
<script type="module">
let THREE;
async function initMars() {
THREE = await import('./js/three.module.js');
const canvas = document.getElementById('marsViewport');
if (!canvas) {
return;
}
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true
});
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.05;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 4.8);
const hemisphereLight = new THREE.HemisphereLight(0x9db4d8, 0x120d0b, 0.16);
scene.add(hemisphereLight);
const sunLight = new THREE.DirectionalLight(0xfff1d6, 2.8);
sunLight.position.set(7, 1.8, 5.5);
sunLight.castShadow = true;
sunLight.shadow.mapSize.set(2048, 2048);
sunLight.shadow.bias = -0.0008;
sunLight.shadow.normalBias = 0.02;
sunLight.shadow.camera.near = 1;
sunLight.shadow.camera.far = 20;
sunLight.shadow.camera.left = -4;
sunLight.shadow.camera.right = 4;
sunLight.shadow.camera.top = 4;
sunLight.shadow.camera.bottom = -4;
sunLight.target.position.set(0, 0, 0);
scene.add(sunLight);
scene.add(sunLight.target);
const fillLight = new THREE.DirectionalLight(0xa4b4d8, 0.18);
fillLight.position.set(-4, -1.5, -3.5);
scene.add(fillLight);
const loader = new THREE.TextureLoader();
const marsTexture = loader.load('./images/planets/mars_4096.jpg');
marsTexture.colorSpace = THREE.SRGBColorSpace;
marsTexture.anisotropy = renderer.capabilities.getMaxAnisotropy();
const marsDisplacement = loader.load('./images/planets/mars_displacement_5760.jpg');
marsDisplacement.anisotropy = renderer.capabilities.getMaxAnisotropy();
const geometry = new THREE.SphereGeometry(1.45, 192, 192);
const material = new THREE.MeshStandardMaterial({
map: marsTexture,
displacementMap: marsDisplacement,
displacementScale: 0.065,
displacementBias: -0.02,
bumpMap: marsDisplacement,
bumpScale: 0.03,
roughness: 0.96,
metalness: 0
});
const mars = new THREE.Mesh(geometry, material);
mars.castShadow = true;
mars.receiveShadow = true;
scene.add(mars);
const state = {
isPointerDown: false,
lastX: 0,
lastY: 0,
velocityX: 0,
velocityY: 0
};
canvas.style.touchAction = 'none';
function resizeRenderer() {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (width === 0 || height === 0) {
return;
}
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
function clampCameraDistance(nextZ) {
return Math.min(Math.max(nextZ, 3.2), 8.5);
}
function onPointerDown(event) {
state.isPointerDown = true;
state.lastX = event.clientX;
state.lastY = event.clientY;
canvas.setPointerCapture(event.pointerId);
}
function onPointerMove(event) {
if (!state.isPointerDown) {
return;
}
const deltaX = event.clientX - state.lastX;
const deltaY = event.clientY - state.lastY;
state.lastX = event.clientX;
state.lastY = event.clientY;
const rotationSpeed = 0.008;
mars.rotation.y += deltaX * rotationSpeed;
mars.rotation.x += deltaY * rotationSpeed;
mars.rotation.x = Math.max(-1.25, Math.min(1.25, mars.rotation.x));
state.velocityX = deltaX * 0.0009;
state.velocityY = deltaY * 0.0009;
}
function onPointerUp(event) {
state.isPointerDown = false;
if (canvas.hasPointerCapture(event.pointerId)) {
canvas.releasePointerCapture(event.pointerId);
}
}
function onWheel(event) {
event.preventDefault();
camera.position.z = clampCameraDistance(camera.position.z + event.deltaY * 0.0035);
}
function renderFrame() {
if (!state.isPointerDown) {
mars.rotation.y += state.velocityX;
mars.rotation.x += state.velocityY;
mars.rotation.x = Math.max(-1.25, Math.min(1.25, mars.rotation.x));
state.velocityX *= 0.94;
state.velocityY *= 0.94;
if (Math.abs(state.velocityX) < 0.00001) {
state.velocityX = 0;
}
if (Math.abs(state.velocityY) < 0.00001) {
state.velocityY = 0;
}
}
renderer.render(scene, camera);
requestAnimationFrame(renderFrame);
}
canvas.addEventListener('pointerdown', onPointerDown);
canvas.addEventListener('pointermove', onPointerMove);
canvas.addEventListener('pointerup', onPointerUp);
canvas.addEventListener('pointercancel', onPointerUp);
canvas.addEventListener('wheel', onWheel, { passive: false });
window.addEventListener('resize', resizeRenderer);
resizeRenderer();
renderFrame();
}
initMars().catch((error) => {
console.error('Mars-Ansicht konnte nicht initialisiert werden.', error);
});
</script>
<?php require __DIR__ . '/footer.php'; ?>