3d-paper-material

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

3D Paper Material

3D 纸张材质

Paper is a fibre field cut out by a torn outline: a height map of laid fibres and formation clouds gives the surface, an alpha mask with pulled-out fibres gives the edge, offset copies give the thickness, and the back light is transmitted by how thin the formation is at each pixel. Drop any one and the sheet is a white plane.
Reach for
paper-cinder-transition
when the paper burns or is revealed by fire; this skill is the unburnt sheet. It is a hung or laid sheet in a scene. A page-turning book needs a bend rig this skill does not provide.
Read
demo.html
for a working build: MeshPhysicalMaterial patched with
onBeforeCompile
, maps baked once into half-float targets, masks painted with Canvas 2D. Its
NOISE
GLSL, formation, kozo-fibre and bark-fleck expressions, back-light transmission,
tearLine
and the fray loop are ported unchanged from the Kibori workshop page (
kibori.html
,
washiMat
,
footTear
).
纸张是一片由撕裂轮廓裁切出来的纤维场:由 laid fibres(帘纹纤维)和 formation clouds(成形云纹)构成的高度图塑造表面,带有抽出纤维的 alpha mask 塑造边缘,偏移副本赋予厚度,而背光透射则取决于每个像素处 formation 的薄厚。缺少其中任何一项,纸张都会变成一块白色平面。
当纸张燃烧或由火焰显现时,请使用
paper-cinder-transition
;本 skill 面向未燃烧的纸张。它用于场景中悬挂或平放的纸张。翻页书籍需要弯曲绑定装置,而本 skill 不提供。
阅读
demo.html
查看可运行实现:使用
onBeforeCompile
修补 MeshPhysicalMaterial,将贴图一次性烘焙到 half-float targets,并用 Canvas 2D 绘制 masks。其 NOISE GLSL、formation、kozo-fibre 和 bark-fleck 表达式、背光透射、
tearLine
与 fray loop 均从 Kibori workshop 页面(
kibori.html
、
washiMat
、
footTear
)原样移植。

The pipeline

