1.3D地图的第三步,增加描边的线条和辉光
1.还记得当初我们引入的map.json文件去生成的地图吗?如果忘了,可以去复习第二章,现在我们要做的是创建一个arr,把map.json的数据以省份的格式放进来,如果直接使用地图的数据,线条的起始点和另一个起始点会交叉在一起,非常混乱
//在 generateGeometry方法下新建 linArr数组
let linArr=[]
2.每次循环清零linArr,并push进当前省份的数据,把当前linArr的省份数据 发给getLin(),去创建线条,
要调整成一下z轴的高度,是地图z轴高度的下面一点点
linArr.push(5.7)
注意代码里我关于linArr数组的操作
multiPolygon.forEach((polygon) => {
// linArr.length=0
//构建几何图形
//清零
linArr.length=0
const shape = new THREE.Shape()
for (let i = 0; i < polygon.length; i++) {
const [x, y] = projection(polygon[i])
//如果有NvN的数据就让他跳过,要不然three.js会报错
if(!x){
continue;
}
//指定我们的起点
if (i === 0) {
shape.moveTo(x, -y)
}
// this. pointArr.push(x)
// this. pointArr.push(-y)
// this. pointArr.push(2)
linArr.push(x)
linArr.push(-y)
linArr.push(5.7)
//后续就开始从起点画线
//如果你使用过canvans画线,那你肯定秒懂,他们是一个道理
shape.lineTo(x, -y)
// lineGeometry.vertices.push(new THREE.Vector3(x, -y, 4))
}
//注意这里
this.getLin(linArr)
//构建地图的 正面贴图 ,这里使用基础材质,关于材质,大家可以去详细看看api
material = new THREE.MeshBasicMaterial ({
// color:'#144685',
transparent: true,
opacity:1,
map:texture,
})
//构建地图的 侧面贴图 ,这里使用基础材质
const material_1 = new THREE.MeshBasicMaterial ({
color: '#558BAB',
transparent: true,
opacity: 0.45,
})
const extrudeSettings = {
depth: 2,
bevelEnabled: false,
}
//这里我们把先前构造的几何图形 通过ExtrudeGeometry 拉伸成几何体
const geometry = new THREE.ExtrudeGeometry(
shape,
extrudeSettings
)
const extrudeSettings_2={
depth: 1,
bevelEnabled: false,
}
const geometry_2 = new THREE.ExtrudeGeometry(
shape,
extrudeSettings_2
)
const material_2 = new THREE.MeshBasicMaterial ({
color: '#ffffff',
transparent: true,
opacity: 0,
})
//把几何体和 我们两个贴图 合成一个 网格对象 mesh
const mesh = new THREE.Mesh(geometry, [material, material_1])
const mesh_2 = new THREE.Mesh(geometry_2, [material_1,material_2])
const mesh_3= mesh_2.clone()
mesh.castShadow=true
//把网格对象加入省份的3d对象
mesh.position.z=4
mesh_2.position.z=1
mesh_3.position.z=-2
province.add(mesh)
province.add(mesh_2)
province.add(mesh_3)
})
3.在生成线条之前,我们需要引入three.meshline来控制线条的宽度
npm i three.meshline
//引入
import { MeshLine, MeshLineMaterial, MeshLineRaycast } from 'three.meshline';
webgl不支持线条的线条的宽度属性,用这个就可以很好实现,还不受版本的限制。 接下来我们看看如何生成线条,我会用大量注释帮助你理解。
getLin(arr){
//Group对象,你可以把他看做一个数组,用这个主要是为了更方便的去控制每一个省份的线条
let groups=new THREE.Group()
if(arr.length!=0){
// 初始化MeshLine
const line = new MeshLine()
// 传入顶点坐标数据
line.setPoints(vertices)
// 生成线材质
let material = new MeshLineMaterial({
useMap: 0,
color: '#0fb9ee',
opacity: 1,
resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
sizeAttenuation: 2,
lineWidth: 0.6,
transparent: true,
wireframe: false,
})
//构建线条的网格对象
const mesh = new THREE.Mesh(line.geometry,material)
//把他放进去分层1 类似于z-index,方便后面实现辉光
mesh.layers.enable(1)
//加入数组
groups.add(mesh)
//把group加入地图对象
map.add(groups)
}
},
1.对于新手来讲,接下来的内容会比较晦涩难懂,我建议你可以结合起来看看three.js的官网案例代码去帮助你理解
three.js案例
2.准备工作,引入包,并开启双层渲染
这些包都是three.js自带的,不要额外引入,去包里找就好了。
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader';
//混合器
let composer
const ENTIRE_SCENE = 0,// 全部的,整个的场景
BLOOM_SCENE = 1; // 光晕场景
const bloomLayer = new THREE.Layers();// 光晕层次-创建一个图层对象
bloomLayer.set(BLOOM_SCENE);// 先把光晕层次设置光晕场景的层次1
const darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });// 跟辉光光晕有关的变量
const materials = {};// 跟辉光光晕有关的变量
const params = {
exposure: 0,// 暴露
bloomStrength: 0.78,// 光晕强度
bloomThreshold: 0,// 光晕阈值
bloomRadius: 0.1,// 光晕半径
};
let bloomPass
let finalComposer
let bloomComposer
主要思路: 请一定要先想明白这里,再继续下去,辉光渲染的主要思路在于 分层渲染,其中的分层 它相当于重新创建了一个渲染的通道,你也可以简单的理解为z-index分层,他们不在一个层次,我们渲染先渲染需要发光的部分 也就是 layers.enable(1)的部分,在渲染另一个 layers.enable(0)的部分,最后再让他们合并。
3.开始构建
顶点和片段着色器
需要用GLSL语言来编写,目前我还不会,这是从官网demo找的代码片段,主要作用就是用于制造光晕
//顶点着色器
<script type="x-shader/x-vertex" id="vertexshader">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
//片段着色器
<script type="x-shader/x-fragment" id="fragmentshader">
uniform sampler2D baseTexture;
uniform sampler2D bloomTexture;
varying vec2 vUv;
void main() {
gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
}
</script>
feng(){
// 通过ShaderPass构造函数把FXAAShader着色器和uniforms构成的对象作为参数,创建一个锯齿通道FXAAShaderPass,然后把锯齿通道插入到composer中。
const effectFXAA = new ShaderPass(FXAAShader);
effectFXAA.uniforms["resolution"].value.set(
0.6 / window.innerWidth,
0.6 / window.innerHeight
); // 渲染区域Canvas画布宽高度
effectFXAA.renderToScreen = true;
// 去掉锯齿---1
const renderScene = new RenderPass(scene, camera);// RenderPass这个通道会在当前场景(scene)和摄像机(camera)的基础上渲染出一个新场景,新建:
// 添加光晕效果---2
bloomPass = new UnrealBloomPass( // UnrealBloomPass通道可实现一个泛光效果。
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5,
0.4,
0.85
);
bloomPass.threshold = params.bloomThreshold;
bloomPass.strength = params.bloomStrength;
bloomPass.radius = params.bloomRadius;
// 添加光晕效果---2
// 着色器通道容器--放进容器里
bloomComposer = new EffectComposer(renderer); // EffectComposer可以理解为着色器通道容器,着色器通道按照先后顺序添加进来并执行
bloomComposer.renderToScreen = false;
bloomComposer.addPass(renderScene);
bloomComposer.addPass(bloomPass); // 添加光晕效果
bloomComposer.addPass(effectFXAA);// 去掉锯齿
// 着色器通道容器--放进容器里
const finalPass = new ShaderPass(
new THREE.ShaderMaterial({
uniforms: {
baseTexture: { value: null },
bloomTexture: { value: bloomComposer.renderTarget2.texture },
},
vertexShader: document.getElementById("vertexshader").textContent,
fragmentShader: document.getElementById("fragmentshader")
.textContent,
defines: {},
}),
"baseTexture"
);
finalPass.needsSwap = true;
finalComposer = new EffectComposer(renderer);
finalComposer.addPass(renderScene);
finalComposer.addPass(finalPass);
finalComposer.addPass(effectFXAA);
},
关键的四个类
ShaderPass:用于添加自定义的GLSL着色器(FXAAShader主要用于制造模糊效果 )
RenderPass:一个新的通道,他会渲染出一个新的场景
UnrealBloomPass:后期处理通道,用于模拟真实世界中的光晕效果,即模拟光线在明亮区域产生的模糊和光晕效果,他有四个参数,主要用来调节光晕
radius:表示光晕散发的半径。
strength:表示光晕的强度,值越大,光晕效果越明显。
threshold:表示产生光晕的光照强度阈值,只有光照强度大于该值的部分才会产生光晕效果。
blurKernel:表示用于模糊的卷积核,可以根据需要自定义。
EffectComposer:用于实现后期处理效果的一个对象,他可以把先前生成的渲染通道合并起来渲染,每个通道都是一种后期处理的效果,包括颗粒 模糊等
然后去吧 渲染器的 alpha打开,渲染器输出的图像将具有透明度,黑色将完全透明。
renderer = new THREE.WebGLRenderer({
alpha: true,
})
重写 render方法,渲染手法重点来了
render() {
scene.traverse((obj) => {
if (bloomLayer.test(obj.layers) === false) {
materials[obj.uuid] = obj.material;
obj.material = darkMaterial;
}
});
bloomComposer.render();
scene.traverse((obj) => {
if (materials[obj.uuid]) {
obj.material = materials[obj.uuid];
delete materials[obj.uuid];
}
});
finalComposer.render();
}
现在已经完工了 你想把哪个几何体设置为辉光效果,就加上 layers.enable(1),让他进入另一个通道渲染
我们回到 getlin方法
const mesh = new THREE.Mesh(line.geometry,material)
mesh.layers.enable(1)