仓库
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");// Materialsconst 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);// Meshconst 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 sizessizes.width = window.innerWidth*.7;sizes.height = window.innerHeight;// Update cameracamera.aspect = sizes.width / sizes.height;camera.updateProjectionMatrix();// Update rendererrenderer.setSize(sizes.width, sizes.height);renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));});/*** Camera*/// Base cameraconst 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 objectsplane.rotation.z = 0.5 * elapsedTime;plane.material.displacementScale = 1+mouseY*0.0003;// Update Orbital Controls// controls.update()// Renderrenderer.render(scene, camera);// Call tick again on the next framewindow.requestAnimationFrame(tick);};tick();
import "./style.css";import * as THREE from "three";import * as dat from "dat.gui";// Texture loaderconst loader = new THREE.TextureLoader();const texture = loader.load("/mountain.jfif");const height = loader.load("height.png");const alpha = loader.load("alpha.png");// Debugconst gui = new dat.GUI();// Canvasconst canvas = document.querySelector("canvas.webgl");// Sceneconst scene = new THREE.Scene();// Objectsconst geometry = new THREE.PlaneBufferGeometry(3, 3, 64, 64);// Materialsconst material = new THREE.MeshStandardMaterial({color: "white",map: texture,displacementMap: height,displacementScale: 0.6,alphaMap: alpha,transparent: true,depthTest: false,});// Meshconst plane = new THREE.Mesh(geometry, material);scene.add(plane);plane.rotation.x = 181;//GUIgui.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);// Lightsconst 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 sizessizes.width = window.innerWidth*.7;sizes.height = window.innerHeight;// Update cameracamera.aspect = sizes.width / sizes.height;camera.updateProjectionMatrix();// Update rendererrenderer.setSize(sizes.width, sizes.height);renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));});/*** Camera*/// Base cameraconst 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 objectsplane.rotation.z = 0.5 * elapsedTime;plane.material.displacementScale = 1+mouseY*0.0003;// Update Orbital Controls// controls.update()// Renderrenderer.render(scene, camera);// Call tick again on the next framewindow.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";// Debugconst gui = new dat.GUI();//GUIgui.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);

