webgl shader学习 有向距离场2

感谢 https://blog.csdn.net/qq_41368247/article/details/106214710,这篇博客是实践,

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>demo5_1 距离场2</title>
  <script src="../external/three.js"></script>
  <script src="../controls/OrbitControls.js"></script>
  <style>
    body {
      overflow: hidden;
      padding: 0;
      margin: 0;
    }
  </style>
</head>
<body>
<div id="container"></div>

<script id="vertexShader" type="x-shader/x-vertex">
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
//  #version 300 es
  uniform vec2 u_resolution;
  uniform float u_time;
  varying vec2 vUv;
  uniform vec2 u_mouse;
  // Signed distance and circle

  // List of some other 2D distances:
  //
  // Circle:               https://www.shadertoy.com/view/3ltSW2
  // Segment:              https://www.shadertoy.com/view/3tdSDj
  // Triangle:             https://www.shadertoy.com/view/XsXSz4
  // Isosceles Triangle:   https://www.shadertoy.com/view/MldcD7
  // Regular Triangle:     https://www.shadertoy.com/view/Xl2yDW
  // Regular Pentagon:     https://www.shadertoy.com/view/llVyWW
  // Regular Octogon:      https://www.shadertoy.com/view/llGfDG
  // Rounded Rectangle:    https://www.shadertoy.com/view/4llXD7
  // Rhombus:              https://www.shadertoy.com/view/XdXcRB
  // Trapezoid:            https://www.shadertoy.com/view/MlycD3
  // Polygon:              https://www.shadertoy.com/view/wdBXRW
  // Hexagram:             https://www.shadertoy.com/view/tt23RR
  // Regular Star:         https://www.shadertoy.com/view/3tSGDy
  // Star5:                https://www.shadertoy.com/view/wlcGzB
  // Ellipse 1:            https://www.shadertoy.com/view/4sS3zz
  // Ellipse 2:            https://www.shadertoy.com/view/4lsXDN
  // Quadratic Bezier:     https://www.shadertoy.com/view/MlKcDD
  // Uneven Capsule:       https://www.shadertoy.com/view/4lcBWn
  // Vesica:               https://www.shadertoy.com/view/XtVfRW
  // Cross:                https://www.shadertoy.com/view/XtGfzw
  // Pie:                  https://www.shadertoy.com/view/3l23RK
  // Arc:                  https://www.shadertoy.com/view/wl23RK
  // Horseshoe:            https://www.shadertoy.com/view/WlSGW1
  // Parabola:             https://www.shadertoy.com/view/ws3GD7
  // Parabola Segment:     https://www.shadertoy.com/view/3lSczz
  // Rounded X:            https://www.shadertoy.com/view/3dKSDc
  // Joint:                https://www.shadertoy.com/view/WldGWM
  // Simple Egg:           https://www.shadertoy.com/view/Wdjfz3
  //
  // and many more here:   http://www.iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm

  /**
  * 正五边形  1. 原点在中心点, 中心点到某一个顶点的向量沿y轴负半轴
  *          2. r表示中心点到边的距离(不是到顶点的距离)
  */
  float sdPentagon( in vec2 p, in float r )
  {
    // sin54°, cos54°, tan36°, !!!注意,k仅作为一个数据的集合!
    const vec3 k = vec3(0.809016994,0.587785252,0.726542528);
    p.x = abs(p.x); // 左右对称
    // 1. 先映射:同样是分为三部分,上部+右上+右下,都要映射到上部
    // 	 将op在(-k.x, k.y)上投影,得到一个系数,若系数是正数说明p点在上部不用处理,被min(0.0, )滤去
    // 	 若为负数说明在目标范围内,p加上1倍的系数*单位垂直向量就可以到达对称轴处
    // 	 再加一倍就到达对称点处,因此下面要乘2,-=其实和上面说到的垂直向量的方向有关
    p -= 2.0*min(dot(vec2(-k.x,k.y),p),0.0)*vec2(-k.x,k.y);
    // 原先在右下部的区域,还得再映射一次,这里的min再滤去上部的部分
    p -= 2.0*min(dot(vec2( k.x,k.y),p),0.0)*vec2( k.x,k.y);

    // 2. 计算方便计算距离的区域
    // 	  这里就是简单的考虑上部就好,因为右上映射上去后取值范围是(-,+),因此正负都要考虑,
    // 	  r*k.z是得到半边长,得到的最后的p是处于上部的点做一条垂线到五边形的上部水平边
    p -= vec2(clamp(p.x,-r*k.z,r*k.z),r);
    return length(p)*sign(p.y);
  }

  /**
  * 正六边形  1. 原点在中心点,有两条边与x轴平行
  *          2. r表示中心点到边的距离(不是到顶点的距离)
  */
  float sdHexagon( in vec2 p, in float r )
  {
    // -cos30, sin30, tan30, !!! 注意,k仅作为一个数据的集合
    const vec3 k = vec3(-0.866025404, 0.5, 0.577350269);
    p = abs(p); // xy轴对称,因此全部只看第一象限即可
    // 观察图像可以看出我们选择的对称轴应该是(cos60, sin60)==(sin30, cos30)
    // (-cos30, sin30)也就是k.xy,是与选择的对称轴垂直的单位向量
    p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
    // 下面同理
    p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
    return length(p)*sign(p.y);
  }

  /**
  * 六角星   1. 原点在中心点,中心点到某个角的连线于y轴平行
  *          2. r表示中心点顶点的距离的1/2
  */
  float sdHexagram( in vec2 p, in float r )
  {
    // -cos60, sin60, tan30, tan60
    const vec4 k = vec4(-0.5,0.8660254038,0.5773502692,1.7320508076);
    p = abs(p);
    // 大体如上,需要注意的是这次的两次对称实际上互不干扰,我们最终要映射到是一条水平边
    // 在六角星中,每个象限内由三条边,而水平边刚好在中间,因此两次对称不干扰(min()都会过滤掉对方)
    // k.xy表示下图红线的垂直单位向量, k.yx表示下图蓝线的垂直单位向量
    p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
    p -= 2.0*min(dot(k.yx,p),0.0)*k.yx;
    p -= vec2(clamp(p.x,r*k.z,r*k.w),r); // 注意水平边的起止范围
    return length(p)*sign(p.y);
  }

  /**
 * N角星  1. 原点在中心点,中心点和凸角的连线在x轴上
 *        2. r表示中心点到某个凸角的距离
 *        3. n表示几个凸角,m表示角的尖锐程度,具体是pi/m = 凹角(向内)的一半,
 * 			例如 m=2,凹角的一半是九十度也就是凹角是一百八,就是不凹,是正多边形
 * 			1<m<2则突出,正常情况下m=[2,n]
 *  例如正五角星,凸角是36°,凹角是252°,周角360 -252 = 108°,(108/2)/180 = PI / m; 则 m = 10/3 = 3.33333...
*/
  float sdStar(in vec2 p, in float r, in int n, in float m)
  {
    // 先计算所需的角度正余弦数据
    float an = 3.141593/float(n);  // 某两个相邻角与原中心点连线形成的角度的一半
    float en = 3.141593/m;         // 凹角的一半
    // 分别计算正余弦
    vec2  acs = vec2(cos(an),sin(an));
    vec2  ecs = vec2(cos(en),sin(en));  // 特殊地,当ecs=(0, 1)时,表示的是正多边形

    // 1. 映射:
    // (这里对iq大神的原代码进行了小修改,只是最终方向有所改变而已)
    // 原句: float bn = mod(atan(p.x,p.y), 2.0*an) - an;
    float bn = mod(atan(p.y,p.x)+an, 2.0*an) - an;
    // 原op的长度保持不变,方向调整到bn方向
    p = length(p)*vec2(cos(bn),abs(sin(bn)));

    // 2. 计算最短距离和符号
    p -= r*acs;
    p += ecs*clamp(dot(p, -ecs), 0.0, r*acs.y/ecs.y);
    return length(p)*sign(p.x);
  }

  /**
  * 十字  1. 原点在十字中心
  *       2. 十字型由两个相同的长方形垂直组成
  *          b.x表示长方形长的一半,b.y表示宽的一半,若b.x==b.y则最终是一个正方形图形
  *       3. r是最终距离的偏移量,不影响整体计算(可以通过调节r得到更漂亮的图形)
  */
  float sdCross( in vec2 p, in vec2 b, float r )
  {
    // 四象限对称
    p = abs(p);
    // 关于直线y=x对称
    p = (p.y>p.x) ? p.yx : p.xy;
    // 假设十字横着的长方形右上角点a, 那么oa就是向量b
    // q是从点a指向点p的向量
    vec2  q = p - b;
    // 三句话实现了五个区域的计算
    float k = max(q.y,q.x);
    vec2  w = (k>0.0) ? q : vec2(b.y-p.x,-k);
    return sign(k)*length(max(w,0.0)) + r;  // r调节最终结果
  }

  /**
  * y轴对称的梯形  1. 原点在中心点(x=底的一半,y=高的一半处)
  *               2. r1:下底的一半,r2:上底的一半, h:高的一半
  */
  float sdTrapezoidY( in vec2 p, in float r1, float r2, float he )
  {
    // 原点指向右上角顶点的向量
    vec2 k1 = vec2(r2,he);
    // 右腰
    vec2 k2 = vec2(r2-r1,2.0*he);
    // 左右对称图形,只看右边
    p.x = abs(p.x);

    // 同样是三块区域,根据距离最短点的位置,划分为:上底部分+下底部分+腰部分
    // 上底下底可以统一处理,即ca,比较容易判断,在y负半轴的点x减去下底长度
    //  y上半轴的点x减上底,小于0说明x轴投影在内部,此时最小距离就是p.y-h
    // 似乎这样可以直接返回了,其实不然,在梯形外部的点确实可以直接返回p.y-h但是在梯形内部的点
    // 我们不清楚它是距离底近还是距腰近(梯形外部的点可以很快判断),所以不能就此返回,最后面要比较
    // 1. 计算与(上/下)底的距离
    vec2 ca = vec2(max(0.0,p.x-((p.y<0.0)?r1:r2)), abs(p.y)-he);

    // 当然,确定的在梯形外部的上底部分和下底部分,可以直接返回
    // if(ca.x<=0.0 && ca.y>0.0) return ca.y;

    // 2. 计算和腰的距离
    vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot(k2, k2), 0.0, 1.0 );
    // 内外判断
    float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0;
    // 取最小的
    return s*sqrt( min(dot(ca, ca),dot(cb, cb)) );
  }

  /**
  * 任意方向的梯形  1. 原点并不指定
  *                2. a表示下底中心点,b上底中心点,ra是下底的一半长度
  */
  float sdTrapezoid( in vec2 p, in vec2 a, in vec2 b, in float ra, float rb )
  {
    // 首先还是先计算
    float rba  = rb-ra;  		// 两半底长度的差
    float baba = dot(b-a,b-a);  // 高的长度的平方,重要
    // ap需要注意,对照Y轴版本,其实这个就是op
    float papa = dot(p-a,p-a);
    // 此处是理解的关键,paba表示的 pa在高ba上的投影的长度占ba长度的比例,范围[0,1]
    // 这个量在下面会经常和0.5比较,因为我们上面的Y轴对称版本里面的h其实是高的一半
    // paba在下面出现时,我们可以认为它就是上面Y对称版本的p.y
    // 当然 paba*sqrt(baba)才是ap在ab上的投影的实际长度,最后一步的时候才会乘此baba
    float paba = dot(p-a,b-a)/baba;

    // 同样分为两段,先计算和底的距离再计算和腰的距离,仔细观察会发现和Y版本一模一样,只是原来的p.y, p.x被替换了
    // 上面我们说 paba可以看作Y版本的p.y,那么这一步就是计算Y版本的p.x
    // 就是勾股定理,斜边是pa,一条直角边是pa在ba(也就是高)上的投影,那么画图我们就能发现,得到的其实是p点距离ba的垂线的长度,
    // 至此其实我们已经相当于构建了一个新的坐标系,新坐标系里面的y轴是梯形的高,也就是ba
    //  x轴是垂直与y轴的,下面得到的x其实是p点在新x轴上投影的长度,我们视为新x,地位相当于Y版本的p.x
    float x = sqrt( papa - paba*paba*baba );

    // 与Y版本相似,当然我们说将paba视为前面的p.y其实不那么准确,paba-0.5才是视为前面的p.y
    // 原因是两个坐标系选择的不同,新坐标系的原点在下底的中点,而Y版本中原点在梯形的中心点,
    // 因此两个坐标系的y轴相对于梯形图像可以视为差了半个高的长度,所以paba-0.5才是视为前面的p.y
    float cax = max(0.0,x-((paba<0.5)?ra:rb));
    float cay = abs(paba-0.5)-0.5;

    // k是腰长的平方
    float k = rba*rba + baba;
    // 也和Y版本一样,k就相当于dot2(k2)
    // 原来的(k1-p)现在是:(x-ra, paba*sqrt(baba)), 原来的右腰k2现在是(rba, sqrt(baba))
    // 点乘即右下底顶点指向P点的向量在腰上投影的长度所占腰长比例
    float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 );
    // 例如f==1时,cbx = x-ra-rba = x-rb, cby = paba - 1.0(这种情况paba肯定>1),这两个分量就表示p点到腰的垂线(反向)
    float cbx = x-ra - f*rba;
    // 这里的cby和上面的cay一样只是一个比例,需要乘baba才是真的距离
    float cby = paba - f;

    float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0;
    return s*sqrt( min(cax*cax + cay*cay*baba,      // 注意乘baba,即高的长度
    cbx*cbx + cby*cby*baba) );
  }

  /**
  * 扇形  1. 原点在圆心处,图形以y轴为对称轴
  *       2. r半径, c是单位向量,扇形右半边界方向
  */
  float sdPie( in vec2 p, in vec2 c, in float r ) {
    // 对称
    p.x = abs(p.x);
    // 1.计算与弧的部分的距离
    float l = length(p) - r;
    // 2.与直线段边界的距离
    float m = sign(c.y*p.x-c.x*p.y) * length(p-c*clamp(dot(p,c),0.0,r));
    // 3. 比较
    return max(l,m);
  }

  /**
  * 弧  1. 原点在圆心处
  *     2. sca表示弧的中心点和原点连线的单位向量,主要用于旋转
  *     3. scb表示原点和右端点的圆心的连线的单位向量
  *     4. ra表示原点到一个端点圆心的距离,rb表示端点圆的半径,也就是弧的宽度的一半
  */
  float sdArc( in vec2 p, in vec2 sca, in vec2 scb, in float ra, float rb )
  {
    // 先旋转到可以左右对称的角度
    // 注意,默认是sca表示pi/2,因此旋转矩阵的角度是:sca角度-pi/2(若sca传入pi/2,那实际上旋转0°)
    // cos(x - pi/2) = sinx   sin(x - pi/2) = -cosx
    // 右乘的旋转矩阵   [cosθ, sinθ ]     [sin, -cos]
    //                 [-sinθ, cosθ]  =  [cos, sin]
    p *= mat2(sca.y,-sca.x,sca.x,sca.y);
    p.x = abs(p.x);
    // k = p*cosθ
    float k = (scb.y*p.x>scb.x*p.y) ? dot(p.xy,scb) : length(p.xy);
    return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb;
  }

  /**
  * Vesica: 1. 实际上是一个圆心在x轴正半轴上的圆,求其关于y轴的镜像,两部分之和
  *          2. r表示圆的半径,d表示圆心与y轴的距离
  * 			  为使图形看起来正常(联通),d的取值范围应该是(-r, r)
  */
    float sdVesica(vec2 p, float r, float d)
    {
      p = abs(p); // 对称
      // (0, b)是两圆的上交点
      float b = sqrt(r*r-d*d);
      // 分为两个部分,一个是最短距离点在圆弧上,一个是该最短距离点在(0, b)处,
      // 判断方法是看op在向量(-d, b)的左边还是右边,左边(逆时针)则取(0,b)点,否则取弧
      return ((p.y-b)*-d>p.x*b) ? length(p-vec2(0.0,b))*sign(-d)  // 与(0, b)点(两圆交点)距离
      : length(p-vec2(d,0.0))-r;    // 与圆心距离
    }

  /**
  * 叉  1. 原点在叉的中心,是两条与x轴角度分别为45°和-45°的椭圆/线条组合而成
  *     2. w是叉的长度,r是宽度,r只需要最后调节即可
  */
  float sdRoundedX( in vec2 p, in float w, in float r )
  {
    // 四个象限对称
    p = abs(p);
    // 投影到(1, 1)
    return length(p-min(p.x+p.y, w)*0.5) - r;
  }

  /**
  * 多边形, N是确定的,GLSL里面不支持动态数组,v是各顶点的坐标的数组
  */
  const int N = 5;
  float sdPoly( in vec2[N] v, in vec2 p )
  {
    const int num = v.length();
    float d = dot(p-v[0],p-v[0]);
    float s = 1.0;
    for( int i=0, j=num-1; i<num; j=i, i++ )
    {
      // 计算p点和每条边的最短距离
      vec2 e = v[j] - v[i];
      vec2 w =    p - v[i];
      vec2 b = w - e*clamp( dot(w,e)/dot(e,e), 0.0, 1.0 );
      d = min( d, dot(b,b) );

      // 通过缠绕数的奇偶规则来判断内外
      bvec3 cond = bvec3( p.y>=v[i].y, p.y<v[j].y, e.x*w.y>e.y*w.x );
      if( all(cond) || all(not(cond)) ) s*=-1.0;
    }
    return s*sqrt(d);
  }

