Skip to content

Commit

Permalink
feat: add drop shadow docs
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Jul 2, 2024
1 parent a1cab33 commit 3ca9b4e
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 71 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ pnpm run dev
- Reducing draw calls by combining batches
- Using spatial indexing to improve pickup efficiency

## Lesson 9 - Draw ellipse, rectangle and polyline
## Lesson 9 - Draw ellipse and rectangle

- Draw ellipse and rectangle with SDF
- Draw line
- How to derive the SDF representation of an ellipse or rounded rectangle
- Render drop-shadow for rounded rectangle
- How to determine if a point is inside an ellipse or rounded rectangle

[infinitecanvas]: https://infinitecanvas.tools/
[Figma]: https://madebyevan.com/figma/building-a-professional-design-tool-on-the-web/
Expand Down
7 changes: 4 additions & 3 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,11 @@ pnpm run dev
- 使用合批减少 draw call
- 使用空间索引提升拾取效率

## 课程 9 - 绘制椭圆、矩形和折线
## 课程 9 - 绘制椭圆和矩形

- 使用 SDF 绘制椭圆和矩形
- 绘制折线
- 推导椭圆和圆角矩形的 SDF 表示
- 为圆角矩形增加阴影
- 如何判定任意点是否在椭圆或圆角矩形内

[infinitecanvas]: https://infinitecanvas.tools/
[Figma]: https://madebyevan.com/figma/building-a-professional-design-tool-on-the-web/
Expand Down
4 changes: 2 additions & 2 deletions packages/lesson_009/examples/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const canvas = await new Canvas({
'https://unpkg.com/@antv/[email protected]/dist/pkg/glsl_wgsl_compiler_bg.wasm',
}).initialized;

for (let i = 0; i < 1000; i++) {
for (let i = 0; i < 1; i++) {
const fill = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
Math.random() * 255,
)},${Math.floor(Math.random() * 255)})`;
Expand Down Expand Up @@ -47,7 +47,7 @@ for (let i = 0; i < 1000; i++) {
y: 0,
width: 100,
height: 100,
fill,
fill: 'rgba(190, 190, 190, 1)',
cornerRadius: 10,
});
canvas.appendChild(rect);
Expand Down
37 changes: 29 additions & 8 deletions packages/lesson_009/src/shaders/sdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,17 +132,33 @@ float sdf_rounded_box(vec2 p, vec2 b, float r) {
return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
}
vec4 erf(vec4 x) {
vec4 s = sign(x), a = abs(x);
// vec4 erf(vec4 x) {
// vec4 s = sign(x), a = abs(x);
// x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
// x *= x;
// return s - s / (x * x);
// }
vec2 erf(vec2 x) {
vec2 s = sign(x), a = abs(x);
x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
x *= x;
return s - s / (x * x);
}
float boxShadow(vec2 lower, vec2 upper, vec2 point, float sigma) {
vec4 query = vec4(point - lower, upper - point);
vec4 integral = 0.5 + 0.5 * erf(query * (sqrt(0.5) / sigma));
return (integral.z - integral.x) * (integral.w - integral.y);
// float boxShadow(vec2 lower, vec2 upper, vec2 point, float sigma) {
// vec4 query = vec4(point - lower, upper - point);
// vec4 integral = 0.5 + 0.5 * erf(query * (sqrt(0.5) / sigma));
// return (integral.z - integral.x) * (integral.w - integral.y);
// }
float rect_shadow(vec2 pixel_position, vec2 origin, vec2 size, float sigma) {
vec2 bottom_right = origin + size;
vec2 x_distance = vec2(pixel_position.x - origin.x, pixel_position.x - bottom_right.x);
vec2 y_distance = vec2(pixel_position.y - origin.y, pixel_position.y - bottom_right.y);
vec2 integral_x = 0.5 + 0.5 * erf(x_distance * (sqrt(0.5) / sigma));
vec2 integral_y = 0.5 + 0.5 * erf(y_distance * (sqrt(0.5) / sigma));
return (integral_x.x - integral_x.y) * (integral_y.x - integral_y.y);
}
void main() {
Expand Down Expand Up @@ -196,7 +212,11 @@ void main() {
innerDistance = sdf_rounded_box(v_FragCoord, r, cornerRadius);
}
float a = boxShadow(vec2(-wh, -1.0), vec2(wh, 1.0), v_FragCoord, 5.0);
float sigma = 0.1;
float padding = 3.0 * sigma;
// padding = 0.0;
// float a = boxShadow(vec2(-wh, -1.0) - padding, vec2(wh, 1.0) + padding, v_FragCoord, sigma);
float a = rect_shadow(v_FragCoord, vec2(-wh, -1.0) - padding, 2.0 * vec2(wh, 1.0), sigma);
float opacity_t = clamp(outerDistance / antialiasedBlur, 0.0, 1.0);
Expand All @@ -207,7 +227,8 @@ void main() {
);
outputColor = mix(vec4(fillColor.rgb, fillColor.a * fillOpacity), strokeColor * strokeOpacity, color_t);
outputColor.a = outputColor.a * opacity * opacity_t * 1.0;
outputColor.a = outputColor.a * opacity * opacity_t;
// outputColor.a = a;
if (outputColor.a < epsilon)
discard;
Expand Down
57 changes: 31 additions & 26 deletions packages/lesson_009/src/shapes/Ellipse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,35 +103,30 @@ export class Ellipse extends Shape implements EllipseAttributes {
this.fill,
this.stroke,
);
const squareX = (x - cx) * (x - cx);
const squareY = (y - cy) * (y - cy);
if (hasFill && hasStroke) {
return (
ellipseDistance(
squareX,
squareY,
rx + halfLineWidth,
ry + halfLineWidth,
) <= 1
return isPointInEllipse(
x,
y,
cx,
cy,
rx + halfLineWidth,
ry + halfLineWidth,
);
}
if (hasFill) {
return ellipseDistance(squareX, squareY, rx, ry) <= 1;
return isPointInEllipse(x, y, cx, cy, rx, ry);
}
if (hasStroke) {
return (
ellipseDistance(
squareX,
squareY,
!isPointInEllipse(
x,
y,
cx,
cy,
rx - halfLineWidth,
ry - halfLineWidth,
) >= 1 &&
ellipseDistance(
squareX,
squareY,
rx + halfLineWidth,
ry + halfLineWidth,
) <= 1
) &&
isPointInEllipse(x, y, cx, cy, rx + halfLineWidth, ry + halfLineWidth)
);
}
return false;
Expand All @@ -153,11 +148,21 @@ export class Ellipse extends Shape implements EllipseAttributes {
}
}

function ellipseDistance(
squareX: number,
squareY: number,
rx: number,
ry: number,
function isPointInEllipse(
x: number,
y: number,
h: number,
k: number,
a: number,
b: number,
) {
return squareX / (rx * rx) + squareY / (ry * ry);
// 计算点到椭圆中心的 x 和 y 坐标差
const dx = x - h;
const dy = y - k;

// 计算点相对于椭圆中心的坐标平方,然后除以半轴长度的平方
const squaredDistance = (dx * dx) / (a * a) + (dy * dy) / (b * b);

// 如果计算结果小于或等于 1,则点在椭圆内
return squaredDistance <= 1;
}
Loading

0 comments on commit 3ca9b4e

Please sign in to comment.