three 实现噪声山脉地形模拟

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、CSDN优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:three实践
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

需求背景

在网上看到一个蛮好看的三维场景

思路

  1. simplex-noise 噪声算法

实现效果

noiseTerrain.vue

<template>
  <div class="noiseTerrain-wrap">

  </div>
</template>

<script lang="ts" setup>
import * as THREE from 'three';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls';
import {usethreeBoxStore} from "@/store/modules/threeBox"
import {onMounted, onUnmounted} from "vue";
import {createNoise2D} from 'simplex-noise';
import GUI from "lil-gui";

const noise2D = createNoise2D();

const threeBoxStore = usethreeBoxStore()
onMounted(() => {
  init()
  initGui()
})

onUnmounted(() => {
  gui.destroy()
  cancelAnimationFrame(timer)
  renderer.clear()
})


// 场景逻辑
let scene, meshBasicMaterial, geometry, mesh, axesHelper, camera, orbitControls, timer

const renderer = threeBoxStore.getRenderer()

const init = () => {
  // 创建场景scene
  scene = new THREE.Scene();

  geometry = new THREE.PlaneGeometry(3000, 3000, 100, 100);

  meshBasicMaterial = new THREE.MeshBasicMaterial({ // 基础网格材质,不受光照影响
    color: new THREE.Color('orange'),
    wireframe: true,
    side: THREE.DoubleSide
  });
  mesh = new THREE.Mesh(geometry, meshBasicMaterial);
  mesh.rotateX(Math.PI / 2); // 绕 x 轴旋转 90 度
  scene.add(mesh);

  // 添加坐标系工具 AxesHelper
  axesHelper = new THREE.AxesHelper(200);
  scene.add(axesHelper);

  // 创建透视相机
  camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.set(450, 150, 100);// 在200,200,200的位置
  camera.lookAt(0, 0, 0); // 看向 0,0,0

  // 创建轨道控制器 OrbitControls
  orbitControls = new OrbitControls(camera, renderer.domElement);
  orbitControls.addEventListener('change', () => {
    // console.log(camera.position)
  })

  const render = () => {
    if (formData.animation) {
      updatePosition()
      mesh.rotateZ(0.003)
    }
    renderer.render(scene, camera);
    timer = requestAnimationFrame(render);
    threeBoxStore.performanceState.update()
  }
  render()
}

const updatePosition = () => {
  const positions = geometry.attributes.position;
  for (let i = 0; i < positions.count; i++) {
    const x = positions.getX(i);
    const y = positions.getY(i);

    const z = noise2D(x / 300, y / 300) * 50;
    const sinNum = Math.sin(Date.now() * 0.002 + x * 0.05) * 10;

    positions.setZ(i, z + sinNum);
  }
  positions.needsUpdate = true; // 触发更新
}

const formDatachange = (k, v) => {
  let positions
  switch (k) {
    case "type":
      animationControl.hide()
      animationControl.setValue(false)
      formData.animation = false
      switch (v) {
        case "随机地形":
          positions = geometry.attributes.position;

          for (let i = 0; i < positions.count; i++) {
            positions.setZ(i, Math.random() * 50);
          }
          positions.needsUpdate = true
          break
        case "噪声地形":
          animationControl.show()
          positions = geometry.attributes.position;

          for (let i = 0; i < positions.count; i++) {
            const x = positions.getX(i);
            const y = positions.getY(i);
            // const z = noise2D(x / 100, y / 100) * 50;
            const z = noise2D(x / 300, y / 300) * 50;
            positions.setZ(i, z);
          }
          positions.needsUpdate = true
          break
      }
      mesh.geometry = geometry
      break
    case "axesHelper":
      v ? scene.add(axesHelper) : scene.remove(axesHelper)
      break
  }
}

// lil-gui逻辑
let gui, animationControl
const formData = {
  type: "",
  axesHelper: true,
  animation: false
}
const initGui = () => {
  gui = new GUI({title: "controls"});
  gui.add(formData, "axesHelper").onChange(axesHelper => formDatachange("axesHelper", axesHelper))
  gui.add(formData, "type", ["随机地形", "噪声地形"]).onChange(type => formDatachange("type", type))
  animationControl = gui.add(formData, "animation").hide()
}

</script>

<style lang="scss" scoped>
.noiseTerrain-wrap {

}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值