调试
js
创建NodeBuilder:
NodeBuilder (NodeBuilder.js:90)
WGSLNodeBuilder (WGSLNodeBuilder.js:180)
createNodeBuilder (WebGPUBackend.js:1972)
`getForRender (Nodes.js:196)` // 创建和构建的分水岭
getNodeBuilderState (RenderObject.js:379)
getMonitor (RenderObject.js:390)
needsRefresh (Nodes.js:797)
_renderObjectDirect (Renderer.js:2977)
renderObject (Renderer.js:2925)
_renderObjects (Renderer.js:2833)
_renderScene (Renderer.js:1486)
render (Renderer.js:1197)
animate (webgpu_reflection_blurred2.html:301)
update (Animation.js:73)
requestAnimationFrame
update (Animation.js:65)
start (Animation.js:77)
(匿名) (Renderer.js:803)
await in (匿名)
init (Renderer.js:753)
setAnimationLoop (Renderer.js:1606)
init (webgpu_reflection_blurred2.html:256)
(匿名) (webgpu_reflection_blurred2.html:77)
构建NodeBuilder.build:
`https://cdn.jsdelivr.net/npm/three/src/nodes/core/NodeBuilder.js`
build() {
const { object, material, renderer } = this;
if ( material !== null ) {
nodeMaterial.build
} else {
this.addFlow( 'compute', object );
}
// setup() -> 阶段 1: 创建可能的 [新] 节点和/或返回一个输出引用节点
// analyze() -> 阶段 2: 分析节点以进行可能的优化和验证
// generate() -> 阶段 3: 生成着色器
for ( const buildStage of defaultBuildStages ) {
this.setBuildStage( buildStage );
if ( this.context.vertex && this.context.vertex.isNode ) {
this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
}
for ( const shaderStage of shaderStages ) {
this.setShaderStage( shaderStage );
const flowNodes = this.flowNodes[ shaderStage ];
for ( const node of flowNodes ) {
if ( buildStage === 'generate' ) {
this.flowNode( node );
} else {
node.build( this );
// 可能会执行(从下到上):
flowStagesNode
flowShaderNode (NodeBuilder.js:2215)
buildFunctionCode (WGSLNodeBuilder.js:1022)
buildFunctionNode (NodeBuilder.js:2167)
call (TSLCore.js:387)
setupOutput (TSLCore.js:450)
getOutputNode (TSLCore.js:461)
build (TSLCore.js:476)
build (VarNode.js:158)
build (Node.js:711)
build (TempNode.js:82)
build (VarNode.js:158)
build (Node.js:711)
build (TempNode.js:82)
build (Node.js:711)
build (TempNode.js:82)
build (VarNode.js:158)
build (StackNode.js:328)
}
}
}
}
this.setBuildStage( null );
this.setShaderStage( null );
// stage 4: build code for a specific output
this.buildCode();
_getWGSLFragmentCode (WGSLNodeBuilder.js:2037)
buildCode (WGSLNodeBuilder.js:1861)
`build (NodeBuilder.js:2794)`对应此处的 `this.buildCode();`
getForRender (Nodes.js:211)
this.buildUpdateNodes();
return this;
}
drawCircle
函数如何转换为最终的 GLSL 字符串
js
## 转换流程分析
### 1. Fn 函数包装
`drawCircle` 通过 `Fn` 函数创建,这会将 JavaScript 函数包装成 `ShaderNodeInternal` 对象 [1](#7-0) 。
### 2. 函数调用处理
当 `drawCircle` 被调用时,会创建 `ShaderCallNodeInternal` 实例,处理参数转换和节点调用 [1](#7-0) 。
### 3. 代码生成过程
在 `GLSLNodeBuilder.buildFunctionCode()` 中生成 GLSL 函数代码 [2](#7-1)
### 4. 节点转换细节
每个 TSL 表达式都会转换为对应的 GLSL 代码:
- `length(pos)` → `length(pos)` (数学函数直接映射) [3](#7-2)
- `dist1.assign()` → `dist1 = ...` (赋值操作)
- `color.rgb.mul()` → `color * ...` (向量乘法) [4](#7-3)
- `max(sub(0.8, abs(dist2)), 0.0)` → `max(0.8 - abs(dist2), 0.0)`
### 5. 最终集成到片段着色器
生成的函数会被添加到片段着色器的代码段中,通过 `_getGLSLFragmentCode()` 方法组装 [5](#7-4)
Node Material 和 TSL 有着密切的关系:Search | DeepWiki
核心关系
NodeMaterial 是基于 TSL 构建的材质系统。NodeMaterial 作为所有基于节点的材质的基类,广泛使用了 TSL (Three.js Shading Language) 提供的类型和功能。 1
技术实现
- 类型系统集成:NodeMaterial 直接导入并使用 TSL 的基础类型,如
float
、vec3
、vec4
、bool
等,这些是构建节点材质的基础构建块。 1 - 材质属性访问:MaterialNode 类负责创建 TSL 对象来访问材质属性,它使用 TSL 的类型系统来确保类型安全。 2
- 预定义 TSL 对象:MaterialNode 使用
nodeImmutable
函数创建了大量预定义的 TSL 对象,如materialColor
、materialOpacity
、materialNormal
等,这些对象可以直接在 NodeMaterial 中使用。 3
TSL 的作用
TSL 是一个完整的着色器语言抽象层,提供了数百个函数和类型,涵盖了从基础数学运算到复杂材质属性的所有功能。 4
使用模式
在 NodeMaterial 中,开发者可以:
- 使用 TSL 提供的材质访问器来获取当前材质的属性
- 通过节点组合来构建复杂的材质效果
- 利用 TSL 的类型安全特性避免着色器编译错误
TSL 提供了着色器编程的"语言"和"词汇",而 NodeMaterial 则是使用这套语言来构建材质的"框架"
- TSL的优势:
- TSL的节点本质上是函数,可以被使用、组合和链接以生成最终着色器。
- TSL自动处理适应不同API的适配,无论是WebGL的GLSL还是WebGPU的WGSL。
Three.js Shading Language (TSL) 笔记
1. 引言与目的
- Why TSL?
- TSL旨在简化着色器的创建过程,使开发者即使没有从头开始编写GLSL代码的经验,也能创建所需的图形效果。
- TSL的目标是创建一个易于使用的着色器创建环境,同时保持渲染器的通用性,并通过模块化和树摇算法优化性能。
2. 示例
- 细节贴图(Detail Map)
- 通过UV缩放和基础纹理的乘法,为游戏内物体表面增加微小细节,如裂缝或凸起。
3. TSL的新旧对比
- Old Method
- 使用
.onBeforeCompile()
方法手动修改GLSL着色器代码,随着代码修改的增加,复杂性迅速上升。
- 使用
- New Method
- TSL通过节点系统简化了代码编写,无需关心组件创建的顺序,节点系统会自动声明和包含一次。
4. 架构
- 构建过程
- 基于三个支柱:设置(setup)、分析(analyze)和生成(generate)。
- 节点可以有多个输入但总是单个输出,通过分析节点来生成代码片段。
5. 常量与显式转换
- 支持创建常量和显式类型转换,如
float()
,int()
,bool()
等。
6. 统一变量(Uniform)
- 用于更新颜色、光照或变换等变量值,无需重新创建着色器程序。
7. 操作符
- 提供了基本的数学操作符,如加(add)、减(sub)、乘(mul)、除(div)等。
8. 函数
- 支持使用传统的JS函数或
tslFn()
接口,后者提供了一个可控环境,允许使用堆栈进行条件赋值。
9. 条件语句
- TSL中的条件语句使用
If
,elseif
,else
构建,支持在tslFn()
中使用。
10. 三元运算符
- 与if-else不同,三元运算符返回一个值,并且可以在
tslFn()
外部使用。
11. 数学函数
- 提供了一系列数学函数,如
abs()
,acos()
,all()
,any()
等。
12. 方法链
- 方法链仅包括操作符、转换器、数学和一些核心函数。
13. 纹理(Texture)
- 提供了从纹理中检索像素值的函数,如
texture()
,cubeTexture()
等。
14. 属性(Attributes)
- 用于获取几何属性,如
uv()
,vertexColor()
,positionGeometry()
等。
15. 位置(Position)
- 描述了与位置相关的属性和变量,如
positionLocal
,positionWorld
等。
16. 法线(Normal)
- 描述了与法线相关的属性和变量,如
normalLocal
,normalView
等。
17. 切线与副法线(Tangent & Bitangent)
- 提供了切线和副法线的属性和变量,如
tangentLocal
,bitangentLocal
等。
18. 相机(Camera)
- 提供了相机相关的属性和变量,如
cameraPosition
,cameraViewMatrix
等。
19. 模型(Model)
- 描述了模型相关的属性和变量,如
modelPosition
,modelWorldMatrix
等。
20. 视口(Viewport)
- 提供了视口相关的属性和函数,如
viewportTopLeft
,viewportResolution
等。
21. 混合模式(Blend Mode)
- 提供了不同的混合模式函数,如
burn()
,dodge()
,overlay()
等。
22. 反射(Reflect)
- 提供了计算反射方向的函数,如
reflectView()
,reflectVector()
等。
23. UV工具(UV Utils)
- 提供了UV相关的工具函数,如
matcapUV()
,rotateUV()
等。
24. 插值(Interpolation)
- 提供了值的插值函数,如
remap()
,remapClamp()
等。
25. 随机数(Random)
- 提供了生成随机数的函数,如
hash()
,range()
等。
26. 振荡器(Oscillator)
- 提供了生成不同波形振荡的函数,如
oscSine()
,oscSquare()
等。
27. 打包(Packing)
- 提供了方向向量与颜色之间的转换函数,如
directionToColor()
,colorToDirection()
等。
28. 函数
- 描述了如何使用
.toVar()
和.varying()
函数来优化计算和创建变量。
29. 从GLSL属性过渡到TSL
- 提供了从GLSL到TSL的属性过渡指南,如
position
到positionGeometry
等。
30. 附加资源
- 提供了Three.js Shading Language的附加资源链接,如使用SketchUp模型等。