使用 ThreeJS + DEM 高程 + GeoJSON + 影像贴图实现真实区域三维模型预览

使用three.js/webgl实现简易地形 在three.js中使用mapbox地形瓦片的方法 阅读详情

在数字孪生、智慧城市、GIS 可视化项目中,经常需要将:

  • DEM 高程数据
  • GeoJSON 行政区
  • 卫星影像

融合成一个真实三维区域模型。

本文将完整介绍如何使用 Three.js 实现:

真实地形
+ 行政区裁切
+ 贴地边界线
+ 卫星影像
+ 三维区域模型

最终效果类似:

  • Cesium 区域裁切
  • GISBox 区域模型
  • 数字孪生地图

一、最终效果


鼠标移入区域地块抬升

1. DEM 地形

通过高程数据抬升网格:平面 -> 山地地形

2. GeoJSON 行政区裁切

支持:

  • 多边形
  • 多区域
  • 洞(holes)

3. 影像贴图

支持:

  • 卫星影像
  • 正射影像
  • 自定义底图

4. 贴地边界高亮

实现:真正贴地行政区边界

避免:

  • 漂浮
  • 穿模
  • 深度闪烁

二、实现代码

1.主入口代码

<script setup>
import { onMounted, onUnmounted, ref, watch } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { MOUSE, TOUCH } from 'three'
import { loadDemTiles } from './lib/dem/demLoader.js'
import { createGlobalBounds, getWorldSize } from './lib/geo/world.js'
import {
  getTerrainBaseZ,
  normalizeRegions,
} from './lib/three/boundary.js'
import { createResourceTracker } from './lib/three/resourceTracker.js'
import { buildRegionModel } from './lib/three/regionMesh.js'
import gsap from 'gsap'

const TERRAIN_CONFIG = {
  segments: 160,
  worldWidth: 12,
  heightScale: 0.0001,
  solidThickness: 0.18,
  outlineOffset: 0.01,
  outlineColor: 0x00ffff,
  outlineOpacity: 0.92,
  boundaryMaskSize: 2048,
  colorMinElevation: 0,
  colorMaxElevation: 4200,
}

const DATA_URLS = {
  geoJson: '/data/json/bj.json',
  imagery: '/data/bj-map.png',
  demTiles: [
    '/data/tif/ASTGTM2_N33E106_dem.tif',
    '/data/tif/ASTGTM2_N33E107_dem.tif',
    '/data/tif/ASTGTM2_N34E106_dem.tif',
    '/data/tif/ASTGTM2_N34E107_dem.tif',
    '/data/tif/ASTGTM2_N34E108_dem.tif',
    '/data/tif/ASTGTM2_N35E106_dem.tif',
    '/data/tif/ASTGTM2_N35E107_dem.tif',
  ],
}

const IMAGERY_TRANSFORM = {
  scaleX: 1,
  scaleY: 1,
  offsetX: 0,
  offsetY: 0,
  repeat: false,
}

const sceneContainerRef = ref(null)
const loadingText = ref('准备加载数据...')
const errorText = ref('')
const tileCount = ref(0)
const imageryScaleX = ref(IMAGERY_TRANSFORM.scaleX)
const imageryScaleY = ref(IMAGERY_TRANSFORM.scaleY)
const imageryOffsetX = ref(IMAGERY_TRANSFORM.offsetX)
const imageryOffsetY = ref(IMAGERY_TRANSFORM.offsetY)
const imageryRepeat = ref(IMAGERY_TRANSFORM.repeat)

let scene
let camera
let renderer
let controls
let animationFrameId = 0
let resources = createResourceTracker()
let imageryTextureRef = null
const terrainUvRecords = []
const hitMeshes = []
let raycaster
let mouse
let regionsGroup

const handlePointerMove = (event) => {
  if (!sceneContainerRef.value || !camera || !regionsGroup) return

  const rect = sceneContainerRef.value.getBoundingClientRect()
  mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
  mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1

  raycaster.setFromCamera(mouse, camera)
  const intersects = raycaster.intersectObjects(hitMeshes, false)

  let hoveredGroup = null
  if (intersects.length > 0) {
    hoveredGroup = intersects[0].object.userData.regionGroup
  }

  regionsGroup.children.forEach(group => {
    if (group.userData.isRegionGroup) {
      const isHovered = (group === hoveredGroup)
      const targetZ = isHovered ? 0.15 : 0
      
      if (group.position.z !== targetZ) {
        gsap.to(group.position, {
          z: targetZ,
          duration: 0.3,
          ease: "power2.out"
        })
      }
    }
  })
}

