2D SDF推导3:长方形与圆角

矩形

长方形可以通过中心点、宽度和高度定义。点位置的分布主要有三种情况,分别是:

  1. P1 点在长方形内部:对于在长方形内部的点,其到最近边界的有符号距离是负值,其绝对值是点到最近边界的最小距离。
  2. P3, P2 点在长方形外部,水平或垂直对齐:如果点位于长方形的延长线上(即垂直或水平对齐),则其到长方形的最近距离将是其到最近边的直线距离。
  3. P4 点在长方形外部,不与任何边对齐:对于不与长方形边界平行或垂直的外部点,其到长方形的最近距离是点到长方形最近角的欧几里得距离。

设长方形中心为 <math xmlns="http://www.w3.org/1998/Math/MathML"> C ( x c , y c ) C(x_c, y_c) </math>C(xc,yc)为中心点 <math xmlns="http://www.w3.org/1998/Math/MathML"> C ( 0 , 0 ) C(0, 0) </math>C(0,0)宽度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> w w </math>w,高度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> h h </math>h,任意点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( x , y ) P(x, y) </math>P(x,y)。由于坐标轴具有对称性,只需要考虑第一象限的四种情况,对于其他象限的距离都可以通过绝对值转换到第一象限。

  1. 计算点 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P 水平和垂直方向上到长方形边界的距离 <math xmlns="http://www.w3.org/1998/Math/MathML"> d x = x − w / 2 dx = x - w/2 </math>dx=x−w/2, <math xmlns="http://www.w3.org/1998/Math/MathML"> d y = y − h / 2 dy = y - h/2 </math>dy=y−h/2
  2. 根据dx$$dy正负情况可以判断如何计算距离 <math xmlns="http://www.w3.org/1998/Math/MathML"> d d </math>d
    1. P1 点在长方形内部, 即 **dx<0 and dy<0**: <math xmlns="http://www.w3.org/1998/Math/MathML"> d = m a x ( d x , d y ) d = max(dx, dy) </math>d=max(dx,dy)
    2. P3, P2 点在长方形外部,水平或垂直对齐即 **oneOf(dx,dy) < 0****, ** <math xmlns="http://www.w3.org/1998/Math/MathML"> d = m a x ( d x , d y ) d = max(dx, dy) </math>d=max(dx,dy)
    3. P4 点在长方形外部,不与任何边对齐, 即 **dx>0 and dy>0**: <math xmlns="http://www.w3.org/1998/Math/MathML"> d = d x 2 + d y 2 d = \sqrt{dx^2 + dy^2} </math>d=dx2+dy2

变成shader代码非常清晰

glsl 复制代码
float sdf_rectangle1(vec2 pct, vec2 wh) {
  vec2 dxy = abs(pct) - wh;
  if (dxy.x > 0. && dxy.y >0.) {
    return length(dxy);
  } else {
    return max(dxy.x, dxy.y);
  }
}

但是要知道shader里面不喜欢if else. 考虑如何让两个condition变成一条语句。考虑情况2,3。 <math xmlns="http://www.w3.org/1998/Math/MathML"> d = m a x ( d x , 0 ) 2 + m a x ( d y , 0 ) 2 d = \sqrt{max(dx, 0)^2 + max(dy, 0)^2} </math>d=max(dx,0)2+max(dy,0)2 在glsl中向量是支持max函数的,可以进一步简化max(dxy, vec2(0.)),但是上述公式在情况1就不适用了,结果为0, 所以进一步考虑到情况1。 <math xmlns="http://www.w3.org/1998/Math/MathML"> m a x ( m i n ( d x , 0 ) , m i n ( d y , 0 ) ) max(min(dx, 0), min(dy,0)) </math>max(min(dx,0),min(dy,0)) 上面公式在情况2,3都为0, 所以最后只需要将两个公式相加,便得到最后的结果,shader代码如下

glsl 复制代码
float sdf_rectangle2(vec2 pct, vec2 wh) {
  vec2 dxy = abs(pct) - wh;
  return length(max(dxy, 0.0)) + max(min(dxy.x, 0.0), min(dxy.y, 0.0));
}

最后得到以下的图形

圆角

实现圆角的原理非常简单,如上图所示,内圈的矩形是实际的矩形,将内圈矩形增加圆角的radius。获取到distance之后,在减去radius便可以实现圆角效果。 具体代码如下

glsl 复制代码
float sdf_rounded_rectangle(vec2 pct, vec2 wh, float radius) {
  vec2 dxy = abs(pct) - wh + radius;
  return length(max(dxy, 0.0)) +  // one of (dx, dy) greater than 0 
          max(min(dxy.x, 0.0), min(dxy.y, 0.0))- // dx, dy lower than 0
          radius;
}

得到下面的效果图

相关推荐
千鼎数字孪生-可视化10 小时前
Web技术栈重塑HMI开发:HTML5+WebGL的轻量化实践路径
前端·html5·webgl
Data_Adventure2 天前
推荐几款开源 Canvas 和 WebGL 图形库
前端·webgl·canvas
康康的幸福生活4 天前
webgl2 方法解析: bufferData()
webgl
xhload3d4 天前
智慧航天运载体系全生命周期监测 | 图扑数字孪生
物联网·3d·智慧城市·html5·webgl·数字孪生·可视化·工业互联网·三维建模·工控·航空航天·火箭升空·智慧航空·智慧航天·火箭发射·火箭回收
魂断蓝桥6664 天前
使用three.js,实现微信3D小游戏系列教程,框架篇(一)
webgl·threejs·微信小游戏·3d建筑·three.js路径规划、三维a*算法、javascript三维导航,·three.js小游戏
三维搬砖者5 天前
基于 Three.js 开发三维引擎-01点类:从原理到实践
webgl·three.js
魂断蓝桥6665 天前
如何基于three.js(webgl)引擎架构,实现3D微信小游戏(第一课)
webgl·three.js·微信小游戏·three.js路径规划、三维a*算法、javascript三维导航,·three.js小游戏
康康的幸福生活6 天前
webgl2 方法解析: getContext()
webgl
庖丁解牛7 天前
3. Babylonjs 中获取相机方向相关
前端·webgl·游戏开发
康康的幸福生活7 天前
webgl2 方法解析: createBuffer()
前端·javascript·webgl