Refactor ShaderLab documention (#2611)

* refactor: shaderlab doc
This commit is contained in:
SwayYan
2025-04-18 11:46:46 +08:00
committed by GitHub
parent 1737e2f5c4
commit 66116ba675
18 changed files with 772 additions and 617 deletions

View File

@@ -2,6 +2,6 @@
"overview": { "title": "材质概述" },
"examples": { "title": "指南和示例" },
"builtinShaders": { "title": "内置着色器" },
"shaderLab": { "title": "ShaderLab 语法" },
"shaderLab": { "title": "ShaderLab" },
"variables": "内置变量"
}

View File

@@ -2,7 +2,7 @@
title: 自定义着色器
---
类似于 Typescript 中的函数、类、属性, Shader 代码也有一套自己的 API 和配套的 [UIScript](/docs/graphics/material/shaderLab/editor/#uiscript)。本文可以帮助你如何基于这些 API 和 [ShaderLab](/docs/graphics/material/shaderLab/intro) 语法,自定义自己的 Shader。
类似于 Typescript 中的函数、类、属性, Shader 代码也有一套自己的 API 和配套的 [UIScript](/docs/graphics/material/shaderLab/script)。本文可以帮助你如何基于这些 API 和 [ShaderLab](/docs/graphics/material/shaderLab/overview) ,自定义自己的 Shader。
## 快速上手
@@ -13,7 +13,7 @@ title: 自定义着色器
style={{ zoom: "50%" }}
/>
引擎会自动帮我们创建好 Shader 文件和 [UIScript](/docs/graphics/material/shaderLab/editor/#uiscript) 文件
引擎会自动帮我们创建好 Shader 文件和 [UIScript](/docs/graphics/material/shaderLab/script) 文件
<img
src="https://gw.alipayobjects.com/zos/OasisHub/6351fa81-5159-4469-bd95-8f21a8f2f4ac/image-20250124162909194.png"

View File

@@ -6,7 +6,7 @@ title: 使用着色器数据
### 设置数据
我们推荐使用 [ShaderLab](/docs/graphics/material/shaderLab/intro) 语法来声明和使用着色器数据:
我们推荐使用 [ShaderLab](/docs/graphics/material/shaderLab/overview) 来声明和使用着色器数据:
```glsl showLineNumbers {3,8,17,21}
Editor {
@@ -41,7 +41,7 @@ Editor {
```
<Callout type="positive">
参考 [ShaderLab 语法](/docs/graphics/material/shaderLab/editor/#properties-属性) 使用更多类型的数据。
参考 [ShaderLab 属性定义 ](/docs/graphics/material/shaderLab/property) 使用更多类型的数据。
</Callout>
当然,我们也可以通过代码设置着色器数据:

View File

@@ -8,7 +8,7 @@ title: 材质概述
## 着色器
着色器(Shader)是一段运行在GPU中的程序通常由顶点着色器和片元着色器组成我们可以借助 [ShaderLab 语法](/docs/graphics/material/shaderLab/intro/) 很方便地书写这部分代码。
着色器(Shader)是一段运行在GPU中的程序通常由顶点着色器和片元着色器组成我们可以借助 [ShaderLab 语法](/docs/graphics/material/shaderLab/overview/) 很方便地书写这部分代码。
## 着色器数据
@@ -20,6 +20,14 @@ Galacean 支持对[混合状态BlendState](/apis/core/#RenderState-BlendSt
我们拿一个透明物体的标准渲染流程来举例,我们希望开启透明混合,并且因为透明物体是叠加渲染的,所以我们还要关闭深度写入,并设置为透明队列;
```ts
material.renderQueueType = RenderQueueType.Transparent;
material.renderState.depthState.writeEnabled = false;
material.renderState.blendState.enabled = true;
```
我们推荐使用 ShaderLab 来设置:
```ts showLineNumbers {3,7-12,14}
Pass "Pass0" {
DepthState {
@@ -40,5 +48,5 @@ Pass "Pass0" {
<Callout type="info">
渲染状态还可以设置为变量,然后通过着色器数据来控制,详情参考 [ShaderLab
文档](/docs/graphics/material/shaderLab/shader)。
渲染状态的设置](/docs/graphics/material/shaderLab/renderState)。
</Callout>

View File

@@ -1,8 +1,10 @@
{
"intro": "总览",
"shader": "Shader",
"editor": "Editor",
"subShader": "SubShader",
"pass": "Pass",
"macro": "宏"
"overview": "总览介绍",
"property": "材质属性定义",
"script": "UIScript 的使用和绑定",
"global": "全局变量声明",
"renderState": "渲染状态设置",
"entry": "顶点、片元着色器程序编写",
"multi": "多 Pass Shader 编写",
"chunk": "Shader 代码段引用"
}

View File

@@ -0,0 +1,57 @@
---
title: Shader 代码段引用
---
为了方便代码段复用ShaderLab 中可以如下使用 `include` 宏进行代码段引用,后续编译过程中会被自动扩展替换。
```glsl showLineNumbers
#include "{includeKey}"
```
为了能使代码段能够通过 `include` 宏进行引用,我们有 2 种方式进行代码段声明:
## 1. 编辑器中创建 着色器 / 着色器片段
创建的代码段 `includeKey` 为该文件在工程中的文件路径,比如下图中,如果想在 Shader 资产 `UnlitShader` 中引用 `ShaderChunk` 代码段,分别可以通过绝对路径和相对路径引用:
```glsl showLineNumbers
// 相对路径
#include "./ShaderChunk.glsl"
// 编辑器解决路径
#include "/ShaderChunk.glsl" // 项目资产根目录
```
<Image src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*3UDUS6OtbPUAAAAAAAAAAAAAeteEAQ/fmt.webp" />
## 2. 脚本中显示注册代码段
```ts showLineNumbers
import { ShaderFactory } from '@galacean/engine';
const commonSource = `// shader chunk`;
ShaderFactory.registerInclude('includeKey', commonSource);
```
ShaderLab 中引用
```glsl showLineNumbers
#include "includeKey"
```
<Callout>
当前版本中,引擎提供了 `PBR` ShaderLab 内置代码段,开发者可以通过这些内置代码段对 `PBR` Shader 进行自定义扩展。需要注意的是,需要对引擎内置 ShaderLab 代码段进行注册后才可使用。
```ts showLineNumbers {4} filename="Typescript"
import { registerIncludes } from "@galacean/engine-shader-shaderlab";
// 对内置 ShaderLab 代码段注册。
registerIncludes();
```
```glsl showLineNumbers filename="ShaderLab"
...
#include "Common.glsl"
#include "BRDF.glsl"
...
```
官方内置 `PBR` ShaderLab 代码段源码 [参考](https://github.com/galacean/engine/blob/main/packages/shader-shaderlab/src/shaders/PBR.gs)
</Callout>

View File

@@ -1,260 +0,0 @@
import { Image } from "@/mdx";
---
# Editor
```glsl
Editor {
Properties {
material_BaseColor("Offset unit scale", Color) = (1,1,1,1);
...
Header("Emissive")
{
material_EmissiveColor("Emissive color", Color) = (1,1,1,1);
...
}
...
}
...
Macros {
[On] UV_OFFSET("UV Offset", Range(1,100)) = 10;
...
Header("") {
SOME_MACRO("label", Int) = 1;
}
}
...
// 指定 Shader 绑定的 UIScript 编辑器项目路径。
UIScript "./shaderScript.ts";
}
```
## Properties 属性
可用于定义绑定了该 Shader 自定义材质的属性,在编辑器中开发者可以通过自定义材质 Inspector 面板对定义的属性进行调整。
<Image
src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*1mjVR5GXXOkAAAAAAAAAAAAADteEAQ/fmt.webp"
figcaption="材质 Inspector 面板"
width="200px"
/>
### 属性定义
```glsl
/**
* @language zh
* Comments description
*/
/**
* @language en
* 注释描述
*/
propertyName("Description", EditType) = [DefaultValue];
```
<Callout type="info">
1. 可以使用 `Header` 指令将有关联的属性组织起来Inspector 对应的面板中也会有相应的层级分类:
```
Header("Emissive") {
material_EmissiveColor("Emissive color", Color) = (1,1,1,1);
...
}
```
2. 通过注释标注 Inspector Hover 提示内容,支持使用 @language 指令进行多语言指定。
</Callout>
当前支持的 EditType 列表如下:
| EditType | Example |
| :-: | :-- |
| Bool | propertyName("Property Description", Boolean) = true; |
| Int | propertyName("Property Description", Int) = 1; <br/>propertyName("Property Description", Range(0,8)) = 1 <br/> propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1 // 枚举 |
| Float | propertyName("Property Description", FLoat) = 0.5; <br/>propertyName("Property Description", Range(0.0, 1.0)) = 0.5; <br/> propertyName("Property Description", Enum(Item1: 1.0, Item2: 2.0, Item3: 3.0)) = 1.0; // 枚举 |
| Texture2D | propertyName("Property Description", Texture2D); |
| TextureCube | propertyName("Property Description", TextureCube); |
| Color | propertyName("Property Description", Color) = (0.25, 0.5, 0.5, 1); |
| Vector2 | propertyName("Property Description", Vector2) = (0.25, 0.5); |
| Vector3 | propertyName("Property Description", Vector3) = (0.25, 0.5, 0.5); |
| Vector4 | propertyName("Property Description", Vector4) = (0.25, 0.5, 0.5, 1.0); |
#### 枚举类型
```glsl
propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1;
```
<Callout typ="warning">
当前只有 Int 和 Float 类型支持枚举且不支持类型混合比如下面的枚举混合了Float和Int将不会被正确解析 ```glsl
propertyName("Property Description", Enum("Item1":1, "Item2":2.0, "Item3": 3)) = 2.0; ```
</Callout>
## Macros
用于将 Shader 中用到的宏反射到 Inspector 中,从而在编辑器中对 Shader 依赖的宏进行灵活的调整。
```
// 开启/关闭
[On/Off]macroName("MacroLabel", EditType) = [DefaultValue];
```
通过`[On/Off]`指令指定宏的默认开关,当前编辑器支持的宏类型如下:
| Type | Example |
| :-----: | :-------------------------------------------------------------------------------------------------------- |
| 无值宏 | macroName("Macro Description"); |
| Bool | macroName("Macro Description", Boolean) = true; |
| Int | macroName("Macro Description", Int) = 1; <br/> macroName("Macro Description", Range(0,8)) = 1; |
| Float | macroName("Macro Description", FLoat) = 0.5; <br/> macroName("Macro Description", Range(0.0, 1.0)) = 0.5; |
| Color | macroName("Macro Description", Color) = (0.25, 0.5, 0.5, 1); |
| Vector2 | macroName("Macro Description", Vector2) = (0.25, 0.5); |
| Vector3 | macroName("Macro Description", Vector3) = (0.25, 0.5, 0.5); |
| Vector4 | macroName("Macro Description", Vector4) = (0.25, 0.5, 0.5, 1.0); |
## UIScript
开发者使用编辑器对自定义材质属性进行调节的同时,还可以通过`UIScript`指定数据变更的回调行为。
- 在 ShaderLab 中绑定 `UIScript`:
```
Editor {
...
UIScript "/path/to/script";
...
}
```
<Image
src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*t4LFQ4KEL6kAAAAAAAAAAAAADteEAQ/fmt.webp"
width="70%"
/>
绑定的`UIScript`脚本路径支持相对路径和绝对路径,以上图项目根目录为例,绝对路径为
`/PBRScript1.ts`,相对路径为`./PBRScript1.ts`
### UIScript API
编辑器通过内置`ShaderUIScript`类暴露相关API
```ts
import { Color, Material, Texture, Vector2, Vector3, Vector4 } from "@galacean/engine";
type ShaderPropertyValue = number | Vector2 | Vector3 | Vector4 | Color | Texture;
type ShaderMacroValue = number | Vector2 | Vector3 | Vector4 | Color;
/**
* Script for extending `Shader` UI logic.
*/
export abstract class ShaderUIScript {
/** @internal */
_propertyCallBacks: Map<string, (material: Material, value: ShaderPropertyValue) => void> = new Map();
/** @internal */
_macroCallBacks: Map<string, (material: Material, defined: boolean, value: ShaderMacroValue) => void> = new Map();
/**
* The method is called when then shader is switched.
* @param material - The material which the shader is bound to
*/
onMaterialShaderSwitched(material: Material): void {}
/**
* Register property change callback.
* @parma propertyName - Property name
* @parma onChanged - Fired on property changed
*/
protected onPropertyChanged(
propertyName: string,
onChanged: (material: Material, value: ShaderPropertyValue) => void
): void {
this._propertyCallBacks.set(propertyName, onChanged);
}
/**
* Register macro change callback.
* @parma macroName - Macro name
* @parma onChanged - Fired on macro changed
*/
protected onMacroChanged(
macroName: string,
onChanged: (material: Material, defined: boolean, value: ShaderMacroValue) => void
): void {
this._macroCallBacks.set(macroName, onChanged);
}
}
```
### UIScript 编写
1. 在编辑器中创建 UIScript 脚本
<Image
src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*Qh4UTZgaY7MAAAAAAAAAAAAADteEAQ/fmt.webp"
width="60%"
figcaption="创建UIScript"
/>
2. 通过继承 ShaderUIScript 类指定属性变更回调。
```ts
import { Material, RenderQueueType, Vector3, BlendFactor, RenderFace, CullMode, BlendMode } from "@galacean/engine";
export default class extends ShaderUIScript {
constructor() {
super();
......
// 在构造函数中注册监听属性回调
this.onPropertyChanged("material_NormalTexture", (material: Material, value) => {
const shaderData = material.shaderData;
if (value) {
shaderData.enableMacro("MATERIAL_HAS_NORMALTEXTURE");
} else {
shaderData.disableMacro("MATERIAL_HAS_NORMALTEXTURE");
}
})
......
}
// 指定 shader 绑定的回调
override onMaterialShaderSwitched(material: Material) {
const shaderData = material.shaderData;
shaderData.disableMacro("MATERIAL_OMIT_NORMAL");
shaderData.enableMacro("MATERIAL_NEED_WORLD_POS");
shaderData.enableMacro("MATERIAL_NEED_TILING_OFFSET");
// default value
const anisotropyInfo = shaderData.getVector3("material_AnisotropyInfo");
if (!anisotropyInfo) {
shaderData.setVector3("material_AnisotropyInfo", new Vector3(1, 0, 0));
} else {
shaderData.setFloat("anisotropy", anisotropyInfo.z);
const PI2 = Math.PI * 2;
const rotationRad = (Math.atan2(anisotropyInfo.y, anisotropyInfo.x) + PI2 ) % PI2;
shaderData.setFloat("anisotropyRotation", rotationRad * (180 / Math.PI))
}
}
}
```
<Callout info="warning">
注意,当前版本 ShaderLab 材质属性模块只是定义了绑定该 Shader 的材质在编辑器中的 Inspector
UI面板并不会替你在`ShaderPass`中声明对应的全局变量,如果`ShaderPass`代码中引用了该变量需在[全局变量](./shader/#全局变量)模块中明确声明补充。
</Callout>

View File

@@ -0,0 +1,113 @@
---
title: 顶点、片元着色器程序编写
---
`Pass` 代表一次 GPU 绘制过程,因此每个 `Pass` 必须包含一个顶点着色器和片元着色器。ShaderLab 中通过 `VertexShader` 和 `FragmentShader` 分别指定其入口函数。
```glsl showLineNumbers {4-5}
...
Pass "0" {
...
VertexShader = vert;
FragmentShader = frag;
v2f vert(a2v o) {
...
}
...
void frag(v2f i) {
...
}
...
}
```
## Attribute 和 Varying 变量声明
跟传统 GLSL Shader 程序一样ShaderLab 通过 Attribute 和 Uniform 变量接收来自 CPU 的渲染数据,同时顶点着色器和片元着色器通过 Varying 变量进行数据传递。不同的是ShaderLab 是通过结构体和着色器入口函数签名来完成对 Attribute 和 Varying 变量的声明。
```glsl showLineNumbers {13, 16, 21, 27-28}
struct Attributes {
vec3 POSITION;
vec4 COLOR_0;
...
}
struct Varyings {
vec2 uv;
vec3 position;
...
}
...
Varyings vert(Attributes attr) {
Varyings vary;
...
vary.position = attr.Position;
...
return vary;
}
void frag(Varyings vary) {
...
gl_FragColor = vary.COLOR_0;
}
...
VertexShader = vert;
FragmentShader = frag;
```
## MRT(多目标渲染)
`ShaderLab` 同时兼容 `GLSL 100` 和 `GLSL 300` 语法,因此你可以使用两种语法进行 `MRT` 指定。
### 1. 通过 `gl_FragData[i]` 进行指定 MRT 指定
```glsl showLineNumbers {2-3}
void main(v2f input) {
gl_FragData[0] = vec4(1.,0.,0.,1.); // render target 0
gl_FragData[1] = vec4(1.,0.,0.,1.); // render target 1
}
```
### 2. 通过入口函数返回结构体指定
```glsl showLineNumbers {1-4}
struct mrt {
layout(location = 0) vec4 fragColor0; // render target 0
layout(location = 1) vec4 fragColor1; // render target 1
}
mrt main(v2f input) {
mrt output;
output.fragColor0 = vec4(1.,0.,0.,1.);
output.fragColor1 = vec4(1.,0.,0.,1.);
return output;
}
```
## 宏
当前 `ShaderLab` 仅支持 GLSL 标准语法中的以下部分宏和宏操作函数:
| 宏 | 描述 |
| :-: | :- |
| `#define` | 等同 GLSL 用法 |
| `#undef` |等同 GLSL 用法 |
|`#if` | 等同 GLSL 用法 |
| `#ifdef`| 等同 GLSL 用法 |
| `#ifndef` | 等同 GLSL 用法 |
| `#else` | 等同 GLSL 用法 |
| `#elif` | 等同 GLSL 用法 |
| `#endif` | 等同 GLSL 用法 |
| `defined` | 等同 GLSL 用法 |
| `#include` | `ShaderLab` 特有,用于代码段引用,详细使用参考[文档](./chunk) |
除了 `#include`,其它的宏使用同 GLSL 标准一致,详细用法参考 [GLSL 语法标准](https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf)。
<Callout type="warning">
ShaderLab 宏会在预处理器阶段被展开,因此宏不能影响 ShaderLab 结构解析,即 `Shader``SubShader``Pass``EditorProperties``EditorMacros` 关键字不能被包含在类似 `#ifdef` 这样的分支宏内。
</Callout>

View File

@@ -0,0 +1,151 @@
---
title: 全局变量声明
---
在总览[章节](./overview/#语法介绍)我们提到,`ShaderLab` 总体语法骨架主要由3个层级模块嵌套组成`Shader`、`SubShader`、`Pass`。每个层级模块都有对应的全局变量域在全局变量域中可以声明4种全局变量渲染状态(RenderState),结构体,函数,以及单变量。
| 变量类型 | 描述 |
| :-- | :-- |
| 渲染状态 | 对应引擎 [RenderState](/apis/galacean/#RenderState) API可以直接在 ShaderLab 中进行设置 |
| 结构体 | 作用等同于 GLSL 中的结构体,同时也用于 Varying 和 Attribute 变量声明定义,详见[文档](./entry/#attribute-和-varying-变量声明) |
| 函数 | 作用等同于 GLSL 中的全局函数 |
| 单变量 | 用于定义 Shader 中的 Uniform 变量 |
<Callout type="info">
### 全局变量作用域
与其他编程语言类似,`ShaderLab` 中的全局变量也有作用域和同名覆盖原则。简单来说,全局变量的作用和可见范围仅限于其声明的模块内部,即 `Shader`、`SubShader` 和 `Pass` 模块。而同名覆盖原则指的是如果在 `Pass` 模块内存在与其上级 `SubShader` 模块内同名的全局变量,则 `Pass` 内的全局变量会覆盖 `SubShader` 内的同名全局变量。`SubShader` 和上级 `Shader` 模块同名变量的覆盖原则同理。
</Callout>
## 渲染状态
包含混合状态(`BlendState`),深度状态(`DepthState`),模板状态(`StencilState`),光栅化状态(`RasterState`),渲染队列(`renderQueueType`)。
### BlendState
```glsl
BlendState state {
Enabled: bool;
ColorBlendOperation: BlendOperation.XXX;
AlphaBlendOperation: BlendOperation.XXX;
SourceColorBlendFactor: BlendFactor.XXX;
SourceAlphaBlendFactor: BlendFactor.XXX;
DestinationColorBlendFactor: BlendFactor.XXX;
DestinationAlphaBlendFactor: BlendFactor.XXX;
ColorWriteMask: float // 0xffffffff
BlendColor: vec4;
AlphaToCoverage: bool;
}
```
<Callout>
[BlendOperation](/apis/galacean/#BlendOperation) 和 [BlendFactor](/apis/galacean/#BlendFactor) 枚举等同引擎 API。
</Callout>
### DepthState
```glsl
DepthState state {
Enabled: bool;
WriteEnabled: bool;
CompareFunction: CompareFunction.XXX;
}
```
<Callout>
[CompareFunction](/apis/galacean/#CompareFunction) 枚举等同引擎 API
</Callout>
### StencilState
```glsl
StencilState state {
Enabled: bool;
ReferenceValue: int;
Mask: float; // 0xffffffff
WriteMask: float; // 0xffffffff
CompareFunctionFront: CompareFunction.XXX;
CompareFunctionBack: CompareFunction.XXX;
PassOperationFront: StencilOperation.XXX;
PassOperationBack: StencilOperation.XXX;
FailOperationFront: StencilOperation.XXX;
FailOperationBack: StencilOperation.XXX;
ZFailOperationFront: StencilOperation.XXX;
ZFailOperationBack: StencilOperation.XXX;
}
```
<Callout>
[CompareFunction](/apis/galacean/#CompareFunction) 和 [StencilOperation](/apis/galacean/#StencilOperation) 枚举等同引擎 API
</Callout>
### RasterState
```glsl
RasterState state {
CullMode: CullMode.XXX;
DepthBias: float;
SlopeScaledDepthBias: float;
}
```
<Callout>
[CullMode](/apis/galacean/#CullMode) 枚举等同引擎 API
</Callout>
### renderQueueType
```glsl showLineNumbers
RenderQueueType = Transparent;
RenderQueueType = Opaque;
RenderQueueType = AlphaTest;
```
以上各个渲染状态设置参考[文档](./renderState)。
## 结构体、函数
等同 `GLSL` 中的语法,下面代码段显示了分别声明在 Shader、SubShader 和 Pass 域内的函数和结构体:
```glsl showLineNumbers {5-9, 15-19, 25-29}
Shader "PlanarShadow" {
...
// 作用域为整个 Shader 模块
mat4 getJointMatrix(sampler2D smp, float index) {
float base = index / renderer_JointCount;
...
return mat4(m0, m1, m2, m3);
}
...
SubShader "Default" {
...
// 作用域为 "Default" SubShader 模块
vec3 ShadowProjectPos(vec4 vertPos) {
vec3 shadowPos;
...
return shadowPos;
}
...
Pass "0" {
...
// a2v 结构体只对 Pass "0" 可见
struct a2v {
vec4 POSITION;
vec4 JOINTS_0;
vec4 WEIGHTS_0;
};
...
}
}
...
}
```
## 单变量
等同 `GLSL` 中的语法,`ShaderLab` 中所有 `Uniform` 变量使用全局单变量语法来声明。
```glsl showLineNumbers
[lowp/mediump/highp] mat4 uMVPMatrix;
```

View File

@@ -1,45 +0,0 @@
---
title: 宏
---
ShaderLab 支持 GLSL 标准语法中的部分宏和宏操作符:
- `#define`
- `#undef`
- `#if`
- `#ifdef`
- `#ifndef`
- `#else`
- `#elif`
- `#endif`
- `defined`
以及额外引入的 `#include` 宏。
<Callout type="warning">
ShaderLab 宏会在预处理器阶段被展开,因此宏不能影响 ShaderLab 结构解析,即 `Shader``SubShader``Pass``EditorProperties``EditorMacros` 关键字不能被包含在类似 `#ifdef` 这样的分支宏内。
</Callout>
## include 宏
为了方便代码段复用ShaderLab 中可以如下使用 `include` 宏进行代码段引用,后续编译过程中会被自动扩展替换。
```glsl
#include "{includeKey}"
```
为了能使代码段可以通过 `include` 宏进行引用,我们有 2 种方式进行代码段声明:
1. 编辑器中创建 着色器 / 着色器片段
创建的代码段 `includeKey` 为该文件在工程中的文件路径,比如 `/Root/Effect.glsl`
2. 脚本中显示注册代码段
```ts
import { ShaderFactory } from '@galacean/engine';
const commonSource = `// shader chunk`;
ShaderFactory.registerInclude('includeKey', commonSource);
```
Shader 文件引入代码段支持相对路径引用,所有相对路径都基于主 Shader 文件路径进行转换。如Shader文件路径为`/root/hair/shader.gs`,引入代码段路径为`/root/hair/common.glsl`,则引入的相对路径为 `#include "./common.glsl"`。

View File

@@ -0,0 +1,65 @@
---
title: 多 Pass Shader 编写
---
有时我们想要绘制一个复杂特效通常需要在一次渲染主循环中渲染多次,因此我们可以将这些关联的 Pass 放到同一个 SubShader 下统一管理起来。通常我们称含有多个 Pass 的 Shader 为 `多-Pass-Shader`。
如下所示,在 SubShader 中声明渲染所需的多个 Pass引擎会根据声明顺序依次渲染 Pass。
```glsl showLineNumbers {6-8}
SubShader "SubShaderName" {
...
// 全局变量区:变量声明,结构体声明,渲染状态声明
...
Pass "Pass0" { ... }
Pass "Pass1" { ... }
Pass "Pass2" { ... }
...
}
```
## UsePass
开发者可以通过 `UsePass` 指令复用 Shader Pass: `UsePass "{pass 路径}"`。 Pass 路径格式为 `{ShaderName/SubShaderName/PassName}`, 比如开发者声明了以下 Shader Pass:
```glsl showLineNumbers {1,3,5}
Shader "water" {
...
SubShader "Default" {
...
Pass "0" {
...
}
...
}
...
}
```
开发者在其它自定义 Shader 中可以通过 `UsePass "water/Default/0"` 进行复用。
此外 `UsePass` 指令同样支持引擎内置 Pass 的复用,目前可复用的引擎内置 `Pass` 如下:
| 内置 Shader | Pass 路径 |
| :-------------: | :-----------------------------: |
| PBR | pbr/Default/Forward |
| Unlit | unlit/Default/Forward |
| Skybox | skybox/Default/Forward |
| Particle-shader | particle-shader/Default/Forward |
| SpriteMask | SpriteMask/Default/Forward |
| Sprite | Sprite/Default/Forward |
<Callout>
SubShader 除了用于组织多 Pass还可以设置 `Tag`,作用相当于引擎 [setTag](/apis/galacean/#SubShader-setTag) API。比如设置 `ReplaceTag`:
```glsl showLineNumbers
SubShader "SubShaderName" {
...
Tags {ReplaceTag = "opaque"}
Pass "PassName" {
...
}
}
```
</Callout>

View File

@@ -1,8 +1,10 @@
---
title: ShaderLab 语法标准
title: ShaderLab
---
## 语法标准
`ShaderLab` 是一个针对 Galacean 引擎打造的 `Shader` 包装语言,它允许开发人员使用熟悉的 `GLSL` 语法编写自定义 `Shader`,同时提供了额外的高级抽象和管理特性以增强开发效率。通过 `ShaderLab`,开发者能够更便捷地定义材质属性、渲染配置和其他效果。尽管 `ShaderLab` 为着色器的编写引入了便利性,但它并不取代 `GLSL`,而是与之兼容。开发者可以在 `ShaderLab` 框架内编写原生 `GLSL` 代码块,享受两者的结合优势。
## 语法介绍
`ShaderLab` 语法骨架如下:
@@ -24,7 +26,16 @@ Shader "ShaderName" {
}
```
## Unlit Demo
### ShaderLab 主要语法模块
| 语法模块 | 描述 |
| :- | :-- |
| [Shader](./shader) | ShaderLab 中通过 `Shader` 模块声明一个 Shader 对象,开发者可以在该模块中添加材质属性、绑定 [Shader UI 脚本](./script)、[声明全局变量](./global)等 |
| [Editor](./ediotr) | 开发者通过 `Editor` 模块对材质 Inspector 面板进行定制 |
| [SubShader](./subShader) | 在 `SubShader` 中指定 [Tag](/apis/galacean/#SubShader-setTag),添加 ShaderPass使用 `UsePass` 指令 |
| [Pass](./pass) | 开发者通过 `Pass` 模块声明 [ShaderPass](/apis/galacean/#ShaderPass) 对象 |
## Unlit ShaderLab 示例
```ts showLineNumbers {2,18,21}
Shader "ShaderName" {

View File

@@ -1,129 +0,0 @@
---
title: Pass
---
```glsl
Pass "PassName" {
Tag {PipelineStage = "ShadowCaster"}
...
// 全局变量区:公共变量声明,结构体声明,函数声明
...
// 渲染管线和渲染状态设置
// 指定顶点着色器和片元着色器 强调glsl语言
VertexShader = vert;
// 指定渲染队列
RenderQueueType = Transparent;
}
```
`Pass` 是 `Shader` 对象的基本元素。简单的着色器对象可能只包含一个 `Pass`,但更复杂的着色器可以包含多个 `Pass`。 它定义了渲染管线特定阶段执行的操作,例如在 GPU 上运行的着色器程序,渲染状态,以及渲染管线相关设置。
## 渲染状态
可以通过以下两种方式指定
1. 显示赋值
```
BlendState = blendState;
```
2. Pass 全局变量域中声明指定
```
BlendState {
渲染状态属性 = 属性值;
}
```
## uniform 变量指定
直接声明成全局变量
```glsl
mediump vec4 u_color;
float material_AlphaCutoff;
mat4 renderer_ModelMat;
vec3 u_lightDir;
```
## varying 变量声明
通过定义顶点着色器出参[结构体](/docs/graphics/material/shaderLab/shader/#结构体函数)和片元着色器入参结构体指定
```glsl
struct v2f {
vec3 color;
};
v2f vert(a2v o) {
...
}
void frag(v2f i) {
...
}
```
## 顶点、片元着色器指定
通过`VertexShader`和`FragmentShader`指定显示指定着色器入口函数
```
VertexShader = vert;
FragmentShader = frag;
```
## 渲染队列设置
通过`RenderQueueType`指令指定,`RenderQueueType`等同与引擎 API。
```
RenderQueueType = Transparent;
```
<Callout type="info">
除了可以在 ShaderLab 中对渲染状态和渲染队列进行设置开发者同样可以通过材质的API进行设置
```ts
// 渲染队列设置
material.renderQueueType = RenderQueueType.Opaque;
// 渲染状态设置 const renderState = material.renderState.depthState; depthState.writeEnabled = false;
````
当 ShaderLab 中声明了渲染状态和渲染队列,材质相应的设置会被忽略。
</Callout>
## MRT(多目标渲染)
ShaderLab 同时兼容 GLSL 100 和 GLSL 300 语法,因此你可以使用两种语法进行 MRT 指定。
1. 通过 `gl_FragData[i]` 进行指定 MRT 指定
```glsl
void main(v2f input) {
gl_FragData[0] = vec4(1.,0.,0.,1.); // render target 0
gl_FragData[1] = vec4(1.,0.,0.,1.); // render target 1
}
```
2. 通过入口函数返回结构体指定
```glsl
struct mrt {
layout(location = 0) vec4 fragColor0; // render target 0
layout(location = 1) vec4 fragColor1; // render target 1
}
mrt main(v2f input) {
mrt output;
output.fragColor0 = vec4(1.,0.,0.,1.);
output.fragColor1 = vec4(1.,0.,0.,1.);
return output;
}
```
````

View File

@@ -0,0 +1,124 @@
---
title: 材质属性定义
---
开发者通过 `Editor` 模块定制绑定该 `Shader` 的材质属性,`Galacean` 编辑器可以自动将材质属性反射到材质资产的 Inspector 面板上。
<div style={{display: "flex"}}>
<Image src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*mwAxRr45kE8AAAAAAAAAAAAAeteEAQ/fmt.webp" width="435px" />
<Image src= "https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*7MrxTIG5fpkAAAAAAAAAAAAAeteEAQ/fmt.webp" width="200px" />
</div>
## Editor 模块语法骨架
```glsl showLineNumbers
Editor {
Properties {
material_BaseColor("Offset unit scale", Color) = (1,1,1,1);
...
Header("Emissive")
{
material_EmissiveColor("Emissive color", Color) = (1,1,1,1);
...
}
...
}
...
Macros {
[On] UV_OFFSET("UV Offset", Range(1,100)) = 10;
...
Header("") {
SOME_MACRO("label", Int) = 1;
}
}
...
// 指定 Shader 绑定的 UIScript 编辑器项目路径。
UIScript "./shaderScript.ts";
}
```
### 材质属性定义
```glsl
/**
* @language zh
* Comments description
*/
/**
* @language en
* 注释描述
*/
propertyName("Description", EditType) = [DefaultValue];
```
<Callout type="info">
1. 可以使用 `Header` 指令将有关联的属性组织起来Inspector 对应的面板中也会有相应的层级分类:
```
Header("Emissive") {
material_EmissiveColor("Emissive color", Color) = (1,1,1,1);
...
}
```
2. 通过注释标注 Inspector Hover 提示内容,支持使用 @language 指令进行多语言指定。
</Callout>
当前支持的 EditType 列表如下:
| EditType | Example |
| :-: | :-- |
| Bool | propertyName("Property Description", Boolean) = true; |
| Int | propertyName("Property Description", Int) = 1; <br/>propertyName("Property Description", Range(0,8)) = 1 <br/> propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1 // 枚举 |
| Float | propertyName("Property Description", FLoat) = 0.5; <br/>propertyName("Property Description", Range(0.0, 1.0)) = 0.5; <br/> propertyName("Property Description", Enum(Item1: 1.0, Item2: 2.0, Item3: 3.0)) = 1.0; // 枚举 |
| Texture2D | propertyName("Property Description", Texture2D); |
| TextureCube | propertyName("Property Description", TextureCube); |
| Color | propertyName("Property Description", Color) = (0.25, 0.5, 0.5, 1); |
| Vector2 | propertyName("Property Description", Vector2) = (0.25, 0.5); |
| Vector3 | propertyName("Property Description", Vector3) = (0.25, 0.5, 0.5); |
| Vector4 | propertyName("Property Description", Vector4) = (0.25, 0.5, 0.5, 1.0); |
#### 枚举类型
```glsl showLineNumbers
propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1;
```
<Callout type="warning">
当前只有 Int 和 Float 类型支持枚举且不支持类型混合比如下面的枚举混合了Float和Int将不会被正确解析 ```glsl
propertyName("Property Description", Enum("Item1":1, "Item2":2.0, "Item3": 3)) = 2.0; ```
</Callout>
### 材质宏属性定义
用于将 Shader 中用到的宏反射到 Inspector 中,从而在编辑器中对 Shader 依赖的宏进行灵活的调整。
```glsl showLineNumbers
// 开启/关闭
[On/Off]macroName("MacroLabel", EditType) = [DefaultValue];
```
通过`[On/Off]`指令指定宏的默认开关,当前编辑器支持的宏类型如下:
| Type | Example |
| :-----: | :-------------------------------------------------------------------------------------------------------- |
| 无值宏 | macroName("Macro Description"); |
| Bool | macroName("Macro Description", Boolean) = true; |
| Int | macroName("Macro Description", Int) = 1; <br/> macroName("Macro Description", Range(0,8)) = 1; |
| Float | macroName("Macro Description", FLoat) = 0.5; <br/> macroName("Macro Description", Range(0.0, 1.0)) = 0.5; |
| Color | macroName("Macro Description", Color) = (0.25, 0.5, 0.5, 1); |
| Vector2 | macroName("Macro Description", Vector2) = (0.25, 0.5); |
| Vector3 | macroName("Macro Description", Vector3) = (0.25, 0.5, 0.5); |
| Vector4 | macroName("Macro Description", Vector4) = (0.25, 0.5, 0.5, 1.0);

View File

@@ -0,0 +1,87 @@
---
title: 渲染状态设置
---
在 Galacean 引擎中,每个 Shader `Pass` 代表一次 GPU 绘制过程,因此我们可以对每个 `Pass`
的渲染状态进行单独设置。在 ShaderLab 中我们有2种设置方式。
### 1. 显示赋值
```glsl showLineNumbers {4, 10}
...
Pass "0" {
...
BlendState blendState {
Enabled = true;
ColorBlendOperation = BlendOperation.Add;
...
}
BlendState = blendState;
...
}
...
```
如全局变量[章节](./global)所示,通过在各个层级全局变量域中声明渲染状态变量,然后通过 `BlendState = blendState` 进行指定。
### 2. 全局变量域中声明指定
```glsl showLineNumbers {4}
...
Pass "0" {
...
BlendState {
Enabled = true;
ColorBlendOperation = BlendOperation.Add;
...
}
...
}
...
```
<Callout type="info">
1. `DepthState`、`StencilState` 和 `RasterState` 的设置同理。
2. 全局变量域也可以是 `SubShader` 或`Shader` 层级,其作用域跟其他全局变量遵循同一[规则](./global/#全局变量作用域)。即如果是在 `SubShader` 层级全局变量域中设置的渲染状态,会对该 `SubShader` 下所有 `Pass` 生效。
</Callout>
## 渲染状态属性设置
单个渲染状态属性设置同样也有2种方式。
```glsl showLineNumbers {7,9}
Shader "Demo" {
...
BlendState customBlendState
{
Enabled = true;
// 常量赋值方式
SourceColorBlendFactor = BlendFactor.SourceColor;
// 变量赋值方式
DestinationColorBlendFactor = material_DstBlend;
}
...
Pass "0" {
...
BlendState = customBlendState;
...
}
}
```
上述案例中对于 `BlendState` 属性赋值展示了 2 种方式: **常量赋值** 和 **变量赋值** 方式:
### 常量赋值
赋值语句右端为指定的对应引擎枚举变量譬如BlendFactor.SourceColor。
<Callout type="warning">
渲染队列的设置例外: `RenderQueueType = Transparent;`
</Callout>
### 变量赋值
赋值语句右端为任一变量名,变量具体值可由开发者通过脚本方式在运行时通过 `ShaderData.setInt("material_DstBlend", BlendFactor.SourceColor)` API 进行指定。

View File

@@ -0,0 +1,138 @@
---
title: UIScript 的使用和绑定
---
开发者使用编辑器对自定义材质属性进行调节的同时,还可以通过 `UIScript` 指令指定数据变更的回调行为。通过 UIScript 暴露的 hook 函数,开发者可以实现属性联动,从而减少声明属性的数量,达到简化 Inspector 面板的目的。
### 在 ShaderLab 中绑定 `UIScript`:
```glsl showLineNumbers
Editor {
...
UIScript "/path/to/script";
...
}
```
绑定的`UIScript`脚本路径支持相对路径和绝对路径,以下图项目根目录为例,绝对路径为
`/PBRScript1.ts`,相对路径为`./PBRScript1.ts`
<Image
src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*t4LFQ4KEL6kAAAAAAAAAAAAADteEAQ/fmt.webp"
width="70%"
/>
### UIScript 接口
编辑器通过内置 `ShaderUIScript` 类暴露相关 API`ShaderUIScript` 的类型定义已经内嵌至 Galacean Web 端脚本编辑器中,完整定义如下:
```ts
import { Color, Material, Texture, Vector2, Vector3, Vector4 } from "@galacean/engine";
type ShaderPropertyValue = number | Vector2 | Vector3 | Vector4 | Color | Texture;
type ShaderMacroValue = number | Vector2 | Vector3 | Vector4 | Color;
/**
* Script for extending `Shader` UI logic.
*/
export abstract class ShaderUIScript {
/** @internal */
_propertyCallBacks: Map<string, (material: Material, value: ShaderPropertyValue) => void> = new Map();
/** @internal */
_macroCallBacks: Map<string, (material: Material, defined: boolean, value: ShaderMacroValue) => void> = new Map();
/**
* The method is called when then shader is switched.
* @param material - The material which the shader is bound to
*/
onMaterialShaderSwitched(material: Material): void {}
/**
* Register property change callback.
* @parma propertyName - Property name
* @parma onChanged - Fired on property changed
*/
protected onPropertyChanged(
propertyName: string,
onChanged: (material: Material, value: ShaderPropertyValue) => void
): void {
this._propertyCallBacks.set(propertyName, onChanged);
}
/**
* Register macro change callback.
* @parma macroName - Macro name
* @parma onChanged - Fired on macro changed
*/
protected onMacroChanged(
macroName: string,
onChanged: (material: Material, defined: boolean, value: ShaderMacroValue) => void
): void {
this._macroCallBacks.set(macroName, onChanged);
}
}
```
### UIScript 编写
1. 在编辑器中创建 UIScript 脚本
<Image
src="https://mdn.alipayobjects.com/huamei_aftkdx/afts/img/A*Qh4UTZgaY7MAAAAAAAAAAAAADteEAQ/fmt.webp"
width="60%"
figcaption="创建UIScript"
/>
2. 通过继承 ShaderUIScript 类指定属性变更回调。
```ts
import { Material, RenderQueueType, Vector3, BlendFactor, RenderFace, CullMode, BlendMode } from "@galacean/engine";
export default class extends ShaderUIScript {
constructor() {
super();
......
// 在构造函数中注册监听属性回调
this.onPropertyChanged("material_NormalTexture", (material: Material, value) => {
const shaderData = material.shaderData;
if (value) {
shaderData.enableMacro("MATERIAL_HAS_NORMALTEXTURE");
} else {
shaderData.disableMacro("MATERIAL_HAS_NORMALTEXTURE");
}
})
......
}
// 指定 shader 绑定的回调
override onMaterialShaderSwitched(material: Material) {
const shaderData = material.shaderData;
shaderData.disableMacro("MATERIAL_OMIT_NORMAL");
shaderData.enableMacro("MATERIAL_NEED_WORLD_POS");
shaderData.enableMacro("MATERIAL_NEED_TILING_OFFSET");
// default value
const anisotropyInfo = shaderData.getVector3("material_AnisotropyInfo");
if (!anisotropyInfo) {
shaderData.setVector3("material_AnisotropyInfo", new Vector3(1, 0, 0));
} else {
shaderData.setFloat("anisotropy", anisotropyInfo.z);
const PI2 = Math.PI * 2;
const rotationRad = (Math.atan2(anisotropyInfo.y, anisotropyInfo.x) + PI2 ) % PI2;
shaderData.setFloat("anisotropyRotation", rotationRad * (180 / Math.PI))
}
}
}
```
<Callout info="warning">
注意,当前版本 ShaderLab 材质属性模块只是定义了绑定该 Shader 的材质在编辑器中的 Inspector
UI面板并不会替你在`ShaderPass`中声明对应的全局变量,如果`ShaderPass`代码中引用了该变量需在[全局变量](./shader/#全局变量)模块中明确声明补充。
</Callout>

View File

@@ -1,132 +0,0 @@
---
title: Shader
---
```glsl
Shader "ShaderName" {
...
// 全局变量区:变量声明,结构体声明,渲染状态声明,材质属性定义
...
SubShader "SubShaderName" {
...
}
...
}
```
ShaderLab 中的 `Shader` 是传统渲染管线中着色器程序和其他引擎渲染设置相关信息的集合封装,它允许在同一个 `Shader` 对象中定义多个着色器程序,并告诉 Galacean 在渲染过程中如何选择使用它们。`Shader` 对象具有嵌套的结构,包分别对应引擎封装的 [Shader](/apis/galacean/#Shader)[SubShader](/apis/galacean/#SubShader) 和 [ShaderPass](/apis/galacean/#ShaderPass) 对象。
## 材质属性定义
在 ShaderLab 中开发者可以通过 [`Editor`](./editor/) 指令为使用该 Shader 的自定义材质定制 Inspector 属性面板。
## 全局变量
可以在 ShaderLab 中声明 4 类全局变量:渲染状态(RenderState),结构体,函数,以及单变量。
### 渲染状态
包含混合状态(BlendState),深度状态(DepthState),模板状态(StencilState),光栅化状态(RasterState)
- BlendState
```glsl
BlendState {
Enabled[n]: bool;
ColorBlendOperation[n]: BlendOperation;
AlphaBlendOperation[n]: BlendOperation;
SourceColorBlendFactor[n]: BlendFactor;
SourceAlphaBlendFactor[n]: BlendFactor;
DestinationColorBlendFactor[n]: BlendFactor;
DestinationAlphaBlendFactor[n]: BlendFactor;
ColorWriteMask[n]: float // 0xffffffff
BlendColor: vec4;
AlphaToCoverage: bool;
}
```
[n] 可省略,在使用 MRT 的情况下, [n] 为指定某个 MRT 渲染状态,省略为设置所有 MRT 状态BlendOperation 和 BlendFactor 枚举等同引擎 API
- DepthState
```glsl
DepthState {
Enabled: bool;
WriteEnabled: bool;
CompareFunction: CompareFunction;
}
```
CompareFunction 枚举等同引擎 API
- StencilState
```glsl
StencilState {
Enabled: bool;
ReferenceValue: int;
Mask: float; // 0xffffffff
WriteMask: float; // 0xffffffff
CompareFunctionFront: CompareFunction;
CompareFunctionBack: CompareFunction;
PassOperationFront: StencilOperation;
PassOperationBack: StencilOperation;
FailOperationFront: StencilOperation;
FailOperationBack: StencilOperation;
ZFailOperationFront: StencilOperation;
ZFailOperationBack: StencilOperation;
}
```
CompareFunction 和 StencilOperation 举等同引擎 API
- RasterState
```glsl
RasterState {
CullMode: CullMode;
DepthBias: float;
SlopeScaledDepthBias: float;
}
```
CullMode 举等同引擎 API
在`ShaderLab`中设置`BlendState`示例:
```glsl
Shader "Demo" {
...
BlendState customBlendState
{
Enabled = true;
// 常量复制方式
SourceColorBlendFactor = BlendFactor.SourceColor;
// 变量赋值方式
DestinationColorBlendFactor = material_DstBlend;
}
...
Pass "0" {
...
BlendState = customBlendState;
...
}
}
```
上述案例中对于 BlendState 属性赋值展示了 2 种方式: *常量赋值*和*变量赋值*方式:
- 常量赋值指赋值语句右端为指定的对应引擎枚举变量譬如BlendFactor.SourceColor
- 变量赋值指赋值语句右端为任一变量名,变量具体值由用户通过脚本方式在运行时通过 ShaderData.setInt("material_DstBlend", BlendFactor.SourceColor) API 进行指定
### 结构体、函数
等同 glsl 中的语法
### 单变量
```glsl
[lowp/mediump/highp] variableType variableName;
```
与其他编程语言类似ShaderLab 中的全局变量也有作用域和同名覆盖原则。简单来说ShaderLab 中的全局变量的作用范围仅限于其声明的 SubShader 或 Pass 模块内部,而同名覆盖原则指的是如果存在与 Pass 内部同名的全局变量,则 Pass 内的全局变量会覆盖 SubShader 内的同名全局变量。

View File

@@ -1,35 +0,0 @@
---
title: SubShader
---
```glsl
SubShader "SubShaderName" {
...
// 全局变量区:变量声明,结构体声明,渲染状态声明
...
Tags {ReplaceTag = "opaque"}
UsePass "ShaderName/SubShaderName/PassName"
Pass "PassName" {
...
}
}
```
## Tags
ShaderLab 中可以通过 `Tags` 指令直接声明和指定,无须通过 `SubShader.setTag` api 方式手动指定。
## UsePass
如果一个 `SubShader` 包含多个 `Pass`,可以通过 `UsePass` 指令复用其他 `Pass` 对象,比如引擎内置的 PBR Pass: `UsePass "pbr/Default/Forward"`
| 内置 Shader | Pass 路径 |
| :-------------: | :-----------------------------: |
| PBR | pbr/Default/Forward |
| Unlit | unlit/Default/Forward |
| Skybox | skybox/Default/Forward |
| Particle-shader | particle-shader/Default/Forward |
| SpriteMask | SpriteMask/Default/Forward |
| Sprite | Sprite/Default/Forward |