// ab.x 表示长轴长度,ab.y表示短轴长度
float sdEllipse( in vec2 p, in vec2 ab )
{
  p = abs(p); if( p.x > p.y ) {p=p.yx;ab=ab.yx;}
  float l = ab.y*ab.y - ab.x*ab.x;
  float m = ab.x*p.x/l;      float m2 = m*m;
  float n = ab.y*p.y/l;      float n2 = n*n;
  float c = (m2+n2-1.0)/3.0; float c3 = c*c*c;
  float q = c3 + m2*n2*2.0;
  float d = c3 + m2*n2;
  float g = m + m*n2;
  float co;
  if( d<0.0 )
  {
    float h = acos(q/c3)/3.0;
    float s = cos(h);
    float t = sin(h)*sqrt(3.0);
    float rx = sqrt( -c*(s + t + 2.0) + m2 );
    float ry = sqrt( -c*(s - t + 2.0) + m2 );
    co = (ry+sign(l)*rx+abs(g)/(rx*ry)- m)/2.0;
  }
  else
  {
    float h = 2.0*m*n*sqrt( d );
    float s = sign(q+h)*pow(abs(q+h), 1.0/3.0);
    float u = sign(q-h)*pow(abs(q-h), 1.0/3.0);
    float rx = -s - u - c*4.0 + 2.0*m2;
    float ry = (s - u)*sqrt(3.0);
    float rm = sqrt( rx*rx + ry*ry );
    co = (ry/sqrt(rm-rx)+2.0*g/rm-m)/2.0;
  }
  vec2 r = ab * vec2(co, sqrt(1.0-co*co));
  return length(r-p) * sign(p.y-r.y);
}

