tiny tool for drawing 3D shapes with WebGPU, wraps on Lagopus.ts.
使用正式 Calcit 0.28.0、Caps 0.1.1、Node.js 24 和 Yarn 4.18.0:
caps --ci
corepack yarn install --immutable
caps verify --toolchain
corepack yarn dev另开终端运行 corepack yarn watch 可持续编译 Calcit。corepack yarn build 生成前端产物;js-out/ 和 dist/ 不纳入版本控制。源码使用 calcit.cirru 和 deps.cirru。
CI 为生产构建设置 VITE_BASE_URL=https://cos-sh.tiye.me/WebGPU-Art/lagopus/,通过 COS action 的 public-base-url 启用内置上传校验。需要 COS_BUCKET、COS_SECRET_ID、COS_SECRET_KEY 和原有 rsync_private_key secrets。保留原服务器部署路径;PR 仅检查和构建,不上传生产资源。
类型门禁使用 calcit --check-only --warn-dyn-method,并保留全部 18 个公开命名空间的检查。配置边界的 :topology 按下文约定检查为 Tag,再转为 WebGPU 所需字符串;watcher 和字符串拼接使用首选 API 名,不依赖 calcit fix。CI 使用可读的正式 Action release tag(并非不可变 SHA),上传验证仅由 COS action v1.2.0 执行。
本轮本地完整严格检查、140 个公开定义检查和 JS/Vite 生产构建通过;尚未验证 WebGPU 实机渲染或实际 COS/服务器部署。
:lagopus.alias :refer $ object
object $ {}
:shader demo-wgsl
:topology :triangle-list
:attrs-list $ []
:: :float32x3 :position
:: :float32x3 :color
:data $ []
:: :vertex (v3 0 0 0) (v3 1 0 0)
:: :vertex (v3 100 0 0) (v3 0 1 0)
:: :vertex (v3 0 100 0) (v3 0 0 1):shadercustom shader file string:topologytopology symbol,:triangle-listor:line-strip:attrs-listlist of attributes, first value providing type, second value is a name(history reason...):datalist of data, each data is a map with keys matching:attrs-list, nested list is supported- optional
:indiceslist of indices, each index is a number. nested list is supported :get-params, to add custom params withuniformbuffer, for examplefn () (js-array 1 1 1 1), notice the values should obey memory alignments
Alternatively, there's also object-writer which provides a writer API, which replaces :data:
:writer $ fn (write!)
write! $ []
:: :vertex (v3 1 1 1)Shader file expects 3 parts:
- params(could be empty)
vertex_main, here you calltransform_perspectiveto display point in 3dfragment_main
// to include `lagopus-perpective.wgsl` , provide function `transform_perspective`
#import lagopus::perspective
struct Params {
_t: f32,
}
@group(0) @binding(0) var<uniform> params: Params;
// structure passing from Vertex to Fragment
struct VertexOut {
@builtin(position) position: vec4f,
};
@vertex
fn vertex_main(
@location(0) position: vec3f,
// data added from attributes put here
) -> VertexOut {
var output: VertexOut;
let p = transform_perspective(position.xyz).point_position;
let scale: f32 = 0.002;
output.position = vec4(p.xyz * scale, 1.0);
return output;
}
@fragment
fn fragment_main(vtx_out: VertexOut) -> @location(0) vec4f {
return vec4f(0.0, 0.0, 0.0, 1.0);
}#import lagopus::perspective
fn transform_perspective(p: vec3f) -> PointResult
#import lagopus::colors
fn hsl2rgb(hsl: vec3f) -> vec3ffn hsl(h: f32, s: f32, l: f32) -> vec3f
#import lagopus::rand
fn rand(n: f32) -> f32fn rand_balanced(n: f32) -> f32fn noise(p: f32) -> f32fn rand2(n: vec2<f32>) -> f32fn noise2(n: vec2<f32>) -> f32
#import lagopus::simplex
fn simplexNoise2(v: vec2<f32>) -> f32
#import lagopus::hsluv
fn hsluvToRgb(tuple: vec3f) -> vec3fbased on 360 and 100
lagopus.comp.curves :refer $ comp-curves comp-polylines break-mark
comp-curves $ {} (; :topology :line-strip)
:curves $ []
-> (range 400)
map $ fn (idx)
let
angle $ * 0.1 idx
r 40
{}
:position $ []
* r $ cos angle
* 0.6 idx
* r $ sin angle
:width 2Another way of defining lines is comp-polylines that seperate segments with :: :breaks.
It does not require "flatterned" list so is supposed to be a little performant.
note that
: vertex pis a short form for:: :vertex psince Calcit0.7.2.
comp-polylines $ {} (; :topology :line-strip)
:writer $ fn $ (write!)
write! $ []
: vertex ([] 0 0 0) width
: vertex ([] 100 100 0) width
, break-mark
: vertex ([] 0 0 10) width
: vertex ([] 200 0 10) width
: vertex ([] 200 20 0) width
: vertex ([] 100 40 0) width
: vertex ([] 100 20 200) width
, break-markalso if you want an extra mark for controlling colors:
comp-polylines-marked $ {} (; :topology :line-strip)
:writer $ fn (write!)
write! $ []
: vertex ([] 0 0 0) width 0
: vertex ([] 100 100 0) width 0
, break-mark
: vertex ([] 0 0 10) width 2
: vertex ([] 200 0 10) width 2
: vertex ([] 200 20 0) width 2
: vertex ([] 100 40 0) width 2
: vertex ([] 100 20 200) width 2
, break-marklagopus.comp.spots :refer $ comp-spots comp-bubbles
comp-spots $ {} (; :topology :line-strip)
:radius 6
:vertex-count 8
:shift 12
:points $ -> (range 80)
map $ fn (idx)
let
r $ * idx 4
[] r
* r $ cos (* 0.1129 idx)
* r $ sin (* 0.123 idx)
comp-bubbles $ {}
:bubbles $ -> (range 600)
map $ fn (idx)
[] (rand-shift 0 area) (rand-shift 0 area) (rand-shift 0 area) (+ 6 $ rand 120)lagopus.comp.curves :refer $ comp-axis
comp-axis $ %some $ {} (:n 20)
:unit 20
; use default options
comp-axis $ %nonelagopus.comp.cube :refer $ comp-cube
comp-cube $ {}
:position $ v3 40 0 0
:radius 40lagopus.comp.sphere :refer $ comp-sphere
comp-sphere $ {} (; :topology :line-strip)
:iteration 4
:radius 160lagopus.comp.plate :refer $ comp-plate
comp-plate $ {} (; :topology :line-strip)
:iteration 20
:radius 160
:color $ [] 0.04 0.8 0.6
:transformer $ fn (i)
hint-fn $ {}
:args $ [] 'quaternion.vector/V3
:return 'quaternion.vector/V3
v+ i $ v3 0 0 -10
; :x-direction $ v3 1 0 0
; :y-direction $ v3 0 1 0
:chromatism 0.14lagopus.comp.button :refer $ comp-button comp-slider comp-drag-point
comp-button
{}
:position $ v3 240 260 0
:color $ [] 0.2 0.9 0.6 1
:size 20
fn (e d!)
hint-fn $ {}
:args $ [] 'JsObject $ :: 'Fn
{} (:args $ [] 'Enum) (:return 'Unit)
:return 'Unit
d! $ :: :tab :sphere
comp-slider
{} $ :position ([] 0 0 0)
fn (change on-slide)
hint-fn $ {}
:args $ [] 'quaternion.complex/Complex $ :: 'Fn
{} (:args $ [] 'Enum) (:return 'Unit)
:return 'Unit
println |Slide change
, &unit
comp-drag-point
{}
:position $ &map:get state :pos
:color $ [] 0.6 0.6 1.0 1.0
fn (move d!)
hint-fn $ {}
:args $ [] 'quaternion.vector/V3 $ :: 'Fn
{} (:args $ [] 'Enum) (:return 'Unit)
:return 'Unit
d! $ :: :state cursor $ assoc state :pos movelagopus.comp.stitch :refer $ comp-stitch
comp-stitch $ {}
:chars $ [] 0xf2dfea34 0xc3c4a59d 0x88737645
:position $ [] 0 0 0lagopus.cursor :refer $ update-states >>Math functions in lagopus.math
fibo-grid-range totalcreate a list of points constructing Fibonacci Sphererotate-3d origin axis-0 angle protate pointparoundaxis-0rotate-3d-fn origin axis-0 anglecreate a function to rotate point p
Shader functions from https://gist.github.com/munrocket/236ed5ba7e409b8bdf1ff6eca5dcdc39
MIT