实现流程

  1. Bake fibres (GPU, once). One full-screen pass writes
    R height, G formation, B fibre, A fleck
    to a half-float target. Height is
    0.5 + fibre*0.34 + (tooth-0.5)*0.1 - fleck*0.2 - crease*0.55
    . Keep
    samples: 0
    on the target and mipmaps on.
  2. Warp the lattice. Domain-warp the fibre coordinates by 0.014 unit and rotate them by
    (gnoise(q*2.1)-0.5)*1.5
    rad. Add a finer, fainter fibre set at 2.7x frequency (max 0.55).
  3. Paint the mask (Canvas 2D, on stock change).
    R
    thin strip inside the tear,
    G
    silhouette plus fray fibres,
    B
    ink with a low-alpha bleed halo,
    A
    the silhouette blurred by a shadow drawn off to the side (edge age). Read back with
    getImageData
    and write into a
    DataTexture
    , rows flipped, alpha as data.
  4. Shape the sheet. A 168 x 240 plane: hung bow, a mountain crease, pin dimples, and a cylinder roll (radius 0.23, angle
    2.6*curl
    ) that keeps length. Add three copies offset along the normal by
    thick/3
    ,
    2thick/3
    ,
    thick
    .
  5. Shade. Albedo from formation and fibre, ink over it, toast and foxing by the age mask. Height goes through a world-unit bump (see gotchas). Alpha is coverage, not a cut.
  6. Transmit.
    emissive += L * tr
    , where
    tr = 0.74 + (0.5-form)*0.85 + (0.5-lowfbm)*0.45 - fibre*0.22 - fleck*0.45
    , times stock gain, plus the thin strip, minus ink.
    L
    is the garden behind: sky, sun patch, black bamboo stems sampled through a mip bias of 1.4.
  7. Macro. Re-bake a second height target and mask over a 0.5 unit window at the focus point and blend it over the base by window edge. Never tile a small texture.
  1. 烘焙纤维(GPU,仅一次)。 一次 full-screen pass 将
    R height, G formation, B fibre, A fleck
    写入 half-float target。高度为
    0.5 + fibre*0.34 + (tooth-0.5)*0.1 - fleck*0.2 - crease*0.55
    。target 保持
    samples: 0
    ,并开启 mipmaps。
  2. 扭曲晶格。 对 fibre coordinates 进行 0.014 unit 的 domain-warp,并按
    (gnoise(q*2.1)-0.5)*1.5
    rad 旋转。再添加一组频率为 2.7 倍、更细且更淡的 fibres(最大值 0.55)。
  3. 绘制 mask(Canvas 2D,纸张类型变化时)。
    R
    是撕裂内部的 thin strip;
    G
    是轮廓及 fray fibres;
    B
    是带低 alpha bleed halo 的 ink;
    A
    是由侧向绘制的阴影模糊出的轮廓(edge age)。通过
    getImageData
    回读并写入
    DataTexture
    ,翻转各行,并将 alpha 作为数据。
  4. 塑造纸张。 使用 168 x 240 plane:包含悬挂弧度、山折折痕、针孔凹痕,以及保持长度不变的 cylinder roll(半径 0.23,角度
    2.6*curl
    )。沿法线分别按
    thick/3
    、
    2thick/3
    、
    thick
    偏移添加三个副本。
  5. 着色。 Albedo 由 formation 和 fibre 生成,ink 叠加于其上,并通过 age mask 添加 toast 和 foxing。Height 通过 world-unit bump 通道处理(见常见陷阱)。Alpha 表示覆盖度,而不是裁切。
  6. 透射。
    emissive += L * tr
    ,其中
    tr = 0.74 + (0.5-form)*0.85 + (0.5-lowfbm)*0.45 - fibre*0.22 - fleck*0.45
    ,再乘 stock gain,加上 thin strip,并减去 ink。
    L
    表示后方花园:天空、阳光色块和黑色竹竿,后者通过 mip bias 1.4 采样。
  7. Macro。 在焦点处 0.5 unit 的窗口内重新烘焙第二个 height target 和 mask,并按窗口边缘将其混合到基础结果上。绝不要平铺小尺寸贴图。

Constants that landed

最终采用的常量

washicotton ragkraft
tint
#f0e8d2
#f0eee6
#a88f70
fibre lattice A / B (per unit)1.9x54 / 44x2.315x17 / 16x147x26 / 26x7
fibre relief, bump height (unit)0.55, 0.00170.32, 0.00240.8, 0.0034
tooth freq, amp230, 0.09430, 0.17300, 0.20
laid lines /unit, amp, chain110, 0.28, 0150, 0.55, 0.5none
tear amplitude (T,R,B,L), fray scale64,72,60,76; x123,58,3,54; x696,3,4,3; x8
thickness (unit), transmission0.0045, 1.00.0085, 0.420.011, 0.16
roughness, sheen0.93, 0.40.9, 0.250.96, 0.10
Scale: 1 unit = 134.8 mm (A4 is 1.55 x 2.2). Base bake 990 px/unit = 7.3 px/mm; macro window 1536 px over 0.5 unit = 22.8 px/mm. Tear samples 3400 per unit. Camera 28 degree FOV; macro distance 0.46-0.50.
washi(和纸)cotton rag(棉浆纸)kraft(牛皮纸)
色调
#f0e8d2
#f0eee6
#a88f70
fibre lattice A / B(每单位)1.9x54 / 44x2.315x17 / 16x147x26 / 26x7
fibre relief、bump 高度(单位)0.55, 0.00170.32, 0.00240.8, 0.0034
tooth 频率、振幅230, 0.09430, 0.17300, 0.20
laid lines/单位、振幅、chain110, 0.28, 0150, 0.55, 0.5无
撕裂振幅(T,R,B,L)、fray 比例64,72,60,76; x123,58,3,54; x696,3,4,3; x8
厚度(单位)、透射0.0045, 1.00.0085, 0.420.011, 0.16
粗糙度、sheen0.93, 0.40.9, 0.250.96, 0.10
比例:1 unit = 134.8 mm(A4 为 1.55 x 2.2)。基础 bake 为 990 px/unit = 7.3 px/mm;macro window 以 1536 px 覆盖 0.5 unit,即 22.8 px/mm。每 unit 采样 3400 个撕裂样本。相机 FOV 为 28 度;macro 距离为 0.46-0.50。

