#glsl #shader #edsl #graphics

nightly cheddar

功能性和实用的GLSL超集DSL

3个不稳定版本

使用旧的Rust 2015

0.2.1 2019年1月19日
0.2.0 2018年9月30日
0.1.0 2018年7月8日

#233渲染


用于 spectra

BSD-3-Clause

65KB
1K SLoC

Cheddar GLSL超集语言

Build Status crates.io docs.rs License

什么是Cheddar?

Cheddar是GLSL的超集。

该包在其文档中包含一个嵌入教程,它将为您提供足够的详细信息,以了解Cheddar是什么以及您应该如何使用它。

贡献

您可以通过分支项目并提供合并请求来贡献。目前需要特别关注的工作主要包括

  • 核心类型和结构。
  • 示例。
  • 语义分析。
  • 细分控制着色器。
  • 细分评估着色器。
  • 适当的公共API。

lib.rs:

Cheddar着色语言。

前言

Cheddar是GLSL的超集,它增加了几个功能以增强GLSL。

  • 在Cheddar中将一些非有效GLSL构造转换为有效,以简化某些着色阶段的编写。
  • 一种更函数式的GPU着色编程方法。
  • 结构、类型和GLSL特定构造的共享。
  • 导入和模块。

该语言被表示为一个DSL。

函数式着色语言

Cheddar使用与GLSL相同的语法(对于大多数构造)。如果您已经编写了一段时间的GLSL,您可能需要大约二十分钟的时间来熟悉Cheddar并开始使用它。

与GLSL的最大不同之处在于Cheddar试图更函数化。它仍然是一种命令式语言;您仍然可以创建变量并更改其内容。但是,您不再写入全局变量(即顶点属性)或不再发射顶点和原语。

核心:语义函数

语义函数是您可以声明的函数,具有使它们特殊化的名称。每个程序员都知道至少一个语义函数:main。如果一个代码单元导出一个名为main的函数,该函数不接受任何参数并返回空值,则此函数将被视为您的二进制文件的入口点。Cheddar通过识别多个语义函数来使用此概念。

注意:可以更改链接器/编译器识别的作为start例程的函数的名称。尽管如此,目前使用Cheddar无法实现这一点。

大多数语义函数直接映射到着色器阶段。使用原始GLSL,如果你想编写一个顶点着色器,你必须提供一个导出main函数的代码单元。这段代码通常会通过in声明读取顶点属性,并将代码传递给下一个阶段(通常是片段着色器,但也可能是几何着色器等)。

这是一个典型、无聊的顶点着色器

layout (location = 0) in vec3 i_pos;
layout (location = 1) in vec4 i_col;

in vec4 o_col;

void main() {
  gl_Position = vec4(i_pos, 1.);
  o_col = i_col;
}

这个简单的顶点着色器只是将它的颜色传递到下一个阶段。

现在让我们尝试将这个代码段转换为Cheddar。首先,Cheddar没有着色器阶段的概念。一切都是一个函数。这使组合变得更好。映射到顶点着色器的语义函数是Cheddar中的map_vertex函数。必须遵守一些规则和定律。

  • map_vertex的签名是自由的,但
    • 它必须返回一个用户定义的类型,其名称是自由的。
    • 它可以有任意多的参数(甚至零),这些参数代表顶点着色器的输入。然而,这些参数的类型必须与GLSL顶点属性兼容。
    • 如果你想忽略一个参数,你只需省略其名称但保留其类型(例如,在C中)。
  • 返回位置的类型必须至少有一个字段,其类型为vec4 – 当前名称受限制,你必须将其称为chdr_Position。该字段表示计算输出的实际顶点位置,你无法从中读取;你只能写入它。如果你需要在map_vertex之后使用它,你需要为你的结构添加另一个字段。这个限制可能在未来的版本中取消。

Cheddar不关心函数和类型出现的顺序,它在编译时会重新组织它们。

上面的代码段对应的Cheddar可能是

struct V {
  vec4 pos;
  vec4 col;
};

V map_vertex(vec3 pos, vec4 col) {
  return V(vec4(pos, 1.), col);
}

当编译为GLSL时,上面的Cheddar代码将看起来像前面的GLSL代码段。你可以看到一些有趣的属性

  • 全局(即in)不再存在,现在是函数参数
  • 你可以忽略函数参数 – 例如,V map_vertex(vec3 pos, vec4)
  • 你不再写入全局(即out),而是返回值

从这个意义上说,Cheddar比原始GLSL更函数式。

你将想要编写一个片段着色器来消耗那个color值。原理是相同的:你需要编写一个接受这个V类型作为单个参数并返回用户定义的类型的语义函数。这个函数必须命名为map_frag_data

让我们编写一个片段着色器,它将使用顶点的颜色显示我们的顶点。

struct F {
  vec4 col;
};

