UNPKG

@orca-fe/x-map

Version:
201 lines (186 loc) 7.63 kB
import * as THREE from 'three'; import { Line2 } from 'three/examples/jsm/lines/Line2'; import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'; import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'; function createMaterial() { return new THREE.RawShaderMaterial({ uniforms: { topColor: { value: [30 / 255, 100 / 255, 200 / 255, 0.3] }, baseColor: { value: [22 / 255, 68 / 255, 159 / 255, 0.65] }, offset: { value: 0 }, noiseScale: { value: 1 }, }, vertexShader: ` precision mediump float; precision mediump int; uniform mat4 modelViewMatrix; // optional uniform mat4 projectionMatrix; // optional attribute vec3 position; attribute vec4 color; varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `, fragmentShader: ` precision mediump float; precision mediump int; uniform vec4 topColor; uniform vec4 baseColor; uniform float offset; uniform float noiseScale; varying vec3 vPosition; vec3 random_perlin( vec3 p ) { p = vec3( dot(p,vec3(127.1,311.7,69.5)), dot(p,vec3(269.5,183.3,132.7)), dot(p,vec3(247.3,108.5,96.5)) ); return -1.0 + 2.0*fract(sin(p)*43758.5453123); } float noise_perlin (vec3 p) { vec3 i = floor(p); vec3 s = fract(p); // 3D网格有8个顶点 float a = dot(random_perlin(i),s); float b = dot(random_perlin(i + vec3(1, 0, 0)),s - vec3(1, 0, 0)); float c = dot(random_perlin(i + vec3(0, 1, 0)),s - vec3(0, 1, 0)); float d = dot(random_perlin(i + vec3(0, 0, 1)),s - vec3(0, 0, 1)); float e = dot(random_perlin(i + vec3(1, 1, 0)),s - vec3(1, 1, 0)); float f = dot(random_perlin(i + vec3(1, 0, 1)),s - vec3(1, 0, 1)); float g = dot(random_perlin(i + vec3(0, 1, 1)),s - vec3(0, 1, 1)); float h = dot(random_perlin(i + vec3(1, 1, 1)),s - vec3(1, 1, 1)); // Smooth Interpolation vec3 u = smoothstep(0.,1.,s); // 根据八个顶点进行插值 return mix(mix(mix( a, b, u.x), mix( c, e, u.x), u.y), mix(mix( d, f, u.x), mix( g, h, u.x), u.y), u.z); } float noise_turbulence(vec3 p) { float f = 0.0; float a = 1.; p = 4.0 * p; for (int i = 0; i < 5; i++) { f += a * abs(noise_perlin(p)); p = 2.0 * p; a /= 2.; } return f; } void main() { float n = noise_turbulence( vec3((vPosition.xy) / noiseScale, offset) ); // vec3 white = vec4(1, 1, 1, 0) * n * (vPosition.z); // gl_FragColor = vec4( baseColor.rgb , (1.0 - vPosition.z ) * mix(0.5, 1.0, n * n ) ); vec4 tColor = mix(baseColor, topColor, vPosition.z); gl_FragColor = vec4(tColor.rgb, tColor.a * (2.0 - vPosition.z ) * mix(0.5, 1.0, n * n ) ); } `, side: THREE.DoubleSide, depthWrite: false, transparent: true, }); } function wallFace(p1, p2, height) { const position = []; position.push(p1[0], p1[1], 0); position.push(p2[0], p2[1], 0); position.push(p1[0], p1[1], height); position.push(p1[0], p1[1], height); position.push(p2[0], p2[1], 0); position.push(p2[0], p2[1], height); // position.push(p1[0], 0, p1[1]); // position.push(p2[0], 0, p2[1]); // position.push(p1[0], height, p1[1]); // position.push(p1[0], height, p1[1]); // position.push(p2[0], 0, p2[1]); // position.push(p2[0], height, p2[1]); return position; } export default class Wall { constructor(options = {}) { this.meshes = []; this.material = createMaterial(); this.obj = new THREE.Group(); this.setPolyline = (polyline) => { this.polyline = polyline; this.updatePolyline(); }; this.clearPolyline = () => { this.meshes.forEach((mesh) => { this.obj.remove(mesh); }); this.meshes = []; }; this.updatePolyline = () => { const { polyline, height, lineWidth, lineColor } = this; this.clearPolyline(); const positions = []; for (let i = 0; i < polyline.length - 1; i++) { positions.push(...wallFace(polyline[i], polyline[i + 1], 1)); } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3)); const wall = new THREE.Mesh(geometry, createMaterial()); wall.scale.set(1, 1, height); this.obj.add(wall); // line const texture = new THREE.TextureLoader().load('/light-blue.png'); texture.rotation = (90 / 180) * Math.PI; // texture.offset.x -= lineWidth / 2; texture.offset.x -= 0.5; texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; const path = polyline.map(([x, z]) => [x, 0, z]).reduce((arr, [x, y, z]) => [...arr, x, y, z], []); const lineGeometry = new LineGeometry(); lineGeometry.setPositions(path); const lineMaterial = new LineMaterial({ transparent: true, color: lineColor, linewidth: lineWidth, opacity: 0.9, }); const lineTop = new Line2(lineGeometry, lineMaterial); // const line = new THREE.Line(lineGeometry, lineMaterial); this.obj.add(lineTop); const shape = new THREE.Shape(); polyline.forEach(([x, y], index) => { if (index === 0) shape.moveTo(x, y); else shape.lineTo(x, y); }); this.lineTop = new THREE.Mesh(new THREE.ShapeGeometry(shape), new THREE.MeshBasicMaterial({ transparent: true, opacity: 0 })); this.lineTop.translateY(height); this.lineTop.rotateX((90 / 180) * Math.PI); this.lineBase = new THREE.Line(new THREE.BufferGeometry().setFromPoints(polyline.map(([x, z]) => new THREE.Vector3(x, 0, z)))); this.obj.add(this.lineTop); // lineTop const pathTop = polyline.map(([x, z]) => [x, height, z]).reduce((arr, [x, y, z]) => [...arr, x, y, z], []); const lineTopGeometry = new LineGeometry(); lineTopGeometry.setPositions(pathTop); const lineBase = new Line2(lineTopGeometry, new LineMaterial({ transparent: true, color: lineColor, linewidth: lineWidth, opacity: 0.7, })); this.obj.add(lineBase); // this.lineTop = lineTop; }; this.nextFrame = (offset = 0.02) => { this.material.uniforms.offset.value += offset; }; const { polyline = [], height = 500, noiseScale, lineWidth = 1, lineColor = 0x000000 } = options; this.polyline = polyline; this.height = height; this.lineWidth = lineWidth; this.lineColor = lineColor; if (noiseScale != null) { this.material.uniforms.noiseScale.value = noiseScale; } this.updatePolyline(); } }