仓库

https://gitee.com/laddish/threejs-plane-2-mountain/tree/master/

预览地址

效果

右边这个 蓝色山体 旋转并且随着鼠标移动变化Three.js-创建一个自动转动的透明山体 - 图1

主要思路

  1. 创建一个平面
  2. 平面添加材质
  3. 渲染
  4. 添加动画

    亮点

    强大的THREE.MeshStandardMaterial
    可以添加
    map,添加材质
    mountain.jpg

displacementMap(通过图片黑白来确定高度),用的是下面这张图片
height.png 白色的地方高 黑色地方低 所以尽量保证 四个角都是黑的
height.png
alphaMap透明度map(本例用来淡化周围的山体)
alpha.png 白色的地方亮 黑色地方暗
alpha.png
中间亮周围暗

主要代码

index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>ThreeJS Starter</title>
  7. </head>
  8. <body>
  9. <div class="container">
  10. <h1>Take the Track</h1>
  11. <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Vero repudiandae dolore inventore aliquam, maiores facilis.</p>
  12. <button>Go Biking</button>
  13. </div>
  14. <canvas class="webgl"></canvas>
  15. </body>
  16. </html>

script.js

材质加载+创建材料

  1. const loader = new THREE.TextureLoader();
  2. const texture = loader.load("/mountain.jfif");
  3. const height = loader.load("height.png");
  4. const alpha = loader.load("alpha.png");
  5. // Materials
  6. const material = new THREE.MeshStandardMaterial({
  7. color: "white",
  8. map: texture,
  9. displacementMap: height,
  10. displacementScale: 0.6,
  11. alphaMap: alpha,
  12. transparent: true,
  13. depthTest: false,
  14. });

创建平面

  1. const geometry = new THREE.PlaneBufferGeometry(3, 3, 64, 64);
  2. // Mesh
  3. const plane = new THREE.Mesh(geometry, material);
  4. scene.add(plane);
  5. plane.rotation.x = 181;

添加光照

  1. const pointLight = new THREE.PointLight("#00a4ff", 2);
  2. pointLight.position.x = 0.2;
  3. pointLight.position.y = 10;
  4. pointLight.position.z = 4.4;
  5. scene.add(pointLight);

相机

  1. /**
  2. * Sizes
  3. */
  4. const sizes = {
  5. width: window.innerWidth*.7,
  6. height: window.innerHeight,
  7. };
  8. window.addEventListener("resize", () => {
  9. // Update sizes
  10. sizes.width = window.innerWidth*.7;
  11. sizes.height = window.innerHeight;
  12. // Update camera
  13. camera.aspect = sizes.width / sizes.height;
  14. camera.updateProjectionMatrix();
  15. // Update renderer
  16. renderer.setSize(sizes.width, sizes.height);
  17. renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  18. });
  19. /**
  20. * Camera
  21. */
  22. // Base camera
  23. const camera = new THREE.PerspectiveCamera(
  24. 75,
  25. sizes.width / sizes.height,
  26. 0.1,
  27. 100
  28. );
  29. camera.position.x = 0;
  30. camera.position.y = 0;
  31. camera.position.z = 4;
  32. scene.add(camera);