F map_frag_data(V v) {
  return F(v.col);
}

就这样!最后,假设我们想在顶点着色器片段着色器之间插入一个几何着色器。这是通过定义concat_map_prim来完成的。这个函数很棘手,因为它不返回任何内容,充当一个生成器。它接受两个参数

  • 输入补丁类型,形式为in YourType[dimension] binding_name
    • YourType 是您定义的顶点类型(在我们的例子中,将是 V)。
    • dimension 是补丁中的顶点数量。
      • 1 将会使您映射点。
      • 2 将会使您映射线。
      • 3 将会使您映射三角形。
    • binding_name 是您希望参数调用的名称。
  • 输出原语类型,形式为 layout (output_prim, max_vertices = nb) out YourOutputType
    • output_prim 是要使用的输出原语。
      • points 将输出简单点。
      • line_strip 将输出带状线。
      • triangle_strip 将输出带状三角形。
    • nb 是您将输出的最大顶点数。

以下是我们当前示例的示例。

struct GV {
  vec4 pos;
  vec4 col;
  vec3 normal;
};

void concat_map_prim(in V[3] verts, layout (triangle_strip, max_vertices = 3) out GV) {
  // …
}

接下来,您想了解的是您应该如何“生成”顶点和原语。这是通过一系列语义函数完成的

  • yield_vertex,它接受一个正确类型的顶点作为单个参数(在我们的例子中为 V)。
  • yield_primitive,它不接受任何参数。

这两个函数都不返回任何内容。前者将 yield 您传递的顶点并将其发送到当前原语。带状原语将连接您发出的每个顶点,直到您调用 yield_primitive 函数,该函数将使用前面的点生成原语,并最终开始另一个原语,如果之后发出其他 yield_vertex 调用。

最后,这些函数应放在同一个代码单元中。Cheddar 不要求您为不同的 着色器阶段 提供不同的字符串,因为语义函数具有不同的签名和名称。

构造共享

Cheddar 的一个主要优势是启用 构造共享。也就是说,您使用的大多数全局符号都将在所有 着色器阶段 之间共享。这并不令人惊讶,因为我们到目前为止只定义了函数,那么我们为什么要重复使用每个类型呢?即使 GLSL 要求您重复 structuniformin / out 声明,Cheddar 也不会,它将在幕后执行所有操作。

如果您声明一个类型,例如

struct MySuperCoolType {
  float a;
  vec3 b;
  mat4 c;
};

并在几个语义函数(比如 map_vertexmap_frag_data)中使用它,Cheddar 将自动生成具有相同类型定义的 MySuperCoolType顶点着色器片段着色器

这种共享对以下内容有效

  • struct 定义。
  • 全局 const 声明。您将喜欢定义 PI 一次并永远。
  • uniform(常规和 )。
  • 您不必担心 inout 是否匹配,因为所有操作都在函数级别以及通过您定义的类型完成。
  • 除了语义函数之外的所有函数。

模块和导入

定义

Cheddar 还提供了一些强制性的重构和组织代码的功能:模块。您所编写的任何代码都属于一个模块。目前,模块导出它们定义的所有符号。

模块有一个名称,并且必须是唯一的。名称的格式为:foo.bar.zoo,并且有直接的文件系统表示。在这里,foo.bar.zoo 模块位于文件 foo/bar/zoo.chdr 中。

导入模块

可以使用 use 关键字导入模块——简单,就像 Rust 一样!——并且可以使用括号 ( ) 来选择项目。

use foo.bar.zoo (PI)

在这里,我们从 foo.bar.zoo 导入 PI

免责声明:目前,导入列表 仅向程序员提供提示,并由 Cheddar 运行时使用。所有符号都被导入。这将在未来的版本中修复。

Cheddar 支持传递依赖关系,并且知道如何解决导入的 菱形问题。如果您遇到任何依赖循环,它也会正确捕获。

关于 warmy 的说明

Cheddar 使用 warmy crate 为您提供模块和简单的依赖关系解决器。当前的情况是,如果您不要求 Cheddar 进行查找,它不会查找您模块的依赖关系。有关如何操作的更多信息,请参阅 Module::substitute_imports 函数。

由于使用了 warmy,Cheddar 还支持自动重新加载阴影模块。

关于验证的说明

目前,Cheddar 在验证您的代码方面做了非常、非常少的验证工作。它主要检查

  • 通过 glsl crate 的 GLSL 语法。
  • 一些 Cheddar 构造,例如您提供的类型或顶点类型中的字段。
  • 依赖模块循环。

**您的代码的语义不会被分析**。这意味着如果您做了类似 float x = true; 的操作,Cheddar 不会抱怨。

正在开发中,但非常欢迎贡献!

关于细分控制和平滑评估着色器的说明

这两个阶段尚未集成到 Cheddar 中。

依赖关系

~2–9.5MB
~84K SLoC