Skip to content

Commit

Permalink
feat: add ellipse sdf in lesson9
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Jun 22, 2024
1 parent 53269e4 commit 528f18c
Show file tree
Hide file tree
Showing 15 changed files with 727 additions and 123 deletions.
51 changes: 26 additions & 25 deletions packages/lesson_009/examples/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,19 @@ const canvas = await new Canvas({
'https://unpkg.com/@antv/[email protected]/dist/pkg/glsl_wgsl_compiler_bg.wasm',
}).initialized;

for (let i = 0; i < 2000; i++) {
for (let i = 0; i < 1000; i++) {
const fill = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
Math.random() * 255,
)},${Math.floor(Math.random() * 255)})`;
const circle = new Circle({
cx: Math.random() * 1000,
cy: Math.random() * 1000,
r: Math.random() * 20,
fill,
stroke: 'black',
strokeWidth: 2,
batchable: true,
});
canvas.appendChild(circle);
// const circle = new Circle({
// cx: Math.random() * 1000,
// cy: Math.random() * 1000,
// r: Math.random() * 20,
// fill,
// stroke: 'black',
// strokeWidth: 2,
// });
// canvas.appendChild(circle);

const ellipse = new Ellipse({
cx: Math.random() * 1000,
Expand All @@ -43,21 +42,23 @@ for (let i = 0; i < 2000; i++) {
});
canvas.appendChild(ellipse);

const rect = new Rect({
x: Math.random() * 1000,
y: Math.random() * 1000,
width: Math.random() * 20,
height: Math.random() * 20,
fill,
});
canvas.appendChild(rect);
// const rect = new Rect({
// x: 0,
// y: 0,
// width: 100,
// height: 100,
// fill,
// rx: 10,
// ry: 50,
// });
// canvas.appendChild(rect);

circle.addEventListener('pointerenter', () => {
circle.fill = 'red';
});
circle.addEventListener('pointerleave', () => {
circle.fill = fill;
});
// circle.addEventListener('pointerenter', () => {
// circle.fill = 'red';
// });
// circle.addEventListener('pointerleave', () => {
// circle.fill = fill;
// });
}

const animate = () => {
Expand Down
4 changes: 3 additions & 1 deletion packages/lesson_009/src/drawcalls/BatchManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Buffer, Device, RenderPass } from '@antv/g-device-api';
import { Drawcall, SDF } from '../drawcalls';
import { Circle, type Shape } from '../shapes';
import { Circle, Ellipse, Rect, type Shape } from '../shapes';

/**
* Since a shape may have multiple drawcalls, we need to cache them and maintain an 1-to-many relationship.
Expand All @@ -11,6 +11,8 @@ import { Circle, type Shape } from '../shapes';
*/
const SHAPE_DRAWCALL_CTORS = new WeakMap<typeof Shape, (typeof Drawcall)[]>();
SHAPE_DRAWCALL_CTORS.set(Circle, [SDF]);
SHAPE_DRAWCALL_CTORS.set(Ellipse, [SDF]);
SHAPE_DRAWCALL_CTORS.set(Rect, [SDF]);

export class BatchManager {
/**
Expand Down
17 changes: 8 additions & 9 deletions packages/lesson_009/src/drawcalls/SDF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,7 @@ export class SDF extends Drawcall {

const instancedData: number[] = [];
this.shapes.forEach((shape) => {
if (shape instanceof Circle) {
instancedData.push(...this.generateBuffer(shape));
}
instancedData.push(...this.generateBuffer(shape));
});
this.#instancedBuffer.setSubData(
0,
Expand Down Expand Up @@ -318,6 +316,7 @@ export class SDF extends Drawcall {

let size: [number, number, number, number];
let type: number;
let rxRy: [number, number] = [0, 0];
if (shape instanceof Circle) {
const { cx, cy, r } = shape;
size = [cx, cy, r, r];
Expand All @@ -327,9 +326,10 @@ export class SDF extends Drawcall {
size = [cx, cy, rx, ry];
type = 1;
} else if (shape instanceof Rect) {
const { x, y, width, height } = shape;
size = [x, y, width, height];
const { x, y, width, height, rx, ry } = shape;
size = [x + width / 2, y + height / 2, width / 2, height / 2];
type = 2;
rxRy = [rx, ry];
}

return [
Expand All @@ -342,14 +342,13 @@ export class SDF extends Drawcall {
sg / 255,
sb / 255,
so,
this.shapes[0].globalRenderOrder / ZINDEX_FACTOR,
shape.globalRenderOrder / ZINDEX_FACTOR,
strokeWidth,
type,
0,
...rxRy,
opacity,
fillOpacity,
strokeOpacity,
0,
type,
];
}
}
53 changes: 42 additions & 11 deletions packages/lesson_009/src/shaders/sdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ out vec2 v_FragCoord;
out vec4 v_StrokeColor;
out float v_StrokeWidth;
out vec4 v_Opacity;
out vec2 v_RxRy;
#else
#endif
out vec2 v_Radius;
Expand All @@ -57,6 +58,7 @@ void main() {
v_StrokeColor = strokeColor;
v_StrokeWidth = strokeWidth;
v_Opacity = a_Opacity;
v_RxRy = a_ZIndexStrokeWidth.zw;
#else
model = u_ModelMatrix;
position = u_PositionSize.xy;
Expand All @@ -69,7 +71,7 @@ void main() {
vec2 radius = size + vec2(strokeWidth / 2.0);
v_FragCoord = a_FragCoord;
v_FragCoord = vec2(a_FragCoord * radius / radius.y);
v_Radius = radius;
gl_Position = vec4((u_ProjectionMatrix
Expand Down Expand Up @@ -100,6 +102,7 @@ in vec2 v_FragCoord;
in vec4 v_StrokeColor;
in float v_StrokeWidth;
in vec4 v_Opacity;
in vec2 v_RxRy;
#else
#endif
in vec2 v_Radius;
Expand All @@ -110,19 +113,26 @@ float sdf_circle(vec2 p, float r) {
return length(p) - r;
}
// @see http://www.iquilezles.org/www/articles/ellipsoids/ellipsoids.htm
float sdf_ellipse(vec2 p, vec2 r) {
float k0 = length(p / r);
float k1 = length(p / (r * r));
return k0 * (k0 - 1.0) / k1;
}
// @see https://www.shadertoy.com/view/4llXD7
float sdf_rounded_box(vec2 p, vec2 b, float r) {
p = abs(p) - b + r;
return length(max(p, 0.0)) + min(max(p.x, p.y), 0.0) - r;
// V1
// float k1 = length(p/r);
// return (k1-1.0)*min(r.x,r.y);
// V3
// float k1 = length(p/r);
// return length(p)*(1.0-1.0/k1);
}
float sdf_rounded_box(vec2 p, vec2 b, vec4 r) {
r.xy = (p.x>0.0)?r.xy : r.zw;
r.x = (p.y>0.0)?r.x : r.y;
vec2 q = abs(p)-b+r.x;
return min(max(q.x,q.y),0.0) + length(max(q,0.0)) - r.x;
}
void main() {
float strokeWidth;
Expand All @@ -131,6 +141,8 @@ void main() {
float opacity;
float fillOpacity;
float strokeOpacity;
float shape;
vec2 rxRy;
#ifdef USE_INSTANCES
fillColor = v_FillColor;
Expand All @@ -139,20 +151,39 @@ void main() {
opacity = v_Opacity.x;
fillOpacity = v_Opacity.y;
strokeOpacity = v_Opacity.z;
shape = v_Opacity.w;
rxRy = v_RxRy;
#else
fillColor = u_FillColor;
strokeColor = u_StrokeColor;
strokeWidth = u_ZIndexStrokeWidth.y;
opacity = u_Opacity.x;
fillOpacity = u_Opacity.y;
strokeOpacity = u_Opacity.z;
shape = u_Opacity.w;
rxRy = u_ZIndexStrokeWidth.zw;
#endif
vec2 r = (v_Radius - strokeWidth) / v_Radius.y;
float outerDistance = sdf_circle(v_FragCoord, 1.0);
float innerDistance = sdf_circle(v_FragCoord, r.x);
float antialiasedBlur = -fwidth(outerDistance);
float wh = v_Radius.x / v_Radius.y;
rxRy = rxRy / v_Radius.y;
float dist = length(v_FragCoord);
float antialiasedBlur = -fwidth(dist);
float outerDistance;
float innerDistance;
// 'circle', 'ellipse', 'rect'
if (shape < 0.5) {
outerDistance = sdf_circle(v_FragCoord, 1.0);
innerDistance = sdf_circle(v_FragCoord, r.x);
} else if (shape < 1.5) {
outerDistance = sdf_ellipse(v_FragCoord, vec2(wh, 1.0));
innerDistance = sdf_ellipse(v_FragCoord, r);
} else if (shape < 2.5) {
outerDistance = sdf_rounded_box(v_FragCoord, vec2(wh, 1.0), vec4(rxRy, rxRy));
innerDistance = sdf_rounded_box(v_FragCoord, r, vec4(rxRy, rxRy));
}
float opacity_t = clamp(outerDistance / antialiasedBlur, 0.0, 1.0);
Expand Down
42 changes: 37 additions & 5 deletions packages/lesson_009/src/shapes/Rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,44 @@ export interface RectAttributes extends ShapeAttributes {
*
*/
height: number;

/**
* For <rect>, rx defines the x-axis radius of the ellipse used to round off the corners of the rectangle.
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/rx
*/
rx: number;

/**
* For <rect>, ry defines the y-axis radius of the ellipse used to round off the corners of the rectangle.
* @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/ry
*/
ry: number;
}

export class Rect extends Shape implements RectAttributes {
#x: number;
#y: number;
#width: number;
#height: number;
#rx: number;
#ry: number;

constructor(attributes: Partial<RectAttributes> = {}) {
super(attributes);

const { x, y, width, height } = attributes;
const { x, y, width, height, rx, ry } = attributes;

this.#x = x ?? 0;
this.#y = y ?? 0;
this.#width = width ?? 0;
this.#height = height ?? 0;
this.#rx = rx ?? ry ?? 0;
this.#ry = ry ?? rx ?? 0;
}

get x() {
return this.#x;
}

set x(x: number) {
if (this.#x !== x) {
this.#x = x;
Expand All @@ -61,7 +76,6 @@ export class Rect extends Shape implements RectAttributes {
get y() {
return this.#y;
}

set y(y: number) {
if (this.#y !== y) {
this.#y = y;
Expand All @@ -73,7 +87,6 @@ export class Rect extends Shape implements RectAttributes {
get width() {
return this.#width;
}

set width(width: number) {
if (this.#width !== width) {
this.#width = width;
Expand All @@ -85,7 +98,6 @@ export class Rect extends Shape implements RectAttributes {
get height() {
return this.#height;
}

set height(height: number) {
if (this.#height !== height) {
this.#height = height;
Expand All @@ -94,6 +106,26 @@ export class Rect extends Shape implements RectAttributes {
}
}

get rx() {
return this.#rx;
}
set rx(rx: number) {
if (this.#rx !== rx) {
this.#rx = rx;
this.renderDirtyFlag = true;
}
}

get ry() {
return this.#ry;
}
set ry(ry: number) {
if (this.#ry !== ry) {
this.#ry = ry;
this.renderDirtyFlag = true;
}
}

containsPoint(xx: number, yy: number) {
// const { x, y, width, height, strokeWidth } = this;

Expand Down
2 changes: 1 addition & 1 deletion packages/site/docs/.vitepress/config/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const en = defineConfig({
link: 'lesson-008',
},
{
text: 'Lesson 009 - Draw ellipse, rectangle and polyline',
text: 'Lesson 009 - Draw ellipse and rectangle',
link: 'lesson-009',
},
],
Expand Down
1 change: 1 addition & 0 deletions packages/site/docs/.vitepress/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default defineConfig({
});
md.use(genjiAttrs);
},
math: true
},
cleanUrls: true,
extends: config,
Expand Down
2 changes: 1 addition & 1 deletion packages/site/docs/.vitepress/config/zh.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const zh = defineConfig({
{ text: '课程6 - 事件系统', link: 'lesson-006' },
{ text: '课程7 - UI 组件', link: 'lesson-007' },
{ text: '课程8 - 性能优化', link: 'lesson-008' },
{ text: '课程9 - 绘制椭圆、矩形和折线', link: 'lesson-009' },
{ text: '课程9 - 绘制椭圆和矩形', link: 'lesson-009' },
],
},
],
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/site/docs/public/rect-sdf-abs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/site/docs/public/rect-sdf-dist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 528f18c

Please sign in to comment.