shader编程-通过交集并集差集实现形状的合并(WebGL-Shader开发基础04)

Posted 点燃火柴

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shader编程-通过交集并集差集实现形状的合并(WebGL-Shader开发基础04)相关的知识,希望对你有一定的参考价值。

shader编程-通过交集、并集、差集实现形状的合并

1. 准备

先复习一下数学概念,交集、并集、差集如下图所示



我们准备拿圆做实验,绘制圆的函数如下

float circle(vec2 st,vec2 center,float radius) {   
  float blur = 0.002;
  float pct = distance(st,center);//计算任意点到圆心的距离
  return 1.0-smoothstep(radius,radius+blur,pct);
}  

2. 交集实现

交集是求两个集合相交的部分,实现的方式有两种,一个是使用min函数,另外一种是将二者相乘,结果是一样的,为了验证给出了两个求交集的函数,如下

//交集
float shapeIntersection(float a,float b){
  return min(a,b);
}

float shapeIntersection1(float a,float b){
  return a*b;
}

调用过程是首先绘制了两个圆,然后对这两个圆求交,具体如下

 void main( void ) {
   //窗口坐标调整为[-1,1],坐标原点在屏幕中心
   vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;

   vec3 line_color = vec3(1.0,1.0,0.0);
   vec3 color = vec3(0.6);//背景色
   float pct = 0.0;

   //绘制两个圆
   float cirlce = circle(st,vec2(-0.2,0.0),0.65);
   float cirlce1 = circle(st,vec2(0.2,0.0),0.65);

   //交集
   pct = shapeIntersection(cirlce,cirlce1);
   //pct = shapeIntersection1(cirlce,cirlce1);
   
   color = mix(color,line_color,pct);
   gl_FragColor = vec4(color, 1);
 }

结果如下

3. 并集实现

并集是求两个集合所覆盖的全部的部分,实现的方式也有两种,一个是使用max函数,另外一种是将二者相加,如下

//并集
float shapeUnion(float a,float b){
  return max(a,b);
}
float shapeUnion1(float a,float b){
  return a+b;
}

调用过程非常简单在上一步的基础上直接调用函数求并集即可

//并集
pct = shapeUnion(cirlce,cirlce1);
//pct = shapeUnion1(cirlce,cirlce1);

结果如下

3. 差集实现

差集的实现,其实要先求交集,然后用集合A减去二者的交集就得到A集合相对于B集合的差集,代码如下

//差集
float shapeDifferenceSet(float a,float b){
  return a-min(a,b);
}

调用过程就不解释了,直接看执行结果

4. 示例代码

<body>
  <div id="container"></div>
  <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
  <script>
    var container;
    var camera, scene, renderer;
    var uniforms;
    var vertexShader = `
      void main() {
        gl_Position = vec4( position, 1.0 );
      } 
    `
    var fragmentShader = `
    #ifdef GL_ES
    precision mediump float;
    #endif
    uniform float u_time;
    uniform vec2 u_mouse;
    uniform vec2 u_resolution;

    float circle(vec2 st,vec2 center,float radius) {   
      float blur = 0.002;
      float pct = distance(st,center);//计算任意点到圆心的距离
      return 1.0-smoothstep(radius,radius+blur,pct);
    }   

    //交集
    float shapeIntersection(float a,float b){
      return min(a,b);
    }
    float shapeIntersection1(float a,float b){
      return a*b;
    }

    //并集
    float shapeUnion(float a,float b){
      return max(a,b);
    }
    float shapeUnion1(float a,float b){
      return a+b;
    }

    //差集
    float shapeDifferenceSet(float a,float b){
      return a-min(a,b);
    }

    void main( void ) {

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

      //窗口坐标调整为[0,1],坐标原点在屏幕左下角
      //vec2 st = gl_FragCoord.xy/u_resolution;

      vec3 line_color = vec3(1.0,1.0,0.0);
      vec3 color = vec3(0.6);//背景色
      float pct = 0.0;

      //绘制两个圆
      float cirlce = circle(st,vec2(-0.2,0.0),0.65);
      float cirlce1 = circle(st,vec2(0.2,0.0),0.65);

      //交集
      pct = shapeIntersection(cirlce,cirlce1);
      pct = shapeIntersection1(cirlce,cirlce1);

      //并集
      pct = shapeUnion(cirlce,cirlce1);
      //pct = shapeUnion1(cirlce,cirlce1);

      //差集
      pct = shapeDifferenceSet(cirlce,cirlce1);
      
      color = mix(color,line_color,pct);

      gl_FragColor = vec4(color, 1);
    }
    `

    init();
    animate();

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

      camera = new THREE.Camera();
      camera.position.z = 1;

      scene = new THREE.Scene();

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

      uniforms = {
        u_time: {
          type: "f",
          value: 1.0
        },
        u_resolution: {
          type: "v2",
          value: new THREE.Vector2()
        },
        u_mouse: {
          type: "v2",
          value: new THREE.Vector2()
        }
      };

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

      var mesh = new THREE.Mesh(geometry, material);
      scene.add(mesh);

      renderer = new THREE.WebGLRenderer();
      //renderer.setPixelRatio(window.devicePixelRatio);

      container.appendChild(renderer.domElement);

      onWindowResize();
      window.addEventListener('resize', onWindowResize, false);

      document.onmousemove = function (e) {
        uniforms.u_mouse.value.x = e.pageX
        uniforms.u_mouse.value.y = e.pageY
      }
    }

    function onWindowResize(event) {
      renderer.setSize(800, 800);
      uniforms.u_resolution.value.x = renderer.domElement.width;
      uniforms.u_resolution.value.y = renderer.domElement.height;
    }

    function animate() {
      requestAnimationFrame(animate);
      render();
    }

    function render() {
      uniforms.u_time.value += 0.02;
      renderer.render(scene, camera);
    }
  </script>
</body>

以上是关于shader编程-通过交集并集差集实现形状的合并(WebGL-Shader开发基础04)的主要内容,如果未能解决你的问题,请参考以下文章

shader编程-三维场景下使用交集并集差集方法CSG建模(WebGL-Shader开发基础10)

shader编程-RayMarching三维场景下使用交集并集差集方法CSG建模(WebGL-Shader开发基础10)

shader编程-RayMarching三维场景下使用交集并集差集方法CSG建模(WebGL-Shader开发基础10)

shader编程-RayMarching三维场景下使用交集并集差集方法CSG建模(WebGL-Shader开发基础10)

shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)

shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)