Rules and the failure each prevents

规则及其预防的失败

  • Cut the outline from four independent tear lines, never a rectangle with a wobble. A single sine or noise edge reads as a CSS wave;
    tearLine
    runs straight, turns, runs again. Give cut sides amplitude 3-4, torn sides 60-100.
  • Pull fibres past the edge and paint them as partial coverage. A hard alpha edge reads as scissors. Fibres are 0.3-0.7 px wide at 30-75% alpha; on a dark wall they show as pale hairs.
  • Stack three offset copies behind the front layer, tinted 0.8-0.94 of the paper. One quad has no thickness and shows nothing side-on. Keep the offset at 0.0045-0.011 unit; more reads as cardboard.
  • Warp and rotate the fibre lattice. Two orthogonal ridged families read as scratched glass or a crosshatch, especially raking-lit.
  • Drive transmission from the same fields as the albedo. A flat emissive constant reads as a lamp inside a card; thin formation must glow, fibre bundles and ink must hold light back.
  • Fade the toast band to zero beyond the edge. Toasting the fray turns pulled-out fibres brown-black against a dark wall.
  • Bleed ink with a halo below the ink threshold and let fibre and formation carry it over. A blurred halo alone is out-of-focus ink;
    smoothstep(0.62,0.85, halo + fibre*0.4 + (0.5-form)*0.5)
    wicks along strands. Guard the term with
    smoothstep(0.03,0.16, halo)
    or fibres far from any ink go black.
  • Round the bark flecks. Kibori's cell hash gives squares at macro; multiply by an ellipse
    smoothstep(0.5,0.12,length((fract(q*cell)-0.5)*vec2(1,1.9)))
    .
  • Bake a window for macro. A base map at 7 px/mm turns to fog at 22 px/mm; a tiled 512 px paper tile shows its seam.
  • 从四条独立的 tear line 裁出轮廓,绝不能使用带波动的矩形。 单个正弦或噪声边缘看起来会像 CSS 波浪;
    tearLine
    会直行、转折、再直行。切割边振幅设为 3-4,撕裂边设为 60-100。
  • 将纤维拉出边缘,并绘制为部分覆盖。 硬 alpha 边缘看起来像剪刀剪出来的。纤维宽 0.3-0.7 px,alpha 为 30-75%;在深色墙面上会呈现为浅色毛发。
  • 在前层后方堆叠三个偏移副本,色调强度为纸张的 0.8-0.94。 单个 quad 没有厚度,侧视时什么也看不出来。偏移保持 0.0045-0.011 unit;再大就会像纸板。
  • 扭曲并旋转 fibre lattice。 两组正交的 ridged families 看起来像划痕玻璃或交叉网纹,在掠射光下尤其明显。
  • 使用与 albedo 相同的场值驱动 transmission。 恒定的 flat emissive 会像卡片内部亮着灯;较薄的 formation 必须发光,而 fibre bundles 和 ink 应阻挡光线。
  • 让 toast band 在边缘之外渐隐至零。 给 fray 添加 toasted 色,会让抽出的纤维在深色墙面上变成棕黑色。
  • 让 ink 在低于墨迹阈值的位置产生 bleed halo,并让 fibre 与 formation 将渗色带向四周。 只有模糊 halo 会像失焦墨迹;
    smoothstep(0.62,0.85, halo + fibre*0.4 + (0.5-form)*0.5)
    会沿纤维渗染。使用
    smoothstep(0.03,0.16, halo)
    保护该项,否则远离任何墨迹的纤维都会变黑。
  • 将 bark flecks 变圆。 Kibori 的 cell hash 在 macro 下会产生方块;乘上一个 ellipse:
    smoothstep(0.5,0.12,length((fract(q*cell)-0.5)*vec2(1,1.9)))
    。
  • 为 macro 烘焙窗口。 在 7 px/mm 下的 base map,到 22 px/mm 时会像一团雾;平铺一张 512 px 纸张贴图则会露出接缝。

