“This is the 29th day of my participation in the First Challenge 2022. For details: First Challenge 2022”
Introduction to the
In this section, multiple particles are mainly used to emit light at different positions according to time on the map boundary, and a luminous line is realized to move on the boundary.
Implementation approach
- Get GeoJSON data for map boundaries.
- Create map wireframes based on the data and create fully transparent particles at each data point.
- through
requestAnimationFrame()
Function to modify the transparency of particles at different positions.
implementation
Based on the template
<! DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8" />
<title>learning</title>
</head>
<body>
<canvas id="c2d" class="c2d" width="1000" height="500"></canvas>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script type="module">
import * as THREE from './file/three.js-dev/build/three.module.js'
import { OrbitControls } from './file/three.js-dev/examples/jsm/controls/OrbitControls.js'
const canvas = document.querySelector('#c2d')
/ / the renderer
const renderer = new THREE.WebGLRenderer({ canvas })
const fov = 40 // Scope of view
const aspect = 2 // The camera defaults to the width ratio of the canvas
const near = 0.1 / / nearly flat
const far = 10000 / / far plane
// Perspective projection camera
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
camera.position.set(0.100.500)
camera.lookAt(0.0.0)
// Control the camera
const controls = new OrbitControls(camera, canvas)
controls.update()
/ / the scene
const scene = new THREE.Scene()
/ / rendering
function render() {
renderer.render(scene, camera)
requestAnimationFrame(render)
}
requestAnimationFrame(render)
</script>
</body>
</html>
Copy the code
Load map data, draw maps and customize geometry
- Mapping China
const lines = []
const geometry = new THREE.BufferGeometry()
let positions = null
let opacitys = null
// Change the coordinates around Beijing
const projection = d3.geoMercator().center([116.412318.39.909843]).translate([0.0])
Copy the code
- Create a variable
lines
Save boundary point data. - create
BufferGeometry()
Geometry, draw transparent points.
- Common method for drawing lines and putting boundary point information into
lines
In the.
let indexBol = true
/** * Border graphic drawing *@param Polygon Polygon number group *@param Color Material color * */
function lineDraw(polygon, color) {
const lineGeometry = new THREE.BufferGeometry()
const pointsArray = new Array()
polygon.forEach((row) = > {
const [x, y] = projection(row)
// Create a 3d point
pointsArray.push(new THREE.Vector3(x, -y, 0))
if (indexBol) {
lines.push([x, -y, 0])
}
})
indexBol = false
// Add multiple points
lineGeometry.setFromPoints(pointsArray)
const lineMaterial = new THREE.LineBasicMaterial({
color: color
})
return new THREE.Line(lineGeometry, lineMaterial)
}
Copy the code
- Used here
indexBol
Variable, because we only need the boundary point data in the first array of map data.
- Load map data and create custom geometry.
const loader = new THREE.FileLoader()
loader.load('./file/100000.json'.(data) = > {
const jsondata = JSON.parse(data)
// The Chinese border
const feature = jsondata.features[0]
const province = new THREE.Object3D()
province.properties = feature.properties.name
/ / data
const coordinates = feature.geometry.coordinates
coordinates.forEach((coordinate) = > {
// coordinate polygon data
coordinate.forEach((rows) = > {
const line = lineDraw(rows, 0xffffff)
province.add(line)
})
})
positions = new Float32Array(lines.flat(1))
// Set the vertex
geometry.setAttribute('position'.new THREE.BufferAttribute(positions, 3))
// Set particle transparency to 0
opacitys = new Float32Array(positions.length).map(() = > 0)
geometry.setAttribute('aOpacity'.new THREE.BufferAttribute(opacitys, 1))
scene.add(province)
})
Copy the code
- Parse map data using
lineDraw()
Function to draw lines and save boundary information. - Add geometry vertex information (
positions
) and transparency information for each vertex (opacitys
)
Draw transparent points using a shading material and a dot grid
- Create control variables.
// Control the color and particle size
const params = {
pointSize: 2.0.pointColor: '#4ec0e9'
}
Copy the code
- Define vertex shaders
const vertexShader = ` attribute float aOpacity; uniform float uSize; varying float vOpacity; Void main(){gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0); gl_PointSize = uSize; vOpacity=aOpacity; } `
Copy the code
three.js
The variable in.projectionMatrix
Is the projection transformation matrix,modelViewMatrix
Is the transformation matrix of camera coordinate system,position
Vertex coordinates.- through
uniform
Global variables get external SettingsuSize
, set the particle size. - through
varying
The variable puts the vertex corresponding to transparencyaOpacity
Pass in the slice shader.
- Defines the slice shader
const fragmentShader = ` varying float vOpacity; uniform vec3 uColor; float invert(float n){ return 1.-n; } void main(){if(vOpacity <=0.2){discard; } vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y)); vec2 cUv=2.*uv-1.; vec4 color=vec4(1./length(cUv)); color*=vOpacity; color.rgb*=uColor; gl_FragColor=color; } `
Copy the code
- through
uniform
Global variables get external SettingsuColor
, base color. - through
varying
Transparency of variable acquisitionvOpacity
. - Set transparency to less than
0.2
Is not executed. - According to the algorithm to obtain the current vertex to show the color, this algorithm is found on the Internet, to achieve the luminous effect.
- Create a shader material and place it in the point material to draw a transparent point.
const material = new THREE.ShaderMaterial({
vertexShader: vertexShader,
fragmentShader: fragmentShader,
transparent: true.// Set transparency
uniforms: {
uSize: {
value: params.pointSize
},
uColor: {
value: new THREE.Color(params.pointColor)
}
}
})
const points = new THREE.Points(geometry, material)
scene.add(points)
Copy the code
- Place vertex and slice shader, set
uniforms
Global variables. - create
Points()
Mesh, draw transparent points, add scene.
- Now all the points are transparent just like the last picture.
Modified particle transparency to create glare effect
let currentPos = 0
let pointSpeed = 20 / / speed
/ / rendering
function render() {
if (points && geometry.attributes.position) {
currentPos += pointSpeed
for (let i = 0; i < pointSpeed; i++) {
opacitys[(currentPos - i) % lines.length] = 0
}
for (let i = 0; i < 200; i++) {
opacitys[(currentPos + i) % lines.length] = i / 50 > 2 ? 2 : i / 50
}
geometry.attributes.aOpacity.needsUpdate = true
}
renderer.render(scene, camera)
requestAnimationFrame(render)
}
Copy the code
- Two loops, the first clearing the last particle transparency modification. The second set the glare length and modify the particle transparency.
.needsUpdate
Must be set before the vertex information in the shader can be modified.