Three.js - baked map texture

Aus Wikizone
Wechseln zu: Navigation, Suche

Links

Blender uv-mapping (mit Three.js workflow

Einführung

Baked maps sind vorgerenderte Texturen. Sie enthalten quasi alle Lichter und Schatten. Sie werden im 3D Programm erstellt und können dann in Blender als Textur geladen werden. Wie man die Textur erstellt findest du unter Blender uv-mapping.

So wird eine Map eingebunden:

Fallstricke:

  • Texture wird falsch gemappt flipY
const bakedTexture = textureLoader.load('baked.jpg')
bakedTexture.flipY = false
  • ColorSpace falsch Farbe nicht so gut wie im original Render Texture und Renderer muss eingestellt werden (z.b. auf sRGB)
bakedTexture.encoding = THREE.sRGBEncoding
//...
renderer.outputEncoding = THREE.sRGBEncoding

Komplettes Beispiel

import './style.css'
import * as dat from 'lil-gui'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js' // if using compressed gltf

/**
 * Base
 */
// Debug
const gui = new dat.GUI({
    width: 400
})

// Canvas
const canvas = document.querySelector('canvas.webgl')

// Scene
const scene = new THREE.Scene()

/**
 * Loaders
 */
// Texture loader
const textureLoader = new THREE.TextureLoader()

// Draco loader if using compressed gltf
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('draco/')

// GLTF loader
const gltfLoader = new GLTFLoader()
gltfLoader.setDRACOLoader(dracoLoader)

/**
 * Textures
 */
const bakedTexture = textureLoader.load('baked.jpg')
bakedTexture.flipY = false
bakedTexture.encoding = THREE.sRGBEncoding

/**
 * Materials
 */
const bakedMaterial = new THREE.MeshBasicMaterial({ map: bakedTexture,side: THREE.DoubleSide })
const portalLightMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff})
/** 
 * Portal
 */
gltfLoader.load(
    'portal.glb',
    (gltf) => {
        //this way we cam traverse bigger scenes but as we merged we could do it manually like:
        //const bakedMesh = gltf.scene.children.find((child) => child.name === 'baked')
        
        gltf.scene.traverse(
            (child) => {
                child.material = bakedMaterial
            }
        )
        
        scene.add(gltf.scene)

        // search emmisive materials
        const emissions = ["Lamp1", "Lamp2", "LampPortal"];
        const lights = gltf.scene.children.filter( child => emissions.includes(child.name) )
        lights.forEach( light => light.material = portalLightMaterial )
        console.log(lights)
        //const portalLightMesh = gltf.scene.children.find((child) => child.name === 'portalLight')// Brunos solution for each light
    }
)
/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height, 0.1, 100)
camera.position.x = 4
camera.position.y = 2
camera.position.z = 4
scene.add(camera)

// Controls
const controls = new OrbitControls(camera, canvas)
controls.enableDamping = true

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias: true
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
renderer.outputEncoding = THREE.sRGBEncoding

/**
 * Animate
 */
const clock = new THREE.Clock()

const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    // Update controls
    controls.update()

    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()