Gotchas, symptom first

常见陷阱:先看症状

  • Fringe fibres vanish or a solid black fringe appears beyond the edge.
    alphaTest
    cuts anything under 0.5. Use
    alphaToCoverage: true
    with
    alphaTest
    0 and antialias on; four MSAA samples dither the partial alpha. Give the shadow depth material its own
    alphaMap
    and
    alphaTest: 0.5
    .
  • A big black hairy shape beside the paper is not a bug. It is the sheet's shadow on the wall. Light the wall with a fill or a faint emissive so the shadow reads brown.
  • Relief looks stronger far away and vanishes in macro. three's
    perturbNormalArb
    normalises the surface derivatives, so slope depends on pixel size. Replace
    bumpmap_pars_fragment
    with the un-normalised Mikkelsen form and give the height in world units.
  • Edge-age or ink texture arrives premultiplied and dark. Never upload data through a canvas texture with alpha. Merge four canvases through
    getImageData
    into a
    DataTexture
    (rows reversed,
    flipY
    false).
  • A blurred edge band is missing on Safari.
    ctx.filter
    is unreliable; draw the shape offset by 20000 px with
    shadowOffsetX = -20000
    so only its blur lands.
  • Half-float target resolves to blocky tiles. MSAA on it; use
    samples: 0
    .
  • The rolled corner shows nothing. Face-on, a 65 degree roll is foreshortened; sway the camera by 0.3 unit at distance 6 or rake the key to about 54 degrees.
  • Fibre swirls read as marbling at overview. The rotation field is too strong; 1.5 rad is the cap, 1.9 reads as floral.
  • 边缘毛丝消失,或边缘外出现实心黑色毛边。
    alphaTest
    会切掉所有低于 0.5 的部分。启用
    alphaToCoverage: true
    ,同时将
    alphaTest
    设为 0 并开启 antialias;4 个 MSAA 样本会对部分 alpha 进行抖动。给 shadow depth material 单独设置
    alphaMap
    和
    alphaTest: 0.5
    。
  • 纸张旁边出现一大块黑色毛状形状并不是 bug。 那是纸张投射在墙上的阴影。用 fill light 或微弱 emissive 照亮墙面,让阴影呈棕褐色。
  • 浮雕在远景中显得过强,却在 macro 中消失。 three 的
    perturbNormalArb
    会对 surface derivatives 做归一化,因此坡度取决于像素尺寸。用未归一化的 Mikkelsen 形式替换
    bumpmap_pars_fragment
    ,并以 world units 提供高度。
  • edge-age 或 ink 贴图到达时已经 premultiplied 且变暗。 绝不要通过带 alpha 的 canvas texture 上传数据。将四个 canvas 通过
    getImageData
    合并到
    DataTexture
    (反转行,
    flipY
    为 false)。
  • Safari 上缺少模糊边缘带。
    ctx.filter
    不可靠;将形状偏移 20000 px 绘制,并设置
    shadowOffsetX = -20000
    ,使最终只有其模糊效果落在目标位置。
  • half-float target 解析后出现块状拼贴。 对它启用 MSAA;使用
    samples: 0
    。
  • 卷曲边角看不出来。 正面视角下,65 度 roll 会被透视压缩;将相机在距离 6 处偏移 0.3 unit,或把主光掠射到约 54 度。
  • overview 中的纤维漩涡像大理石纹。 旋转场过强;1.5 rad 是上限,1.9 会呈现花卉图案。

Playback and lifecycle

播放与生命周期

