import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; /** * Creates and returns the core Three.js scene objects: renderer, scene, camera, controls. * Tweak the defaults below or pass overrides to customize. */ const DEFAULTS = { // Renderer antialias: true, alpha: true, // transparent background (for OBS chroma key, etc.) shadowMap: true, // Camera fov: 30, near: 0.1, far: 50, cameraPosition: [0, 1, 0.9], controlsTarget: [0, 1.1, 0], // Scene background: null, // null = transparent, or set to a THREE.Color backgroundImage: '/hintergrund.jpg', // NEU: Pfad zu deinem Bild im "public" Ordner! // Lighting lights: [ // NEU: castShadow auf true gesetzt für realistischere Schatten { type: 'directional', color: 0xffffff, intensity: 1, position: [3, 15, -5], castShadow: true }, { type: 'ambient', color: 0xffffff, intensity: 2.1 }, ], // Debug helpers (set to true to enable) gridHelper: false, axesHelper: false, }; export function createScene(overrides = {}) { const opts = { ...DEFAULTS, ...overrides }; // --- Renderer --- const renderer = new THREE.WebGLRenderer({ antialias: opts.antialias, alpha: opts.alpha, }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); // NEU: Wichtig für korrekte VRM/MToon Farben, sonst wirkt alles grau/verwaschen renderer.outputColorSpace = THREE.SRGBColorSpace; if (opts.shadowMap) { renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; } document.body.appendChild(renderer.domElement); // --- Scene --- const scene = new THREE.Scene(); // NEU: Logik für das Hintergrundbild if (opts.backgroundImage) { const textureLoader = new THREE.TextureLoader(); scene.background = textureLoader.load(opts.backgroundImage); scene.background.colorSpace = THREE.SRGBColorSpace; // Verhindert falsche Farben im Bild } else if (opts.background !== null) { scene.background = opts.background instanceof THREE.Color ? opts.background : new THREE.Color(opts.background); } // --- Camera --- const camera = new THREE.PerspectiveCamera( opts.fov, window.innerWidth / window.innerHeight, opts.near, opts.far, ); camera.position.set(...opts.cameraPosition); // --- Controls --- const controls = new OrbitControls(camera, renderer.domElement); controls.target.set(...opts.controlsTarget); controls.update(); // --- Lighting --- for (const lightDef of opts.lights) { let light; if (lightDef.type === 'directional') { light = new THREE.DirectionalLight(lightDef.color, lightDef.intensity); if (lightDef.position) light.position.set(...lightDef.position); if (lightDef.castShadow) { light.castShadow = true; // NEU: Schattenqualität verbessern light.shadow.mapSize.width = 1024; light.shadow.mapSize.height = 1024; light.shadow.bias = -0.001; } } else if (lightDef.type === 'ambient') { light = new THREE.AmbientLight(lightDef.color, lightDef.intensity); } else if (lightDef.type === 'point') { light = new THREE.PointLight(lightDef.color, lightDef.intensity); if (lightDef.position) light.position.set(...lightDef.position); } if (light) scene.add(light); } // --- Debug helpers --- if (opts.gridHelper) { const size = typeof opts.gridHelper === 'number' ? opts.gridHelper : 50; scene.add(new THREE.GridHelper(size, size)); } if (opts.axesHelper) { const size = typeof opts.axesHelper === 'number' ? opts.axesHelper : 5; scene.add(new THREE.AxesHelper(size)); } // --- Resize handler --- window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); return { renderer, scene, camera, controls }; }