const applyCurrentImageryTransform = () => {
  if (!imageryTextureRef) {
    return
  }

  imageryTextureRef.wrapS = imageryRepeat.value ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping
  imageryTextureRef.wrapT = imageryRepeat.value ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping
  imageryTextureRef.needsUpdate = true

  for (const record of terrainUvRecords) {
    const uvAttribute = record.geometry.getAttribute('uv1')
    const target = uvAttribute.array
    const source = record.globalUvBase

    for (let index = 0; index < source.length; index += 2) {
      const u = source[index]
      const v = source[index + 1]
      target[index] = (u - 0.5) * imageryScaleX.value + 0.5 + imageryOffsetX.value
      target[index + 1] = (v - 0.5) * imageryScaleY.value + 0.5 + imageryOffsetY.value
    }

    uvAttribute.needsUpdate = true
  }
}

watch([imageryScaleX, imageryScaleY, imageryOffsetX, imageryOffsetY, imageryRepeat], () => {
  applyCurrentImageryTransform()
})

const disposeScene = () => {
  cancelAnimationFrame(animationFrameId)
  animationFrameId = 0

  if (sceneContainerRef.value) {
    sceneContainerRef.value.removeEventListener('pointermove', handlePointerMove)
  }

  if (controls) {
    controls.dispose()
    controls = null
  }

  resources.dispose()
  resources = createResourceTracker()
  imageryTextureRef = null
  terrainUvRecords.splice(0, terrainUvRecords.length)
  hitMeshes.splice(0, hitMeshes.length)
  regionsGroup = null

  if (renderer) {
    renderer.dispose()
    renderer.forceContextLoss?.()
    renderer.domElement.remove()
    renderer = null
  }

  if (scene) {
    scene.clear()
    scene = null
  }
}

const handleResize = () => {
  if (!renderer || !camera || !sceneContainerRef.value) {
    return
  }

  const { clientWidth, clientHeight } = sceneContainerRef.value
  camera.aspect = clientWidth / clientHeight
  camera.updateProjectionMatrix()
  renderer.setSize(clientWidth, clientHeight)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
}

const animate = () => {
  animationFrameId = window.requestAnimationFrame(animate)
  controls?.update()
  renderer?.render(scene, camera)
}

const initScene = () => {
  const container = sceneContainerRef.value
  if (!container) {
    throw new Error('未找到渲染容器')
  }

  scene = new THREE.Scene()
  scene.background = new THREE.Color('#07111d')
  scene.fog = new THREE.Fog('#07111d', 12, 28)

  camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 0.1, 200)
  camera.up.set(0, 0, 1)
  camera.position.set(-7.5, -9.5, 8.5)
  camera.lookAt(0, 0, 0)

  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(container.clientWidth, container.clientHeight)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  container.appendChild(renderer.domElement)

  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true
  controls.enableRotate = true
  controls.screenSpacePanning = true
  controls.target.set(0, 0, 0.8)
  controls.minDistance = 4
  controls.maxDistance = 30
  controls.minPolarAngle = 0.18
  controls.maxPolarAngle = Math.PI / 2.02
  controls.mouseButtons = {
    LEFT: MOUSE.ROTATE,
    MIDDLE: MOUSE.DOLLY,
    RIGHT: MOUSE.PAN,
  }
  controls.touches = {
    ONE: TOUCH.ROTATE,
    TWO: TOUCH.DOLLY_PAN,
  }

  raycaster = new THREE.Raycaster()
  mouse = new THREE.Vector2()
  container.addEventListener('pointermove', handlePointerMove)

  scene.add(new THREE.AmbientLight(0xffffff, 1.65))

  const keyLight = new THREE.DirectionalLight(0xffffff, 2.6)
  keyLight.position.set(7, -6, 10)
  scene.add(keyLight)

  const rimLight = new THREE.DirectionalLight(0x9cc7ff, 1.2)
  rimLight.position.set(-8, 10, 7)
  scene.add(rimLight)
}

