房岳 发表于 2024-9-11 01:25:22

ThreeJS Shader的效果样例飞线、粒子和模型轮廓高亮(三)

一、飞线效果
  
  功能说明:支持设置点的个数,飞线速度、起始和终止颜色值、线宽、线的大小
  原理:
    1. 首先绘制一条与线长度相同的线,线中各点的大小逐渐变小
    2. 如何让线动起来?假设点的个数总共为num个,传入的点的下标为a,通过变化的时间计算出移动的下标b,如果a+b>=num则代表,该点可见,否则不可见
    3. 如何设置移动速度?传入的uTime变化在毫秒之间,通过乘以一个具体的数据,则可以将变化的时间放大,改变乘以的数据是移动速度的关键
    4. 改变飞线大小?初始设置飞线的大小是整条线,通过控制飞线的结束位置以及调整点变化的速度就可以调整飞线的大小
/**
* 创建飞线
* 支持设置起始和终止颜色值、飞线长度、线宽等属性、移动速率
*/
function addFlyLine() {
const vertex = `
    attribute float aIndex;
    uniform float uTime;
    uniform float uNum; // 线上点个数
    uniform float uWidth; // 线宽
    uniform float uLength; // 线宽
    uniform float uSpeed; // 飞线速度
    varying float vIndex; // 内部点下标
    void main() {
      vec4 viewPosition = viewMatrix * modelMatrix * vec4(position, 1.0);
      gl_Position = projectionMatrix * viewPosition;
      
      vIndex = aIndex;
      // 通过时间点的增加作为点移动的下标,从末端的第一个点开始,已num点为一个轮回,往复执行运动
      float num = mod(floor(uTime * uSpeed * 100.0), uNum);
      // 只绘制部分点,多余的不绘制
      if (aIndex + num >= uNum) {
      float size = (uNum - mod(aIndex + num, uNum) * (1.0 / uLength)) / uNum * uWidth;
      gl_PointSize = size;
      }
    }
`;

const frag = `
    varying float vIndex;
    uniform float uTime;
    uniform float uLength; // 线宽
    uniform float uNum;
    uniform float uSpeed;
    uniform vec3 uSColor;
    uniform vec3 uEColor;
    void main() {
      // 默认绘制的点是方形的,通过舍弃可以绘制成圆形
      float distance = length(gl_PointCoord - vec2(0.5, 0.5));
      if (distance > 0.5) {
      discard;
      }
      
      float num = mod(floor(uTime * uSpeed * 100.0), uNum);
      // 根据点的下标计算渐变色值
      vec3 color = mix(uSColor, uEColor, (num + vIndex - uNum) / uNum);
      // 越靠近末端透明度越大
      float opacity = (uNum - (num + vIndex - uNum)) / uNum;
      // 根据长度计算显示点的个数,多余的透明显示
      if (vIndex + num >= uNum && vIndex + num <= uNum * (1.0 + uLength)) {
      gl_FragColor = vec4(color, opacity);
      } else {
      gl_FragColor = vec4(color, 0);
      }
    }
`;

const nums = 500;

const curve = new THREE.CubicBezierCurve3(
    new THREE.Vector3(-6.0, 0.0, 0.0),
    new THREE.Vector3(0.0, 3.0, 9.0),
    new THREE.Vector3(4.0, 5.0, 0.0),
    new THREE.Vector3(6.0, 4.0, 3.0)
);
const curveArr = curve.getPoints(nums);
const flatArr = curveArr.map(e => e.toArray());
const lastArr = flatArr.flat();
const indexArr = [...Array(nums + 1).keys()];
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(lastArr), 3));
geometry.setAttribute('aIndex', new THREE.BufferAttribute(new Float32Array(indexArr), 1));
const material = new THREE.LineBasicMaterial({ color: 0x0000ff });

// 创建一根曲线
const curveObject = new THREE.Line(geometry, material);
// scene.add(curveObject);

