@davepagurek/glsl-autodiff

中文文档
0.0.20发布于 2023/06/10MITTypeScriptCommonJS近 30 天下载 2780

厌倦了做数学 让你的顶点遮平正常? 同样的。 使用此库来写入您的函数一次并自动生成衍生出!

中文使用文档
文档目录10 节
  1. 演示
  2. 启动码
  3. 为什么?
  4. API 使用量
  5. 投入和产出
  6. 业务
  7. 其他设置
  8. JS/TS的进口
  9. 在浏览器中导入
  10. 捐款

glsl- 自定义

厌倦了做数学 让你的顶点遮平正常? 同样的。 使用此库来写入您的函数一次并自动生成衍生出!

演示

airplane-small

P5 编辑器中的活版

启动码

自动diff + 球形映射照明 自动diff + 球形映射照明 + 凸起映射
P5 编辑器模板 P5 编辑器模板
代码模板 代码模板

为什么?

有时候,我想取代 网状顶点在一个顶点的阴影。 这样做之后,一地表的常态应该改变:

image

然而,每个顶点的常态不会自动更新! 手动更新顶点常态需要您接受位移函数的衍生. 这个库将这个过程自动化,这样可以减少时间,并减少用户出错的可能性. 如果你感兴趣,读 更深入的博客文章 说明为什么有必要这样做和如何运作.

API 使用量

这暴露了一个 gen 函数生成 GLSL ,您可以在阴影码中切入。 在电话里 gen(),您提供了一个召回 收录 ad,AutoDiff引擎,并用来构建阴影码. 我们通过制作一个操作图来建立遮蔽器,我们通过该图输入实现产出。 每个函数调用都会创建代表此操作的对象. 一旦有了图表,我们就可以使用它自动计算出衍生物!

AutoDiff 引擎的方法设计类似您通常会如何写出一个阴影器,但使用方法链路而不是操作员符号,类似于如何写 p5的矢量类作品. 它看起来像这样的行动:

import { gen } from `@davepagurek/glsl-autodiff'

const vert = `
void main(void) {
  vec4 objSpacePosition = vec4(aPosition, 1.0);

  float x = objSpacePosition.x;
  float y = objSpacePosition.y;
  ${gen((ad) => {
    const x = ad.param('x')
    const y = ad.param('y')
    const time = ad.param('time')
    const speedX = 1.5
    const speedY = 2.8

    let offset = ad.val(0)
    for (let i = 0; i < 3; i++) {
      offset = offset.add(ad.sin(
        ad.sum(
          offset.mult(0.5).add(x.mult(speedX)).add(y.mult(speedY)),
          time.mult(0.002),
        )
      ))
    }
    offset = offset.mult(0.1)
    offset.output('z')
    offset.outputDeriv('dzdx', x)
    offset.outputDeriv('dzdy', y)
  })}
  objSpacePosition.z = z;
  vec3 slopeX = vec3(1.0, 0.0, dzdx);
  vec3 slopeY = vec3(0.0, 1.0, dzdy);
  vNormal = uNormalMatrix * normalize(cross(slopeX, slopeY));

  vec4 worldSpacePosition = uModelViewMatrix * objSpacePosition;
  gl_Position = uProjectionMatrix * worldSpacePosition;
}
`

投入和产出

参数 是您在 AutoDiff 外部创建的变量,但是您想要在 AutoDiff 内部引用的变量。 根据其可变类型,采用以下方法之一来创建: 1

interface AutoDiff {
  param(name: string): Param
  vec2Param: (name: string) => VecParam
  vec3Param: (name: string) => VecParam
  vec4Param: (name: string) => VecParam
}

产出 用于将AutoDiff内部的计算结果存储到AutoDiff外部可访问的名称。 还可以输出一个输入参数的计算衍生物.

interface Op {
  output(name: string);
  outputDeriv(name: string, param: Param);
}

举例来说,我们可以使用 param 引用一个制服,用它做一些数学,并输出结果:

const vert = `
// A uniform we want to reference
uniform vec3 position;