const renderTerrain = async () => {
  errorText.value = ''
  tileCount.value = 0
  disposeScene()
  initScene()

  loadingText.value = '正在加载行政区划...'
  const geoResponse = await fetch(DATA_URLS.geoJson)
  if (!geoResponse.ok) {
    throw new Error('加载 GeoJSON 失败')
  }
  const geoJson = await geoResponse.json()
  const regions = normalizeRegions(geoJson)

  loadingText.value = '正在加载 DEM 数据...'
  const demTiles = await loadDemTiles(DATA_URLS.demTiles, {
    onProgress: ({ phase, name, index, total }) => {
      const label = phase === 'fetch' ? '下载' : phase === 'raster' ? '解析' : '完成'
      loadingText.value = `正在${label} ${name} (${index + 1}/${total})`
    },
  })

  const sceneBounds = createGlobalBounds(demTiles)
  const textureBounds = createGlobalBounds(regions)
  
  const worldSize = getWorldSize(sceneBounds, TERRAIN_CONFIG.worldWidth)

  loadingText.value = '正在加载影像...'
  const imageryTexture = resources.track(await new THREE.TextureLoader().loadAsync(DATA_URLS.imagery))
  imageryTexture.colorSpace = THREE.SRGBColorSpace
  imageryTexture.flipY = false
  imageryTexture.channel = 1
  imageryTexture.needsUpdate = true
  imageryTextureRef = imageryTexture

  regionsGroup = new THREE.Group()
  const terrainTiles = demTiles
  const baseZ = getTerrainBaseZ(terrainTiles, TERRAIN_CONFIG.heightScale, TERRAIN_CONFIG.solidThickness)

  for (const region of regions) {
    const { group, hitMesh, uvRecord } = buildRegionModel(region, {
      globalBounds: textureBounds,
      worldSize,
      terrainTiles,
      imageryTexture,
      imageryTransform: {
        scaleX: imageryScaleX.value,
        scaleY: imageryScaleY.value,
        offsetX: imageryOffsetX.value,
        offsetY: imageryOffsetY.value,
      },
      heightScale: TERRAIN_CONFIG.heightScale,
      solidThickness: TERRAIN_CONFIG.solidThickness,
      outlineOffset: TERRAIN_CONFIG.outlineOffset,
      outlineColor: TERRAIN_CONFIG.outlineColor,
      outlineOpacity: TERRAIN_CONFIG.outlineOpacity,
      baseZ,
      segments: TERRAIN_CONFIG.segments,
      track: resources.track,
    })

    regionsGroup.add(group)
    hitMeshes.push(hitMesh)
    terrainUvRecords.push(uvRecord)
    tileCount.value += 1
    loadingText.value = `已完成 ${tileCount.value}/${regions.length} 个区域`
  }

  applyCurrentImageryTransform()

  scene.add(regionsGroup)

  loadingText.value = `地形加载完成,共渲染 ${regions.length} 个区域`
  animate()
}

onMounted(async () => {
  try {
    await renderTerrain()
    window.addEventListener('resize', handleResize)
  } catch (error) {
    errorText.value = error instanceof Error ? error.message : '地形初始化失败'
  }
})

onUnmounted(() => {
  window.removeEventListener('resize', handleResize)
  disposeScene()
})
</script>

<template>
  <main class="app-shell">
    <section class="viewer-panel">
      <div ref="sceneContainerRef" class="scene-container"></div>

      <div class="status-card">
        <p class="status-card__label">加载状态</p>
        <p>{{ errorText || loadingText }}</p>
      </div>

      <div v-if="errorText" class="error-card">
        <strong>场景初始化失败</strong>
        <p>{{ errorText }}</p>
      </div>
    </section>
  </main>
</template>
2.dem高程
import { fromArrayBuffer } from 'geotiff'