const uniform = {
    uTime: { value: 0.01 },
    uSColor: { value: new THREE.Color(0x4effd6) },
    uEColor: { value: new THREE.Color(0xffffff) },
    uWidth: { value: 6.0 },
    uNum: { value: nums },
    uSpeed: { value: 2 },
    uLength: { value: 0.15 }
}
setShader(geometry, vertex, frag, undefined, uniform, true)
}添加飞线function setShader(geometry, vertexShader, fragmentShader, position = , uniforms = {}, isLine = false) {
material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    side: THREE.DoubleSide,
    uniforms: uniforms,
    transparent: true
});
let planeMesh = new THREE.Mesh(geometry, material);
if (isLine) {
    planeMesh = new THREE.Points(geometry, material);
}
planeMesh.position.x = position;
planeMesh.position.y = position;
planeMesh.position.z = position;
scene.add(planeMesh);
}添加材质 
二、实现粒子效果 
  
  实现原理:
    1. 根据AI生成一个特殊形状的点模型
    2. 通过 distance 将立方体点改为球形
    3. 计算点的距离,越远的点透明度越高,增加层次感
/**
* 创建一个立体的涵道,根据传入的时间自动缩放
*/
function addHole() {
const vertex = `
    varying vec3 vColor;
    uniform float uTime;
    void main() {
      float scale = mod(uTime / 3.0 + 0.3, 1.0);
      vec4 viewPosition = viewMatrix * modelMatrix * vec4(position, 1.0);
      gl_Position = projectionMatrix * viewPosition * vec4(vec3(scale), 1.0);
      gl_PointSize = 6.0;
    }
`;

const frag = `
    void main() {
      float distance = length(gl_PointCoord.xy - 0.5);
      // 默认绘制的点是立方体的,通过判断uv点的距离大小可以绘制出球形
      if (distance > 0.5) {
      discard;
      }
      // 边缘模糊化,点的半径较大时效果比较明显
      float opacity = smoothstep(0.5, 0.1, distance);
      gl_FragColor = vec4(0.24, 0.12, 0.96, opacity);
    }
`;
// 创建一个自定义的几何体
const geometry = new THREE.BufferGeometry();
const vertices = [];
const indices = [];

// 使用三维参数方程生成心形的顶点
const segmentsU = 100;
const segmentsV = 30;
for (let i = 0; i <= segmentsU; i++) {
      const u = (i / segmentsU) * 2 * Math.PI;
      for (let j = 0; j <= segmentsV; j++) {
          const v = (j / segmentsV) * 2 * Math.PI;
          const x = (1 + Math.cos(v)) * Math.cos(u);
          const y = (1 + Math.cos(v)) * Math.sin(u);
          const z = Math.sin(v);
          vertices.push(x, y, z);
      }
}

// 生成索引数据
for (let i = 0; i < segmentsU; i++) {
      for (let j = 0; j < segmentsV; j++) {
          const a = i * (segmentsV + 1) + j;
          const b = (i + 1) * (segmentsV + 1) + j;
          const c = (i + 1) * (segmentsV + 1) + j + 1;
          const d = i * (segmentsV + 1) + j + 1;
          indices.push(a, b, d);
          indices.push(b, c, d);
      }
}

// 将顶点数据和索引数据添加到几何体中
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setIndex(indices);

setShader(geometry, vertex, frag, undefined, { uTime: { value: 0.01 } }, true)
}使用UnrealBloomPass实现高亮效果   
 
  
function setShader(geometry, vertexShader, fragmentShader, position = , uniforms = {}, isLine = false) {
material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    side: THREE.DoubleSide,
    uniforms: uniforms,
    transparent: true,
    blending: THREE.AdditiveBlending, // 多个元素的颜色相互叠加,颜色可能会变亮,会叠加setClearColor设置的背景色
});
material.depthTest = true;
material.depthWrite = false;
let planeMesh = new THREE.Mesh(geometry, material);
if (isLine) {
    planeMesh = new THREE.Points(geometry, material);
}
planeMesh.position.x = position;
planeMesh.position.y = position;
planeMesh.position.z = position;
scene.add(planeMesh);
}使用UnrealBloomPass实现球体辉光效果      
 

来源:https://www.cnblogs.com/codeOnMar/p/18233672
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: ThreeJS Shader的效果样例飞线、粒子和模型轮廓高亮(三)