// 雾 开始应用雾的最小距离。距离小于活动摄像机“near”个单位的物体将不会被雾所影响。 结束计算、应用雾的最大距离,距离大于活动摄像机“far”个单位的物体将不会被雾所影响。
const fog = new THREE.Fog("#262837", 1, 15);
scene.fog = fog;
// 设置渲染器的背景颜色
renderer.setClearColor(fog.color);
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import GUI from "lil-gui";
/**
* Base
*/
// Debug
const gui = new GUI();
// Canvas
const canvas = document.querySelector("canvas.webgl");
// Scene
const scene = new THREE.Scene();
// 雾 开始应用雾的最小距离。距离小于活动摄像机“near”个单位的物体将不会被雾所影响。 结束计算、应用雾的最大距离,距离大于活动摄像机“far”个单位的物体将不会被雾所影响。
const fog = new THREE.Fog("#262837", 1, 15);
scene.fog = fog;
/**
* Textures
*/
const textureLoader = new THREE.TextureLoader();
const doorColorTexture = textureLoader.load("/textures/door/color.jpg");
const doorAlphaTexture = textureLoader.load("/textures/door/alpha.jpg");
const doorAmbientOcclusionTexture = textureLoader.load(
"/textures/door/ambientOcclusion.jpg"
);
const doorHeightTexture = textureLoader.load("/textures/door/height.jpg");
const doorNormalTexture = textureLoader.load("/textures/door/normal.jpg");
const doorMetalnessTexture = textureLoader.load("/textures/door/metalness.jpg");
const doorRoughnessTexture = textureLoader.load("/textures/door/roughness.jpg");
// doorColorTexture.colorSpace = THREE.SRGBColorSpace
const bricksColorTexture = textureLoader.load("/textures/bricks/color.jpg");
const bricksAmbientOcclusionTexture = textureLoader.load(
"/textures/bricks/ambientOcclusion.jpg"
);
const bricksNormalTexture = textureLoader.load("/textures/bricks/normal.jpg");
const bricksRoughnessTexture = textureLoader.load(
"/textures/bricks/roughness.jpg"
);
bricksColorTexture.colorSpace = THREE.SRGBColorSpace;
const grassColorTexture = textureLoader.load("/textures/grass/color.jpg");
const grassAmbientOcclusionTexture = textureLoader.load(
"/textures/grass/ambientOcclusion.jpg"
);
const grassNormalTexture = textureLoader.load("/textures/grass/normal.jpg");
const grassRoughnessTexture = textureLoader.load(
"/textures/grass/roughness.jpg"
);
grassColorTexture.colorSpace = THREE.SRGBColorSpace;
// 草地平铺
grassColorTexture.repeat.set(8, 8);
grassAmbientOcclusionTexture.repeat.set(8, 8);
grassNormalTexture.repeat.set(8, 8);
grassRoughnessTexture.repeat.set(8, 8);
grassColorTexture.wrapS = THREE.RepeatWrapping;
grassAmbientOcclusionTexture.wrapS = THREE.RepeatWrapping;
grassNormalTexture.wrapS = THREE.RepeatWrapping;
grassRoughnessTexture.wrapS = THREE.RepeatWrapping;
grassColorTexture.wrapT = THREE.RepeatWrapping;
grassAmbientOcclusionTexture.wrapT = THREE.RepeatWrapping;
grassNormalTexture.wrapT = THREE.RepeatWrapping;
grassRoughnessTexture.wrapT = THREE.RepeatWrapping;
const windowColorTexture = textureLoader.load("/textures/window/color.png");
windowColorTexture.colorSpace = THREE.SRGBColorSpace;
/**
* House
*/
// Group
const house = new THREE.Group();
scene.add(house);
//墙壁屋子宽4米,高2.5米,长4米
const walls = new THREE.Mesh(
new THREE.BoxGeometry(4, 2.5, 4),
new THREE.MeshStandardMaterial({
// color: '#ac8e82'
map: bricksColorTexture,
aoMap: bricksAmbientOcclusionTexture,
aoMapIntensity: 2,
normalMap: bricksNormalTexture,
roughnessMap: bricksRoughnessTexture,
})
);
walls.geometry.setAttribute(
"uv2",
new THREE.Float32BufferAttribute(walls.geometry.attributes.uv.array, 2)
);
walls.position.y = 2.5 / 2;
house.add(walls);
// 屋顶
const roof = new THREE.Mesh(
new THREE.ConeGeometry(4, 2, 4),
new THREE.MeshStandardMaterial({
color: "#b35f45",
})
);
roof.position.y = 2.5 + 1;
roof.rotation.y = Math.PI * 0.25;
house.add(roof);
// 门
const door = new THREE.Mesh(
new THREE.PlaneGeometry(2.2, 2.2, 100, 100),
new THREE.MeshStandardMaterial({
// color: '#ff7d67'
map: doorColorTexture,
alphaMap: doorAlphaTexture,
transparent: true,
aoMap: doorAmbientOcclusionTexture, // 环境遮挡贴图,显示阴影部分
aoMapIntensity: 2,
displacementMap: doorHeightTexture, //位移贴图,显示高度
displacementScale: 0.1,
normalMap: doorNormalTexture, //法线贴图,显示高光部分
metalnessMap: doorMetalnessTexture, // 金属贴图,显示金属部分
// metalness: 0.45,
roughnessMap: doorRoughnessTexture, // 粗糙度贴图,显示非金属部分
// roughness: 0.65,
})
);
// door.material.aoMapIntensity = 2; //环境遮挡效果的强度。默认值为1。零是不遮挡效果。
door.geometry.setAttribute(
"uv2",
new THREE.Float32BufferAttribute(door.geometry.attributes.uv.array, 2)
);
door.position.y = 1;
door.position.z = 2 + 0.01; // Slightly in front of the walls to avoid z-fighting
house.add(door);
//灌木
const bushGeometry = new THREE.SphereGeometry(1, 16, 16);
const bushMaterial = new THREE.MeshStandardMaterial({ color: "#89c854" });
const bush1 = new THREE.Mesh(bushGeometry, bushMaterial);
bush1.scale.set(0.5, 0.5, 0.5);
bush1.position.set(0.8, 0.2, 2.2);
const bush2 = new THREE.Mesh(bushGeometry, bushMaterial);
bush2.scale.set(0.25, 0.25, 0.25);
bush2.position.set(1.4, 0.1, 2.1);
const bush3 = new THREE.Mesh(bushGeometry, bushMaterial);
bush3.scale.set(0.4, 0.4, 0.4);
bush3.position.set(-0.8, 0.1, 2.2);
const bush4 = new THREE.Mesh(bushGeometry, bushMaterial);
bush4.scale.set(0.15, 0.15, 0.15);
bush4.position.set(-1.25, 0.05, 2.2);
house.add(bush1, bush2, bush3, bush4);
// 墓园
const graves = new THREE.Group();
scene.add(graves);
for (let i = 0; i < 50; i++) {
const angle = Math.random() * Math.PI * 2;
const radius = 3 + Math.random() * 6;
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
const grave = new THREE.Mesh(
new THREE.BoxGeometry(0.6, 0.8, 0.2),
new THREE.MeshStandardMaterial({ color: "#b2b6b1" })
);
grave.position.set(x, 0.3, z);
grave.rotation.y = (Math.random() - 0.5) * 0.4;
grave.rotation.z = (Math.random() - 0.5) * 0.4;
grave.castShadow = true;
graves.add(grave);
}
//门灯
const doorLight = new THREE.PointLight("#ff7d67", 1, 7);
doorLight.position.set(0, 2.2, 2.7);
house.add(doorLight);
// 窗户
const planeGeometry = new THREE.PlaneGeometry(1, 1)
const windowMaterial = new THREE.MeshStandardMaterial({
transparent: true,
map:windowColorTexture
})
// 右边
const window1 = new THREE.Mesh(planeGeometry, windowMaterial)
window1.position.y = 1.25
window1.position.z = 0
window1.position.x = 2 + 0.01
window1.rotation.y = Math.PI / 2
// 左边
const window2 = new THREE.Mesh(planeGeometry, windowMaterial)
window2.position.y = 1.25
window2.position.z = 0
window2.position.x = -2.01
window2.rotation.y = Math.PI * 1.5
// 后边
const window3 = new THREE.Mesh(planeGeometry, windowMaterial)
window3.position.y = 1.25
window3.position.z = -2.01
window3.position.x = 0
window3.rotation.y = Math.PI
house.add(window1, window2, window3)
//鬼魂
const ghost1 = new THREE.PointLight("#ff00ff", 2, 3);
ghost1.position.set(0, 1, 0);
ghost1.castShadow = true;
scene.add(ghost1);
const ghost2 = new THREE.PointLight("#00ffff", 2, 3);
ghost2.position.set(4, 1, 0);
ghost2.castShadow = true;
scene.add(ghost2);
const ghost3 = new THREE.PointLight("#ffff00", 2, 3);
ghost3.position.set(-4, 1, 0);
ghost3.castShadow = true;
scene.add(ghost3);
// Floor
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({
// color: "#a9c388"
map: grassColorTexture,
aoMap: grassAmbientOcclusionTexture,
aoMapIntensity: 2,
normalMap: grassNormalTexture,
roughnessMap: grassRoughnessTexture,
})
);
floor.geometry.setAttribute(
"uv2",
new THREE.Float32BufferAttribute(floor.geometry.attributes.uv.array, 2)
);
floor.rotation.x = -Math.PI * 0.5;
floor.position.y = 0;
scene.add(floor);
/**
* Lights
*/
// Ambient light
const ambientLight = new THREE.AmbientLight("#b9d5ff", 0.12);
gui.add(ambientLight, "intensity").min(0).max(1).step(0.001);
scene.add(ambientLight);
// Directional light
const moonLight = new THREE.DirectionalLight("#b9d5ff", 0.6);
moonLight.position.set(4, 5, -2);
gui.add(moonLight, "intensity").min(0).max(1).step(0.001);
gui.add(moonLight.position, "x").min(-5).max(5).step(0.001);
gui.add(moonLight.position, "y").min(-5).max(5).step(0.001);
gui.add(moonLight.position, "z").min(-5).max(5).step(0.001);
scene.add(moonLight);
/**
* 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(
75,
sizes.width / sizes.height,
0.1,
100
);
camera.position.x = 4;
camera.position.y = 2;
camera.position.z = 5;
scene.add(camera);
// Controls
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
/**
* Renderer
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// 设置渲染器的背景颜色
renderer.setClearColor(fog.color);
// 开启阴影
renderer.shadowMap.enabled = true;
floor.receiveShadow = true;
moonLight.castShadow = true;
for (let index = 0; index < house.children.length; index++) {
const element = house.children[index];
element.castShadow = true;
}
// 阴影优化
doorLight.shadow.mapSize.width = 256
doorLight.shadow.mapSize.height = 256
doorLight.shadow.camera.far = 7
moonLight.shadow.mapSize.width = 256
moonLight.shadow.mapSize.height = 256
moonLight.shadow.camera.far = 15
ghost1.shadow.mapSize.width = 256
ghost1.shadow.mapSize.height = 256
ghost1.shadow.camera.far = 7
ghost2.shadow.mapSize.width = 256
ghost2.shadow.mapSize.height = 256
ghost2.shadow.camera.far = 7
ghost3.shadow.mapSize.width = 256
ghost3.shadow.mapSize.height = 256
ghost3.shadow.camera.far = 7
/**
* Animate
*/
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime();
// 鬼魂移动
ghost1.position.x = Math.cos(elapsedTime * 0.5) * 4;
ghost1.position.z = Math.sin(elapsedTime * 0.5) * 4;
ghost1.position.y = Math.sin(elapsedTime * 3.5) * 1 + 1;
ghost2.position.x = Math.cos(elapsedTime * 0.5) * 5;
ghost2.position.z = Math.sin(elapsedTime * 0.5) * 5;
ghost2.position.y = Math.sin(elapsedTime * 4) * 1 + 1;
ghost3.position.x = Math.cos(elapsedTime * 0.5) * 3;
ghost3.position.z = Math.sin(elapsedTime * 0.5) * 3;
ghost3.position.y = Math.sin(elapsedTime * 3) * 1 + 1;
// Update controls
controls.update();
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();