export async function loadDemTiles(paths, { onProgress } = {}) {
  const tiles = []

  for (let index = 0; index < paths.length; index += 1) {
    const path = paths[index]
    const name = path.split('/').pop()
    onProgress?.({ phase: 'fetch', name, index, total: paths.length })

    const response = await fetch(path)
    if (!response.ok) {
      throw new Error(`加载 DEM 失败: ${name}`)
    }

    const arrayBuffer = await response.arrayBuffer()
    const tiff = await fromArrayBuffer(arrayBuffer)
    const image = await tiff.getImage()
    const width = image.getWidth()
    const height = image.getHeight()
    const imageBounds = image.getBoundingBox()
    const pixelWidth = (imageBounds[2] - imageBounds[0]) / width
    const pixelHeight = (imageBounds[3] - imageBounds[1]) / height
    const bounds = {
      // GeoTIFF bbox describes the raster's outer extent. Shrink by half a pixel
      // so adjacent tiles meet at pixel centers instead of overlapping.
      minLon: imageBounds[0] + pixelWidth / 2,
      minLat: imageBounds[1] + pixelHeight / 2,
      maxLon: imageBounds[2] - pixelWidth / 2,
      maxLat: imageBounds[3] - pixelHeight / 2,
    }

    onProgress?.({ phase: 'raster', name, index, total: paths.length })

    const raster = await image.readRasters({ interleave: true, samples: [0] })
    const elevation = raster instanceof Float32Array ? raster : new Float32Array(raster)

    tiles.push({
      path,
      name,
      bounds,
      width,
      height,
      elevation,
    })

    onProgress?.({ phase: 'done', name, index, total: paths.length })
  }

  return tiles
}

3.代码太多自己上github看吧https://github.com/hzc12345678/MountainRiver3D.git。记得自己找相应的tif高程数据、json行政区数据和影像贴图,代码中没有高程数据其他都有。

Qoder Desktop:面向中文开发者的本地Agent运行时 Agent(智能体)是大模型落地工程化的核心范式,其本质是将任务规划、工具调用与模型推理闭环整合的自主执行系统。Qwen3.7-Max作为专为代码与工具调用优化的开源大模型,凭借轻量级参数与强结构化输出能力,成为本地Agent的理想推理引擎。Qoder Desktop并非简单UI套壳,而是深度适配Qwen系列的本地Agent运行时——它内置任务分解器、工具调度器与空间计算工具集(如Three.js遥感可视化),支持离线部署、编辑器上下文感知与插件化技能扩展。典型应用场景涵盖遥感三维可视化、VS Code自动 阅读详情

相关推荐

three3D地图

基于Vue实现Three.js+GeoJSON三维可视化炫酷地图(省市标签、地图流光、动态文字、高光定位、星空背景)

木木开发屋的博客 6549

Threejs GeoJSON离线3D地图

<img src="https://img-bss.csdn.net/201904081438008300.jpg" alt="" /><img src="https://img-bss.csdn.net/201904081438077931.jpg" alt="" /><img src="https://img-bss.csdn.net/201904081438242679.gif" alt="" />

Threejs项目实战之三:根据JSON格式数据创建三维地图

使用threejs根据json格式地图数据创建三维地图,并实现鼠标交互

九仞山的专栏 6804

全开源数字孪生系统搭建方案:全套技术文档

本文介绍了一套全开源、零商用授权的数字孪生系统搭建方案,覆盖从三维建模到Web端渲染的全流程技术栈。方案包含七大核心模块:三维建模(Blender、QGIS、IfcOpenShell)、数据传输、数据存储与仿真、Web3D渲染引擎、前端可视化及容器化部署。重点推荐Blender作为通用建模工具,QGIS处理地理信息数据,IfcOpenShell解析BIM模型。整套方案采用Vue3、Three.js、ECharts等技术,支持MQTT物联网协议和Docker部署,适用于智慧园区、工业产线等场景,具有零成本、可

深耕 Java 后端与企业级软件架构设计,专注微服务、分布式系统、大模型 AI 应用架构落地。 1188

Three.js实现逼真的3D地形与水体效果

Three.js 是一个基于WebGL的JavaScript库,它简化了在浏览器中创建和显示3D图形的过程。Three.js 凭借其简洁的API和丰富的功能集合,在WebGL之上建立了一个更加容易理解和操作的3D图形层。将理论知识应用到实际的3D项目中时,需要考虑地形的规模、细节级别和性能优化等多个方面。例如,在一个大型的开放世界游戏中,可能需要生成数公里范围的复杂地形,同时要保持稳定的帧率和流畅的用户交互体验。这不仅需要精心设计的地形生成逻辑,还需要灵活运用各种优化技术。创建基础网格,使用。

weixin_42181686的博客 1456

threejs 中国地图轮廓动画geojson 版本

threejs 中国地图轮廓动画geojson 版本

Three.js+GeoJSON实现三维地图显示

Three+GeoJSON实现对地理坐标转换并生成中国三维地图。

damadashen的博客 1万+

ShanHeShui:使用threejs的地形和水

山河水 使用threejs的地形和水

【THREE.JS学习(3)】使用THREEJS加载GeoJSON地图数据

