纯shader实现动态警告可视化组件(three.js实战16)

Posted 点燃火柴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了纯shader实现动态警告可视化组件(three.js实战16)相关的知识,希望对你有一定的参考价值。

1. demo效果

waring-circle

2. 实现要点

2.1 绘制外层

外层由两部分组成,一个是组成圆环的小圆点,一个是动态变换的弧段,具体实现如下

vec4 drawOutCirclePoint(vec2 st){
  vec4 outColor = vec4(0.0);
  vec2 outerVertexs[60];

  float outerRadius = 0.5;

  float angle = 0.0;

  float one_point_angle = PI * 2.0 / 60.0;

  for(int i = 0; i < 60; i++){
    angle += one_point_angle;
    outerVertexs[i] = vec2(cos(angle)*outerRadius,sin(angle)*outerRadius);
  } 

  float pct = 0.0;

  //绘制外层圆点
  for(int i = 0; i < 60;i++){
    vec3 pointColor = vec3(0.17,0.97,1.0);
    pct = circle(st,outerVertexs[i],0.01,0.002);
    vec4 layer = vec4(pointColor.rgb, pct);
    //混合图层
    outColor = mix(outColor, layer, layer.a);
  }

  return outColor;
}
//绘制外层旋转圆环
vec4 drawOuterFlowingRing(vec2 st){ 
  vec2 p = st;
  float time = u_time * 1.5;
  vec3 outRingColor = vec3(0.17,0.97,1.0);
  
  float angle = -(time - sin(time + PI) * cos(time )) - time *.95;
  mat2 rot = mat2(cos(angle),sin(angle),-sin(angle),cos(angle));
  p = rot * p;
  
  float L = length(p);
  float f = 0.;
  
  f = smoothstep(L-.005, L, .51);
  f -= smoothstep(L,L + 0.005, .49);
  //f = step(sin(L * 200. + u_time * p.x)*.5+.5,.25); 
  
  float t = mod(time,2.0*PI) - PI;
  float t1 = sin(t) *  (PI - .25);
  
  float a = atan(p.y,p.x) ;
  f = f * step(a,t1);

  vec4 outColor = vec4(outRingColor.rgb, f);
  return outColor;
}

2.2绘制中间环状圆

绘制圆环比较简单,只需要用两个圆相减即可

 float circle(vec2 uv,vec2 p, float r,float blur){
   float dist = length(uv-p);//计算屏幕上任意点到指定点p点的距离
   float c = smoothstep(r,r-blur,dist);
   return c;
 }
 //绘制中层圆环
 vec4 drawInnerRing(vec2 st){ 
   vec3 ringColor = vec3(202.0/255.0,73.0/255.0,205.0/255.0);
   float pct = circle(st,vec2(0.0),0.4,0.02)-circle(st,vec2(0.0),0.3,0.02);
   vec4 outColor = vec4(ringColor.rgb, pct);
   return outColor;
 }

2.3 绘制中心感叹号

//绘制梯形
float trapezoid( in vec2 p, in float topWidth, float bottomWidth, float height,float blur )
{

  if(topWidth > bottomWidth){
    p *= rotate2d(PI);
  }
  float width = max(topWidth,bottomWidth);
  float k1 = height/((bottomWidth-topWidth)/2.0);
  float fun = p.y-k1*p.x-width;
  float k2 = -height/((bottomWidth-topWidth)/2.0);
  float fun2 = p.y-k2*p.x-width;

  if(p.y > -height/2.0 && p.y < height/2.0){

    //return smoothstep(k1*p.x+width,k1*p.x,fun);
    return smoothstep(k1*p.x+blur*width,k1*p.x,fun)-smoothstep(k2*p.x,k2*p.x + blur*width,fun2);
  
  }else{
    return 0.0;
  }
} 
//绘制中心感叹号
vec4 drawExclamatoryMark(vec2 st) {
  vec3 color = vec3(0.17,0.97,1.0);
  vec4 outColor = vec4(0.0);
  float pct = 0.0;

  st.y -= 0.05;
  pct = trapezoid( st, 0.28, 0.24, 0.2, 2.);

  vec4 layer = vec4(color.rgb, pct);
  outColor = mix(outColor, layer, layer.a);

  st.y += 0.2;
  pct = circle(st,vec2(0.0),0.04,0.02);

  vec4 layer1 = vec4(color.rgb, pct);
  outColor = mix(outColor, layer1, layer1.a);

  return outColor;
}