The demo idles with a slow camera sway;
prefers-reduced-motion
renders one composed still and only re-renders on control input. It pauses on
document.hidden
and resets its time base on resume, clamps
dt
to 1/30 s, caps device pixel ratio at 2, sizes from a
ResizeObserver
, and eases back-light and macro with
1 - exp(-dt*k)
. Controls are real radios, checkboxes and ranges with focus rings and a live status line.
演示以缓慢的相机摆动作为空闲动画;
prefers-reduced-motion
会渲染一张合成静帧,并仅在控件输入时重新渲染。它会在
document.hidden
时暂停,并在恢复时重置时间基准,将
dt
限制为 1/30 s,将 device pixel ratio 限制为 2,通过
ResizeObserver
调整尺寸,并以
1 - exp(-dt*k)
平滑过渡 back-light 和 macro。控件均为原生 radio、checkbox 和 range,带有 focus ring 和实时状态行。

Cost (measured, 1440x900 headless Chrome on Metal, DPR 1)

成本(实测,1440x900 headless Chrome on Metal,DPR 1)

Idle sway about 85 fps; 325k triangles in 7 draw calls (front plus three layers is 4x81k); stock switch rebuilds in about 320 ms (masks 4 canvases, bake, geometry); macro window in about 150 ms. GPU memory about 100 MB (base target 40, macro target 25, masks 33). What costs: the four mask canvases and the tens of thousands of fray strokes. What does not: the bake pass and the normal taps. The cheap lever: base density (
PPU_B
); 660 px/unit halves memory and keeps overview sharp.
空闲摆动约为 85 fps;325k triangles,7 个 draw calls(front 加另外三层,共 4x81k);纸张类型切换后的重建约为 320 ms(4 个 mask canvases、bake 和 geometry);macro window 约为 150 ms。GPU 内存约为 100 MB(base target 40、macro target 25、masks 33)。主要开销来自四个 mask canvases 和数万条 fray strokes;bake pass 和 normal taps 不会造成明显开销。最有效的降本杠杆是 base density(
PPU_B
);660 px/unit 可将内存减半,同时保持 overview 清晰。

Provenance

来源

Extracted from the Kibori 木彫 launch page, where washi is the back-lit shoji panes, the torn footer sheet and the paper lantern; this build restages those generators on one sheet with a macro mode.
提取自 Kibori 木彫 发布页,其中 washi 用于背光的 shoji panes、底部撕裂纸片和纸灯笼;此构建将这些生成器重新整合到同一张纸上,并加入 macro 模式。

Self-score against the anchored rubric (0 = flat plane, 5 = tileable noise texture, 8 = directional fibre with real edge, 10 = passes a macro test against a photograph)

对照锚定评分标准的自评(0 = 平面,5 = 可平铺噪声纹理,8 = 带真实边缘的方向性纤维,10 = macro 测试中能与照片相媲美)

itemscorewhy
fibre structure8warped, rotating strands, two scales; no fibre cross-overs in depth
deckle edge8four independent tears, fray fibres, feather strip; fibres do not shadow
thickness6three stepped layers; steps show at extreme rake, no cross-section fibres
translucency8formation-driven, stems, amber sun patch; no scattering blur by thickness
curl and crease6roll is convincing, crease reads faint at overview
ink6fibre wicking narrow; no pooling at stroke ends
aged edges and shadow6toast and foxing good; shadow on the wall is a hard shape
texel density822.8 px/mm in macro, 7.3 px/mm overview
项目分数原因
fibre structure8扭曲、旋转的纤维束,两种尺度;深度上没有纤维交叉
deckle edge8四条独立撕裂线、fray fibres、羽化条带;纤维不产生阴影
thickness6三个阶梯层;极端掠射角下可见层级,但没有横截面纤维
translucency8由 formation 驱动,包含竹竿与琥珀色阳光色块;没有按厚度计算散射模糊
curl and crease6卷曲效果可信,overview 下折痕较淡
ink6沿纤维渗墨范围较窄;笔画末端没有积墨
aged edges and shadow6toast 和 foxing 效果良好;墙面上的阴影形状过硬
texel density8macro 为 22.8 px/mm,overview 为 7.3 px/mm