float sdbEllipsoidV1( in vec2 p, in vec2 r )
{
  float k1 = length(p/r);
  return (k1-1.0)*min(r.x,r.y);
}

// 改进型
float sdbEllipsoidV2( in vec2 p, in vec2 r )
{
  float k1 = length(p/r);
  float k2 = length(p/(r*r));
  return (k1-1.0)*k1/k2;
}


  const float PI = 3.1415926;
  const float dd = PI / 180.0;

  void main( )
  {
    vec2 p = vUv * 2.0 - 1.0;

//    float d = sdPentagon(p, 0.4);
//    float d = sdHexagon(p, 0.4);
//    float d = sdHexagram(p, 0.4);
//    float d = sdStar(p, 0.3, 5, 3.3333);
//    float d = sdCross(p, vec2(0.5, 0.15), 0.0); //u_time*0.01
//    float d = sdTrapezoidY(p, 0.2, 0.1, 0.34);
//    float d = sdTrapezoid(p, vec2(0.0), vec2(0.1, 0.3), 0.2, 0.1);
//    float d = sdPie(p, vec2(cos(-77.0 * dd), sin(-77.0 * dd) ), 0.33);  //cos(-33), sin(-33) cos(radians(-77.0))
//    float d = sdArc(p, normalize(vec2(0.4, 0.4)), normalize(vec2(0.4, -0.2)), 0.5, 0.1); //vec2(0.0, 1.0)
//    float d = sdVesica(p, 0.3, 0.12); //sdVesica(p, 0.3, -0.15)
//    float d = sdRoundedX(p, 0.6, 0.1); //sdVesica(p, 0.3, -0.15)
//    vec2[] polygon = vec2[](v0,v1,v2,v3,v4);
//    float d = sdPoly(vec2[](vec2(-0.4, -0.4),vec2(0.4, 0.4),vec2(0.8, 0.2),vec2(-0.5, 0.3),vec2(0.5, -0.6)), p);
//    float d = sdEllipse(p, vec2(0.4, 0.25));
//    float d = sdbEllipsoidV1(p, vec2(0.4, 0.25));
    float d = sdbEllipsoidV2(p, vec2(0.4, 0.25));

    // coloring
    vec3 col = vec3(1.0) - sign(d)*vec3(0.1,0.4,0.7);
    col *= 1.0 - exp(-3.0*abs(d));
    col *= 0.8 + 0.2*cos(150.0*d);  // + u_time*6.0
    col = mix( col, vec3(1.0), 1.0-smoothstep(0.0,0.01,abs(d)) );

    gl_FragColor = vec4(col,1.0);
  }

