this.loadModel('apple.glb').then((model) => {
let obj = model.children[0].children[0];
let geometry = obj.geometry;
//放大两倍
geometry.scale(2, 2, 2);
//形状点整体居中
geometry.center();
//渲染方式为画点
let material = new THREE.PointsMaterial({
size: 1,//点大小
color: new THREE.Color(that.color), //颜色
transparent: true, //开启透明
});
this.mesh = new THREE.Points(geometry, material);
this.scene.add(this.mesh);
//设置视角
this.setView(that.cameraPos, that.controlsPos);
})
BufferGeometry
,以上是苹果模型的BufferGeometry,如果是一些像球体等封装好的图元,可以采用以下方式转换成对应的缓存图元 //转为缓存图元
geometry = new THREE.BufferGeometry().fromGeometry(geometry);
material.onBeforeCompile = (shader) => {
console.log(shader)
})
shader.fragmentShader
可以看到编译前的代码,有点多和复杂,不用读懂,我们只需要替换其中要用的一句代码,变成我们需要的结果。 material.onBeforeCompile = (shader) => {
//修改片元着色器
shader.fragmentShader = shader.fragmentShader.replace(
`gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,
` //需要替换的代码……`
);
//计算离中心点距离
float d=distance(gl_PointCoord, vec2(0.5, 0.5));
//离中心点0.5以外没有颜色
if(d>0.5) discard;
//复用替换前的代码
gl_FragColor = vec4(outgoingLight , diffuseColor.a );
float d=distance(gl_PointCoord, vec2(0.5, 0.5));
if(d < 0.3){//保持原色
gl_FragColor = diffuseColor;
}else{
//透明渐变
gl_FragColor.rgb = diffuseColor.rgb;
float cd =(1.0-d*2.0);
gl_FragColor.a=diffuseColor.a*cd*0.5;
}
let material = new THREE.PointsMaterial({
size: 1,
color: new THREE.Color(that.color),
transparent: true,
depthTest: false//关闭深度测试
});
获取顶点位置,然后复制存一份作为原始值,另一份设置成全部点落在底
geometry.boundingBox
:形状的包围框,可以获取底部的位置和顶部的位置
const positions = geometry.attributes.position;
const pos = positions.clone();
//底部位置
const bottom = geometry.boundingBox.min.y;
this.distance = bottom;
//顶部位置
this.max = geometry.boundingBox.max.y;
const count = pos.count;
for (let i = 0; i < count; i++) {
//将y轴坐标全部置底
pos.setXYZ(i, pos.getX(i), bottom, pos.getZ(i));
}
pos.needsUpdate = true;
geometry.setAttribute('position', pos);
geometry.setAttribute('initialPosition', positions.clone());
geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);
动起来,让世界变得精彩
this.speed
:运动速度this.speed1
:运动加速度this.distance
:上升的距离动画逻辑:
animateAction() {
if (this.mesh && this.distance <= this.max) {
this.speed += this.speed1;
this.distance += this.speed;
let dist = this.distance;
const positions = this.mesh.geometry.attributes.position;
const initialPositions = this.mesh.geometry.attributes.initialPosition;
const count = positions.count;
let t = this.max - this.distance;
for (let i = 0; i < count; i++) {
const iy = initialPositions.getY(i);
positions.setXYZ(i, positions.getX(i), iy <= dist ? iy : dist, positions.getZ(i));
}
//通知材质的着色器,点要更新
positions.needsUpdate = true;
}
}
注意:赋值改变点位置后,一定要positions.needsUpdate = true;通知点位置属性要更新
const positions = geometry.attributes.position;
geometry.setAttribute('initialPosition', positions.clone());
const pos = positions.clone();
const count = pos.count;
const displacement = new Float32Array(count);
for (let i = 0; i < count; i++) {
//随机偏移值
displacement[i] = that.minDistance + that.distance * Math.random();
}
pos.needsUpdate = true;
geometry.setAttribute('position', pos);
//偏移值赋值
geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 1));
geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);
动画逻辑:
this.time
时间增长值注意:偏移距离要乘以法向量,这样才能让点四面八方地分布
animateAction() {
if (this.mesh && this.time >= 0) {
this.speed += this.speed1;
this.time += this.speed;
const positions = this.mesh.geometry.attributes.position;
const normal = this.mesh.geometry.attributes.normal;
const initialPositions = this.mesh.geometry.attributes.initialPosition;
const displacement = this.mesh.geometry.attributes.displacement;
const count = positions.count;
let t = 2.0 - this.time;
for (let i = 0; i < count; i++) {
//计算该时间的偏移距离
const d = displacement.getX(i) * t;
const ix = initialPositions.getX(i);
const iy = initialPositions.getY(i);
const iz = initialPositions.getZ(i);
const nx = normal.getX(i);
const ny = normal.getY(i);
const nz = normal.getZ(i);
//初始点减去偏移距离
positions.setXYZ(i, ix - nx * d, iy - ny * d, iz - nz * d);
}
positions.needsUpdate = true;
if (this.time >= 2) {//结束动画
this.time = -1;
}
}
}
const positions = geometry.attributes.position;
const b = geometry.boundingBox;
this.max = Math.max(
Math.abs(b.min.x),
Math.abs(b.min.y),
Math.abs(b.min.z),
Math.abs(b.max.x),
Math.abs(b.max.y),
Math.abs(b.max.z)
);
geometry.setAttribute('initialPosition', positions.clone());
const pos = positions.clone();
const count = pos.count;
for (let i = 0; i < count; i++) {
//全部点设置为原点
pos.setXYZ(i, 0, 0, 0);
}
pos.needsUpdate = true;
是是
geometry.setAttribute('position', pos);
geometry.attributes.position.setUsage(THREE.DynamicDrawUsage);
动画逻辑:
注意:每个点与原点的距离有正负值之分,对比时要用绝对值!
animateAction() {
if (this.mesh && this.time >= 0) {
this.speed += this.speed1;
this.time += this.speed;
const positions = this.mesh.geometry.attributes.position;
const normal = this.mesh.geometry.attributes.normal;
const initialPositions = this.mesh.geometry.attributes.initialPosition;
const count = positions.count;
const radius = this.time * this.max;
for (let i = 0; i < count; i++) {
const nx = normal.getX(i);
const ny = normal.getY(i);
const nz = normal.getZ(i);
const ix = initialPositions.getX(i);
const iy = initialPositions.getY(i);
const iz = initialPositions.getZ(i);
positions.setXYZ(
i,
radius >= Math.abs(ix) ? ix : radius * nx,
radius >= Math.abs(iy) ? iy : radius * ny,
radius >= Math.abs(iz) ? iz : radius * nz
);
}
positions.needsUpdate = true;
if (this.time >= 1) {
this.time = -1;
}
}
}
let colors = [];
for (let i = 0; i < positions.count; i++) {
colors.push(Math.random(), Math.random(), Math.random());
}
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
let material = new THREE.PointsMaterial({
size: 1,
color: new THREE.Color(that.color),
vertexColors: true,//顶点颜色
transparent: true,
depthTest: false
});
const colors = this.mesh.geometry.attributes.color;
const count = colors.count;
for (let i = 0; i < count; i++) {
colors.setXYZ(i, Math.random(), Math.random(), Math.random());
}
colors.needsUpdate = true;
注意:点颜色值改变要通知颜色属性要更新
为什么开启点颜色后可以有不同颜色深度的效果呢?
material.onBeforeCompile = (shader) => {
//修改片元着色器,使其变成发光圆点
shader.fragmentShader = shader.fragmentShader.replace(
`gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,
`gl_FragColor = vec4( outgoingLight, diffuseColor.a )`
);
};
2. 代码真的好多,看得好头大!是时候展现你的着色器常识了!这个点颜色值肯定是从顶点着色器那边穿过来的,搜一下varying
全局变量,果不其然,可以发现一下代码!
173: #ifdef USE_COLOR
174: varying vec3 vColor;
175: #endif
229: #ifdef USE_COLOR
230: diffuseColor.rgb *= vColor;
231: #endif
3. 破案!diffuseColor
是点显示的颜色,默认的时候是材质的color属性值,如果开启vertexColors
后,vColor
传过来,会执行颜色值相乘,即颜色值叠加,就会出现这样不同的深度的颜色。
以上点的运动都是通过计算传入最终位置结果,但其实可以通过修改顶点着色器也能实现同样的效果。
three.js真的封装很全,大家可以弄点报错,看看人家的着色器代码,学习一下,也方便以后修改着色器代码,自定义效果!
https://github.com/xiaolidan00/my-three
阅读量:419
点赞量:0
收藏量:0