void main(void) {
  ${AutoDiff.gen((ad) => {
    // Get a computation graph node representing the uniform
    const position = ad.vec3Param('position')
    
    // Do stuff with it
    const offset = ad.sum(position.x(), position.y(), position.z())
    
    // Output it so the rest of the shader can reference it
    offset.output('offset')
  })}
  vec3 worldSpacePosition = position;
  worldSpacePosition.z += offset; // Use offset, an output, outside of AutoDiff
  gl_Position = uProjectionMatrix * vec4(worldSpacePosition, 1.0);
}
`

业务

考虑到任何平面图节点(Op),使用以下任何方法生成一个与同名GLSL操作相应的新节点:

interface Op {
  neg(): Op; // Multiply by -1
  add(...params: Input[]): Op;
  sub(val: Input): Op;
  mult(...params: Input[]): Op;
  div(param: Input): Op;
  pow(param: Input): Op;
  sqrt(): Op;
  sin(): Op;
  cos(): Op;
  tan(): Op;
  mix(b: Input, amt: Input): Op;
  clamp(min: Input, max: Input): Op;
  min(...params: Input[]): Op;
  max(...params: Input[]): Op;
  ifElse(thenOp: Input, elseOp: Input): Op; // Ternary with the receiving Op being the condition
  vecIfElse(thenOp: VectorOp, elseOp: VectorOp): VectorOp; // Ternary but where the if/else values are vectors
}

说明a Input 要么是另一个节点,要么是数字字形.

给定矢量图形节点( Q)VectorOp,扩展 Op),使用下列任何方法再次生成一个带有计算结果的新节点:

interface VectorOp {
  neg(): VectorOp;
  add(...params: VectorOp[]): VectorOp;
  scale(k: Input): VectorOp; // When multiplying by a scalar
  mult(...params: VectorOp[]): VectorOp; // When multiplying by another vector
  mix(other: VectorOp, amt: Input): VectorOp;
  clamp(min: VectorOp, max: VectorOp): VectorOp;
  min(...params: VectorOp[]): VectorOp;
  max(...params: VectorOp[]): VectorOp;
  dot(other: VectorOp): Op;
  length(): Op;
  dist(other: VectorOp): Op;
}

您也可以直接从自动编辑器的引擎中构造节点 gen 回调 :

interface AutoDiff {
  // Create new values
  val: (n: number) => Op;
  vec2: (x: Input, y: Input) => Vec;
  vec3: (x: Input, y: Input, z: Input) => Vec;
  vec4: (x: Input, y: Input, z: Input, w: Input) => Vec;

  // Create a new operation with scalar inputs
  sum: (...params: Input[]) => any;
  prod: (...params: Input[]) => any;
  sqrt: (param: Input) => any;
  sin: (input: Input) => any;
  cos: (input: Input) => any;
  tan: (input: Input) => any;
  mix: (a: Input, b: Input, amt: Input) => any;
  clamp: (val: Input, min: Input, max: Input) => any;
  min: (...params: Input[]) => any;
  max: (...params: Input[]) => any;
  ifElse: (ifOp: Input, thenOp: Input, elseOp: Input) => any;
  
  // Create a new operation with vector inputs
  vecSum: (...params: VectorOp[]) => VectorOp;
  vecProd: (...params: VectorOp[]) => VectorOp;
  vecMix: (a: VectorOp, b: VectorOp, amt: Input) => VectorOp;
  vecClamp: (val: VectorOp, min: VectorOp, max: VectorOp) => VectorOp;
  vecMin: (...params: VectorOp[]) => VectorOp;
  vecMax: (...params: VectorOp[]) => VectorOp;
  dot: (a: VectorOp, b: VectorOp) => Op;
  length: (val: VectorOp) => Op;
  dist: (a: VectorOp, b: VectorOp) => Op;
}

其他设置

在极罕见的场合, 你写代码 Just WorksTM! 对于另外99%的时间, AutoDiff 包含调试选项, 您可以通过将额外的设置对象传递到 gen 函数 :

gen: (
  cb: (ad: AD) => void,
  settings?: Partial<ADSettings>,
) => string;

type ADSettings = {
  maxDepthPerVariable: number;
  debug: boolean;
};

当你准备好 debug: true,在输出的 GLSL 中,每行直接来自您所写的代码,就会得到一个注释引用,其来源是:

投入 产出
const vert = `
void main(void) {
  vec4 objSpacePosition = vec4(aPosition, 1.0);
  float origZ = objSpacePosition.z;
  float x = objSpacePosition.x;
  float y = objSpacePosition.y;
  ${AutoDiff.gen((ad) => {
    const x = ad.param('x')
    const y = ad.param('y')
    const time = ad.param('time')
    const speedX = 1.5
    const speedY = 2.8
let offset = ad.val(0)
for (let i = 0; i < 3; i++) {
  offset = offset.add(ad.sin(
    ad.sum(
      offset.mult(0.5).add(x.mult(speedX)).add(y.mult(speedY)),
      time.mult(0.002),
    )
  ))
}
offset = offset.mult(0.1)
offset.output('z')

}, { debug: true, maxDepthPerVariable: 4 })} objSpacePosition.z += z; vec4 worldSpacePosition = uModelViewMatrix * objSpacePosition; gl_Position = uProjectionMatrix * worldSpacePosition; } `

void main(void) {
  vec4 objSpacePosition = vec4(aPosition, 1.0);
  float origZ = objSpacePosition.z;
  float x = objSpacePosition.x;
  float y = objSpacePosition.y;

// From vert<@file:///Users/dpagurek/Documents/Projects/glsl-autodiff/test/sine_plane/test.js:31:12 float _glslad_v14=(((0.0000*0.5000)+(x*1.5000))+(y*2.8000))+(time*0.0020);

// From vert<@file:///Users/dpagurek/Documents/Projects/glsl-autodiff/test/sine_plane/test.js:30:23 float _glslad_v16=0.0000+(sin(_glslad_v14));

// From vert<@file:///Users/dpagurek/Documents/Projects/glsl-autodiff/test/sine_plane/test.js:31:12 float _glslad_v27=(((_glslad_v16*0.5000)+(x*1.5000))+(y*2.8000))+(time*0.0020);

// From vert<@file:///Users/dpagurek/Documents/Projects/glsl-autodiff/test/sine_plane/test.js:30:23 float _glslad_v29=_glslad_v16+(sin(_glslad_v27));

// From vert<@file:///Users/dpagurek/Documents/Projects/glsl-autodiff/test/sine_plane/test.js:31:12 float _glslad_v40=(((_glslad_v29*0.5000)+(x*1.5000))+(y*2.8000))+(time*0.0020); float z=((_glslad_v29+(sin(_glslad_v40)))*0.1000);

objSpacePosition.z += z; vec4 worldSpacePosition = uModelViewMatrix * objSpacePosition; gl_Position = uProjectionMatrix * worldSpacePosition; }

有时太过或太少 最终在一线,所以你也可以供应 maxDepthPerVariable 在创建中间变量之前提供最多数量的嵌入操作。 如果没有说明,我们使用 maxDepthPerVariable: 8。要缩合成尽可能少的行,请使用 Infinity。使每个操作都成为自己的变量,使用1.

JS/TS的进口

将此库添加到您的工程中 :

yarn add @davepagurek/glsl-autodiff

然后导入 gen 方法 :

import { gen } from '@davepagurek/glsl-autodiff'

const myShader = `
  // some shader code here
  ${gen((ad) => {
    // Use autodiff here
  })}
