@orca-fe/x-map
Version:
201 lines (186 loc) • 7.63 kB
JavaScript
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();
}
}