动画

  1. /**
  2. * Animate
  3. */
  4. document.addEventListener("mousemove", animateTerrain, false);
  5. let mouseY = 0;
  6. function animateTerrain(event) {
  7. mouseY = event.clientY;
  8. }
  9. const clock = new THREE.Clock();
  10. const tick = () => {
  11. const elapsedTime = clock.getElapsedTime();
  12. // Update objects
  13. plane.rotation.z = 0.5 * elapsedTime;
  14. plane.material.displacementScale = 1+mouseY*0.0003;
  15. // Update Orbital Controls
  16. // controls.update()
  17. // Render
  18. renderer.render(scene, camera);
  19. // Call tick again on the next frame
  20. window.requestAnimationFrame(tick);
  21. };
  22. tick();
  1. import "./style.css";
  2. import * as THREE from "three";
  3. import * as dat from "dat.gui";
  4. // Texture loader
  5. const loader = new THREE.TextureLoader();
  6. const texture = loader.load("/mountain.jfif");
  7. const height = loader.load("height.png");
  8. const alpha = loader.load("alpha.png");
  9. // Debug
  10. const gui = new dat.GUI();
  11. // Canvas
  12. const canvas = document.querySelector("canvas.webgl");
  13. // Scene
  14. const scene = new THREE.Scene();
  15. // Objects
  16. const geometry = new THREE.PlaneBufferGeometry(3, 3, 64, 64);
  17. // Materials
  18. const material = new THREE.MeshStandardMaterial({
  19. color: "white",
  20. map: texture,
  21. displacementMap: height,
  22. displacementScale: 0.6,
  23. alphaMap: alpha,
  24. transparent: true,
  25. depthTest: false,
  26. });
  27. // Mesh
  28. const plane = new THREE.Mesh(geometry, material);
  29. scene.add(plane);
  30. plane.rotation.x = 181;
  31. //GUI
  32. gui.add(plane.rotation, "x").min(0).max(200);
  33. gui.add(plane.rotation, "y").min(0).max(200);
  34. gui.add(plane.rotation, "z").min(0).max(200);
  35. // Lights
  36. const pointLight = new THREE.PointLight("#00a4ff", 2);
  37. pointLight.position.x = 0.2;
  38. pointLight.position.y = 10;
  39. pointLight.position.z = 4.4;
  40. scene.add(pointLight);
  41. gui.add(pointLight.position, "x").min(0).max(200);
  42. gui.add(pointLight.position, "y").min(0).max(200);
  43. gui.add(pointLight.position, "z").min(0).max(200);
  44. const col = { color: "#00ff00" };
  45. gui.addColor(col, "color").onChange(() => {
  46. pointLight.color.set(col.color);
  47. });
  48. /**
  49. * Sizes
  50. */
  51. const sizes = {
  52. width: window.innerWidth*.7,
  53. height: window.innerHeight,
  54. };
  55. window.addEventListener("resize", () => {
  56. // Update sizes
  57. sizes.width = window.innerWidth*.7;
  58. sizes.height = window.innerHeight;
  59. // Update camera
  60. camera.aspect = sizes.width / sizes.height;
  61. camera.updateProjectionMatrix();
  62. // Update renderer
  63. renderer.setSize(sizes.width, sizes.height);
  64. renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  65. });
  66. /**
  67. * Camera
  68. */
  69. // Base camera
  70. const camera = new THREE.PerspectiveCamera(
  71. 75,
  72. sizes.width / sizes.height,
  73. 0.1,
  74. 100
  75. );
  76. camera.position.x = 0;
  77. camera.position.y = 0;
  78. camera.position.z = 4;
  79. scene.add(camera);
  80. // Controls
  81. // const controls = new OrbitControls(camera, canvas)
  82. // controls.enableDamping = true
  83. /**
  84. * Renderer
  85. */
  86. const renderer = new THREE.WebGLRenderer({
  87. canvas: canvas,
  88. alpha: 1,
  89. });
  90. renderer.setSize(sizes.width, sizes.height);
  91. renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  92. /**
  93. * Animate
  94. */
  95. document.addEventListener("mousemove", animateTerrain, false);
  96. let mouseY = 0;
  97. function animateTerrain(event) {
  98. mouseY = event.clientY;
  99. }
  100. const clock = new THREE.Clock();
  101. const tick = () => {
  102. const elapsedTime = clock.getElapsedTime();
  103. // Update objects
  104. plane.rotation.z = 0.5 * elapsedTime;
  105. plane.material.displacementScale = 1+mouseY*0.0003;
  106. // Update Orbital Controls
  107. // controls.update()
  108. // Render
  109. renderer.render(scene, camera);
  110. // Call tick again on the next frame
  111. window.requestAnimationFrame(tick);
  112. };
  113. tick();

style.css

  1. * {
  2. margin: 0;
  3. padding: 0;
  4. }
  5. html,
  6. body {
  7. height: 100vh;
  8. font-family: 'Poppins';
  9. background: rgba(27, 27, 31)
  10. }
  11. .webgl {
  12. position: fixed;
  13. top: 0;
  14. right: 0;
  15. outline: none;
  16. }
  17. .container {
  18. position: absolute;
  19. top: 0;
  20. left: 0;
  21. z-index: 1;
  22. color: white;
  23. margin: 16em 8em;
  24. }
  25. h1 {
  26. font-size: 4rem;
  27. ;
  28. }
  29. p {
  30. font-size: 1.3rem;
  31. width: 55%;
  32. color: rgb(107, 107, 122);
  33. }
  34. button {
  35. font-size: 1.3rem;
  36. padding: .5em 1em;
  37. margin-top: 3em;
  38. background-color: rgb(0, 114, 255);
  39. border: none;
  40. color: white;
  41. font-weight: bold;
  42. text-transform: uppercase;
  43. }

GUI调试部分

需要安装包dat.gui

  1. import * as dat from "dat.gui";
  2. // Debug
  3. const gui = new dat.GUI();
  4. //GUI
  5. gui.add(plane.rotation, "x").min(0).max(200);
  6. gui.add(plane.rotation, "y").min(0).max(200);
  7. gui.add(plane.rotation, "z").min(0).max(200);