3. demo代码

<body>
  <div id="container"></div>
  <script type="text/javascript" src="../three/build/three.js"></script>

  <script>
    var container;
    var camera, scene, planeMesh, renderer;
    var clock = new THREE.Clock(); // 创建THREE.Clock对象
    var uniforms = {
      u_resolution: {
        type: "v2",
        value: new THREE.Vector2()
      },
      radius: {
        type: "f",
        value: 0.5
      },
      u_time: {
        type: "f",
        value: 0.5
      }
    };
    var vertexShader = `
    attribute vec3 position;
    void main() {
      gl_Position = vec4( position, 1.0 );
    }
    `
    var fragmentShader = `
    #define PI 3.1415926535897932384626433832795
    #ifdef GL_ES
    precision mediump float;
    #endif
    uniform vec2 u_resolution;
    uniform float u_time;

    //二维旋转矩阵
    mat2 rotate2d(float _angle){
      return mat2(cos(_angle),-sin(_angle),sin(_angle),cos(_angle));
    }
    
    float dot2(in vec2 v ) { return dot(v,v); }

    //绘制梯形
    float trapezoid( in vec2 p, in float topWidth, float bottomWidth, float height,float blur )
    {

      if(topWidth > bottomWidth){
        p *= rotate2d(PI);
      }
      float width = max(topWidth,bottomWidth);
      float k1 = height/((bottomWidth-topWidth)/2.0);
      float fun = p.y-k1*p.x-width;
      float k2 = -height/((bottomWidth-topWidth)/2.0);
      float fun2 = p.y-k2*p.x-width;

      if(p.y > -height/2.0 && p.y < height/2.0){

        //return smoothstep(k1*p.x+width,k1*p.x,fun);
        return smoothstep(k1*p.x+blur*width,k1*p.x,fun)-smoothstep(k2*p.x,k2*p.x + blur*width,fun2);
      
      }else{
        return 0.0;
      }
    } 

    float circle(vec2 uv,vec2 p, float r,float blur){

      float dist = length(uv-p);//计算屏幕上任意点到指定点p点的距离
      float c = smoothstep(r,r-blur,dist);

      return c;
    }

    vec4 drawOutCirclePoint(vec2 st){
      vec4 outColor = vec4(0.0);
      vec2 outerVertexs[60];

      float outerRadius = 0.5;

      float angle = 0.0;

      float one_point_angle = PI * 2.0 / 60.0;

      for(int i = 0; i < 60; i++){
        angle += one_point_angle;
        outerVertexs[i] = vec2(cos(angle)*outerRadius,sin(angle)*outerRadius);
      } 

      float pct = 0.0;

      //绘制外层圆点
      for(int i = 0; i < 60;i++){
        vec3 pointColor = vec3(0.17,0.97,1.0);
        pct = circle(st,outerVertexs[i],0.01,0.002);
        vec4 layer = vec4(pointColor.rgb, pct);
        //混合图层
        outColor = mix(outColor, layer, layer.a);
      }

      return outColor;
    }
    //绘制外层旋转圆环
    vec4 drawOuterFlowingRing(vec2 st){ 
      vec2 p = st;
      float time = u_time * 1.5;
      vec3 outRingColor = vec3(0.17,0.97,1.0);
      
      float angle = -(time - sin(time + PI) * cos(time )) - time *.95;
      mat2 rot = mat2(cos(angle),sin(angle),-sin(angle),cos(angle));
      p = rot * p;
      
      float L = length(p);
      float f = 0.;
      
      f = smoothstep(L-.005, L, .51);
      f -= smoothstep(L,L + 0.005, .49);
      //f = step(sin(L * 200. + u_time * p.x)*.5+.5,.25); 
      
      float t = mod(time,2.0*PI) - PI;
      float t1 = sin(t) *  (PI - .25);
      
      float a = atan(p.y,p.x) ;
      f = f * step(a,t1);

      vec4 outColor = vec4(outRingColor.rgb, f);
      return outColor;
    }

    //绘制中层圆环
    vec4 drawInnerRing(vec2 st){ 
      vec3 ringColor = vec3(202.0/255.0,73.0/255.0,205.0/255.0);
      float pct = circle(st,vec2(0.0),0.4,0.02)-circle(st,vec2(0.0),0.3,0.02);
      vec4 outColor = vec4(ringColor.rgb, pct);
      return outColor;
    }

    //绘制中心感叹号
    vec4 drawExclamatoryMark(vec2 st) {
      vec3 color = vec3(0.17,0.97,1.0);
      vec4 outColor = vec4(0.0);
      float pct = 0.0;

      st.y -= 0.05;
      pct = trapezoid( st, 0.28, 0.24, 0.2, 2.);

      vec4 layer = vec4(color.rgb, pct);
      outColor = mix(outColor, layer, layer.a);

      st.y += 0.2;
      pct = circle(st,vec2(0.0),0.04,0.02);

      vec4 layer1 = vec4(color.rgb, pct);
      outColor = mix(outColor, layer1, layer1.a);

      return outColor;
    }

    void main( void ) {

      //转换为窗口坐标[0,1],坐标原点在屏幕左下角
      //vec2 st = gl_FragCoord.xy/u_resolution.y;
      //窗口坐标调整为[-1,1],坐标原点在屏幕中心
      vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;

      vec4 lastColor = vec4(0.0);

      //绘制外层环绕圆点
      vec4 layer1 = drawOutCirclePoint(st);
      lastColor = mix(lastColor, layer1, layer1.a);

      //绘制外层旋转圆环
      vec4 layer2 = drawOuterFlowingRing(st);
      lastColor = mix(lastColor, layer2, layer2.a);

      //绘制中层紫色圆环
      vec4 layer3 = drawInnerRing(st);
      lastColor = mix(lastColor, layer3, layer3.a);

      //绘制中心感叹号
      vec4 layer4 = drawExclamatoryMark(st);
      lastColor = mix(lastColor, layer4, layer4.a);

      gl_FragColor = lastColor;
    }
    `
    init();
    animate();

    function init() {
      container = document.getElementById('container');

      camera = new THREE.Camera();

      scene = new THREE.Scene();


      var geometry = new THREE.PlaneBufferGeometry(2, 2);

      var material = new THREE.RawShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertexShader,
        fragmentShader: fragmentShader
      });

      planeMesh = new THREE.Mesh(geometry, material);


      scene.add(planeMesh);


      renderer = new THREE.WebGLRenderer();
      renderer.setSize(1000, 800); //设置窗口大小800px*800px

      container.appendChild(renderer.domElement);
      uniforms.u_resolution.value.x = renderer.domElement.width;
      uniforms.u_resolution.value.y = renderer.domElement.height;

    }


    function animate() {
      requestAnimationFrame(animate);

      const elapsed = clock.getElapsedTime();

      planeMesh.material.uniforms.u_time.value = clock.getElapsedTime();

      renderer.render(scene, camera);
    }
  </script>
</body>

以上是关于纯shader实现动态警告可视化组件(three.js实战16)的主要内容,如果未能解决你的问题,请参考以下文章

纯shader实现移动的箭头(three.js实战15)

纯shader实现雷达扫描效果(three.js实战13)

一个展示百分比的动态可视化组件(three.js实战11)

ShaderJoy —— 纯 shader 实现立方体的渲染(含线框效果,虚线线框效果),带你了解渲染管线内部细节和原理GLSL

开源Gio.js:一个基于 Three.js 的 Web3D 地球数据可视化库

three.js / glsl 用 shader 写出夜空中的萤火虫