`

在浏览器中导入

下载库 从 Github 发布标签 并将此库添加到您的 HTML 文件 :

<script type="text/javascript" src="autodiff.js"></script>

然后,使用全球 AutoDiff 变量,包括 gen 函数 :

const myShader = `
  // some shader code here
  ${AutoDiff.gen((ad) => {
    // Use autodiff here
  })}
`

捐款

想帮忙吗? 有些事情我想在某个时候去做 在“问题”标签中为后代写下 我喜欢帮忙!

设置回放:

git clone [email protected]:davepagurek/glsl-autodiff.git
cd glsl-autodiff
yarn install

编译 build/autodiff.js 财务报告和审定财务报表 build/autodiff.d.ts数字 :

yarn build

将所有编译的代码推向 Github Pages 用于演示( 如果您是我或有写权限) :

yarn deploy

向国家预防机制发布新版本:

yarn publish

版本与发布记录

@davepagurek/glsl-autodiff 共发布 20 个版本,首次发布于 2021/07/09;过去 12 个月发布了 0 个版本。

版本发布日期距今
0.0.20latest2023/06/103 年前
0.0.192023/05/193 年前
0.0.182023/05/073 年前
0.0.172023/05/033 年前
0.0.162023/05/033 年前
0.0.152021/07/225 年前

关于 @davepagurek/glsl-autodiff 的常见问题

@davepagurek/glsl-autodiff 是什么?

厌倦了做数学 让你的顶点遮平正常? 同样的。 使用此库来写入您的函数一次并自动生成衍生出!

如何安装 @davepagurek/glsl-autodiff?

在项目目录运行 npm install @davepagurek/glsl-autodiff,或使用 pnpm add / yarn add / bun add。

@davepagurek/glsl-autodiff 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@davepagurek/glsl-autodiff 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@davepagurek/glsl-autodiff 可以商用吗?

@davepagurek/glsl-autodiff 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@davepagurek/glsl-autodiff 的下载量有多少?

根据 npm 官方下载统计,@davepagurek/glsl-autodiff 在 2026/10/12 – 2026/11/10 这 30 天内被下载 2,780 次,日均约 93 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@davepagurek/glsl-autodiff 还在维护吗?

最新版本 0.0.20 发布于 2023/06/10(3 年前);至今共发布 20 个版本,其中过去 12 个月发布了 0 个;首次发布于 2021/07/09。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。