仓库
https://gitee.com/laddish/threejs-plane-2-mountain/tree/master/
预览地址
效果
主要思路
displacementMap(通过图片黑白来确定高度),用的是下面这张图片
height.png 白色的地方高 黑色地方低 所以尽量保证 四个角都是黑的
alphaMap透明度map(本例用来淡化周围的山体)
alpha.png 白色的地方亮 黑色地方暗
中间亮周围暗
主要代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ThreeJS Starter</title>
</head>
<body>
<div class="container">
<h1>Take the Track</h1>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Vero repudiandae dolore inventore aliquam, maiores facilis.</p>
<button>Go Biking</button>
</div>
<canvas class="webgl"></canvas>
</body>
</html>
材质加载+创建材料
const loader = new THREE.TextureLoader();
const texture = loader.load("/mountain.jfif");
const height = loader.load("height.png");
const alpha = loader.load("alpha.png");
// Materials
const material = new THREE.MeshStandardMaterial({
color: "white",
map: texture,
displacementMap: height,
displacementScale: 0.6,
alphaMap: alpha,
transparent: true,
depthTest: false,
});
创建平面
const geometry = new THREE.PlaneBufferGeometry(3, 3, 64, 64);
// Mesh
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
plane.rotation.x = 181;
添加光照
const pointLight = new THREE.PointLight("#00a4ff", 2);
pointLight.position.x = 0.2;
pointLight.position.y = 10;
pointLight.position.z = 4.4;
scene.add(pointLight);
相机
/**
* Sizes
*/
const sizes = {
width: window.innerWidth*.7,
height: window.innerHeight,
};
window.addEventListener("resize", () => {
// Update sizes
sizes.width = window.innerWidth*.7;
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 = 0;
camera.position.y = 0;
camera.position.z = 4;
scene.add(camera);
动画
/**
* Animate
*/
document.addEventListener("mousemove", animateTerrain, false);
let mouseY = 0;
function animateTerrain(event) {
mouseY = event.clientY;
}
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime();
// Update objects
plane.rotation.z = 0.5 * elapsedTime;
plane.material.displacementScale = 1+mouseY*0.0003;
// Update Orbital Controls
// controls.update()
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();
import "./style.css";
import * as THREE from "three";
import * as dat from "dat.gui";
// Texture loader
const loader = new THREE.TextureLoader();
const texture = loader.load("/mountain.jfif");
const height = loader.load("height.png");
const alpha = loader.load("alpha.png");
// Debug
const gui = new dat.GUI();
// Canvas
const canvas = document.querySelector("canvas.webgl");
// Scene
const scene = new THREE.Scene();
// Objects
const geometry = new THREE.PlaneBufferGeometry(3, 3, 64, 64);
// Materials
const material = new THREE.MeshStandardMaterial({
color: "white",
map: texture,
displacementMap: height,
displacementScale: 0.6,
alphaMap: alpha,
transparent: true,
depthTest: false,
});
// Mesh
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
plane.rotation.x = 181;
//GUI
gui.add(plane.rotation, "x").min(0).max(200);
gui.add(plane.rotation, "y").min(0).max(200);
gui.add(plane.rotation, "z").min(0).max(200);
// Lights
const pointLight = new THREE.PointLight("#00a4ff", 2);
pointLight.position.x = 0.2;
pointLight.position.y = 10;
pointLight.position.z = 4.4;
scene.add(pointLight);
gui.add(pointLight.position, "x").min(0).max(200);
gui.add(pointLight.position, "y").min(0).max(200);
gui.add(pointLight.position, "z").min(0).max(200);
const col = { color: "#00ff00" };
gui.addColor(col, "color").onChange(() => {
pointLight.color.set(col.color);
});
/**
* Sizes
*/
const sizes = {
width: window.innerWidth*.7,
height: window.innerHeight,
};
window.addEventListener("resize", () => {
// Update sizes
sizes.width = window.innerWidth*.7;
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 = 0;
camera.position.y = 0;
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,
alpha: 1,
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
/**
* Animate
*/
document.addEventListener("mousemove", animateTerrain, false);
let mouseY = 0;
function animateTerrain(event) {
mouseY = event.clientY;
}
const clock = new THREE.Clock();
const tick = () => {
const elapsedTime = clock.getElapsedTime();
// Update objects
plane.rotation.z = 0.5 * elapsedTime;
plane.material.displacementScale = 1+mouseY*0.0003;
// Update Orbital Controls
// controls.update()
// Render
renderer.render(scene, camera);
// Call tick again on the next frame
window.requestAnimationFrame(tick);
};
tick();
style.css
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100vh;
font-family: 'Poppins';
background: rgba(27, 27, 31)
}
.webgl {
position: fixed;
top: 0;
right: 0;
outline: none;
}
.container {
position: absolute;
top: 0;
left: 0;
z-index: 1;
color: white;
margin: 16em 8em;
}
h1 {
font-size: 4rem;
;
}
p {
font-size: 1.3rem;
width: 55%;
color: rgb(107, 107, 122);
}
button {
font-size: 1.3rem;
padding: .5em 1em;
margin-top: 3em;
background-color: rgb(0, 114, 255);
border: none;
color: white;
font-weight: bold;
text-transform: uppercase;
}
GUI调试部分
需要安装包dat.gui
import * as dat from "dat.gui";
// Debug
const gui = new dat.GUI();
//GUI
gui.add(plane.rotation, "x").min(0).max(200);
gui.add(plane.rotation, "y").min(0).max(200);
gui.add(plane.rotation, "z").min(0).max(200);