Three.js中的坐标系是以单位为米(m)的坐标系,而在地理数据中,如Geojson使用的是经纬度作为坐标系,所以在使用、加载的过程中需要对数据进行坐标转换,才能够正确的显示地理数据。和mapbox、cesium之类的webgis加载数据不同(原理差不多),不能直接加载json数据,然后直接显示,需要我们对Json数据进行解析,然后按照一定的方式来生成图像。其中,{OrbitControls}为控制器,加载后可以通过鼠标来移动加载数据的方向、放缩等。接着,对加载的文件进行处理。STEP 3:开始渲染。

yuelizhe4774的博客 6456

告别Modern Standby噩梦:Win11睡眠断网冻结的根源剖析与S3睡眠回归实战

本文深入剖析了Windows 11中Modern Standby(现代待机)模式导致睡眠后断网、系统冻结等问题的根源,指出其与硬件驱动、后台活动的兼容性矛盾。文章提供了通过修改注册表键值强制系统回归稳定S3睡眠状态的详细实战步骤,帮助用户彻底解决睡眠故障,实现可靠的远程访问与挂机任务。

weixin_29210727的博客 609

鸿蒙OS开机动画背后的秘密:从代码到视觉的完整解析(附源码分析)

本文深入解析了鸿蒙OS开机动画的系统级实现原理。从bootanimation服务的启动配置、与Render Service的协同,到基于VSync信号的帧率同步与绘制流水线,结合OpenHarmony源码进行了逐行解读。文章揭示了其事件驱动的优雅退出机制,并探讨了视频支持、多屏适配等高级特性与定制化可能,为开发者理解鸿蒙图形子系统提供了硬核技术视角。

sony5的博客 974

Three.js(5):基于geotiff.js与DEM生成三维地形场景

