241 lines
6.7 KiB
PHP
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'; ?>
|