threejs物体扫描
前言
在 vue2 中实现 threejs单物体扫描
一、地形
地形是基于 ImprovedNoise 创建,可以创建具有一定规律的随机点,用来模拟地形效果很好。
//引入
import{ ImprovedNoise } from'three/examples/jsm/math/ImprovedNoise'//柏林噪声
funZ(width, height) {
let size = width * height;
let data = new Uint8Array(size);
let perlin = new ImprovedNoise();
// 控制地面显示效果 可以尝试0.01 0.1 1等不值
// 0.1凹凸不平的地面效果 1山脉地形效果
let quality = 1.2;
// z值不同每次执行随机出来的地形效果不同
let z = Math.random() * 100;
for (let j = 0; j < 4; j++) {
for (let i = 0; i < size; i++) {
// x的值0 1 2 3 4 5 6...
let x = i % width;
// ~表示按位取反 两个~就是按位取反后再取反
// ~~相当于Math.floor(),效率高一点
// y重复若干个值
let y = ~~(i / width);
// 通过噪声生成数据
data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75);
// console.log(y);
}
// 循环执行的时候,quality累乘 乘的系数是1 显示效果平面
quality *= 5;
}
return data;
}
// width,height两个变量用控制平面几何体顶点数量
// 行列两个方向顶点数量不同 显示效果不同 分别为100和250显示不同的效果
let width = 250, height = 250;
// 生成地形顶点高度数据
let data = this.funZ(width, height);
//创建一个平面地形,行列两个方向顶点数据分别为width,height
let geometry = new THREE.PlaneBufferGeometry(400, 400, width - 1, height - 1);
geometry.rotateX(-Math.PI / 2);
// 访问几何体的顶点位置坐标数据
let vertices = geometry.attributes.position.array;
// 改变顶点高度值
for (let i = 0, j = 0, l = vertices.length; i < l; i++, j += 3) {
vertices[j + 1] = data[i] * 0.8;
}
// 不执行computeVertexNormals,没有顶点法向量数据
geometry.computeVertexNormals();
let mymaterial = this.initShader(light)
this.planeMaterial = mymaterial
let mesh = new THREE.Mesh(geometry, mymaterial);
mesh.position.set(200,200,0)
scene.add(mesh);
二、材质
1.shader
代码如下(示例):
initShader(light){
let vertexShader = `
varying vec3 vNormal;
varying vec2 vUv;
uniform vec3 myLight;
varying vec3 lightToPos;
varying vec3 myPosition;
${THREE.ShaderChunk[ "common" ]}
${THREE.ShaderChunk[ "bsdfs" ]}
${THREE.ShaderChunk[ "shadowmap_pars_vertex" ]}
void main()
{
${THREE.ShaderChunk['beginnormal_vertex']}
${THREE.ShaderChunk['defaultnormal_vertex']}
${THREE.ShaderChunk[ "begin_vertex" ]}
${THREE.ShaderChunk[ "project_vertex" ]}
${THREE.ShaderChunk[ "worldpos_vertex" ]}
${THREE.ShaderChunk[ "shadowmap_vertex" ]}
vec3 mvposition = vec3((modelViewMatrix * vec4( position, 1.0 )).xyz);
lightToPos = myLight - mvposition;
vNormal = normalize(normalMatrix * normal);
vUv = uv;
myPosition = vec3((modelMatrix * vec4( position, 1.0 )).xyz);
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}`
//float dir = length(vNormal * vec3(0.0, 0.0, 1.0));
let fragmentShader = `
uniform vec3 myLight;
uniform float myTime;
varying vec3 lightToPos;
varying vec3 vNormal;
varying vec3 myPosition;
varying vec2 vUv;
${THREE.ShaderChunk[ "common" ]}
${THREE.ShaderChunk[ "packing" ]}
${THREE.ShaderChunk[ "bsdfs" ]}
${THREE.ShaderChunk[ "lights_pars_begin" ]}
${THREE.ShaderChunk[ "shadowmap_pars_fragment" ]}
${THREE.ShaderChunk[ "shadowmask_pars_fragment" ]}
void main(){
float diff = max( dot( vNormal , normalize(lightToPos) ) , 0.0 );
vec3 diffuse = diff * vec3(0.9,0.5,0.1);//diff * lightColor
float lighty = abs(myTime - myPosition.x);
if(lighty<10.0 && myTime>0.0){
float lighty_Rev = 1.0 - lighty/10.0;
diffuse = mix(diffuse , vec3(1,1,1) , lighty_Rev);
}
gl_FragColor = vec4( diffuse, 1.0);
}`
//着色器材质
let sm = new THREE.ShaderMaterial({
uniforms: THREE.UniformsUtils.merge( [
THREE.UniformsLib[ "lights" ],
{
myLight: {type: 'v3', value: light.position},
myTime: {value:0.0}
},
]),
vertexShader: vertexShader,
fragmentShader: fragmentShader,
// side:THREE.DoubleSide,//双面渲染
side: THREE.FrontSide,
});
return sm
},
2.使用
代码如下(示例):
/* 扫描 */
saomiao(isClick){
if(isClick && this.state=='stop'){this.state = 'moving'}
if(this.state=='stop'){return}
this.myTime += 1.8
if(this.myTime >400){
this.myTime = 0
this.state = 'stop'
}
this.updateMaterial()
},
/* 材质变化参数 */
updateMaterial(){
this.planeMaterial.uniforms.myTime.value = this.myTime
}
animation(){
this.saomiao(false);//扫描
this.controls.update();//更新控制器插件
this.stats.update();//更新刷新率插件
this.draw();//渲染
window.requestAnimationFrame(this.animation);
},
<button @click="saomiao(true)">扫描</button>
总结
这种方式实现扫描效果需要对时间与距离进行计算,不知道有没有大神优化。下一篇写一个后期处理的扫描。