</script>
<script>
  //https://blog.csdn.net/qq_41368247/article/details/106214710
  var container;
  var camera, scene, renderer;
  var uniforms;
  var mesh;

  init();
  animate();

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

    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    camera.position.set(0, 0, 2);

    scene = new THREE.Scene();
    scene.add(new THREE.AxesHelper(20));

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

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

    var material = new THREE.ShaderMaterial( {
      uniforms: uniforms,
      side: THREE.DoubleSide,
      vertexShader: document.getElementById( 'vertexShader' ).textContent,
      fragmentShader: document.getElementById( 'fragmentShader' ).textContent
    } );
    material.extensions.derivatives = true;
    var material2 = new THREE.MeshBasicMaterial({color: '#00bbbb', wireframe: true, side: THREE.DoubleSide})

    mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
    var canvas = document.createElement( 'canvas' );
    var context = canvas.getContext( 'webgl2' );
    renderer = new THREE.WebGLRenderer( { canvas: canvas, context: context } );
    renderer.setClearColor( 0x0, 1 );
    renderer.setPixelRatio( window.devicePixelRatio );

    container.appendChild( renderer.domElement );
    let orbit = new THREE.OrbitControls( camera, renderer.domElement );

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

  function onWindowResize( event ) {
    renderer.setSize( window.innerWidth, window.innerHeight );
    // 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.01;  //0.008
    renderer.render( scene, camera );
  }

  document.addEventListener('mousemove', onDocumentMouseMove, false);
  function onDocumentMouseMove(event) {
    let vector = new THREE.Vector3(( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5);
    vector = vector.unproject(camera);

    let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    let lst = raycaster.intersectObjects([mesh]);
    if (lst.length == 0) {
      return;
    }
    let curPos = lst[0].point;
    // console.log(curPos);
    let ux = (curPos.x+1)/2, uy = (curPos.y+1)/2;
    uniforms.u_mouse.value.x = ux;
    uniforms.u_mouse.value.y = uy;
    // console.log("ux: " + ux + ", uy: " + uy);
  }
</script>
</body>
</html>

注意 sdEllipse,sdbEllipsoidV1, sdbEllipsoidV2得到的图形是不同的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值