1、DEM数据准备 2、引入geotiff库 npm i geotiff 3、使用geotiff库读取DEM数据 import * as GeoTIFF from "geotiff"; const tif = await GeoTIFF.fromUrl("hjq/feature/dem/dem1_108_100_84_clip2.tif"); const tifImage = await tif.getImage(); const image = { width: tifImage

碰碰的博客 5012

手把手教你用EGM96模型校正DEM数据(含在线工具推荐)

本文详细介绍了如何使用EGM96地球重力场模型校正DEM数据中的高程偏差。通过对比WGS84椭球面与大地水准面的差异,解释了校正原理,并提供了从在线工具快速验证到Python脚本批量处理的全流程实战指南,帮助GIS开发者和数据分析师确保高程数据基准一致,提升空间分析精度。

ttt77的博客 1008

Three.js重建真实地形【教程】

在这个教程中,我们将学习如何搭建一个3D场景,如何编写GLSL程序并使用Vertex Shader和Fragment Shader重建澳洲著名的Ululu巨岩附近的真实地形。为了方便起见,我们将使用Three.js和React来编写本教程中的示例代码。 0、飞行模拟器 你可能知道微软去年8月发布的游戏 Flight Simulator,如果你和我一样对飞行模拟器或在计算机中重建现实生活中场景,可能已经花了很多时间敬畏地注意到这个游戏中的那些惊人的细节。 Terrain in Microsoft Fligh

新缸中之脑 7010

HA集群 执行 hdfs haadmin -transitionToActive 失败

最近把一个单NameNode集群变更为HA集群,过程中都很顺利,nn1和nn2两个NameNode都显示standby状态,但是把nn1或者nn2切换到active状态时就会报错抛异常。 查看master上namenode启动日志,如下: Incompatible clusterID for journal Storage Directory /home/hadoop-zero/bigdat...

qq_34727158的博客 1764

欧姆龙485通讯示例程序_【精品实验】PLC学习神器与温湿度变送器的通讯

BFH点击蓝字关注我们↑↑↑↑  PLC学习神器提供RS485接口,可支持无协议RS指令、无协议RS2指令、Modbus通讯功能,可通过设置通讯格式改变通讯协议,RS485对应的通讯格式地址是D8400;  本案例以RS485接口与温湿度变送器通讯,通讯格式如下一温湿度变送器的说明书介绍1.1主要技术参数1.2 通讯协议 1、所有通讯回路都应遵照主/从方式。依照主从方式,数据可以在一个主...

weixin_39860201的博客 1万+

threejs 山脉地形高度可视化

本节课算是一个练习题,用到的知识点前面几节都将讲解过,所以视频主要把思路给大家说一遍,然后大家根据课程课程思路自己手写一遍。一个山脉地形的高度可视化,具体说就是地形不同的高度设置不同的颜色值。有多种方式,下面就举一个设置顶点颜色。,计算山脉不同高度位置点的颜色。借助颜色对象的颜色插值方法。

u014291990的博客 1240

华为2024机考真题集合

2.中位数是指把一组数据从小到大排列,最中间的那个数,如果这组数据的个数是奇数,那最中间那个就是中位数,如果这组数据的个数为偶数,那就把中间的两个数之和除以 2,所得的结果就是中位数。在实际考试中会随机抽三道题目进行考察,其中两道100分卷题和一道200分卷题,总共400分。幼儿园组织活动,老师布置了一个任务:每个小朋友去了解与自己同一个小区的小朋友还有几个。3.查找整型数组中元素的众数并组成一个新的数组,求新数组的中位数。1.众数是指一组数据中出现次数量多的那个数,众数可以是多个。

qq_25360769的博客 5093

[原]Three.js的绘制流程(三)----地形

最简单的地形是单一的平面, 这个通过 var geo = new THREE.PlaneGeometry(2, 2, 256, 256) 几何体构建, 可以设定平面的切分块的数量。 var pmesh = new THREE.Mesh(geo, material); 可以为平面提供纹理, 从而是地面看起来更真实一些,而纹理坐标在geo中已经自动设定好了。 因此...

weixin_30642561的博客 659

CentOS7下快速修复libssl.so.1.1缺失问题(附腾讯云镜像加速下载)

本文详细介绍了在CentOS7系统中快速修复libssl.so.1.1缺失问题的工程化实践方案。通过编译安装OpenSSL 1.1.x版本、配置动态链接器及创建符号链接等步骤,有效解决共享对象文件缺失问题,并提供了腾讯云镜像加速下载的优化方案,帮助开发者高效部署现代应用。

weixin_33711647的博客 370

基于Simulink的BCH码编解码仿真建模示例

BCH码是一类循环码,能够纠正一定数量的随机错误。灵活性:可以根据需要选择不同的码长和纠错能力。高效性:适用于短码长和中等码长的场景,广泛用于卫星通信、存储系统等。实现简单:编码和解码过程可以通过硬件或软件高效实现。在本示例中,我们将使用MATLAB/Simulink中的和模块来完成BCH码的编解码操作,并通过仿真验证其误码率(BER)性能。通过本指南,我们介绍了如何基于Simulink搭建一个BCH码的编解码模型,并进行了仿真以验证其性能。背景介绍:理解BCH码的基本原理及其在通信系统中的应用。

xiaoheshang_123的博客 191

ThreeJS-geoJson-开发记录

使用墨卡托投影将geoJson经纬度数据转为平面坐标数据,进而使用threejs ExtrudeGeometry生成所示地图三维模型 主要技术点 三维渲染 threejs 数据转换 d3-geo 变换代码 const projection = geoMercator().center([104.0, 37.5]).scale(36).translate([0, 0]); center: 中心经纬度 scale: 转换后坐标缩放系数 translate:转换后平移量 功能点 简单光效 自主材质调节(演

yichen454的博客 2685

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

运用threejs渲染地形

使用threejs渲染地形;

白菜博客 1178

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

【04】基于 Three.js 的 3D 地形代码解析(webgl_geometry_terrain_raycast.html)

摘要:本文将对一段使用 Three.js 库构建的 3D 地形场景相关代码进行详细解析,涵盖从基础的场景初始化、对象创建,到交互逻辑以及纹理生成等多个方面,旨在帮助读者更好地理解 Three.js 在创建复杂 3D 地形场景中的应用。Three.js 是一个功能强大且广泛应用于 Web 端 3D 图形开发的 JavaScript 库,它提供了丰富的 API 来创建各种绚丽的 3D 场景、动画以及交互效果。接下来我们要解析的这段代码就是利用 Three.js 构建了一个特定的 3D 场景,并实现了一些交互功能

Jiaberrr的博客 1506

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

three.js通过GEO数据生成3D地图

地图下载 https://datav.aliyun.com/portal/school/atlas/area_selector

qq_45487080的博客 1397
上一篇: Ccesium 实现地形开挖,多区域
戊己庚辛1
博客等级 码龄4年 52粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值