@basementstudio/shader-lab (英语)
@basementstudio/shader-lab 是一个从编辑器导出 Shader Lab 组件的可移植反应运行时间.
它支持三个主要使用案例:
- 在 React 中直接渲染成份
- 在您自己的场景中使用配体作为纹理
- 使用 Shader 实验室效果层作为您自己的场景的后处理
安装
npm install @basementstudio/shader-lab three
bun add @basementstudio/shader-lab three
同伴依赖关系
reactreact-domthree
所需资源
- 运行时间需要浏览器中的 WebGPU 支持
ShaderLabComposition此套件中的钩子是客户端 API- 媒体层期望在
layer.asset.src - 组成纹理输出为画布后置,可以被 WebGL 或 WebGPU 主机场景所消耗
- 后处理必须运行在同一程序上
WebGPURenderer当你经过的场景纹理
所支持的效果层包括ASCII,blob跟踪,CRT,方向模糊,分色,半通,墨子,粒子网格,图案,像素排序,后缀,切片,边缘检测,移位图,和色调偏差.
Blob跟踪层检测出下方内容的移动(或亮度)区域,并用闭路电视风格的形状,标签,连接线条和出轨来设定. 一种内在效应可以在检测到的形状内通过 params.innerEffectType (任何过后效应类型)和 params.innerEffectParams (该效应参数的JSON字符串覆盖;参见 ShaderLabBlobInnerEffect 类型)。 设定 params.outputMode 改为 "mask" 以释放白黑相接的填充器使用 compositeMode: "mask"。 。 。 .
隔离群体
导出配置可以包含层 kind: "group", (中文(简体) ), type: "group",以及 params: {}儿童通过 parentId. 平面阵列使用最先,最后深度顺序:先是组,再是其所有后人,再是下个兄弟姐妹. 失踪或无效 parentId 在场景根上放置一层. ID必须是独一无二的,巢穴仅限于8个组.
例如,顺序 [title, portraitGroup, halftone, photo, background] 与 halftone.parentId 财务报告和审定财务报表 photo.parentId 设置为 portraitGroup.id 将半音限制在相片上。 每个组开始透明,处理内容,然后对结果应用自己的不透明与混合模式. 隐藏一个团体隐藏整个子树而不改变孩子的能见度. 组不透明/可视性可以使用普通的地层时间轨.
组使用 compositeMode: "filter", (中文(简体) ), hue: 0,以及 saturation: 1; 覆盖口罩和组色调整在此迭代中没有被曝光. 团体目标目前每组花费约32米B,为1080p. 现有的平面配置继续工作,没有任何新领域.
字体
ASCII 和文本层在运行时通过读取 CSS 解析字体家族
自定义属性( e)--geist-mono, (中文(简体) ), --bsmnt-grotesque, (中文(简体) ), --adhesion从
文档根。 每个变量的值用作 CSS
font-family glyph 光栅化; 当变量未被设定时, 层
返回到系统字体.
包船 三个公开许可的家庭 - Geist Sans, Geist
Mono,和BSMNT Grotesque——带有一个样式表,宣布 @font-face
规则和设置变量:
import "@basementstudio/shader-lab/fonts.css"
在Shader Lab编辑室(Adhesion, Blob,
包克克克克克尼切克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克克
有商业执照。 它们在主机Shader Lab应用程序中使用,但
故意不与这个包一起分发。 使用它们(或
在主机应用程序中装入字体并设置匹配的 CSS 变量
其姓氏,例如:. :root { --adhesion: "Adhesion" }。 。 。 .
API 概览
高级API
ShaderLabCompositionuseShaderLab
高级 API
useShaderLabCanvasSourceuseShaderLabPostProcessingSourceuseShaderLabTextureSourceShaderLabCanvasSourceShaderLabPostProcessingSourceShaderLabTextureSource
使用 useShaderLab 除非您特别需要手动计时或生命周期控制.
1. 组成
"use client"
import {
ShaderLabComposition,
type ShaderLabConfig,
} from "@basementstudio/shader-lab"
const config: ShaderLabConfig = {
layers: [],
timeline: {
duration: 6,
loop: true,
tracks: [],
},
}
export default function Example() {
return (
<div style={{ width: "100%", maxWidth: 1200 }}>
<ShaderLabComposition config={config} />
</div>
)
}
处理运行时间错误 :
<ShaderLabComposition
config={config}
onRuntimeError={(message) => {
if (message) {
console.error(message)
} else {
console.info("Shader Lab runtime error resolved.")
}
}}
/>
onRuntimeError 当运行时间出错时使用字符串消息调用
(启动失败, 阴影编译错误, 缺失 WebGPU)
支持)。 报告出错后,称为一次 null 何时
错误被解决后, UIs 可以恢复错误 。 它从来没有叫 null
在报告出错之前.
2. 使用构成作为纹理
useShaderLab 能够管理运行时间并返回即时使用 THREE.CanvasTexture。 。 。 .
"use client"
import {
useShaderLab,
type ShaderLabConfig,
} from "@basementstudio/shader-lab"
import { OrbitControls, PerspectiveCamera } from "@react-three/drei"
import { Canvas, useFrame } from "@react-three/fiber"
import { useMemo, useRef } from "react"
import * as THREE from "three"
import { WebGPURenderer } from "three/webgpu"
function Scene({ config }: { config: ShaderLabConfig }) {
const { texture } = useShaderLab(config, {
width: 1024,
height: 1024,
})
const material = useMemo(
() => new THREE.MeshBasicMaterial({ color: 0xffffff }),
[]
)
const meshRef = useRef<THREE.Mesh | null>(null)
useFrame((_, delta) => {
if (meshRef.current) {
meshRef.current.rotation.y += delta * 0.3
}
if (texture) {
texture.needsUpdate = true
material.map = texture
material.needsUpdate = true
}
})
return (
<>
<color attach="background" args={["#0a0a0a"]} />
<PerspectiveCamera makeDefault fov={50} position={[0, 0, 4]} />
<mesh ref={meshRef} material={material}>
<sphereGeometry args={[1, 64, 64]} />
</mesh>
<OrbitControls enableDamping />
</>
)
}
export function TexturedExample({ config }: { config: ShaderLabConfig }) {
return (
<Canvas
gl={async (props) => {
const renderer = new WebGPURenderer(
props as ConstructorParameters<typeof WebGPURenderer>[0]
)
await renderer.init()
return renderer
}}
>
<Scene config={config} />
</Canvas>
)
}
纹理说明
texture由软件包的内部输出画布支持canvas也可从下列网址获得:useShaderLab如果您想要在原始画布级别整合- 运行时间在内部更新您的组成
- 此纹理路径可移植到WebGL和WebGPU主机场景中, 因为软件包输出有画布支持
3. 使用Shader Lab作为后处理
后处理, useShaderLab 暴露一个 postprocessing 手柄.
你把场景变成纹理 然后把纹理交给沙德实验室.
"use client"
import {
useShaderLab,
type ShaderLabConfig,
} from "@basementstudio/shader-lab"
import { Canvas, useFrame, useThree } from "@react-three/fiber"
import { PerspectiveCamera } from "@react-three/drei"
import { createPortal } from "@react-three/fiber"
import { useEffect, useMemo, useRef } from "react"
import * as THREE from "three"
import { float, texture as tslTexture, uv, vec2 } from "three/tsl"
import { MeshBasicNodeMaterial, WebGPURenderer } from "three/webgpu"
function PostProcessedScene({ config }: { config: ShaderLabConfig }) {
const { gl, scene, size } = useThree()
const renderer = gl as unknown as WebGPURenderer
const cameraRef = useRef<THREE.PerspectiveCamera | null>(null)
const sceneTargetRef = useRef<THREE.WebGLRenderTarget | null>(null)
const { postprocessing } = useShaderLab(config, {
renderer,
width: size.width,
height: size.height,
})
const presentScene = useMemo(() => new THREE.Scene(), [])
const presentCamera = useMemo(
() => new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1),
[]
)
const presentTextureNode = useMemo(() => {
const sampleUv = vec2(uv().x, float(1).sub(uv().y))
return tslTexture(new THREE.Texture(), sampleUv)
}, [])
const presentMaterial = useMemo(() => {
const material = new MeshBasicNodeMaterial()
material.colorNode = presentTextureNode.rgb
return material
}, [presentTextureNode])
useEffect(() => {
const target = new THREE.WebGLRenderTarget(size.width, size.height)
sceneTargetRef.current = target
return () => {
sceneTargetRef.current = null
target.dispose()
}
}, [size.height, size.width])
useEffect(() => {
sceneTargetRef.current?.setSize(size.width, size.height)
postprocessing.resize(size.width, size.height)
}, [postprocessing, size.height, size.width])
useFrame((state, delta) => {
const target = sceneTargetRef.current
const camera = cameraRef.current
if (!(target && camera && postprocessing.ready)) return
renderer.setRenderTarget(target)
renderer.render(scene, camera)
const output = postprocessing.render(
target.texture,
state.clock.elapsedTime,
delta
)
if (output) {
presentTextureNode.value = output
}
renderer.setRenderTarget(null)
renderer.render(presentScene, presentCamera)
}, 1)
return (
<>
<PerspectiveCamera ref={cameraRef} makeDefault position={[0, 2, 5]} />
<mesh position={[-1.2, 0, 0]}>
<boxGeometry args={[1.4, 1.4, 1.4]} />
<meshStandardMaterial color="#e74c3c" />
</mesh>
<mesh position={[1.2, 0, 0]}>
<sphereGeometry args={[0.8, 32, 32]} />
<meshStandardMaterial color="#3498db" />
</mesh>
{createPortal(
<mesh frustumCulled={false}>
<planeGeometry args={[2, 2]} />
<primitive attach="material" object={presentMaterial} />
</mesh>,
presentScene
)}
</>
)
}
export function PostProcessingExample({
config,
}: {
config: ShaderLabConfig
}) {
return (
<Canvas
gl={async (props) => {
const renderer = new WebGPURenderer(
props as ConstructorParameters<typeof WebGPURenderer>[0]
)
await renderer.init()
return renderer
}}
>
<PostProcessedScene config={config} />
</Canvas>
)
}
后处理说明
- 后处理仅为同级
- 从同一处传出你的场景纹理
WebGPURenderer你给了useShaderLab - WebGL 主机渲染器不支持后处理
- 只有效果的 沙德 实验室配置 最自然适合这里
- 保留后处理
width编号 :height财务报告和审定财务报表postprocessing.resize(...)与输入同步渲染目标大小,特别是用于屏幕-空间效果,如去色、像素和ASCII postprocessing.texture总是指向最近的输出纹理
外部输入纹理和输出纹理使用正( 非多倍化) RGB 还有阿尔法 纹理路径通过效果传递来保持输入覆盖; 效果输出α控制效果强度. 源层使用源层 组成。 在传递前将多倍输入转换为正α. 遗留面具层仍然使用他们现有的 RGB- darken 行为,以及 画布构成背景仍然不透明.
useShaderLab
const { canvas, ready, texture, postprocessing } = useShaderLab(config, options)
选项
| 选项 | 说明 |
|---|---|
width | 内部运行时间输出宽度 |
height | 内部运行时间输出高度 |
pixelRatio | 可选像素比覆盖 |
canvas | 可选的现有画布,使组成 |
renderer | 可选共享 WebGPURenderer 用于后处理 |
返回值
| 外地 | 说明 |
|---|---|
canvas | 内部输出画布 |
ready | true 当管理过的纹理路径初始化时 |
texture | 管理 THREE.CanvasTexture 由运行时显示布支持 |
postprocessing | 处理同级接线员后处理 |
postprocessing
| 外地 | 说明 |
|---|---|
ready | true 后处理源初始化时 |
texture | 最近处理的产出纹理 |
resize(width, height, pixelRatio?) | 调整运行时间目标大小 |
render(inputTexture, time, delta) | 向输出纹理中渲染 |
高级 API
这些API是当您想要更直接地控制时间,调整行为大小,或对象生命周期时提供的.
用于:
- 您已经拥有了自己的渲染回路, 想要手动驱动更新
- 您想要原始输出画布而不是管理
CanvasTexture - 你想自己拥有完整的后处理管道
- 您需要使用 React 以外的运行时间( 但仍在浏览器/ 客户端运行时间)
钩子
useShaderLabCanvasSourceuseShaderLabPostProcessingSourceuseShaderLabTextureSourceuseShaderLabTexture
useShaderLabCanvasSource
需要原始画布和人工计时时时使用此功能.
const { canvas, ready, resize, update } = useShaderLabCanvasSource(config, {
width: 1024,
height: 1024,
})
useFrame((state, delta) => {
if (!ready) return
update(state.clock.elapsedTime, delta)
})
这在下列情况下是有用的:
- 你想创造你自己
THREE.CanvasTexture - 你想把帆布喂进别的管道
- 您想要确切决定何时更新组成
useShaderLabPostProcessingSource
想要控制整个场景到文字到屏幕的流量时使用这个.
const { ready, error, resize, texture, update } =
useShaderLabPostProcessingSource(effectConfig, {
renderer,
width: size.width,
height: size.height,
})
useFrame((state, delta) => {
if (!(ready && sceneTarget)) return
renderer.setRenderTarget(sceneTarget)
renderer.render(scene, camera)
const output = update(
sceneTarget.texture,
state.clock.elapsedTime,
delta
)
renderer.setRenderTarget(null)
// present(output)
})
确保当场景改变目标大小时, Shader Lab 后处理源会调整大小 。 屏幕-空间效应使用这个尺寸来确定它们的采样网格.
这在下列情况下是有用的:
- 你已经有你自己的目标了
- 您要决定最终输出是如何呈现的
- 您需要低于
useShaderLab().postprocessing
useShaderLabTextureSource
这暴露出下层的内部纹理来源.
只有在您特别想要内部纹理路径并理解渲染器的制约时才使用. 在大多数情况下, useShaderLab 或者说 useShaderLabCanvasSource 是更好的选择.
类
ShaderLabCanvasSourceShaderLabPostProcessingSourceShaderLabTextureSource
类API是钩子的非反应等同物.
典型流量 :
const source = new ShaderLabCanvasSource(config, {
width: 1024,
height: 1024,
})
await source.initialize()
source.update(time, delta)
source.resize(width, height)
source.dispose()
当 :
- 你在外面反应
- 你想要全手工控制生命周期
- 您正在将 Shader Lab 整合到自定义运行时间或框架中
自定义阴影层
自定义阴影层允许您使用 GPU 阴影层写入 Three.js TSL( 三分语言)您的草图完全运行在 GPU 作为节点图 —— 没有 GLSL 或 WGSL 可写入 .
如何运作
您的代码必须导出一个被包裹的函数 Fn() 返回 TSL 节点( vec3 或 vec4) 。 编辑器会编译您的 TypeScript, 条形导入, 并评价结果为带有前置范围的沙盒. 一切从 three/tsl 而Shader Lab 通用图书馆作为全球图书馆可供使用——不需要进口.
export const sketch = Fn(() => {
const color = vec3(uv().x, uv().y, sin(time).mul(0.5).add(0.5))
return color
})
点击 应用 (或作再作活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活活 导出项名默认为 sketch 但可以在条目导出字段中更改.
源模式 vs 效果模式
自定义阴影层有一个 效果模式 切换 :
-
源模式 (默认)——地层从头产生像素,像梯度或图像. 您的草图输出每个像素的颜色 。 输出为线性( sRGB → 线性通过)
pow(2.2))与堆肥管道相匹配. -
效果模式 ——地层发挥后处理作用. 它通过它接收下方所有层的复合结果
inputTexture并改变它。 由于输入已经处于线性空间,因此没有应用γ转换.
注射范围
这些变量被注入到您的草图中,并作为全局数据提供:
| 变量 | 类型 | 说明 |
|---|---|---|
time | float | 以秒计耗时, 不断更新 |
inputTexture | TextureNode | 下面的复合层(效果模式). 在源模式下, 这是空白的纹理 。 直接用于默认- UV 颜色, 或调用 .sample(customUV) 以自定义坐标重采样 |
TSL 全球
全部从 three/tsl 已提供。 关键:
| 函数 | 说明 |
|---|---|
Fn(() => { ... }) | 定义延后的阴影函数( 需要的包装) |
float, (中文(简体) ), vec2, (中文(简体) ), vec3, (中文(简体) ), vec4 | 平面和向量类型的构造器 |
uv() | 碎片紫外线坐标 (0-1) |
screenSize | 将传送分辨率视为 vec2 |
sin, (中文(简体) ), cos, (中文(简体) ), abs, (中文(简体) ), floor, (中文(简体) ), round, (中文(简体) ), pow, (中文(简体) ), mix, (中文(简体) ), clamp, (中文(简体) ), smoothstep, (中文(简体) ), dot, (中文(简体) ), min, (中文(简体) ), max | 标准数学 |
texture(tex, uv) | 给定紫外线上的纹理样本 |
uniform(value) | 创建统一节点 |
select(cond, a, b) | 初选 |
If(cond, () => { ... }).Else(...) | 有条件分支 .toVar() 任务 |
节点用方法链: a.add(b), (中文(简体) ), a.mul(b), (中文(简体) ), a.sub(b), (中文(简体) ), a.div(b), (中文(简体) ), a.negate(), (中文(简体) ), a.toVar() (使可变), a.assign(b) (将 a 移动到 Var 节点).
沙德实验室设施
这些是与 TSL 一起注入的额外助手 :
噪声
| 函数 | 签署 | 说明 |
|---|---|---|
simplexNoise3d | (vec3) → float | 3D 简单音响 |
simplexNoise4d | (vec4) → float | 4D 简单音响( 使用) .w (时间) |
perlinNoise3d | (vec3) → float | 3D 佩林噪声 |
valueNoise3d | (vec3) → float | 三维值噪声 |
voronoiNoise3d | (vec3) → vec2 | 3D 沃罗诺伊.x =细胞分解, .y =边角) |
fbm | (vec3) → float | 分形布朗运动(2-octave simplex) |
curlNoise3d | (vec3) → vec3 | 3D 卷曲噪声 |
curlNoise4d | (vec4) → vec3 | 4D 卷曲噪声 |
ridgeNoise | (vec3) → float | 山脊噪声 |
turbulence | (vec3) → float | 骚动的噪音 |
记事
| 函数 | 签署 | 说明 |
|---|---|---|
reinhardTonemap | (vec3) → vec3 | 莱因哈德语调图 |
acesTonemap | (vec3) → vec3 | ACES 电影音调图 |
technicolorTonemap | (vec3) → vec3 | 温暖的科技色彩外观 |
cinematicTonemap | (vec3) → vec3 | 有线的电影S曲线 |
bleachBypassTonemap | (vec3) → vec3 | 脱饱和高孔 |
crossProcessTonemap | (vec3) → vec3 | 跨进程颜色变化 |
totosTonemap | (vec3) → vec3 | Basement 的自定义胶片色调图 |
SDF 原始人
| 函数 | 签署 | 说明 |
|---|---|---|
sdBox2d | (vec2, float) → float | 2D 框签名距离 |
sdSphere | (vec3, float) → float | 3D 球体签名距离 |
sdDiamond | (vec2, float) → float | 2D 钻石签名距离 |
sdRhombus | (vec2, vec2) → float | 2D Rhombus 签名距离 |
数学空间( S)
| 函数 | 签署 | 说明 |
|---|---|---|
screenAspectUV | (screenSize, range?) → vec2 | 以原产地为中心、被视线校正的紫外线 |
rotate | (vec2, float) → vec2 | 2D 按角度旋转(弧度) |
smin | (float, float, float) → float | 最小平滑 |
smax | (float, float, float) → float | 最大滑动度 |
atan2 | (float, float) → float | 两相弧度 |
tanh | (float) → float | 双曲正切 |
sinh | (float) → float | 双曲正弦 |
cosh | (float) → float | 双曲余弦 |
cosinePalette | (t, a, b, c, d, e?) → vec3 | 基于余弦的调色板生成器 |
复杂的数学
所有复杂业务的使用 vec2 地点 .x 真实的, .y = 虚构:
complexMul, (中文(简体) ), complexDiv, (中文(简体) ), complexPow, (中文(简体) ), complexSqrt, (中文(简体) ), complexSin, (中文(简体) ), complexCos, (中文(简体) ), complexTan, (中文(简体) ), complexLog, (中文(简体) ), complexConj, (中文(简体) ), complexToPolar, (中文(简体) ), complexMobius
模式
| 函数 | 签署 | 说明 |
|---|---|---|
bloom | (float) → float | 软发光曲线 |
bloomEdgePattern | (float, float, float) → float | 边缘意识开花 |
canvasWeavePattern | (vec2, float) → float | 织布纹理 |
grainTexturePattern | (vec2) → float | 胶片粒纹理 |
repeatingPattern | (vec2, float) → vec2 | 倾斜紫外线帮助器 |
实例
出处模式 — 有色调的动画梯度 :
export const sketch = Fn(() => {
const uv0 = screenAspectUV(screenSize)
const color = vec3(
uv0.x.add(0.5),
uv0.y.add(0.5),
sin(time).mul(0.5).add(0.5)
).toVar()
color.assign(technicolorTonemap(color))
return color
})
效果模式——自定义紫外线的样本输入有扭曲:
export const sketch = Fn(() => {
const warp = sin(uv().y.mul(20).add(time.mul(2))).mul(0.02)
const warped = vec2(uv().x.add(warp), uv().y)
return inputTexture.sample(warped)
})
效果模式——简单的色彩操作 :
export const sketch = Fn(() => {
const color = inputTexture.toVar()
const luma = dot(color.rgb, vec3(0.299, 0.587, 0.114))
return vec4(vec3(luma), color.a)
})
包含运行时间支持
包含模式支持中的图像和视频层 params.transparentBounds: true
以揭示其空地边界下的内容。 关闭设置或使用
false 以保留黑色边界。 此选项不影响封面模式或制作
组成背景透明.
- 渐变
- 文本
- 自定义阴影( 来源和效果模式)
- 图像和视频来源
- 实时相机输入
- ASC 二届理事会
- 图案
- 墨迹
- 半圆
- 抖动
- CRT 网络
- 粒子网格
- 像素排序
工具出口
createRuntimeClockadvanceRuntimeClockbuildRuntimeFrameevaluateTimelineForLayersresolveEvaluatedLayers- 运行时间配置和时间轴类型
页:1
ShaderLabComposition仅在config.composition,否则将填充由您布局定义的大小useShaderLab是大多数应用程序集成推荐的切入点- 如果您只需要在 DOM 中直接渲染成分, 请使用
ShaderLabComposition - 如果需要全手工控制,请使用下级源 API
- 纹理输出是可移植的,因为它由管理的画布支撑
- 后处理目前需要共享
THREE.WebGPURenderer - 如果您使用三发式反射
[email protected]你可能会看到THREE.Clock从@react-three/fiber; 警告是上游的, 不是由此套件发出的