diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a528e6990..ed3efdf7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,6 +48,20 @@ jobs: git clone https://github.com/galacean/engine-lottie.git git clone https://github.com/galacean/engine-spine.git -b 4.2 + - name: Checkout editor repository + uses: actions/checkout@v4 + with: + repository: galacean/editor + submodules: true + path: editor + token: ${{ secrets.CLONE_EDITOR_TOKEN }} + + - name: Install and Build for editor + working-directory: ./editor + run: | + pnpm install --no-frozen-lockfile + pnpm build:decorators + - name: Install and Link Engine and Build for Toolkit working-directory: ./engine-toolkit run: | @@ -92,6 +106,8 @@ jobs: cp -r ${{ github.workspace }}/engine-lottie/dist ${{ github.workspace }}/temp/@galacean/engine-lottie mkdir -p ${{ github.workspace }}/temp/@galacean/engine-spine cp -r ${{ github.workspace }}/engine-spine/dist ${{ github.workspace }}/temp/@galacean/engine-spine + mkdir -p ${{ github.workspace }}/temp/@galacean/editor-decorators + cp -r ${{ github.workspace }}/editor/packages/decorators/dist ${{ github.workspace }}/temp/@galacean/editor-decorators find ${{ github.workspace }}/temp - name: Ensure Adapter Directory Exists @@ -122,7 +138,8 @@ jobs: "${{ github.workspace }}/temp/@galacean/engine-physics-physx/dist/browser.js", "${{ github.workspace }}/temp/@galacean/engine-toolkit/umd/browser.js", "${{ github.workspace }}/temp/@galacean/engine-lottie/dist/browser.js", - "${{ github.workspace }}/temp/@galacean/engine-spine/dist/browser.js" + "${{ github.workspace }}/temp/@galacean/engine-spine/dist/browser.js", + "${{ github.workspace }}/temp/@galacean/editor-decorators/dist/browser.js" ], "wasm": [ "${{ github.workspace }}/packages/physics-physx/libs/physx.release.wasm" diff --git a/.github/workflows/trigger-website-build.yml b/.github/workflows/trigger-website-build.yml new file mode 100644 index 000000000..5634cf644 --- /dev/null +++ b/.github/workflows/trigger-website-build.yml @@ -0,0 +1,22 @@ +name: Trigger Website Build + +on: + push: + paths: + - 'docs/**' + workflow_dispatch: + +jobs: + trigger-website: + runs-on: ubuntu-latest + + steps: + - name: Trigger website build + run: | + curl -L \ + -X POST \ + -H "Accept: application/vnd.github.everest-preview+json" \ + -H "Authorization: Bearer ${{ secrets.WEBSITE_TRIGGER_TOKEN }}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/galacean/website/dispatches \ + -d '{"event_type": "engine_update","client_payload": {"engine_commit": "${{ github.event.head_commit.message }}"}}' diff --git a/docs/en/graphics/postProcess/effects.md b/docs/en/graphics/postProcess/effects.md index ecd34cc58..c5156fa9a 100644 --- a/docs/en/graphics/postProcess/effects.md +++ b/docs/en/graphics/postProcess/effects.md @@ -5,6 +5,8 @@ title: Post Process Effects ## Bloom +[API](/apis/core/#BloomEffect) + - **Down Scale**: Controls the starting resolution that this effect begins processing, you can choose `Half`, `Quarter`. @@ -58,6 +60,8 @@ title: Post Process Effects ## Tonemapping +[API](/apis/core/#TonemappingEffect) + - **Mode**: Tone mapping algorithm. You can choose `Neutral` and `ACES`. `Netural` mode is particularly suitable for situations where only range remapping is required with minimal impact on hue and saturation; `ACES` mode uses the ACES reference color space of movies, which can produce film-like contrast effects, but the performance consumption is large. diff --git a/docs/en/graphics/postProcess/postProcess.mdx b/docs/en/graphics/postProcess/postProcess.mdx index a08a1adcc..a7a1fc76a 100644 --- a/docs/en/graphics/postProcess/postProcess.mdx +++ b/docs/en/graphics/postProcess/postProcess.mdx @@ -12,7 +12,7 @@ The post-processing system can "process" the results rendered by the camera. rightText="ON" /> -## Post-Processing Configuration +## Post-Processing Component There are two modes for post-processing: @@ -88,6 +88,40 @@ In addition to the camera preview area, the viewport can also display post-proce + +### 4. Pro code +```ts +// Get post-processing component +const postProcessComponent = entity.getComponent(PostProcess); + +// Set properties for component +postProcessComponent.isGlobal = false; + +// Get post-processing effect +const bloomEffect = postProcessComponent.getEffect(BloomEffect); + +// You can also add post-processing effects manually +const bloomEffect = postProcessComponent.addEffect(BloomEffect); + +// Setting properties of post-processing effects +bloomEffect.intensity.value = 1; +bloomEffect.threshold.value = 0.5; +``` + + For specific post-processing effects configuration, please refer to the [Post-Processing Effects + List](/en/docs/graphics/postProcess/effects) + + + +## Post-processing mask +As mentioned above, the post-processing component has a property called [layer](/apis/core/#PostProcess-layer). When there are multiple post-processing components in the scene, you can use it with the camera's [post-processing mask](/apis/core/#Camera-postProcessMask) to determine the effective post-processing component; +This method is only used to determine the source of data blend. If you only want certain entities to have post-processing, you can use the multi-scene solution and add the Entity that does not need post-processing to another Scene. + + + + + + ## Best Practices - Regarding the `HDR` switch in the camera, if the majority of pixels in the scene do not exceed 1 (e.g., no HDR textures used), avoid enabling HDR. When enabled, the engine first renders to a `R11G11B10_UFloat` format RenderTarget before rendering to the screen, incurring performance overhead. diff --git a/docs/zh/graphics/_meta.json b/docs/zh/graphics/_meta.json index 477f07b48..60c7812f5 100644 --- a/docs/zh/graphics/_meta.json +++ b/docs/zh/graphics/_meta.json @@ -20,9 +20,6 @@ "material": { "title": "材质" }, - "shader": { - "title": "着色器" - }, "texture": { "title": "纹理" }, diff --git a/docs/zh/graphics/material/_meta.json b/docs/zh/graphics/material/_meta.json index f0bf6cedb..cd6effacf 100644 --- a/docs/zh/graphics/material/_meta.json +++ b/docs/zh/graphics/material/_meta.json @@ -1,6 +1,7 @@ { - "material": { "title": "材质总览" }, - "composition": { "title": "材质组成" }, - "editor": { "title": "编辑器使用" }, - "script": { "title": "脚本使用" } + "overview": { "title": "材质概述" }, + "examples": { "title": "指南和示例" }, + "builtinShaders": { "title": "内置着色器" }, + "shaderLab": { "title": "ShaderLab" }, + "variables": "内置变量" } diff --git a/docs/zh/graphics/shader/builtins/_meta.json b/docs/zh/graphics/material/builtinShaders/_meta.json similarity index 75% rename from docs/zh/graphics/shader/builtins/_meta.json rename to docs/zh/graphics/material/builtinShaders/_meta.json index 6c2013fab..195d72430 100644 --- a/docs/zh/graphics/shader/builtins/_meta.json +++ b/docs/zh/graphics/material/builtinShaders/_meta.json @@ -1,7 +1,6 @@ { - "intro": {"title": "介绍"}, "pbr": "PBR", "unlit": "Unlit", "blinnPhong": "Blinn Phong", "digitalHuman": "数字人" -} \ No newline at end of file +} diff --git a/docs/zh/graphics/shader/builtins/blinnPhong.mdx b/docs/zh/graphics/material/builtinShaders/blinnPhong.md similarity index 88% rename from docs/zh/graphics/shader/builtins/blinnPhong.mdx rename to docs/zh/graphics/material/builtinShaders/blinnPhong.md index c17257011..2d49b1d82 100644 --- a/docs/zh/graphics/shader/builtins/blinnPhong.mdx +++ b/docs/zh/graphics/material/builtinShaders/blinnPhong.md @@ -4,11 +4,11 @@ title: Blinn Phong [BlinnPhongMaterial](/apis/core/#BlinnPhongMaterial) 材质是经典的材质之一,虽然不是基于物理渲染,但是其高效的渲染算法和基本齐全的光学部分,流传至今仍可以适用很多的场景。 - + ## 编辑器使用 -blinn +blinn ## 参数介绍 @@ -25,4 +25,4 @@ title: Blinn Phong | [shininess](/apis/core/#BlinnPhongMaterial-shininess) | 镜面反射系数。值越大镜面反射效果越聚拢。 | | [tilingOffset](/apis/core/#BlinnPhongMaterial-tilingOffset) | 纹理坐标的缩放与偏移。是一个 Vector4 数据,分别控制纹理坐标在 uv 方向上的缩放和偏移,参考 [案例](/embed/tiling-offset) | -如果需要通过脚本使用材质,可以前往[材质的使用教程](/docs/graphics/material/script)。 +如果需要通过脚本使用材质,可以前往[材质的使用教程](/docs/graphics/material/examples/create/#3-脚本方式)。 diff --git a/docs/zh/graphics/shader/builtins/digitalHuman/eye.mdx b/docs/zh/graphics/material/builtinShaders/digitalHuman/eye.mdx similarity index 100% rename from docs/zh/graphics/shader/builtins/digitalHuman/eye.mdx rename to docs/zh/graphics/material/builtinShaders/digitalHuman/eye.mdx diff --git a/docs/zh/graphics/shader/builtins/digitalHuman/hair.mdx b/docs/zh/graphics/material/builtinShaders/digitalHuman/hair.mdx similarity index 100% rename from docs/zh/graphics/shader/builtins/digitalHuman/hair.mdx rename to docs/zh/graphics/material/builtinShaders/digitalHuman/hair.mdx diff --git a/docs/zh/graphics/shader/builtins/digitalHuman/skin.mdx b/docs/zh/graphics/material/builtinShaders/digitalHuman/skin.mdx similarity index 100% rename from docs/zh/graphics/shader/builtins/digitalHuman/skin.mdx rename to docs/zh/graphics/material/builtinShaders/digitalHuman/skin.mdx diff --git a/docs/zh/graphics/shader/builtins/pbr.mdx b/docs/zh/graphics/material/builtinShaders/pbr.mdx similarity index 100% rename from docs/zh/graphics/shader/builtins/pbr.mdx rename to docs/zh/graphics/material/builtinShaders/pbr.mdx diff --git a/docs/zh/graphics/shader/builtins/unlit.mdx b/docs/zh/graphics/material/builtinShaders/unlit.md similarity index 62% rename from docs/zh/graphics/shader/builtins/unlit.mdx rename to docs/zh/graphics/material/builtinShaders/unlit.md index 720457ce8..456b1282f 100644 --- a/docs/zh/graphics/shader/builtins/unlit.mdx +++ b/docs/zh/graphics/material/builtinShaders/unlit.md @@ -4,11 +4,11 @@ title: Unlit 在一些简单的场景中,可能不希望计算光照,引擎提供了 [UnlitMaterial](/apis/core/#UnlitMaterial),使用了最精简的 shader 代码,只需要提供颜色或者纹理即可渲染。Unlit 材质适用于烘焙好的模型渲染,它只需要设置一张基本纹理或者颜色,即可展现离线渲染得到的高质量渲染结果,但是缺点是无法实时展现光影交互,因为 Unlit 由纹理决定渲染,不受任何光照影响。 - + ## 编辑器使用 -unlit +unlit ## 参数介绍 @@ -24,7 +24,7 @@ title: Unlit 如[烘焙教程](/docs/art/bake-blender)介绍,如果我们已经制作完了烘焙贴图,希望有一种**便捷材质**,颜色只由烘焙纹理影响,不用添加灯光,不用调试法线,也不用调试金属粗糙度等高阶属性,那么你可以试试 Galacean 的 [UnlitMaterial](/apis/core/#UnlitMaterial), glTF 有专门的[KHR_materials_unlit ](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit)插件,Galacean 会解析插件,生成 Unlit 材质。 -image.png +image.png 测试模型:[TREX.zip](https://www.yuque.com/attachments/yuque/0/2021/zip/381718/1623651429048-7f6a3610-d5cb-4a73-97f5-0d37d0c63b2c.zip?_lake_card=%7B%22src%22%3A%22https%3A%2F%2Fwww.yuque.com%2Fattachments%2Fyuque%2F0%2F2021%2Fzip%2F381718%2F1623651429048-7f6a3610-d5cb-4a73-97f5-0d37d0c63b2c.zip%22%2C%22name%22%3A%22TREX.zip%22%2C%22size%22%3A499161%2C%22type%22%3A%22application%2Fx-zip-compressed%22%2C%22ext%22%3A%22zip%22%2C%22status%22%3A%22done%22%2C%22taskId%22%3A%22u458bcbec-d647-4328-8036-3d5eb12860f%22%2C%22taskType%22%3A%22upload%22%2C%22id%22%3A%22ua8a5baad%22%2C%22card%22%3A%22file%22%7D) @@ -32,34 +32,34 @@ title: Unlit 1. 导入模型 - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/e5dbfb61-5c0c-4ca5-8c7f-bde353d4c211/1623651809057-138f49cf-6fe7-4f54-8161-c7e157ec85fd-20210614150752343.png) 2. 修改 Shader 默认的 shader 类型为 BSDF ,我们需要将材质属性栏 surface 里面的 shader 类型修改为 **Background**。 - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/abf1e279-1f78-4d21-8c1f-d58d7f74992c/1623652169374-7f39e5f0-6639-4795-8565-b8f0b09420ed-20210614150804567.png) - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/c8c51e5f-c7c6-44a3-87e2-dc649e13fddb/1623652230768-69cd6f7e-175d-4f9f-9042-b3629d422b8e.png) 3. 添加烘焙纹理 添加烘焙好的纹理,将 Color 和 Shader 连接在一起 - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/50c69e7b-c099-4a2d-b546-8a55ff4f9309/1623652264008-7ae4c13c-6430-44b0-995e-2c23c9f117a7-20210614150846797.png) - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/6ed13e19-a9e5-4454-a0d5-ad27b3cabe14/1623652368637-6dda44be-4cde-4f65-a72f-d39b5d3f60ce.png) - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/e9a99c9c-f661-4666-86bc-d8e91030c0f7/1623652380351-501dd929-7f96-4578-b49a-11724a0782a7.png) 4. 导出 glTF 若预览正常,导出 glTF 。 - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/4b6b5f8f-ebd2-46af-85c7-9a26b5f66a2e/1623652403568-450291a8-1a0b-4cf4-8e71-c183a05632b0-20210614150902221.png) - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/1fe38185-399e-4f56-bff4-c39ba4ae3a2a/1623652462007-85b065a3-69fa-4d80-9dfd-834ef66da12a.png) 将刚才导出来的 glTF 文件拖入编辑器或者 [glTF 预览器](https://galacean.antgroup.com/engine/gltf-viewer),若材质类型为 **UnlitMaterial**,说明已经导出了 glTF 的 [KHR_materials_unlit](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit) 插件,且 Galacean 已经解析成 Unlit 材质。 - +![image.png](https://gw.alipayobjects.com/zos/OasisHub/fbb6ba43-f7d7-4757-a1d3-590083d30573/1623652636074-d8bb8437-f885-43fd-8957-8e14ae9fd8c0-20210614150914493.png) diff --git a/docs/zh/graphics/material/composition.mdx b/docs/zh/graphics/material/composition.mdx deleted file mode 100644 index 6a53bf3d9..000000000 --- a/docs/zh/graphics/material/composition.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: 材质组成 ---- - -Galacean 材质包含 **[着色器(shader)](/docs/graphics/shader/intro/)、渲染状态(renderStates)、着色器数据(shaderData)**。着色器可以编写顶点、片元代码来决定渲染管线输出到屏幕上像素的颜色;渲染状态可以对渲染管线的上下文做一些额外配置;着色器数据封装了 CPU 传到 GPU 的一些数据集,比如颜色、矩阵、纹理等。 - -## 渲染状态 - -Galacean 将对渲染管线的配置封装在了 [RenderState 对象](/apis/core/#RenderState) 中,可以分别对[混合状态(BlendState)](/apis/core/#RenderState-BlendState)、[深度状态(DepthState)](/apis/core/#RenderState-DepthState)、[模版状态(StencilState)](/apis/core/#RenderState-StencilState)、[光栅状态(RasterState)](/apis/core/#RenderState-RasterState)进行配置。我们拿一个透明物体的标准渲染流程来举例,我们希望开启混合模式并设置混合因子,并且因为透明物体是叠加渲染的,所以我们还要关闭深度写入; - -```typescript -const renderState = material.renderState; - -// 1. 设置颜色混合因子。 -const blendState = renderState.blendState; -const target = blendState.targetBlendState; - -// src 混合因子为(As,As,As,As) -target.sourceColorBlendFactor = target.sourceAlphaBlendFactor = BlendFactor.SourceAlpha; -// dst 混合因子为(1 - As,1 - As,1 - As,1 - As)。 -target.destinationColorBlendFactor = target.destinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; -// 操作方式为 src + dst */ -target.colorBlendOperation = target.alphaBlendOperation = BlendOperation.Add; - -// 2. 开启颜色混合 -target.enabled = true; - -// 3. 关闭深度写入。 -const depthState = renderState.depthState; -depthState.writeEnabled = false; - -// 4. 设置透明渲染队列 -renderState.renderQueueType = RenderQueueType.Transparent; -``` - -> 有关渲染状态的更多选项可以分别查看相应的[API 文档](/apis/core/#RenderState)。 - -其中渲染队列可以决定这个材质在当前场景中的渲染顺序,引擎底层会对不同范围的渲染队列进行一些特殊处理,如 [RenderQueueType.Transparent](/apis/core/#RenderQueueType-transparent) 会从远到近进行渲染, [RenderQueueType.Opaque](/apis/core/#RenderQueueType-Opaque) 则会从近到远进行渲染。 - -```typescript -material.renderQueueType = RenderQueueType.Opaque; -``` - -针对相同的渲染队列,我们还可以设置 [Renderer](/apis/core/#Renderer) 的 `priority` 属性来强制决定渲染顺序,默认为 0,数字越大越后面渲染,如: - -```typescript -renderer.priority = -1; // 优先渲染 -``` diff --git a/docs/zh/graphics/material/editor.mdx b/docs/zh/graphics/material/editor.mdx deleted file mode 100644 index 9d65d398f..000000000 --- a/docs/zh/graphics/material/editor.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: 编辑器使用 ---- - -## 编辑器使用 - -### 1. 手动创建材质 - -image-20240206163405147 - -### 2. 导入模型 - -参考[模型的导入和使用](/docs/graphics/model/use/)教程,我们可以先将模型导入到编辑器,一般情况下,模型已经自动绑定好材质,用户可以不用做任何操作;如果想要修改材质,我们需要点击 `duplicate & remap` 按钮来生成一份该材质的副本,然后再编辑该材质副本。 - -remap - -切换着色器时不会重置着色器数据,比如基础颜色为红色,那么即使切换着色器,基础颜色仍为红色。 - -image-20231009112713870 - -### 3. 调整材质 - -具体操作详见[着色器教程](/docs/graphics/shader/intro/)。 diff --git a/docs/zh/graphics/material/examples/_meta.json b/docs/zh/graphics/material/examples/_meta.json new file mode 100644 index 000000000..08c8a278f --- /dev/null +++ b/docs/zh/graphics/material/examples/_meta.json @@ -0,0 +1,6 @@ +{ + "create": { "title": "创建材质" }, + "builtin": { "title": "使用内置着色器" }, + "custom": { "title": "自定义着色器" }, + "shaderData": { "title": "使用着色器数据" } +} diff --git a/docs/zh/graphics/material/examples/builtin.mdx b/docs/zh/graphics/material/examples/builtin.mdx new file mode 100644 index 000000000..8bcbb17a0 --- /dev/null +++ b/docs/zh/graphics/material/examples/builtin.mdx @@ -0,0 +1,30 @@ +--- +title: 使用内置着色器 +--- + +目前 Galacean 引擎内置了许多常用的 Shader,诸如 + +| 类型 | 描述 | +| :-- | :-- | +| [Unlit](/docs/graphics/material/builtinShaders/unlit) | Unlit Shader 适用于烘焙好的模型渲染,她只需要设置一张基本纹理或者颜色,即可展现离线渲染得到的高质量渲染结果,但是缺点是无法实时展现光影交互,因为 Unlit 由纹理决定渲染,不受任何光照影响,可参考 [烘焙教程](/docs/art/bake-blender) 和 [导出 Unlit 教程](/docs/graphics/shader/builtins/unlit) | +| [Blinn Phong](/docs/graphics/material/builtinShaders/blinnPhong/) | Blinn Phong Shader 适用于那些对真实感没有那么高要求的场景,虽然没有遵循物理,但是其高效的渲染算法和基本齐全的光学部分,可以适用很多的场景。 | +| [PBR](/docs/graphics/material/builtinShaders/pbr) | PBR Shader 适合需要真实感渲染的应用场景,因为 PBR 是基于物理的渲染,遵循能量守恒,开发者通过调整金属度、粗糙度、灯光等参数,能够保证渲染效果都是物理正确的。 | +| [头发](/docs/graphics/material/builtinShaders/digitalHuman/hair) | 头发着色基于着色模型 kajiya-Kay ,近似的模拟了发丝表面双层各向异性高光,在头发渲染中俗称“天使环”。 | +| [皮肤](/docs/graphics/material/builtinShaders/digitalHuman/skin) | 皮肤采用 Spherical Gaussial 模型,可以灵活的自定义不同的 Diffusion Profile (扩散剖面),模拟人类皮肤或者普通的次表面散射效果。 | +| [眼睛](/docs/graphics/material/builtinShaders/digitalHuman/eye) | 眼睛着色器为眼球模型提供真实的渲染,使你的创作呈现逼真的艺术效果。 | + +可以在编辑器中直接调试内置着色器的对应属性观察实时渲染效果变化。 + + + +内置 Shader 基本上都有以下通用参数: + +| 参数 | 应用 | +| :-- | :-- | +| [isTransparent](/apis/core/#BaseMaterial-isTransparent) | 是否透明。可以设置材质是否透明。如果设置为透明,可以通过 [BlendMode](/apis/core/#BaseMaterial-blendMode) 来设置颜色混合模式。 | +| [alphaCutoff](/apis/core/#BaseMaterial-alphaCutoff) | 透明度裁剪值。可以设置裁剪值,在着色器中,透明度小于此数值的片元将会被裁减,参考 [案例](/embed/blend-mode) | +| [renderFace](/apis/core/#BaseMaterial-renderFace) | 渲染面。可以决定渲染正面、背面、双面。 | +| [blendMode](/apis/core/#BaseMaterial-blendMode) | 颜色混合模式。当设置材质为透明后,可以设置此枚举来决定颜色混合模式,参考 [案例](/embed/blend-mode) | +| [tilingOffset](/apis/core/#BaseMaterial-tilingOffset) | 纹理坐标的缩放与偏移。是一个 Vector4 数据,分别控制纹理坐标在 uv 方向上的缩放和偏移,参考 [案例](/embed/tiling-offset) | + +每个 Shader 有不同的渲染效果和应用场景,更多详情参考 [内置着色器教程](/docs/graphics/material/builtinShaders/pbr/)。 diff --git a/docs/zh/graphics/material/examples/create.mdx b/docs/zh/graphics/material/examples/create.mdx new file mode 100644 index 000000000..f72a25dcd --- /dev/null +++ b/docs/zh/graphics/material/examples/create.mdx @@ -0,0 +1,48 @@ +--- +title: 创建材质 +--- + +创建材质一般有以下 3 种方式: + +## 1. 手动创建 + + + +## 2. 模型中的材质 + +参考[模型的导入和使用](/docs/graphics/model/use/)教程,我们可以先将模型导入到编辑器,一般情况下,模型已经自动绑定好材质,用户可以不用做任何操作; + +### 2.1. Remap 材质 + +如果想要修改模型材质,我们可以点击 `duplicate and remap` 按钮来生成一份该材质的副本,注意 Remap 会修改模型预设,如下图所有模型实例都会受到影响: + + + +### 2.2. 增量修改 + +如果只想修改某个模型的材质,可以使用增量修改的功能: + + + +## 3. 脚本方式 + +我们还可以通过脚本来创建/修改材质,我们将脚本挂载到立方体实体上,实现替换材质的一个 Demo: + +```ts showLineNumbers +export default class extends Script { + onStart() { + // 获取所有 renderer + const renderers = []; + this.entity.getComponentsIncludeChildren(MeshRenderer, renderers); + const renderer = renderers[0]; + + // 直接修改材质 + const material = renderer.getMaterial(); + material.baseColor.set(1, 0, 0, 1); + + // 或者替换材质 + const pbrMaterial = new PBRMaterial(engine); + const material = renderer.setMaterial(material); + } +} +``` diff --git a/docs/zh/graphics/shader/shaderAPI.mdx b/docs/zh/graphics/material/examples/custom.mdx similarity index 93% rename from docs/zh/graphics/shader/shaderAPI.mdx rename to docs/zh/graphics/material/examples/custom.mdx index 02b354ff1..b46c98028 100644 --- a/docs/zh/graphics/shader/shaderAPI.mdx +++ b/docs/zh/graphics/material/examples/custom.mdx @@ -1,13 +1,8 @@ --- -title: Shader API【实验】 +title: 自定义着色器 --- - - 目前这个版本还处于实验性质,仅可在`编辑器`中使用。如果您想在 `Pro Code` 中使用,需要引入 - `@galacean/engine-shader-shaderlab` 包。请注意,下个版本的 API 可能会发生变更,届时我们会及时通知您。 - - -类似于 Typescript 中的函数、类、属性, Shader 代码也有一套自己的 API。本文可以帮助你如何基于这些 API 和 `ShaderLab` 语法,编写自己的 Shader。 +类似于 Typescript 中的函数、类、属性, Shader 代码也有一套自己的 API 和配套的 [UIScript](/docs/graphics/material/shaderLab/script)。本文可以帮助你如何基于这些 API 和 [ShaderLab](/docs/graphics/material/shaderLab/overview) ,自定义自己的 Shader。 ## 快速上手 @@ -18,6 +13,13 @@ title: Shader API【实验】 style={{ zoom: "50%" }} /> +引擎会自动帮我们创建好 Shader 文件和 [UIScript](/docs/graphics/material/shaderLab/script) 文件 + + + 默认的 Unlit 模板已经内置了蒙皮计算和 Shadow Pass,可以看到骨骼动画和阴影都能正常渲染: @@ -160,7 +162,7 @@ vec4 fog(vec4 color, vec3 positionVS); ### Transform -提供了模型空间、视图空间、世界空间、相机坐标等[系统变量](/docs/graphics/shader/custom/#属性): +提供了模型空间、视图空间、世界空间、相机坐标等[系统变量](/docs/graphics/material/variables/): ```glsl mat4 renderer_LocalMat; @@ -243,9 +245,7 @@ void calculateBlendShape(Attributes attributes, inout vec4 position, inout vec3 ## PBR API -除了通用 API,PBR 也封装了一些列如 BRDF 光照模型的 API,这是 `ForwardPassPBR` 的一些核心链路,用户拓展别的材质,比如 SSS,薄膜干涉等时候,有时候也需要这些功能,可以尝试 `#include` 复用这些 API。 - - +除了通用 API,PBR 也封装了一些列如 `BRDF` 光照模型的 API,用户拓展别的材质时可以尝试 `#include` 复用这些 API。 ### AttributesPBR diff --git a/docs/zh/graphics/material/examples/shaderData.mdx b/docs/zh/graphics/material/examples/shaderData.mdx new file mode 100644 index 000000000..6e5f4fe8d --- /dev/null +++ b/docs/zh/graphics/material/examples/shaderData.mdx @@ -0,0 +1,104 @@ +--- +title: 使用着色器数据 +--- + +跟别的编程语言一样,我们在写着色器过程中也会用到很多变量,首先引擎有很多 [内置变量](/docs/graphics/material/variables),这部分的变量我们是不需要手动设置的,可以直接在 shader 中使用。接下来我们看看如何设置和使用自定义数据。 + +### 设置数据 + +我们推荐使用 [ShaderLab](/docs/graphics/material/shaderLab/overview) 来声明和使用着色器数据: + +```glsl showLineNumbers {3,8,17,21} +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("Common") { + SOME_MACRO("label", Int) = 1; + } + } + + ... + + // 指定 Shader 绑定的 UIScript 编辑器项目路径。 + UIScript "./shaderScript.ts"; +} +``` + + + 参考 [ShaderLab 属性定义 ](/docs/graphics/material/shaderLab/property) 使用更多类型的数据。 + + +当然,我们也可以通过代码设置着色器数据: + +```ts showLineNumbers +// shaderData 可以分别保存在 scene 、camera 、renderer、 material 中。 +const shaderData = material.shaderData; + +// 上传不同类型的着色器数据 +shaderData.setFloat("u_float", 1.5); +shaderData.setInt("u_int", 1); +shaderData.setInt("u_bool", 1); +shaderData.setVector2("u_vec2", new Vector2(1, 1)); +shaderData.setVector3("u_vec3", new Vector3(1, 1, 1)); +shaderData.setVector4("u_vec4", new Vector4(1, 1, 1, 1)); +shaderData.setMatrix("u_matrix", new Matrix()); +shaderData.setIntArray("u_intArray", new Int32Array(10)); +shaderData.setFloatArray("u_floatArray", new Float32Array(10)); +shaderData.setTexture("u_sampler2D", texture2D); +shaderData.setTexture("u_samplerCube", textureCube); +shaderData.setTextureArray("u_samplerArray", [texture2D, textureCube]); + +// 开启宏开关 +shaderData.enableMacro("DISCARD"); +// 关闭宏开关 +shaderData.disableMacro("DISCARD"); + +// 开启变量宏 +shaderData.enableMacro("LIGHT_COUNT", "3"); + +// 切换变量宏。这里底层会自动 disable 上一个宏,即 “LIGHT_COUNT 3” +shaderData.enableMacro("LIGHT_COUNT", "2"); + +// 关闭变量宏 +shaderData.disableMacro("LIGHT_COUNT"); +``` + +### 获取数据 + +当你设置完数据后,你已经可以在 Shader 中直接使用,如下: + +```glsl showLineNumbers filename="test.glsl" +float test = u_float; +int test = u_int; +bool test = u_bool; +vec2 test = u_vec2; +mat4 test = u_matrix; +sampler2D test = u_sampler2D; +... +``` + +你也可以通过代码获取这个变量的值: + +```ts showLineNumbers filename="test.ts" +const test = shaderData.getFloat("u_float"); +const test = shaderData.getInt("u_int"); +const test = shaderData.getVector2("u_vec2"); +const test = shaderData.getMatrix("u_matrix"); +const test = shaderData.getTexture("u_sampler2D"); +``` diff --git a/docs/zh/graphics/material/material.mdx b/docs/zh/graphics/material/material.mdx deleted file mode 100644 index bf7bde7bb..000000000 --- a/docs/zh/graphics/material/material.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: 材质总览 ---- - -材质是指用于描述物体外观和表面特性的属性集合。材质决定了模型在渲染过程中如何与光线交互,从而影响其视觉呈现效果。 - -image-20240206153815596 - -本节包含以下相关信息: - -- [材质组成](/docs/graphics/material/composition/) -- [编辑器使用](/docs/graphics/material/editor/) -- [脚本使用](/docs/graphics/material/script/) diff --git a/docs/zh/graphics/material/overview.mdx b/docs/zh/graphics/material/overview.mdx new file mode 100644 index 000000000..4aa677554 --- /dev/null +++ b/docs/zh/graphics/material/overview.mdx @@ -0,0 +1,52 @@ +--- +title: 材质概述 +--- + + + +材质包含 **着色器(shader)、着色器数据(shaderData)、渲染状态(renderStates)**。 + +## 着色器 + +着色器(Shader)是一段运行在GPU中的程序,通常由顶点着色器和片元着色器组成,我们可以借助 [ShaderLab 语法](/docs/graphics/material/shaderLab/overview/) 很方便地书写这部分代码。 + +## 着色器数据 + +跟别的编程语言一样,我们在写着色器过程中也会用到很多变量,我们可以参考 [内置变量文档](/docs/graphics/material/variables/) 查找想要的内置变量;也可以通过 [使用着色器数据教程](/docs/graphics/material/examples/shaderData) 手动上传着色器数据和宏开关: + +## 渲染状态 + +Galacean 支持对[混合状态(BlendState)](/apis/core/#RenderState-BlendState)、[深度状态(DepthState)](/apis/core/#RenderState-DepthState)、[模版状态(StencilState)](/apis/core/#RenderState-StencilState)、[光栅状态(RasterState)](/apis/core/#RenderState-RasterState)进行配置。 + +我们拿一个透明物体的标准渲染流程来举例,我们希望开启透明混合,并且因为透明物体是叠加渲染的,所以我们还要关闭深度写入,并设置为透明队列; + +```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 { + WriteEnabled = false; + } + + BlendState { + Enabled = true; + SourceColorBlendFactor = BlendFactor.SourceAlpha; + DestinationColorBlendFactor = BlendFactor.OneMinusSourceAlpha; + SourceAlphaBlendFactor = BlendFactor.One; + DestinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; + } + + RenderQueueType = Transparent; +} +``` + + + 渲染状态还可以设置为变量,然后通过着色器数据来控制,详情参考 [ShaderLab + 渲染状态的设置](/docs/graphics/material/shaderLab/renderState)。 + diff --git a/docs/zh/graphics/material/script.mdx b/docs/zh/graphics/material/script.mdx deleted file mode 100644 index 64f5a757c..000000000 --- a/docs/zh/graphics/material/script.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: 脚本使用 ---- - -编辑器导出的材质只有 [Material](/apis/core/#Material) 基础类,而通过代码可以创建引擎已经封装好的 [PBRMaterial](/apis/core/#PBRMaterial),[UnlitMaterial](/apis/core/#UnlitMaterial),[BlinnPhongMaterial](/apis/core/#BlinnPhongMaterial)。 - -## 获取材质 - -### 1. 从已有 renderer 中获取 - -```typescript -// 获取想要修改的 renderer -const renderer = entity.getComponent(MeshRenderer); - -// 或者获取所有 renderer -const renderers = []; -entity.getComponentsIncludeChildren(MeshRenderer, renderers); - -// 通过 `getMaterial` 获取当前 renderer 的第 i 个材质, 默认第 0 个。 -const material = renderer.getMaterial(); -``` - -### 2. 替换 renderer 中的材质 - -我们也可以直接替换材质类型,比如将模型重新赋予一个 PBR 材质: - -```typescript -// 获取想要修改的 renderer -const renderer = entity.getComponent(MeshRenderer); - -// 创建材质 -const material = new PBRMaterial(engine); - -// 通过 `setMaterial` 设置当前 renderer 的第 i 个材质, 默认第 0 个。 -const material = renderer.setMaterial(material); -``` - -### 3. 创建内置材质 - -```typescript -const pbrMaterial = new PBRMaterial(engine); -const bpMaterial = new BlinnPhongMaterial(engine); -const unlitMaterial = new UnlitMaterial(engine); -``` - -### 4. 创建自定义材质 - -```typescript -// Shader.create 的具体步骤参考着色器教程 -const customMaterial = new Material(engine, Shader.find("***")); -``` - -## 修改材质 - -### 1. 修改内置材质 - -```typescript -// 设置透明,引擎已经封装好对应渲染状态的设置 -pbrMaterial.isTransparent = true; -// 设置透明度 -pbrMaterial.baseColor.a = 0.5; -// 金属、粗糙度等其他配置 -pbrMaterial.metallic = 1; -pbrMaterial.baseTexture = **; -``` - -### 2. 修改自定义材质 - -```typescript -const shaderData = material.shaderData; -// 获取想要设置的着色器数据 -const baseColor = shaderData.setFloat("material_BaseColor"); - -// 修改着色器数据 -baseColor.a = 0.5; -shaderData.setTexture("material_BaseTexture", texture); -shaderData.enable("MATERIAL_HAS_BASETEXTURE"); -// 更多的着色器操作,详见着色器文档 -``` diff --git a/docs/zh/graphics/material/shaderLab/_meta.json b/docs/zh/graphics/material/shaderLab/_meta.json new file mode 100644 index 000000000..0752dca48 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/_meta.json @@ -0,0 +1,10 @@ +{ + "overview": "总览介绍", + "property": "材质属性定义", + "script": "UIScript 的使用和绑定", + "global": "全局变量声明", + "renderState": "渲染状态设置", + "entry": "顶点、片元着色器程序编写", + "multi": "多 Pass Shader 编写", + "chunk": "Shader 代码段引用" +} diff --git a/docs/zh/graphics/material/shaderLab/chunk.mdx b/docs/zh/graphics/material/shaderLab/chunk.mdx new file mode 100644 index 000000000..cb450b76a --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/chunk.mdx @@ -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" // 项目资产根目录 +``` + + + +## 2. 脚本中显示注册代码段 + +```ts showLineNumbers +import { ShaderFactory } from '@galacean/engine'; + +const commonSource = `// shader chunk`; +ShaderFactory.registerInclude('includeKey', commonSource); +``` +ShaderLab 中引用 +```glsl showLineNumbers +#include "includeKey" +``` + + + +当前版本中,引擎提供了 `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) + diff --git a/docs/zh/graphics/material/shaderLab/entry.mdx b/docs/zh/graphics/material/shaderLab/entry.mdx new file mode 100644 index 000000000..a47746835 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/entry.mdx @@ -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)。 + + +ShaderLab 宏会在预处理器阶段被展开,因此宏不能影响 ShaderLab 结构解析,即 `Shader`,`SubShader`,`Pass`,`EditorProperties`,`EditorMacros` 关键字不能被包含在类似 `#ifdef` 这样的分支宏内。 + + diff --git a/docs/zh/graphics/material/shaderLab/global.mdx b/docs/zh/graphics/material/shaderLab/global.mdx new file mode 100644 index 000000000..a47e939a8 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/global.mdx @@ -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 变量 | + + +### 全局变量作用域 + +与其他编程语言类似,`ShaderLab` 中的全局变量也有作用域和同名覆盖原则。简单来说,全局变量的作用和可见范围仅限于其声明的模块内部,即 `Shader`、`SubShader` 和 `Pass` 模块。而同名覆盖原则指的是如果在 `Pass` 模块内存在与其上级 `SubShader` 模块内同名的全局变量,则 `Pass` 内的全局变量会覆盖 `SubShader` 内的同名全局变量。`SubShader` 和上级 `Shader` 模块同名变量的覆盖原则同理。 + + +## 渲染状态 + +包含混合状态(`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; + } + ``` + + [BlendOperation](/apis/galacean/#BlendOperation) 和 [BlendFactor](/apis/galacean/#BlendFactor) 枚举等同引擎 API。 + + +### DepthState + + ```glsl + DepthState state { + Enabled: bool; + WriteEnabled: bool; + CompareFunction: CompareFunction.XXX; + } + ``` + + [CompareFunction](/apis/galacean/#CompareFunction) 枚举等同引擎 API + + +### 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; + } + ``` + + [CompareFunction](/apis/galacean/#CompareFunction) 和 [StencilOperation](/apis/galacean/#StencilOperation) 枚举等同引擎 API + + +### RasterState + + ```glsl + RasterState state { + CullMode: CullMode.XXX; + DepthBias: float; + SlopeScaledDepthBias: float; + } + ``` + + + [CullMode](/apis/galacean/#CullMode) 枚举等同引擎 API + + +### 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; +``` diff --git a/docs/zh/graphics/material/shaderLab/multi.mdx b/docs/zh/graphics/material/shaderLab/multi.mdx new file mode 100644 index 000000000..f78de0db0 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/multi.mdx @@ -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 | + + +SubShader 除了用于组织多 Pass,还可以设置 `Tag`,作用相当于引擎 [setTag](/apis/galacean/#SubShader-setTag) API。比如设置 `ReplaceTag`: + +```glsl showLineNumbers +SubShader "SubShaderName" { + ... + Tags {ReplaceTag = "opaque"} + + Pass "PassName" { + ... + } +} +``` + diff --git a/docs/zh/graphics/material/shaderLab/overview.mdx b/docs/zh/graphics/material/shaderLab/overview.mdx new file mode 100644 index 000000000..786a22ccd --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/overview.mdx @@ -0,0 +1,146 @@ +--- +title: ShaderLab +--- + +`ShaderLab` 是一个针对 Galacean 引擎打造的 `Shader` 包装语言,它允许开发人员使用熟悉的 `GLSL` 语法编写自定义 `Shader`,同时提供了额外的高级抽象和管理特性以增强开发效率。通过 `ShaderLab`,开发者能够更便捷地定义材质属性、渲染配置和其他效果。尽管 `ShaderLab` 为着色器的编写引入了便利性,但它并不取代 `GLSL`,而是与之兼容。开发者可以在 `ShaderLab` 框架内编写原生 `GLSL` 代码块,享受两者的结合优势。 + +## 语法介绍 + +`ShaderLab` 语法骨架如下: + +```glsl +Shader "ShaderName" { + ... + Editor { + ... + } + ... + SubShader "SubShaderName" { + ... + Pass "PassName" { + ... + } + ... + } + ... +} +``` + +### 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" { + Editor { + Properties { + material_BaseColor("Main Color", Color) = (0, 0, 0, 1); + material_BaseTexture("Texture", Texture2D); + material_AlphaCutoff("Alpha Cutoff", Range(0, 1, 0.01)) = 0; + + Header("Common"){ + isTransparent("Transparent", Boolean) = false; + renderFace("Render Face", Enum(Front:0, Back:1, Double:2)) = 0; + blendMode("Blend Mode", Enum(Normal:0, Additive:1)) = 0; + } + } + + UIScript "${uiScriptPath}"; + } + + SubShader "Default" { + UsePass "pbr/Default/ShadowCaster" + + Pass "Pass0" { + DepthState { + WriteEnabled = depthWriteEnabled; + } + + BlendState { + Enabled = blendEnabled; + SourceColorBlendFactor = sourceColorBlendFactor; + DestinationColorBlendFactor = destinationColorBlendFactor; + SourceAlphaBlendFactor = sourceAlphaBlendFactor; + DestinationAlphaBlendFactor = destinationAlphaBlendFactor; + } + + RasterState{ + CullMode = rasterStateCullMode; + } + + RenderQueueType = renderQueueType; + + struct Attributes { + vec3 POSITION; + vec2 TEXCOORD_0; + vec4 JOINTS_0; + vec4 WEIGHTS_0; + }; + + struct Varyings { + vec2 uv; + }; + + #include "Common.glsl" + #include "Skin.glsl" + #include "Transform.glsl" + + vec4 material_BaseColor; + float material_AlphaCutoff; + sampler2D material_BaseTexture; + + VertexShader = vert; + FragmentShader = frag; + + Varyings vert(Attributes attr) { + Varyings v; + + vec4 position = vec4(attr.POSITION, 1.0); + + // Skin + #ifdef RENDERER_HAS_SKIN + mat4 skinMatrix = getSkinMatrix(attr); + position = skinMatrix * position; + #endif + + gl_Position = renderer_MVPMat * position; + v.uv = attr.TEXCOORD_0; + + return v; + } + + void frag(Varyings v) { + vec4 baseColor = material_BaseColor; + + #ifdef MATERIAL_HAS_BASETEXTURE + vec4 textureColor = texture2D(material_BaseTexture, v.uv); + #ifndef ENGINE_IS_COLORSPACE_GAMMA + textureColor = gammaToLinear(textureColor); + #endif + baseColor *= textureColor; + #endif + + #ifdef MATERIAL_IS_ALPHA_CUTOFF + if( baseColor.a < material_AlphaCutoff ) { + discard; + } + #endif + + gl_FragColor = baseColor; + + #ifndef ENGINE_IS_COLORSPACE_GAMMA + gl_FragColor = linearToGamma(gl_FragColor); + #endif + + } + } + } +} +``` diff --git a/docs/zh/graphics/material/shaderLab/property.mdx b/docs/zh/graphics/material/shaderLab/property.mdx new file mode 100644 index 000000000..6217bb689 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/property.mdx @@ -0,0 +1,124 @@ +--- +title: 材质属性定义 +--- + +开发者通过 `Editor` 模块定制绑定该 `Shader` 的材质属性,`Galacean` 编辑器可以自动将材质属性反射到材质资产的 Inspector 面板上。 + +
+ + + + + +
+ +## 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]; +``` + + + +1. 可以使用 `Header` 指令将有关联的属性组织起来,Inspector 对应的面板中也会有相应的层级分类: + +``` +Header("Emissive") { + material_EmissiveColor("Emissive color", Color) = (1,1,1,1); + ... +} +``` + +2. 通过注释标注 Inspector Hover 提示内容,支持使用 @language 指令进行多语言指定。 + + + +当前支持的 EditType 列表如下: + +| EditType | Example | +| :-: | :-- | +| Bool | propertyName("Property Description", Boolean) = true; | +| Int | propertyName("Property Description", Int) = 1;
propertyName("Property Description", Range(0,8)) = 1
propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1 // 枚举 | +| Float | propertyName("Property Description", FLoat) = 0.5;
propertyName("Property Description", Range(0.0, 1.0)) = 0.5;
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; +``` + + + 当前只有 Int 和 Float 类型支持枚举,且不支持类型混合,比如下面的枚举混合了Float和Int,将不会被正确解析 ```glsl + propertyName("Property Description", Enum("Item1":1, "Item2":2.0, "Item3": 3)) = 2.0; ``` + + +### 材质宏属性定义 + +用于将 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;
macroName("Macro Description", Range(0,8)) = 1; | +| Float | macroName("Macro Description", FLoat) = 0.5;
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); diff --git a/docs/zh/graphics/material/shaderLab/renderState.mdx b/docs/zh/graphics/material/shaderLab/renderState.mdx new file mode 100644 index 000000000..af20252e8 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/renderState.mdx @@ -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; + ... + } + + ... +} +... +``` + + +1. `DepthState`、`StencilState` 和 `RasterState` 的设置同理。 + +2. 全局变量域也可以是 `SubShader` 或`Shader` 层级,其作用域跟其他全局变量遵循同一[规则](./global/#全局变量作用域)。即如果是在 `SubShader` 层级全局变量域中设置的渲染状态,会对该 `SubShader` 下所有 `Pass` 生效。 + + +## 渲染状态属性设置 + +单个渲染状态属性设置同样也有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。 + + +渲染队列的设置例外: `RenderQueueType = Transparent;` + + +### 变量赋值 + +赋值语句右端为任一变量名,变量具体值可由开发者通过脚本方式在运行时通过 `ShaderData.setInt("material_DstBlend", BlendFactor.SourceColor)` API 进行指定。 diff --git a/docs/zh/graphics/material/shaderLab/script.mdx b/docs/zh/graphics/material/shaderLab/script.mdx new file mode 100644 index 000000000..ddc5b9750 --- /dev/null +++ b/docs/zh/graphics/material/shaderLab/script.mdx @@ -0,0 +1,138 @@ +--- +title: UIScript 的使用和绑定 +--- + +开发者使用编辑器对自定义材质属性进行调节的同时,还可以通过 `UIScript` 指令指定数据变更的回调行为。通过 UIScript 暴露的 hook 函数,开发者可以实现属性联动,从而减少声明属性的数量,达到简化 Inspector 面板的目的。 + +### 在 ShaderLab 中绑定 `UIScript`: + +```glsl showLineNumbers +Editor { + ... + UIScript "/path/to/script"; + ... +} +``` + +绑定的`UIScript`脚本路径支持相对路径和绝对路径,以下图项目根目录为例,绝对路径为 +`/PBRScript1.ts`,相对路径为`./PBRScript1.ts` + + + +### 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 void> = new Map(); + + /** @internal */ + _macroCallBacks: Map 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 脚本 + + + +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)) + } + } +} + +``` + + + 注意,当前版本 ShaderLab 材质属性模块只是定义了绑定该 Shader 的材质在编辑器中的 Inspector + UI面板,并不会替你在`ShaderPass`中声明对应的全局变量,如果`ShaderPass`代码中引用了该变量需在[全局变量](./shader/#全局变量)模块中明确声明补充。 + \ No newline at end of file diff --git a/docs/zh/graphics/material/variables.mdx b/docs/zh/graphics/material/variables.mdx new file mode 100644 index 000000000..d8e51d3e0 --- /dev/null +++ b/docs/zh/graphics/material/variables.mdx @@ -0,0 +1,60 @@ +--- +title: 内置变量 +--- + +Shader 代码中会经常用到内置变量,一种是**逐顶点**的 `attribute` 变量,另一种是**逐 shader** 的 `uniform` 变量。 + +下面是引擎内置变量,可以在写 Shader 的时候进行参考: + +### 顶点输入 + +| 逐顶点数据 | attribute name | 数据类型 | +| :------------- | :------------- | :------- | +| 顶点 | POSITION | vec3 | +| 法线 | NORMAL | vec3 | +| 切线 | TANGENT | vec4 | +| 顶点颜色 | COLOR_0 | vec4 | +| 骨骼索引 | JOINTS_0 | vec4 | +| 骨骼权重 | WEIGHTS_0 | vec4 | +| 第一套纹理坐标 | TEXCOORD_0 | vec2 | +| 第二套纹理坐标 | TEXCOORD_1 | vec2 | + +### 属性 + +#### 渲染器 + +| 名字 | 类型 | 解释 | +| :----------------- | :--- | ------------------ | +| renderer_LocalMat | mat4 | 模型本地坐标系矩阵 | +| renderer_ModelMat | mat4 | 模型世界坐标系矩阵 | +| renderer_MVMat | mat4 | 模型视口矩阵 | +| renderer_MVPMat | mat4 | 模型视口投影矩阵 | +| renderer_NormalMat | mat4 | 法线矩阵 | + +#### 相机 + +| 名字 | 类型 | 解释 | +| :----------------------- | :-------- | --------------------------------------------------------------------- | +| camera_ViewMat | mat4 | 视口矩阵 | +| camera_ProjMat | mat4 | 投影矩阵 | +| camera_VPMat | mat4 | 视口投影矩阵 | +| camera_ViewInvMat | mat4 | 视口逆矩阵 | +| camera_Position | vec3 | 相机位置 | +| camera_DepthTexture | sampler2D | 深度信息纹理 | +| camera_OpaqueTexture | sampler2D | 非透明纹理 | +| camera_DepthBufferParams | Vec4 | 相机深度缓冲参数:(x: 1.0 - far / near, y: far / near, z: 0, w: 0) | +| camera_ProjectionParams | Vec4 | 投影矩阵相关参数:(x: flipProjection ? -1 : 1, y: near, z: far, w: 0) | + +#### 时间 + +| 名字 | 类型 | 解释 | +| :---------------- | :--- | :-------------------------------------------------------- | +| scene_ElapsedTime | vec4 | 引擎启动后经过的总时间:(x: t, y: sin(t), z:cos(t), w: 0) | +| scene_DeltaTime | vec4 | 距离上一帧的间隔时间:(x: dt, y: 0, z:0, w: 0) | + +#### 雾 + +| 名字 | 类型 | 解释 | +| :-- | :-- | :-- | +| scene_FogColor | vec4 | 雾的颜色 | +| scene_FogParams | vec4 | 雾的参数:(x: -1/(end-start), y: end/(end-start), z: density / ln(2), w: density / sqr(ln(2)) | diff --git a/docs/zh/graphics/postProcess/customPostProcess.mdx b/docs/zh/graphics/postProcess/customPostProcess.mdx index 985e25707..0533b2603 100644 --- a/docs/zh/graphics/postProcess/customPostProcess.mdx +++ b/docs/zh/graphics/postProcess/customPostProcess.mdx @@ -81,13 +81,18 @@ const customShader = Shader.create( 我们新建一个 Pass,在 [onRender 钩子](/apis/core/#PostProcessUberPass-onRender) 里面直接 [Blitter](/apis/core/#Blitter) 到屏幕,然后将这个 Pass 添加到引擎中。 -```ts showLineNumbers {10,15} filename="CustomPostProcessPass.ts" +```ts showLineNumbers {15,20} filename="CustomPostProcessPass.ts" class CustomPass extends PostProcessPass { private _blitMaterial: Material; constructor(engine: Engine) { super(engine); this._blitMaterial = new Material(this.engine, customShader); + + // 我们这里关掉深度测试和深度写入,保证每次 Blit 都能覆盖到屏幕上。 + const depthState = this._blitMaterial.renderState.depthState; + depthState.enabled = false; + depthState.writeEnabled = false; } onRender(_, srcTexture: Texture2D, dst: RenderTarget): void { diff --git a/docs/zh/graphics/postProcess/effects.mdx b/docs/zh/graphics/postProcess/effects.mdx index 2355e81b5..1ea69ee5d 100644 --- a/docs/zh/graphics/postProcess/effects.mdx +++ b/docs/zh/graphics/postProcess/effects.mdx @@ -5,6 +5,8 @@ title: 后处理效果 ## 泛光(Bloom) +[API](/apis/core/#BloomEffect) + Bloom 特效可以给图像增加光晕质感。 @@ -76,6 +78,8 @@ Bloom 特效可以给图像增加光晕质感。 ## 色调映射(Tonemapping) +[API](/apis/core/#TonemappingEffect) + 色调映射可以重映射图像的 HDR 值,改善整个画面的对比度、色彩饱和度。 diff --git a/docs/zh/graphics/postProcess/postProcess.mdx b/docs/zh/graphics/postProcess/postProcess.mdx index 735288f82..58b68df44 100644 --- a/docs/zh/graphics/postProcess/postProcess.mdx +++ b/docs/zh/graphics/postProcess/postProcess.mdx @@ -12,7 +12,7 @@ title: 后处理总览 rightText="ON" /> -## 后处理配置 +## 后处理组件 后处理有两种模式: @@ -79,6 +79,35 @@ title: 后处理总览 +### 4. Pro code +```ts +// 获取后处理组件 +const postProcessComponent = entity.getComponent(PostProcess); + +// 设置后处理组件的一些属性 +postProcessComponent.isGlobal = false; + +// 获取已有的后处理特效 +const bloomEffect = postProcessComponent.getEffect(BloomEffect); + +// 也可以手动添加后处理特效 +const bloomEffect = postProcessComponent.addEffect(BloomEffect); + +// 设置后处理特效的属性 +bloomEffect.intensity.value = 1; +bloomEffect.threshold.value = 0.5; +``` +具体的后处理效果配置,请参考 [后处理效果列表](/docs/graphics/postProcess/effects) + + +## 后处理遮罩 +前面提到,后处理组件有一个属性叫做[层级](/apis/core/#PostProcess-layer),当场景中有多个后处理组件时,可以配合相机的[后处理遮罩](/apis/core/#Camera-postProcessMask)使用,来决定生效的后处理组件; +此功能只是用来决定数据融合的来源,如果你只想要某些物体具有后处理,可以使用多场景方案,将不需要后处理的 Entity 添加到另外一个 Scene 下面。 + + + + + ## 最佳实践 - 关于相机中 `HDR` 开关,如果场景中绝大部分像素计算没有超过 1(比如没有使用 HDR 贴图), 尽量别开启 HDR,开启后引擎会先渲染到 `R11G11B10_UFloat` 格式的 RenderTarget 中,再渲染到屏幕上,有性能开销。 diff --git a/docs/zh/graphics/shader/_meta.json b/docs/zh/graphics/shader/_meta.json deleted file mode 100644 index 4f3fcb37a..000000000 --- a/docs/zh/graphics/shader/_meta.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "intro": { "title": "着色器简介" }, - "class": { "title": "Shader 对象" }, - "builtins": { "title": "内置着色器" }, - "custom": { "title": "自定义着色器" }, - "assets": { "title": "Shader 资产" }, - "shaderLab": { "title": "ShaderLab" }, - "shaderAPI": { "title": "Shader API【实验】" } -} diff --git a/docs/zh/graphics/shader/assets.mdx b/docs/zh/graphics/shader/assets.mdx deleted file mode 100644 index 702dc4b4b..000000000 --- a/docs/zh/graphics/shader/assets.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Shader 资产 ---- - -Shader 资产是 Galacean 编辑器项目中定义 Shader 对象的资产,目前包含两种: - -- Shader 主文件 - -以 `.gs` 为后缀名,是 ShaderLab 编译的入口文件。 - -- Shader Chunk - -以 `.glsl` 为后缀名,是可以复用的 ShaderLab 代码片段,通过 `#include` 宏引入。 - - - `EditorProperties` 和 `EditorMacros` 只能声明在 Shader 主文件内,不能通过 `#include` 宏引入。 - - -## Shader 资产创建 - - - -编辑器中可以提供了3个 Shader 资产文件模板: `Unlit Shader`, `PBR Shader` 和 `Shader Chunk`。和脚本组件类似,选中 Shader 资产可以在 Inspector 界面预览 Shader 代码,双击或者在代码编辑页面选中可以实时编辑 Shader 代码。 - -前往 [Shader API 教程](/docs/graphics/shader/shaderAPI/) 了解如何基于模板拓展 Shader。 diff --git a/docs/zh/graphics/shader/builtins/intro.mdx b/docs/zh/graphics/shader/builtins/intro.mdx deleted file mode 100644 index c1d90024b..000000000 --- a/docs/zh/graphics/shader/builtins/intro.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: 介绍 ---- - -目前 Galacean 引擎内置了许多常用的 Shader,诸如 - -| 类型 | 描述 | -| :-- | :-- | -| [Unlit ](/docs/graphics/shader/builtins/unlit) | Unlit Shader 适用于烘焙好的模型渲染,她只需要设置一张基本纹理或者颜色,即可展现离线渲染得到的高质量渲染结果,但是缺点是无法实时展现光影交互,因为 Unlit 由纹理决定渲染,不受任何光照影响,可参考 [烘焙教程](/docs/art/bake-blender) 和 [导出 Unlit 教程](/docs/graphics/shader/builtins/unlit) | -| [Blinn Phong ](/docs/graphics/shader/builtins/blinnPhong) | Blinn Phong Shader 适用于那些对真实感没有那么高要求的场景,虽然没有遵循物理,但是其高效的渲染算法和基本齐全的光学部分,可以适用很多的场景。 | -| [PBR ](/docs/graphics/shader/builtins/pbr) | PBR Shader 适合需要真实感渲染的应用场景,因为 PBR 是基于物理的渲染,遵循能量守恒,开发者通过调整金属度、粗糙度、灯光等参数,能够保证渲染效果都是物理正确的。 | - -可以在编辑器中直接调试内置着色器的对应属性观察实时渲染效果变化。 - - - -> 与之对应的,也可以通过设置 PBRMaterial , BlinnPhongMaterial 与 UnlitMaterial 材质的 API 达到相同效果。 - -| 参数 | 应用 | -| :-- | :-- | -| [isTransparent](/apis/core/#BaseMaterial-isTransparent) | 是否透明。可以设置材质是否透明。如果设置为透明,可以通过 [BlendMode](/apis/core/#BaseMaterial-blendMode) 来设置颜色混合模式。 | -| [alphaCutoff](/apis/core/#BaseMaterial-alphaCutoff) | 透明度裁剪值。可以设置裁剪值,在着色器中,透明度小于此数值的片元将会被裁减,参考 [案例](/embed/blend-mode) | -| [renderFace](/apis/core/#BaseMaterial-renderFace) | 渲染面。可以决定渲染正面、背面、双面。 | -| [blendMode](/apis/core/#BaseMaterial-blendMode) | 颜色混合模式。当设置材质为透明后,可以设置此枚举来决定颜色混合模式,参考 [案例](/embed/blend-mode) | -| [tilingOffset](/apis/core/#BlinnPhongMaterial-tilingOffset) | 纹理坐标的缩放与偏移。是一个 Vector4 数据,分别控制纹理坐标在 uv 方向上的缩放和偏移,参考 [案例](/embed/tiling-offset) | diff --git a/docs/zh/graphics/shader/class.mdx b/docs/zh/graphics/shader/class.mdx deleted file mode 100644 index 3787fd234..000000000 --- a/docs/zh/graphics/shader/class.mdx +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Shader ---- - -在[着色器简介](/docs/graphics/shader/intro/)我们了解了着色器的基本概念,Galacean 引擎基于着色器程序,将其他渲染相关的信息进行了统一封装,形成了 Shader 对象。Shader 对象和 [材质](/docs/graphics/material/material/) 共同决定了被渲染物体最终的渲染结果。 - -## Shader - -Shader 对象包含以下内容: - -- name - -Shader 实例对象名。Shader 对象创建后会被引擎缓存,后续复用可以通过 `Shader.find()` 以 `Shader.name` 为索引进行查找,因此,引擎规定在单个引擎实例下,Shader 的 name 属性必须唯一。 - -- SubShader - -一个 Shader 对象包含至少一个 SubShader 对象。 - -## SubShader - -一个着色器程序可能会运行在不同的 GPU 硬件平台、不同的渲染管线中,因此 Shader 对象通过 tags 键值对的方式为不同的硬件平台、渲染管线指定不同的渲染逻辑,即 SubShader 对象。 - -SubShader 包含以下内容: - -- Tags - -Tags 是用于被渲染管线消费的 key-value 键值对,通常用于指定 Shader 兼容的硬件平台、渲染管线等信息,当前引擎内置管线消费的 Tags 有 - -| Key | value | -| ------------- | ------------ | -| pipelineStage | Forward | -| pipelineStage | DepthOnly | -| pipelineStage | ShadowCaster | -| pipelineStage | DepthOnly | - -此外还可以通过 [Camera.setReplacementShader](/apis/galacean/#Camera-resetReplacementShader),指定渲染管线的`replacementTag`,实现对设置指定 `Tag` 的 `SubShader` 替换的目的。 - -Tags 可以通过 [SubShader.setTag](/apis/galacean/#SubShader-setTag) 和 [ShaderPass.setTag](/apis/galacean/#ShaderPass-setTag)进行指定,ShaderLab 指定 Tag 方式详见[文档](/docs/graphics/shader/shaderLab/syntax/subShader/#tags) - -- Passes - -一个 SubShader 包含至少一个 ShaderPass 对象。 - -- RenderStates - -SubShader 下所有 ShaderPass 共享的 [RenderState 渲染状态](/docs/graphics/material/composition/#渲染状态) - -## ShaderPass - -ShaderPass 封装了具体的着色器程序,以及执行最终渲染时的[渲染状态](/docs/graphics/material/composition/#渲染状态)。 - - - 同一 SubShader 下的 ShaderPass 按照数组顺序依次渲染,渲染效果逐步叠加,形成 Shader 在当前帧的最终渲染结果。 - diff --git a/docs/zh/graphics/shader/custom.mdx b/docs/zh/graphics/shader/custom.mdx deleted file mode 100644 index 7c91435c6..000000000 --- a/docs/zh/graphics/shader/custom.mdx +++ /dev/null @@ -1,299 +0,0 @@ ---- -title: 自定义着色器 ---- - -业务中可能有一些特殊的渲染需求,例如水流特效,这时候就需要通过**自定义着色器** (Shader)去实现。 - - - -## 创建着色器 - -[Shader 类](/apis/core/#Shader) 封装了顶点着色器、片元着色器、着色器预编译、平台精度、平台差异性。他的创建和使用非常方便,用户只需要关注 shader 算法本身,而不用纠结于使用什么精度,亦或是使用 GLSL 哪个版本的写法。下面是一个简单的 demo: - -```javascript -import { Material, Shader, Color } from "@galacean/engine"; - -//-- Shader 代码 -const vertexSource = ` - uniform mat4 renderer_MVPMat; - - attribute vec3 POSITION; - - void main() { - gl_Position = renderer_MVPMat * vec4(POSITION, 1.0); - } - `; - -const fragmentSource = ` - uniform vec4 u_color; - - void main() { - gl_FragColor = u_color; - } - `; - -// 创建自定义 shader(整个 runtime 只需要创建一次) -Shader.create("demo", vertexSource, fragmentSource); - -// 创建材质 -const material = new Material(engine, Shader.find("demo")); -``` - -`Shader.create()`用来将 shader 添加到引擎的缓存池子中,因此整个 runtime 只需要创建一次,接下来就可以通过 [Shader.find(name)](/apis/galacean/#Shader-find) 来反复使用. - -> 注:引擎已经预先 create 了 blinn-phong、pbr、shadow-map、shadow、skybox、framebuffer-picker-color、trail。用户可以直接使用这些内置 shader,并且不能重名创建。 - -在上述的案例中,因为我们没有上传 `u_color` 变量,所以片元输出还是黑色的(uniform 默认值),接下来我们来介绍下引擎内置的 shader 变量以及如何上传自定义变量。 - -## 内置 shader 变量 - -在上面,我们给 material 赋予了 shader,这个时候程序已经可以开始渲染了。 - -> 需要注意的是,shader 代码中有两种变量,一种是**逐顶点**的 `attribute` 变量,另一种是**逐 shader** 的 `uniform` 变量。(在 GLSL300 后,统一为 in 变量) - -引擎已经自动上传了一些常用变量,用户可以直接在 shader 代码中使用,如顶点数据和 mvp 数据,下面是引擎默认上传的变量。 - -### 顶点输入 - -| 逐顶点数据 | attribute name | 数据类型 | -| :------------- | :------------- | :------- | -| 顶点 | POSITION | vec3 | -| 法线 | NORMAL | vec3 | -| 切线 | TANGENT | vec4 | -| 顶点颜色 | COLOR_0 | vec4 | -| 骨骼索引 | JOINTS_0 | vec4 | -| 骨骼权重 | WEIGHTS_0 | vec4 | -| 第一套纹理坐标 | TEXCOORD_0 | vec2 | -| 第二套纹理坐标 | TEXCOORD_1 | vec2 | - -### 属性 - -#### 渲染器 - -| 名字 | 类型 | 解释 | -| :----------------- | :--- | ------------------ | -| renderer_LocalMat | mat4 | 模型本地坐标系矩阵 | -| renderer_ModelMat | mat4 | 模型世界坐标系矩阵 | -| renderer_MVMat | mat4 | 模型视口矩阵 | -| renderer_MVPMat | mat4 | 模型视口投影矩阵 | -| renderer_NormalMat | mat4 | 法线矩阵 | - -#### 相机 - -| 名字 | 类型 | 解释 | -| :----------------------- | :-------- | --------------------------------------------------------------------- | -| camera_ViewMat | mat4 | 视口矩阵 | -| camera_ProjMat | mat4 | 投影矩阵 | -| camera_VPMat | mat4 | 视口投影矩阵 | -| camera_ViewInvMat | mat4 | 视口逆矩阵 | -| camera_Position | vec3 | 相机位置 | -| camera_DepthTexture | sampler2D | 深度信息纹理 | -| camera_DepthBufferParams | Vec4 | 相机深度缓冲参数:(x: 1.0 - far / near, y: far / near, z: 0, w: 0) | -| camera_ProjectionParams | Vec4 | 投影矩阵相关参数:(x: flipProjection ? -1 : 1, y: near, z: far, w: 0) | - -#### 时间 - -| 名字 | 类型 | 解释 | -| :---------------- | :--- | :-------------------------------------------------------- | -| scene_ElapsedTime | vec4 | 引擎启动后经过的总时间:(x: t, y: sin(t), z:cos(t), w: 0) | -| scene_DeltaTime | vec4 | 距离上一帧的间隔时间:(x: dt, y: 0, z:0, w: 0) | - -#### 雾 - -| 名字 | 类型 | 解释 | -| :-- | :-- | :-- | -| scene_FogColor | vec4 | 雾的颜色 | -| scene_FogParams | vec4 | 雾的参数:(x: -1/(end-start), y: end/(end-start), z: density / ln(2), w: density / sqr(ln(2)) | - -## 上传着色器数据 - -> attribute 逐顶点数据的上传请参考 [网格渲染器](/docs/graphics/mesh/modelMesh),这里不再赘述。 - -除了内置的变量,我们可以在着色器中上传任何自定义名字的变量,我们唯一要做的就是根据着色器数据类型,使用正确的接口。上传的接口全部保存在 [ShaderData](/apis/core/#ShaderData) 中,而 shaderData 实例对象又分别保存在引擎的四大类 [Scene](/apis/core/#Scene)、[Camera](/apis/core/#Camera)、[Renderer](/apis/core/#Renderer)、[Material](/apis/core/#Material) 中,我们只需要分别往这些 shaderData 中调用接口,上传变量,引擎便会在底层自动帮我们组装这些数据,并进行判重等性能的优化。 - - - -### 着色器数据分开的好处 - -着色器数据分别保存在引擎的四大类 [Scene](/apis/core/#Scene)、[Camera](/apis/core/#Camera)、[Renderer](/apis/core/#Renderer)、[Material](/apis/core/#Material) 中,这样做的好处之一就是底层可以根据上传时机上传某一块 uniform,提升性能;另外,将材质无关的着色器数据剥离出来,可以实现共享材质,比如两个 renderer ,共享了一个材质,虽然都要操控同一个 shader,但是因为这一部分 shader 数据的上传来源于两个 renderer 的 shaderData,所以是不会影响彼此的渲染结果的。 - -如: - -```typescript -const renderer1ShaderData = renderer1.shaderData; -const renderer2ShaderData = renderer2.shaderData; -const materialShaderData = material.shaderData; - -materialShaderData.setColor("material_color", new Color(1, 0, 0, 1)); -renderer1ShaderData.setFloat("u_progross", 0.5); -renderer2ShaderData.setFloat("u_progross", 0.8); -``` - -### 调用接口 - -着色器数据的类型和分别调用的 API 如下: - -| shader 类型 | ShaderData API | -| :-- | :-- | -| `bool` 、 `int` | setInt( value: number ) | -| `float` | setFloat( value: number )` | -| `bvec2`、`ivec2`、`vec2` | setVector2( value:Vector2 ) | -| `bvec3`、`ivec3`、`vec3` | setVector3( value:Vector3 ) | -| `bvec4`、`ivec4`、`vec4` | setVector4( value:Vector4 ) | -| `mat4` | setMatrix( value:Matrix ) | -| `float[]` 、`vec2[]` 、`vec3[]`、 `vec4[]` 、`mat4[]` | setFloatArray( value:Float32Array ) | -| `bool[]`、 `int[]` 、`bvec2[]`、 `bvec3[]` 、`bvec4[]`、 `ivec2[]`、 `ivec3[]` 、`ivec4[]` | setIntArray( value:Int32Array ) | -| `sampler2D` 、 `samplerCube` | setTexture( value:Texture ) | -| `sampler2D[]` 、 `samplerCube[]` | setTextureArray( value:Texture[] ) | - -代码演示如下: - -```glsl -// shader - -uniform float u_float; -uniform int u_int; -uniform bool u_bool; -uniform vec2 u_vec2; -uniform vec3 u_vec3; -uniform vec4 u_vec4; -uniform mat4 u_matrix; -uniform int u_intArray[10]; -uniform float u_floatArray[10]; -uniform sampler2D u_sampler2D; -uniform samplerCube u_samplerCube; -uniform sampler2D u_samplerArray[2]; - -// GLSL 300: -// in float u_float; -// ... -``` - -```typescript -// shaderData 可以分别保存在 scene 、camera 、renderer、 material 中。 -const shaderData = material.shaderData; - -shaderData.setFloat("u_float", 1.5); -shaderData.setInt("u_int", 1); -shaderData.setInt("u_bool", 1); -shaderData.setVector2("u_vec2", new Vector2(1, 1)); -shaderData.setVector3("u_vec3", new Vector3(1, 1, 1)); -shaderData.setVector4("u_vec4", new Vector4(1, 1, 1, 1)); -shaderData.setMatrix("u_matrix", new Matrix()); -shaderData.setIntArray("u_intArray", new Int32Array(10)); -shaderData.setFloatArray("u_floatArray", new Float32Array(10)); -shaderData.setTexture("u_sampler2D", texture2D); -shaderData.setTexture("u_samplerCube", textureCube); -shaderData.setTextureArray("u_samplerArray", [texture2D, textureCube]); -``` - -> **注**:为了性能考虑,引擎暂不支持 结构体数组上传、数组单个元素上传。 - -### 宏开关 - -除了 uniform 变量之外,引擎将 shader 中的[宏定义](https://www.wikiwand.com/en/OpenGL_Shading_Language)也视为一种变量,因为宏定义的开启/关闭 将生成不同的着色器变种,也会影响渲染结果。 - -如 shader 中有这些宏相关的操作: - -```glsl -#ifdef DISCARD - discard; -#endif - -#ifdef LIGHT_COUNT - uniform vec4 u_color[ LIGHT_COUNT ]; -#endif -``` - -也是通过 [ShaderData](/apis/core/#Shader-enableMacro) 来操控宏变量: - -```typescript -// 开启宏开关 -shaderData.enableMacro("DISCARD"); -// 关闭宏开关 -shaderData.disableMacro("DISCARD"); - -// 开启变量宏 -shaderData.enableMacro("LIGHT_COUNT", "3"); - -// 切换变量宏。这里底层会自动 disable 上一个宏,即 “LIGHT_COUNT 3” -shaderData.enableMacro("LIGHT_COUNT", "2"); - -// 关闭变量宏 -shaderData.disableMacro("LIGHT_COUNT"); -``` - -## 封装自定义材质 - -这部分的内容是结合上文所有内容,给用户一个简单的封装示例,希望对您有所帮助: - -```typescript -import { Material, Shader, Color, Texture2D, BlendFactor, RenderQueueType } from "@galacean/engine"; - -//-- Shader 代码 -const vertexSource = ` - uniform mat4 renderer_MVPMat; - - attribute vec3 POSITION; - attribute vec2 TEXCOORD_0; - varying vec2 v_uv; - - void main() { - gl_Position = renderer_MVPMat * vec4(POSITION, 1.0); - v_uv = TEXCOORD_0; - } - `; - -const fragmentSource = ` - uniform vec4 u_color; - varying vec2 v_uv; - - #ifdef TEXTURE - uniform sampler2D u_texture; - #endif - - void main() { - vec4 color = u_color; - - #ifdef TEXTURE - color *= texture2D(u_texture, v_uv); - #endif - - gl_FragColor = color; - - } - `; - -Shader.create("demo", vertexSource, fragmentSource); - -export class CustomMaterial extends Material { - set texture(value: Texture2D) { - if (value) { - this.shaderData.enableMacro("TEXTURE"); - this.shaderData.setTexture("u_texture", value); - } else { - this.shaderData.disableMacro("TEXTURE"); - } - } - - set color(val: Color) { - this.shaderData.setColor("u_color", val); - } - - // make it transparent - set transparent() { - const target = this.renderState.blendState.targetBlendState; - const depthState = this.renderState.depthState; - - target.enabled = true; - target.sourceColorBlendFactor = target.sourceAlphaBlendFactor = BlendFactor.SourceAlpha; - target.destinationColorBlendFactor = target.destinationAlphaBlendFactor = BlendFactor.OneMinusSourceAlpha; - depthState.writeEnabled = false; - this.renderQueueType = RenderQueueType.Transparent; - } - - constructor(engine: Engine) { - super(engine, Shader.find("demo")); - } -} -``` diff --git a/docs/zh/graphics/shader/intro.mdx b/docs/zh/graphics/shader/intro.mdx deleted file mode 100644 index 92e438ce7..000000000 --- a/docs/zh/graphics/shader/intro.mdx +++ /dev/null @@ -1,25 +0,0 @@ - - -## 着色器简介 - -着色器(Shader)是一段运行在GPU中的程序,通常由2个“入口函数”组成,我们称之为顶点着色器(Vertex Shader)和片元着色器(Fragment Shader),分别对应于渲染管线的两个不同阶段。下面是一幅简化的引擎渲染过程(渲染管线)的图示,我们聚焦于着色器部分进行描述 - -```mermaid -flowchart LR - A[CPU应用层] -->|准备顶点和其他\n渲染计算相关的数据| B{Vertex Shader} - subgraph Shader / 着色器 - B --> |计算顶点在三维空间的真实坐标\n以及其他后续渲染阶段需要的数据| C{Fragment Shader} - end - C -->|基于上一个渲染阶段的计算结果数据\n计算每个顶点的最终像素值| D[帧图像] -``` - -
简化的渲染管线
- -本章节我们将介绍如下内容: -|小节|内容| -|:--:|:--:| -|[Shader对象](/docs/graphics/shader/class/)|引擎中Shader对象的概括和基本用法| -|[内置着色器](/docs/graphics/shader/builtins/intro/)|引擎内置的常用着色器| -|[Shader资产](/docs/graphics/shader/assets/)|在编辑器中如何创建、修改Shader资产| -|[ShaderLab](/docs/graphics/shader/shaderLab/intro/)|一种更为便捷的创建Shader的方式| - diff --git a/docs/zh/graphics/shader/shaderLab/_meta.json b/docs/zh/graphics/shader/shaderLab/_meta.json deleted file mode 100644 index e838ec343..000000000 --- a/docs/zh/graphics/shader/shaderLab/_meta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "intro": "介绍", - "create": "创建", - "syntax": "语法标准", - "usage": "使用" -} \ No newline at end of file diff --git a/docs/zh/graphics/shader/shaderLab/create.mdx b/docs/zh/graphics/shader/shaderLab/create.mdx deleted file mode 100644 index c06c3a3f2..000000000 --- a/docs/zh/graphics/shader/shaderLab/create.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: 创建 ---- - -## 在编辑器中创建 - -编辑器中可以添加 3 种 ShaderLab 模板: `Unlit`、`PBR`、和 着色器片段 - - - -其中 **`Unlit`** 和 **`PBR`** 是使用 ShaderLab 语法进行编写的着色器模板,**着色器片段** 则是为了方便代码段复用,ShaderLab 中可以如下使用 `include` 宏进行代码段引用,后续编译过程中会被自动扩展替换。使用方式详见语法标准模块。 - -## 在脚本中创建 - -当前`ShaderLab`尚未集成到引擎 core 核心包中,需要在引擎初始化时传入新建的`ShaderLab`对象,否则引擎无法解析使用`ShaderLab`语法编写的 Shader。 - -1. `ShaderLab` 初始化 - -```ts -import { ShaderLab } from "@galacean/engine-shaderlab"; - -const shaderLab = new ShaderLab(); -// 使用ShaderLab初始化Engine -const engine = await WebGLEngine.create({ canvas: "canvas", shaderLab }); -``` - -2. 创建 Shader - -```glsl -// 直接使用 ShaderLab 创建 Shader -const shader = Shader.create(galaceanShaderCode); -``` diff --git a/docs/zh/graphics/shader/shaderLab/intro.mdx b/docs/zh/graphics/shader/shaderLab/intro.mdx deleted file mode 100644 index ddd93d441..000000000 --- a/docs/zh/graphics/shader/shaderLab/intro.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: ShaderLab ---- - -> 在[自定义着色器](/docs/graphics/shader/custom/)章节我们了解到如何使用 WebGL 原生 GLSL 语言创建自定义 Shader,本节我们将介绍另一种创建 Shader 的方式 --- ShaderLab。 - -`ShaderLab` 是一个针对 Galacean 引擎打造的 Shader 包装语言,它允许开发人员使用熟悉的 [GLSL](https://www.khronos.org/files/opengles_shading_language.pdf) 语法编写自定义 Shader,同时提供了额外的高级抽象和管理特性以增强开发效率。在[材质组成](/docs/graphics/material/composition/)章节我们提到,未引入 ShaderLab 前各类[渲染状态](/docs/graphics/material/composition/#渲染状态)的设置需要开发者手动调用 api 进行设置,有了 ShaderLab 后,开发者能够直接在 "Shader" 文件中对渲染状态进行设置和指定,此外还能定义绑定 Shader 的材质渲染参数,映射到编辑器的 Inspector 面板中,方便开发者即时调整渲染效果。 - -尽管 ShaderLab 为着色器的编写引入了便利性,但它并不取代 GLSL,而是与之兼容。开发者可以在 ShaderLab 框架内编写原生 GLSL 代码块,享受两者的结合优势。ShaderLab 使用流程如下: - -```mermaid -flowchart LR - 创建着色器 --> 编辑shaderlab --> 调试shaderlab -``` - -以下是一个简单的 ShaderLab 使用示例,其中包含了两个 Shader。`normal` Shader 定义了一个只实现 MVP 转换的顶点着色器,并且通过 Uniform 变量指定了像素颜色的片元着色器。另外,`lines` Shader 是一个使用 ShaderLab 进行改造的 [shadertoy](https://www.shadertoy.com/view/DtXfDr) 示例。 - - diff --git a/docs/zh/graphics/shader/shaderLab/syntax/_meta.json b/docs/zh/graphics/shader/shaderLab/syntax/_meta.json deleted file mode 100644 index 58a527e0c..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/_meta.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "intro": "总览", - "shader": "Shader", - "editor": "Editor", - "subShader": "SubShader", - "pass": "Pass", - "macro": "宏" -} diff --git a/docs/zh/graphics/shader/shaderLab/syntax/editor.mdx b/docs/zh/graphics/shader/shaderLab/syntax/editor.mdx deleted file mode 100644 index d48c815d6..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/editor.mdx +++ /dev/null @@ -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 面板对定义的属性进行调整。 - - - -### 属性定义 - -```glsl -/** - * @language zh - * Comments description - */ - -/** - * @language en - * 注释描述 - */ -propertyName("Description", EditType) = [DefaultValue]; -``` - - - -1. 可以使用 `Header` 指令将有关联的属性组织起来,Inspector 对应的面板中也会有相应的层级分类: - -``` -Header("Emissive") { - material_EmissiveColor("Emissive color", Color) = (1,1,1,1); - ... -} -``` - -2. 通过注释标注 Inspector Hover 提示内容,支持使用 @language 指令进行多语言指定。 - - - -当前支持的 EditType 列表如下: - -| EditType | Example | -| :-: | :-- | -| Bool | propertyName("Property Description", Boolean) = true; | -| Int | propertyName("Property Description", Int) = 1;
propertyName("Property Description", Range(0,8)) = 1
propertyName("Property Description", Enum(Item1: 1, Item2: 2, Item3: 3)) = 1 // 枚举 | -| Float | propertyName("Property Description", FLoat) = 0.5;
propertyName("Property Description", Range(0.0, 1.0)) = 0.5;
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; -``` - - - 当前只有 Int 和 Float 类型支持枚举,且不支持类型混合,比如下面的枚举混合了Float和Int,将不会被正确解析 ```glsl - propertyName("Property Description", Enum("Item1":1, "Item2":2.0, "Item3": 3)) = 2.0; ``` - - -## 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;
macroName("Macro Description", Range(0,8)) = 1; | -| Float | macroName("Macro Description", FLoat) = 0.5;
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"; - ... -} -``` - - -绑定的`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 void> = new Map(); - - /** @internal */ - _macroCallBacks: Map 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 脚本 - - - -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)) - } - } -} - -``` - - - 注意,当前版本 ShaderLab 材质属性模块只是定义了绑定该 Shader 的材质在编辑器中的 Inspector - UI面板,并不会替你在`ShaderPass`中声明对应的全局变量,如果`ShaderPass`代码中引用了该变量需在[全局变量](./shader/#全局变量)模块中明确声明补充。 - diff --git a/docs/zh/graphics/shader/shaderLab/syntax/intro.mdx b/docs/zh/graphics/shader/shaderLab/syntax/intro.mdx deleted file mode 100644 index 6bee935bb..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/intro.mdx +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: ShaderLab 语法标准 ---- - -## 在编辑器中编辑着色器 - -双击我们在上一步创建的着色器资产即可跳转到代码编辑页 - -> 未来版本会推出 Galacean VSCode 插件,该插件会为`ShaderLab`提供语法检测和自动补全功能以及代码同步功能,敬请期待 - - - -## 语法标准 - -`ShaderLab` 语法骨架如下: - -```glsl -Shader "ShaderName" { - ... - Editor { - ... - } - ... - SubShader "SubShaderName" { - ... - Pass "PassName" { - ... - } - ... - } - ... -} -``` - -主要包含 [Shader](/docs/graphics/shader/shaderLab/syntax/shader/),[SubShader](/docs/graphics/shader/shaderLab/syntax/subShader/) 和 [ShaderPass](/docs/graphics/shader/shaderLab/syntax/pass/) 模块。 diff --git a/docs/zh/graphics/shader/shaderLab/syntax/macro.mdx b/docs/zh/graphics/shader/shaderLab/syntax/macro.mdx deleted file mode 100644 index 07146a0b2..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/macro.mdx +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: 宏 ---- - -ShaderLab 支持 GLSL 标准语法中的部分宏和宏操作符: -- `#define` -- `#undef` -- `#if` -- `#ifdef` -- `#ifndef` -- `#else` -- `#elif` -- `#endif` -- `defined` - -以及额外引入的 `#include` 宏。 - - -ShaderLab 宏会在预处理器阶段被展开,因此宏不能影响 ShaderLab 结构解析,即 `Shader`,`SubShader`,`Pass`,`EditorProperties`,`EditorMacros` 关键字不能被包含在类似 `#ifdef` 这样的分支宏内。 - - -## 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"`。 \ No newline at end of file diff --git a/docs/zh/graphics/shader/shaderLab/syntax/pass.mdx b/docs/zh/graphics/shader/shaderLab/syntax/pass.mdx deleted file mode 100644 index 556d409ad..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/pass.mdx +++ /dev/null @@ -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/shader/shaderLab/syntax/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; -``` - - -除了可以在 ShaderLab 中对渲染状态和渲染队列进行设置,开发者同样可以通过材质的API进行设置,如 -```ts -// 渲染队列设置 -material.renderQueueType = RenderQueueType.Opaque; - -// 渲染状态设置 const renderState = material.renderState.depthState; depthState.writeEnabled = false; - -```` -当 ShaderLab 中声明了渲染状态和渲染队列,材质相应的设置会被忽略。 - - - -## 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; -} -``` -```` diff --git a/docs/zh/graphics/shader/shaderLab/syntax/shader.mdx b/docs/zh/graphics/shader/shaderLab/syntax/shader.mdx deleted file mode 100644 index a96e6cf89..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/shader.mdx +++ /dev/null @@ -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 内的同名全局变量。 diff --git a/docs/zh/graphics/shader/shaderLab/syntax/subShader.mdx b/docs/zh/graphics/shader/shaderLab/syntax/subShader.mdx deleted file mode 100644 index 7323dee5b..000000000 --- a/docs/zh/graphics/shader/shaderLab/syntax/subShader.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: SubShader ---- - -```glsl -SubShader "SubShaderName" { - ... - // 全局变量区:变量声明,结构体声明,渲染状态声明 - ... - Tags {ReplaceTag = "opaque"} - - UsePass "ShaderName/SubShaderName/PassName" - - Pass "PassName" { - ... - } -} -``` - -## Tags - -在 [Shader 对象](/docs/graphics/shader/class/) 章节我们了解了 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 | diff --git a/docs/zh/graphics/shader/shaderLab/usage.mdx b/docs/zh/graphics/shader/shaderLab/usage.mdx deleted file mode 100644 index 73248627f..000000000 --- a/docs/zh/graphics/shader/shaderLab/usage.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: "使用" ---- - -有了使用 `ShaderLab` 编写的自定义着色器资产后,我们可以通过将着色器绑定到新建的材质实现用户自定义材质。 - - - -- `ShaderLab`反射材质属性 - -如果我们在 `ShaderLab` 中编写了`材质属性定义`模块,模块中定义的属性会暴露在绑定该 Shader 的材质资产 Inspector 面板中 - - - -## 一个利用多 Pass 技术实现平面阴影的示例 - - diff --git a/e2e/case/postProcess-HDR-bloom-ACES.ts b/e2e/case/postProcess-HDR-bloom-ACES.ts index a0c1d3be5..0fc07a114 100644 --- a/e2e/case/postProcess-HDR-bloom-ACES.ts +++ b/e2e/case/postProcess-HDR-bloom-ACES.ts @@ -18,8 +18,8 @@ initPostProcessEnv((camera: Camera, resArray) => { const tonemappingEffect = postProcess.addEffect(TonemappingEffect); tonemappingEffect.mode.value = TonemappingMode.ACES; - bloomEffect.threshold.value = 0.5; - bloomEffect.intensity.value = 1; + bloomEffect.threshold.value = 1.5; + bloomEffect.intensity.value = 10; bloomEffect.dirtTexture.value = dirtTexture; bloomEffect.dirtIntensity.value = 5; tonemappingEffect.mode.value = TonemappingMode.ACES; diff --git a/e2e/package.json b/e2e/package.json index 0b47da803..cd36155d8 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,7 +1,7 @@ { "name": "@galacean/engine-e2e", "private": true, - "version": "1.4.12", + "version": "1.4.15", "license": "MIT", "scripts": { "case": "vite serve .dev --config .dev/vite.config.js", diff --git a/package.json b/package.json index d4acfc2a0..2198b99f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-root", - "version": "1.4.12", + "version": "1.4.15", "packageManager": "pnpm@9.3.0", "private": true, "scripts": { diff --git a/packages/core/package.json b/packages/core/package.json index abf04a286..5066630fa 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-core", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/core/src/2d/sprite/SpriteRenderer.ts b/packages/core/src/2d/sprite/SpriteRenderer.ts index 654cda067..4ee335010 100644 --- a/packages/core/src/2d/sprite/SpriteRenderer.ts +++ b/packages/core/src/2d/sprite/SpriteRenderer.ts @@ -18,6 +18,7 @@ import { SpriteMaskInteraction } from "../enums/SpriteMaskInteraction"; import { SpriteModifyFlags } from "../enums/SpriteModifyFlags"; import { SpriteTileMode } from "../enums/SpriteTileMode"; import { Sprite } from "./Sprite"; +import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer"; /** * Renders a Sprite for 2D graphics. @@ -32,7 +33,7 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer { @ignoreClone private _drawMode: SpriteDrawMode; - @assignmentClone + @ignoreClone private _assembler: ISpriteAssembler; @assignmentClone private _tileMode: SpriteTileMode = SpriteTileMode.Continuous; @@ -240,11 +241,11 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer { /** * The mask layer the sprite renderer belongs to. */ - get maskLayer(): number { + get maskLayer(): SpriteMaskLayer { return this._maskLayer; } - set maskLayer(value: number) { + set maskLayer(value: SpriteMaskLayer) { this._maskLayer = value; } @@ -287,7 +288,6 @@ export class SpriteRenderer extends Renderer implements ISpriteRenderer { */ override _cloneTo(target: SpriteRenderer, srcRoot: Entity, targetRoot: Entity): void { super._cloneTo(target, srcRoot, targetRoot); - target._assembler.resetData(target); target.sprite = this._sprite; target.drawMode = this._drawMode; } diff --git a/packages/core/src/2d/text/TextRenderer.ts b/packages/core/src/2d/text/TextRenderer.ts index 4596bf8ac..5d798a91a 100644 --- a/packages/core/src/2d/text/TextRenderer.ts +++ b/packages/core/src/2d/text/TextRenderer.ts @@ -21,6 +21,7 @@ import { Font } from "./Font"; import { ITextRenderer } from "./ITextRenderer"; import { SubFont } from "./SubFont"; import { TextUtils } from "./TextUtils"; +import { SpriteMaskLayer } from "../../enums/SpriteMaskLayer"; /** * Renders a text for 2D graphics. @@ -254,11 +255,11 @@ export class TextRenderer extends Renderer implements ITextRenderer { /** * The mask layer the sprite renderer belongs to. */ - get maskLayer(): number { + get maskLayer(): SpriteMaskLayer { return this._maskLayer; } - set maskLayer(value: number) { + set maskLayer(value: SpriteMaskLayer) { this._maskLayer = value; } diff --git a/packages/core/src/BasicResources.ts b/packages/core/src/BasicResources.ts index 6adbce09a..85ea26a0b 100644 --- a/packages/core/src/BasicResources.ts +++ b/packages/core/src/BasicResources.ts @@ -8,7 +8,7 @@ import { BufferBindFlag } from "./graphic/enums/BufferBindFlag"; import { BufferUsage } from "./graphic/enums/BufferUsage"; import { MeshTopology } from "./graphic/enums/MeshTopology"; import { VertexElementFormat } from "./graphic/enums/VertexElementFormat"; -import { Material } from "./material"; +import { BlinnPhongMaterial, Material } from "./material"; import { PrefilteredDFG } from "./material/utils/PrefilteredDFG"; import { ModelMesh } from "./mesh"; import { Shader } from "./shader/Shader"; @@ -118,6 +118,7 @@ export class BasicResources { readonly textDefaultMaterial: Material; readonly spriteMaskDefaultMaterial: Material; + private _blinnPhongMaterial: BlinnPhongMaterial; private _prefilteredDFGTexture: Texture2D; get prefilteredDFGTexture(): Texture2D { @@ -196,6 +197,13 @@ export class BasicResources { this.spriteMaskDefaultMaterial = this._createSpriteMaskMaterial(engine); } + /** + * @internal + */ + _getBlinnPhongMaterial(): BlinnPhongMaterial { + return (this._blinnPhongMaterial ||= new BlinnPhongMaterial(this.engine)); + } + /** * @internal */ diff --git a/packages/core/src/Camera.ts b/packages/core/src/Camera.ts index b5be34d5c..edc9c06d2 100644 --- a/packages/core/src/Camera.ts +++ b/packages/core/src/Camera.ts @@ -681,6 +681,7 @@ export class Camera extends Component { } this._renderPipeline.render(context, cubeFace, mipLevel, ignoreClearFlags); engine._renderCount++; + context.camera = null; } /** diff --git a/packages/core/src/Engine.ts b/packages/core/src/Engine.ts index c155d0a6d..816128aa6 100644 --- a/packages/core/src/Engine.ts +++ b/packages/core/src/Engine.ts @@ -492,10 +492,7 @@ export class Engine extends EventDispatcher { private _destroy(): void { this._sceneManager._destroyAllScene(); - this._resourceManager._destroy(); - this._textDefaultFont = null; - this._fontMap = null; this.inputManager._destroy(); this._batcherManager.destroy(); @@ -505,16 +502,11 @@ export class Engine extends EventDispatcher { // Cancel animation this.pause(); + Shader._clear(this); this._hardwareRenderer.destroy(); this.removeAllEventListeners(); - this._animate = null; - this._sceneManager = null; - this._resourceManager = null; - this._canvas = null; - this._time = null; - this._waitingDestroy = false; this._destroyed = true; } @@ -661,6 +653,7 @@ export class Engine extends EventDispatcher { this._hardwareRenderer.resetState(); this._lastRenderState = new RenderState(); // Clear shader pools + Shader._clear(this); this._shaderProgramPools.length = 0; const { resourceManager } = this; diff --git a/packages/core/src/Entity.ts b/packages/core/src/Entity.ts index a24eb6f1d..ef934ae10 100644 --- a/packages/core/src/Entity.ts +++ b/packages/core/src/Entity.ts @@ -240,7 +240,13 @@ export class Entity extends EngineObject { throw `The entity ${this.name} is not in the hierarchy`; } - this._setSiblingIndex(this._isRoot ? this._scene._rootEntities : this._parent._children, value); + if (this._isRoot) { + this._setSiblingIndex(this._scene._rootEntities, value); + } else { + const parent = this._parent; + this._setSiblingIndex(parent._children, value); + parent._dispatchModify(EntityModifyFlags.Child, parent); + } } /** @@ -621,7 +627,7 @@ export class Entity extends EngineObject { if (oldParent != null) { Entity._removeFromChildren(oldParent._children, this); this._parent = null; - this._dispatchModify(EntityModifyFlags.Child, oldParent); + oldParent._dispatchModify(EntityModifyFlags.Child, oldParent); } } @@ -686,6 +692,7 @@ export class Entity extends EngineObject { this._removeFromParent(); this._parent = parent; if (parent) { + this._isRoot = false; parent._addToChildrenList(siblingIndex, this); const oldScene = this._scene; @@ -808,7 +815,6 @@ export class Entity extends EngineObject { } } } - this._dispatchModify(EntityModifyFlags.Child, this); } //--------------------------------------------------------------deprecated---------------------------------------------------------------- diff --git a/packages/core/src/RenderPipeline/Blitter.ts b/packages/core/src/RenderPipeline/Blitter.ts index e6ea1ef2e..8bc1d79d1 100644 --- a/packages/core/src/RenderPipeline/Blitter.ts +++ b/packages/core/src/RenderPipeline/Blitter.ts @@ -92,5 +92,6 @@ export class Blitter { ); rhi.drawPrimitive(blitMesh._primitive, blitMesh.subMesh, program); + rendererShaderData.setTexture(Blitter._blitTextureProperty, null); } } diff --git a/packages/core/src/RenderPipeline/MaskManager.ts b/packages/core/src/RenderPipeline/MaskManager.ts index 2464594f3..8454e1d58 100644 --- a/packages/core/src/RenderPipeline/MaskManager.ts +++ b/packages/core/src/RenderPipeline/MaskManager.ts @@ -43,9 +43,7 @@ export class MaskManager { drawMask(context: RenderContext, pipelineStageTagValue: string, maskLayer: SpriteMaskLayer): void { const incrementMaskQueue = MaskManager.getMaskIncrementRenderQueue(); - incrementMaskQueue.clear(); const decrementMaskQueue = MaskManager.getMaskDecrementRenderQueue(); - decrementMaskQueue.clear(); this._buildMaskRenderElement(maskLayer, incrementMaskQueue, decrementMaskQueue); @@ -53,9 +51,11 @@ export class MaskManager { incrementMaskQueue.batch(batcherManager); batcherManager.uploadBuffer(); incrementMaskQueue.render(context, pipelineStageTagValue, RenderQueueMaskType.Increment); + incrementMaskQueue.clear(); decrementMaskQueue.batch(batcherManager); batcherManager.uploadBuffer(); decrementMaskQueue.render(context, pipelineStageTagValue, RenderQueueMaskType.Decrement); + decrementMaskQueue.clear(); } clearMask(context: RenderContext, pipelineStageTagValue: string): void { @@ -63,8 +63,6 @@ export class MaskManager { if (preMaskLayer !== SpriteMaskLayer.Nothing) { if (this.hasStencilWritten) { const decrementMaskQueue = MaskManager.getMaskDecrementRenderQueue(); - decrementMaskQueue.clear(); - const masks = this._allSpriteMasks; for (let i = 0, n = masks.length; i < n; i++) { const mask = masks.get(i); @@ -75,6 +73,7 @@ export class MaskManager { decrementMaskQueue.batch(batcherManager); batcherManager.uploadBuffer(); decrementMaskQueue.render(context, pipelineStageTagValue, RenderQueueMaskType.Decrement); + decrementMaskQueue.clear(); } else { const engine = context.camera.engine; engine._hardwareRenderer.clearRenderTarget(engine, CameraClearFlags.Stencil, null); diff --git a/packages/core/src/animation/AnimationClip.ts b/packages/core/src/animation/AnimationClip.ts index 79982db52..0286c2c02 100644 --- a/packages/core/src/animation/AnimationClip.ts +++ b/packages/core/src/animation/AnimationClip.ts @@ -212,14 +212,16 @@ export class AnimationClip extends EngineObject { */ _sampleAnimation(entity: Entity, time: number): void { const { _curveBindings: curveBindings } = this; + const components = AnimationCurveOwner._components; for (let i = curveBindings.length - 1; i >= 0; i--) { const curve = curveBindings[i]; const targetEntity = entity.findByPath(curve.relativePath); if (targetEntity) { const component = curve.typeIndex > 0 - ? targetEntity.getComponents(curve.type, AnimationCurveOwner._components)[curve.typeIndex] + ? targetEntity.getComponents(curve.type, components)[curve.typeIndex] : targetEntity.getComponent(curve.type); + components.length = 0; if (!component) { continue; } diff --git a/packages/core/src/animation/Animator.ts b/packages/core/src/animation/Animator.ts index 9a891f4a4..9594e68a4 100644 --- a/packages/core/src/animation/Animator.ts +++ b/packages/core/src/animation/Animator.ts @@ -400,7 +400,7 @@ export class Animator extends Component { const { _curveBindings: curves } = animatorState.clip; const { curveOwnerPool: layerCurveOwnerPool } = animatorLayerData; - + const components = AnimationCurveOwner._components; for (let i = curves.length - 1; i >= 0; i--) { const curve = curves[i]; const { relativePath } = curve; @@ -408,9 +408,9 @@ export class Animator extends Component { if (targetEntity) { const component = curve.typeIndex > 0 - ? targetEntity.getComponents(curve.type, AnimationCurveOwner._components)[curve.typeIndex] + ? targetEntity.getComponents(curve.type, components)[curve.typeIndex] : targetEntity.getComponent(curve.type); - + components.length = 0; if (!component) { continue; } diff --git a/packages/core/src/asset/AssetPromise.ts b/packages/core/src/asset/AssetPromise.ts index 726c55b95..39bf32fc9 100644 --- a/packages/core/src/asset/AssetPromise.ts +++ b/packages/core/src/asset/AssetPromise.ts @@ -3,19 +3,61 @@ */ export class AssetPromise implements PromiseLike { /** - * Return a new resource Promise through the provided asset promise collection. - * The resolved of the new AssetPromise will be triggered when all the Promises in the provided set are completed. - * @param - Promise Collection - * @returns AssetPromise + * Creates a new resolved AssetPromise. + * @returns A resolved AssetPromise. */ - static all(promises: (PromiseLike | T)[]) { - return new AssetPromise((resolve, reject, setTaskCompleteProgress) => { - const count = promises.length; + static resolve(): AssetPromise; + + /** + * Creates a new resolved AssetPromise fork the provided value. + * @param value - A value + * @returns A AssetPromise whose internal state matches the provided promise. + */ + static resolve(value: T): AssetPromise>; + + /** + * Creates a new resolved AssetPromise for the provided value. + * @param value - A PromiseLike + * @returns A AssetPromise whose internal state matches the provided promise. + */ + static resolve(value: PromiseLike): AssetPromise>; + + static resolve(value?: T | PromiseLike): AssetPromise> | AssetPromise { + if (value === undefined) { + return new AssetPromise((resolve) => resolve()); + } else if (value instanceof AssetPromise || value instanceof Promise) { + return new AssetPromise>((resolve, reject) => { + value.then((resolved) => resolve(resolved as Awaited), reject); + }); + } else { + return new AssetPromise>((resolve) => resolve(value as Awaited)); + } + } + + /** + * Creates a AssetPromise that is resolved with an array of results when all of the provided PromiseLike resolve, or rejected when any PromiseLike is rejected. + * @param values An array of PromiseLikes + * @returns A new AssetPromise. + */ + static all( + values: T extends [] ? [...T] : readonly [...T] + ): AssetPromise<{ [K in keyof T]: UnwrapPromise }>; + + /** + * Creates a AssetPromise that is resolved with an array of results when all of the provided PromiseLikes resolve, or rejected when any PromiseLikes is rejected. + * @param values An iterable of PromiseLikes + * @returns A new AssetPromise. + */ + static all(values: Iterable>): AssetPromise[]>; + + static all(values: [...T]): AssetPromise<{ [K in keyof T]: UnwrapPromise }> { + return new AssetPromise<{ [K in keyof T]: UnwrapPromise }>((resolve, reject, setTaskCompleteProgress) => { + const count = Array.from(values).length; const results: T[] = new Array(count); let completed = 0; if (count === 0) { - return resolve(results); + return resolve(results as { [K in keyof T]: UnwrapPromise }); } function onComplete(index: number, resultValue: T) { @@ -24,24 +66,24 @@ export class AssetPromise implements PromiseLike { setTaskCompleteProgress(completed, count); if (completed === count) { - resolve(results); + resolve(results as { [K in keyof T]: UnwrapPromise }); } } - function onProgress(promise: PromiseLike | T, index: number) { - if (promise instanceof Promise || promise instanceof AssetPromise) { + function onProgress(promise: any, index: number) { + if (promise instanceof AssetPromise || promise instanceof Promise) { promise.then(function (value) { onComplete(index, value); }, reject); } else { Promise.resolve().then(() => { - onComplete(index, promise as T); + onComplete(index, promise); }); } } for (let i = 0; i < count; i++) { - onProgress(promises[i], i); + onProgress(values[i], i); } }); } @@ -215,3 +257,4 @@ type TaskCompleteProgress = { }; type TaskCompleteCallback = (loaded: number, total: number) => void; type TaskDetailCallback = (url: string, loaded: number, total: number) => void; +type UnwrapPromise = T extends PromiseLike ? U : T; diff --git a/packages/core/src/asset/ResourceManager.ts b/packages/core/src/asset/ResourceManager.ts index 64ff7ac31..71e55c831 100644 --- a/packages/core/src/asset/ResourceManager.ts +++ b/packages/core/src/asset/ResourceManager.ts @@ -1,3 +1,4 @@ +import { IClone } from "@galacean/engine-design"; import { ContentRestorer, Engine, EngineObject, Logger, Utils } from ".."; import { AssetPromise } from "./AssetPromise"; import { GraphicsResource } from "./GraphicsResource"; @@ -361,7 +362,8 @@ export class ResourceManager { const paths = queryPath ? this._parseQueryPath(queryPath) : []; // Get remote asset base url - const remoteAssetBaseURL = this._virtualPathResourceMap[assetBaseURL]?.path ?? assetBaseURL; + const remoteConfig = this._virtualPathResourceMap[assetBaseURL]; + const remoteAssetBaseURL = remoteConfig?.path ?? assetBaseURL; // Check cache const cacheObject = this._assetUrlPool[remoteAssetBaseURL]; @@ -403,11 +405,14 @@ export class ResourceManager { throw `loader not found: ${item.type}`; } + const subpackageName = remoteConfig?.subpackageName; + // Check sub asset if (queryPath) { // Check whether load main asset const mainPromise = - loadingPromises[remoteAssetBaseURL] || this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); + loadingPromises[remoteAssetBaseURL] || + this._loadSubpackageAndMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL, subpackageName); mainPromise.catch((e) => { this._onSubAssetFail(remoteAssetBaseURL, queryPath, e); }); @@ -415,6 +420,17 @@ export class ResourceManager { return this._createSubAssetPromiseCallback(remoteAssetBaseURL, remoteAssetURL, queryPath); } + return this._loadSubpackageAndMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL, subpackageName); + } + + // For adapter mini-game platform + private _loadSubpackageAndMainAsset( + loader: Loader, + item: LoadItem, + remoteAssetBaseURL: string, + assetBaseURL: string, + subpackageName: string + ): AssetPromise { return this._loadMainAsset(loader, item, remoteAssetBaseURL, assetBaseURL); } @@ -561,29 +577,29 @@ export class ResourceManager { * @internal * @beta Just for internal editor, not recommended for developers. */ - getResourceByRef(ref: { refId: string; key?: string; isClone?: boolean }): Promise { + getResourceByRef(ref: { refId: string; key?: string; isClone?: boolean }): AssetPromise { const { refId, key, isClone } = ref; const obj = this._objectPool[refId]; - let promise; + let promise: AssetPromise; if (obj) { - promise = Promise.resolve(obj); + promise = AssetPromise.resolve(obj); } else { const resourceConfig = this._idResourceMap[refId]; if (!resourceConfig) { Logger.warn(`refId:${refId} is not find in this._idResourceMap.`); - return Promise.resolve(null); + return AssetPromise.resolve(null); } let url = resourceConfig.virtualPath; if (key) { url += "?q=" + key; } - promise = this.load({ + promise = this.load({ url, type: resourceConfig.type }); } - return promise.then((item) => (isClone ? item.clone() : item)); + return promise.then((item) => (isClone ? (item).clone() : item)); } /** @@ -599,6 +615,7 @@ export class ResourceManager { } }); } + //-----------------Editor temp solution----------------- } @@ -642,6 +659,7 @@ type EditorResourceItem = { type: string; id: string; dependentAssetMap?: { [key: string]: string }; + subpackageName?: string; }; type SubAssetPromiseCallbacks = Record< // main asset url, ie. "https://***.glb" diff --git a/packages/core/src/input/pointer/PointerManager.ts b/packages/core/src/input/pointer/PointerManager.ts index 887390107..fc6d8a201 100644 --- a/packages/core/src/input/pointer/PointerManager.ts +++ b/packages/core/src/input/pointer/PointerManager.ts @@ -115,7 +115,7 @@ export class PointerManager implements IInput { } pointer = pointerPool[j]; if (!pointer) { - pointer = new Pointer(j); + pointer = pointerPool[j] = new Pointer(j); engine._physicsInitialized && pointer._addEmitters(PhysicsPointerEventEmitter, eventPool); PointerManager._pointerEventEmitters.forEach((emitter) => { pointer._addEmitters(emitter, eventPool); diff --git a/packages/core/src/physics/PhysicsScene.ts b/packages/core/src/physics/PhysicsScene.ts index 0616918f3..bbcf59863 100644 --- a/packages/core/src/physics/PhysicsScene.ts +++ b/packages/core/src/physics/PhysicsScene.ts @@ -315,18 +315,26 @@ export class PhysicsScene { } return shape.collider.entity.layer & layerMask && shape.isSceneQuery; }; + let outIDX: number; + let outDistance: number; + let outPosition: Vector3; + let outNormal: Vector3; if (hitResult != undefined) { const result = this._nativePhysicsScene.raycast(ray, distance, onRaycast, (idx, distance, position, normal) => { - const hitShape = Engine._physicalObjectsMap[idx]; - hitResult.entity = hitShape._collider.entity; - hitResult.shape = hitShape; - hitResult.distance = distance; - hitResult.normal.copyFrom(normal); - hitResult.point.copyFrom(position); + outIDX = idx; + outDistance = distance; + outPosition = position; + outNormal = normal; }); if (result) { + const hitShape = Engine._physicalObjectsMap[outIDX]; + hitResult.entity = hitShape._collider.entity; + hitResult.shape = hitShape; + hitResult.distance = outDistance; + hitResult.point.copyFrom(outPosition); + hitResult.normal.copyFrom(outNormal); return true; } else { hitResult.entity = null; diff --git a/packages/core/src/postProcess/PostProcessUberPass.ts b/packages/core/src/postProcess/PostProcessUberPass.ts index 34c6d43a9..7aef36f9b 100644 --- a/packages/core/src/postProcess/PostProcessUberPass.ts +++ b/packages/core/src/postProcess/PostProcessUberPass.ts @@ -121,7 +121,7 @@ export class PostProcessUberPass extends PostProcessPass { const thresholdKnee = thresholdLinear * 0.5; // Hardcoded soft knee const bloomParams = bloomShaderData.getVector4(BloomEffect._bloomParams); const scatterLerp = MathUtil.lerp(0.05, 0.95, scatter.value); - bloomParams.x = threshold.value; + bloomParams.x = thresholdLinear; bloomParams.y = thresholdKnee; bloomParams.z = scatterLerp; const bloomIntensityParams = uberShaderData.getVector4(BloomEffect._bloomIntensityParams); diff --git a/packages/core/src/postProcess/shaders/Bloom/BloomPrefilter.glsl b/packages/core/src/postProcess/shaders/Bloom/BloomPrefilter.glsl index c7788a3dc..304046082 100644 --- a/packages/core/src/postProcess/shaders/Bloom/BloomPrefilter.glsl +++ b/packages/core/src/postProcess/shaders/Bloom/BloomPrefilter.glsl @@ -51,5 +51,6 @@ void main(){ // Clamp colors to positive once in prefilter. Encode can have a sqrt, and sqrt(-x) == NaN. Up/Downsample passes would then spread the NaN. color = max(color, 0.0); - gl_FragColor = vec4(color, samplerColor.a); -} \ No newline at end of file + // Bloom is addtive blend mode, we should set alpha 0 to avoid browser background color dark when canvas alpha and premultiplyAlpha is true + gl_FragColor = vec4(color, 0.0); +} diff --git a/packages/core/src/shader/Shader.ts b/packages/core/src/shader/Shader.ts index f837eea3e..e4a392474 100644 --- a/packages/core/src/shader/Shader.ts +++ b/packages/core/src/shader/Shader.ts @@ -174,6 +174,30 @@ export class Shader implements IReferable { return Shader._shaderMap[name]; } + /** + * @internal + */ + static _clear(engine: Engine): void { + const shaderMap = Shader._shaderMap; + for (const key in shaderMap) { + const shader = shaderMap[key]; + const subShaders = shader._subShaders; + for (let i = 0, n = subShaders.length; i < n; i++) { + const passes = subShaders[i].passes; + for (let j = 0, m = passes.length; j < m; j++) { + const pass = passes[j]; + const passShaderProgramPools = pass._shaderProgramPools; + for (let k = passShaderProgramPools.length - 1; k >= 0; k--) { + const shaderProgramPool = passShaderProgramPools[k]; + if (shaderProgramPool.engine !== engine) continue; + shaderProgramPool._destroy(); + passShaderProgramPools.splice(k, 1); + } + } + } + } + } + private static _applyConstRenderStates( renderState: RenderState, key: RenderStateElementKey, diff --git a/packages/core/src/ui/UIUtils.ts b/packages/core/src/ui/UIUtils.ts index 001770af3..87bd26ebd 100644 --- a/packages/core/src/ui/UIUtils.ts +++ b/packages/core/src/ui/UIUtils.ts @@ -40,15 +40,16 @@ export class UIUtils { Matrix.multiply(virtualCamera.projectionMatrix, virtualCamera.viewMatrix, virtualCamera.viewProjectionMatrix); renderContext.applyVirtualCamera(virtualCamera, false); renderContext.rendererUpdateFlag |= ContextRendererUpdateFlag.ProjectionMatrix; - uiRenderQueue.clear(); uiCanvas._prepareRender(renderContext); uiRenderQueue.pushRenderElement(uiCanvas._renderElement); uiRenderQueue.batch(batcherManager); batcherManager.uploadBuffer(); uiRenderQueue.render(renderContext, "Forward"); + uiRenderQueue.clear(); engine._renderCount++; } } + renderContext.camera = null; } } diff --git a/packages/design/package.json b/packages/design/package.json index aa8293337..61ccea040 100644 --- a/packages/design/package.json +++ b/packages/design/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-design", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/galacean/package.json b/packages/galacean/package.json index fbafd848d..fd1eb6a49 100644 --- a/packages/galacean/package.json +++ b/packages/galacean/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/loader/package.json b/packages/loader/package.json index d72b729ef..e0a87ffdd 100644 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-loader", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/loader/src/GLTFLoader.ts b/packages/loader/src/GLTFLoader.ts index cff590de7..c43a62d3e 100644 --- a/packages/loader/src/GLTFLoader.ts +++ b/packages/loader/src/GLTFLoader.ts @@ -8,9 +8,9 @@ import { resourceLoader, ResourceManager } from "@galacean/engine-core"; +import { getMeshoptDecoder, ready } from "./gltf/extensions/MeshoptDecoder"; import { GLTFResource } from "./gltf/GLTFResource"; import { GLTFParserContext } from "./gltf/parser"; -import { getMeshoptDecoder, ready } from "./gltf/extensions/MeshoptDecoder"; @resourceLoader(AssetType.GLTF, ["gltf", "glb"]) export class GLTFLoader extends Loader { @@ -44,10 +44,22 @@ export class GLTFLoader extends Loader { ...params }); - return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress) => { + return new AssetPromise((resolve, reject, setTaskCompleteProgress, setTaskDetailProgress, onTaskCancel) => { context._setTaskCompleteProgress = setTaskCompleteProgress; context._setTaskDetailProgress = setTaskDetailProgress; - context.parse().then(resolve).catch(reject); + onTaskCancel(() => { + const getPromises = context._getPromises; + for (let i = 0, n = getPromises.length; i < n; i++) { + getPromises[i].cancel(); + } + }); + context + .parse() + .then(resolve) + .catch((e) => { + glTFResource.destroy(); + reject(e); + }); }); } } diff --git a/packages/loader/src/gltf/GLTFResource.ts b/packages/loader/src/gltf/GLTFResource.ts index dacf56d57..26c776870 100644 --- a/packages/loader/src/gltf/GLTFResource.ts +++ b/packages/loader/src/gltf/GLTFResource.ts @@ -71,7 +71,8 @@ export class GLTFResource extends ReferResource { materials && this._disassociationSuperResource(materials); if (meshes) { for (let i = 0, n = meshes.length; i < n; i++) { - this._disassociationSuperResource(meshes[i]); + const meshArr = meshes[i]; + meshArr && this._disassociationSuperResource(meshArr); } } } @@ -79,7 +80,7 @@ export class GLTFResource extends ReferResource { private _disassociationSuperResource(resources: ReferResource[]): void { for (let i = 0, n = resources.length; i < n; i++) { // @ts-ignore - resources[i]._disassociationSuperResource(this); + resources[i]?._disassociationSuperResource(this); } } diff --git a/packages/loader/src/gltf/GLTFUtils.ts b/packages/loader/src/gltf/GLTFUtils.ts index 79607a097..35f648801 100644 --- a/packages/loader/src/gltf/GLTFUtils.ts +++ b/packages/loader/src/gltf/GLTFUtils.ts @@ -1,5 +1,7 @@ import { + AssetPromise, IndexFormat, + Logger, Texture2D, TextureFilterMode, TypedArray, @@ -132,7 +134,7 @@ export class GLTFUtils { context: GLTFParserContext, bufferViews: IBufferView[], accessor: IAccessor - ): Promise { + ): AssetPromise { const componentType = accessor.componentType; const TypedArray = GLTFUtils.getComponentType(componentType); const dataElementSize = GLTFUtils.getAccessorTypeSize(accessor.type); @@ -140,47 +142,51 @@ export class GLTFUtils { const elementStride = dataElementSize * dataElementBytes; const accessorCount = accessor.count; - let promise: Promise; + let promise: AssetPromise; if (accessor.bufferView !== undefined) { const bufferViewIndex = accessor.bufferView; const bufferView = bufferViews[bufferViewIndex]; + promise = context + .get(GLTFParserType.BufferView, accessor.bufferView) + .then((bufferViewData) => { + const bufferIndex = bufferView.buffer; + const bufferByteOffset = bufferViewData.byteOffset ?? 0; + const byteOffset = accessor.byteOffset ?? 0; - promise = context.get(GLTFParserType.BufferView, accessor.bufferView).then((bufferViewData) => { - const bufferIndex = bufferView.buffer; - const bufferByteOffset = bufferViewData.byteOffset ?? 0; - const byteOffset = accessor.byteOffset ?? 0; + const bufferStride = bufferView.byteStride; - const bufferStride = bufferView.byteStride; - - let bufferInfo: BufferInfo; - // According to the glTF official documentation only byteStride not undefined is allowed - if (bufferStride !== undefined && bufferStride !== elementStride) { - const bufferSlice = Math.floor(byteOffset / bufferStride); - const bufferCacheKey = bufferViewIndex + ":" + componentType + ":" + bufferSlice + ":" + accessorCount; - const accessorBufferCache = context.accessorBufferCache; - bufferInfo = accessorBufferCache[bufferCacheKey]; - if (!bufferInfo) { - const offset = bufferByteOffset + bufferSlice * bufferStride; - const count = accessorCount * (bufferStride / dataElementBytes); + let bufferInfo: BufferInfo; + // According to the glTF official documentation only byteStride not undefined is allowed + if (bufferStride !== undefined && bufferStride !== elementStride) { + const bufferSlice = Math.floor(byteOffset / bufferStride); + const bufferCacheKey = bufferViewIndex + ":" + componentType + ":" + bufferSlice + ":" + accessorCount; + const accessorBufferCache = context.accessorBufferCache; + bufferInfo = accessorBufferCache[bufferCacheKey]; + if (!bufferInfo) { + const offset = bufferByteOffset + bufferSlice * bufferStride; + const count = accessorCount * (bufferStride / dataElementBytes); + const data = new TypedArray(bufferViewData.buffer, offset, count); + accessorBufferCache[bufferCacheKey] = bufferInfo = new BufferInfo(data, true, bufferStride); + bufferInfo.restoreInfo = new BufferDataRestoreInfo( + new RestoreDataAccessor(bufferIndex, TypedArray, offset, count) + ); + } + } else { + const offset = bufferByteOffset + byteOffset; + const count = accessorCount * dataElementSize; const data = new TypedArray(bufferViewData.buffer, offset, count); - accessorBufferCache[bufferCacheKey] = bufferInfo = new BufferInfo(data, true, bufferStride); + bufferInfo = new BufferInfo(data, false, elementStride); bufferInfo.restoreInfo = new BufferDataRestoreInfo( new RestoreDataAccessor(bufferIndex, TypedArray, offset, count) ); } - } else { - const offset = bufferByteOffset + byteOffset; - const count = accessorCount * dataElementSize; - const data = new TypedArray(bufferViewData.buffer, offset, count); - bufferInfo = new BufferInfo(data, false, elementStride); - bufferInfo.restoreInfo = new BufferDataRestoreInfo( - new RestoreDataAccessor(bufferIndex, TypedArray, offset, count) - ); - } - return bufferInfo; - }); + return bufferInfo; + }) + .catch((e) => { + Logger.error("GLTFUtil getAccessorBuffer error", e); + }); } else { const count = accessorCount * dataElementSize; const data = new TypedArray(count); @@ -189,7 +195,7 @@ export class GLTFUtils { new RestoreDataAccessor(undefined, TypedArray, undefined, count) ); - promise = Promise.resolve(bufferInfo); + promise = AssetPromise.resolve(bufferInfo); } return accessor.sparse @@ -240,47 +246,50 @@ export class GLTFUtils { const indicesBufferView = bufferViews[indices.bufferView]; const valuesBufferView = bufferViews[values.bufferView]; - return Promise.all([ + return AssetPromise.all([ context.get(GLTFParserType.BufferView, indices.bufferView), context.get(GLTFParserType.BufferView, values.bufferView) - ]).then(([indicesUint8Array, valuesUin8Array]) => { - const indicesByteOffset = (indices.byteOffset ?? 0) + (indicesUint8Array.byteOffset ?? 0); - const indicesByteLength = indicesUint8Array.byteLength; - const valuesByteOffset = (values.byteOffset ?? 0) + (valuesUin8Array.byteOffset ?? 0); - const valuesByteLength = valuesUin8Array.byteLength; + ]) + .then(([indicesUint8Array, valuesUin8Array]) => { + const indicesByteOffset = (indices.byteOffset ?? 0) + (indicesUint8Array.byteOffset ?? 0); + const indicesByteLength = indicesUint8Array.byteLength; + const valuesByteOffset = (values.byteOffset ?? 0) + (valuesUin8Array.byteOffset ?? 0); + const valuesByteLength = valuesUin8Array.byteLength; - restoreInfo.typeSize = accessorTypeSize; - restoreInfo.sparseCount = count; + restoreInfo.typeSize = accessorTypeSize; + restoreInfo.sparseCount = count; - const IndexTypeArray = GLTFUtils.getComponentType(indices.componentType); + const IndexTypeArray = GLTFUtils.getComponentType(indices.componentType); + const indexLength = indicesByteLength / IndexTypeArray.BYTES_PER_ELEMENT; + const indicesArray = new IndexTypeArray(indicesUint8Array.buffer, indicesByteOffset, indexLength); + restoreInfo.sparseIndices = new RestoreDataAccessor( + indicesBufferView.buffer, + IndexTypeArray, + indicesByteOffset, + indexLength + ); - const indexLength = indicesByteLength / IndexTypeArray.BYTES_PER_ELEMENT; - const indicesArray = new IndexTypeArray(indicesUint8Array.buffer, indicesByteOffset, indexLength); - restoreInfo.sparseIndices = new RestoreDataAccessor( - indicesBufferView.buffer, - IndexTypeArray, - indicesByteOffset, - indexLength - ); + const valueLength = valuesByteLength / TypedArray.BYTES_PER_ELEMENT; + const valuesArray = new TypedArray(valuesUin8Array.buffer, valuesByteOffset, valueLength); + restoreInfo.sparseValues = new RestoreDataAccessor( + valuesBufferView.buffer, + TypedArray, + valuesByteOffset, + valueLength + ); - const valueLength = valuesByteLength / TypedArray.BYTES_PER_ELEMENT; - const valuesArray = new TypedArray(valuesUin8Array.buffer, valuesByteOffset, valueLength); - restoreInfo.sparseValues = new RestoreDataAccessor( - valuesBufferView.buffer, - TypedArray, - valuesByteOffset, - valueLength - ); - - for (let i = 0; i < count; i++) { - const replaceIndex = indicesArray[i]; - for (let j = 0; j < accessorTypeSize; j++) { - data[replaceIndex * accessorTypeSize + j] = valuesArray[i * accessorTypeSize + j]; + for (let i = 0; i < count; i++) { + const replaceIndex = indicesArray[i]; + for (let j = 0; j < accessorTypeSize; j++) { + data[replaceIndex * accessorTypeSize + j] = valuesArray[i * accessorTypeSize + j]; + } } - } - bufferInfo.data = data; - }); + bufferInfo.data = data; + }) + .catch((e) => { + Logger.error("GLTFUtil processingSparseData error", e); + }); } static getIndexFormat(type: AccessorComponentType): IndexFormat { diff --git a/packages/loader/src/gltf/extensions/EXT_meshopt_compression.ts b/packages/loader/src/gltf/extensions/EXT_meshopt_compression.ts index 06533440d..2d3ba90ce 100644 --- a/packages/loader/src/gltf/extensions/EXT_meshopt_compression.ts +++ b/packages/loader/src/gltf/extensions/EXT_meshopt_compression.ts @@ -1,3 +1,4 @@ +import { AssetPromise, Logger } from "@galacean/engine-core"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; @@ -6,18 +7,23 @@ import { getMeshoptDecoder } from "./MeshoptDecoder"; @registerGLTFExtension("EXT_meshopt_compression", GLTFExtensionMode.CreateAndParse) class EXT_meshopt_compression extends GLTFExtensionParser { - override createAndParse(context: GLTFParserContext, schema: IEXTMeshoptCompressionSchema): Promise { - return context.get(GLTFParserType.Buffer, schema.buffer).then((arrayBuffer) => { - return getMeshoptDecoder().then((decoder) => - decoder.decodeGltfBuffer( - schema.count, - schema.byteStride, - new Uint8Array(arrayBuffer, schema.byteOffset, schema.byteLength), - schema.mode, - schema.filter - ) - ); - }); + override createAndParse(context: GLTFParserContext, schema: IEXTMeshoptCompressionSchema): AssetPromise { + return context + .get(GLTFParserType.Buffer, schema.buffer) + .then((arrayBuffer) => { + return getMeshoptDecoder().then((decoder) => + decoder.decodeGltfBuffer( + schema.count, + schema.byteStride, + new Uint8Array(arrayBuffer, schema.byteOffset, schema.byteLength), + schema.mode, + schema.filter + ) + ); + }) + .catch((e) => { + Logger.error("EXT_meshopt_compression: buffer error", e); + }); } } diff --git a/packages/loader/src/gltf/extensions/EXT_texture_webp.ts b/packages/loader/src/gltf/extensions/EXT_texture_webp.ts index fb2d39ad1..ff8a33578 100644 --- a/packages/loader/src/gltf/extensions/EXT_texture_webp.ts +++ b/packages/loader/src/gltf/extensions/EXT_texture_webp.ts @@ -1,4 +1,4 @@ -import { SystemInfo, Texture2D } from "@galacean/engine-core"; +import { AssetPromise, SystemInfo, Texture2D } from "@galacean/engine-core"; import type { ITexture } from "../GLTFSchema"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext } from "../parser/GLTFParserContext"; @@ -26,13 +26,13 @@ class EXT_texture_webp extends GLTFExtensionParser { } } - override async createAndParse( + override createAndParse( context: GLTFParserContext, schema: EXTWebPSchema, textureInfo: ITexture, textureIndex: number, isSRGBColorSpace: boolean - ): Promise { + ): AssetPromise { const webPIndex = schema.source; const { sampler, source: fallbackIndex = 0, name: textureName } = textureInfo; const texture = GLTFTextureParser._parseTexture( diff --git a/packages/loader/src/gltf/extensions/GALACEAN_materials_remap.ts b/packages/loader/src/gltf/extensions/GALACEAN_materials_remap.ts index 036ebccbf..c2b338eb3 100644 --- a/packages/loader/src/gltf/extensions/GALACEAN_materials_remap.ts +++ b/packages/loader/src/gltf/extensions/GALACEAN_materials_remap.ts @@ -1,4 +1,4 @@ -import { Material } from "@galacean/engine-core"; +import { AssetPromise, Material } from "@galacean/engine-core"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext } from "../parser/GLTFParserContext"; import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; @@ -6,7 +6,7 @@ import { IGalaceanMaterialRemap } from "./GLTFExtensionSchema"; @registerGLTFExtension("GALACEAN_materials_remap", GLTFExtensionMode.CreateAndParse) class GALACEAN_materials_remap extends GLTFExtensionParser { - override createAndParse(context: GLTFParserContext, schema: IGalaceanMaterialRemap): Promise { + override createAndParse(context: GLTFParserContext, schema: IGalaceanMaterialRemap): AssetPromise { const { engine } = context.glTFResource; // @ts-ignore const promise = engine.resourceManager.getResourceByRef(schema); diff --git a/packages/loader/src/gltf/extensions/GLTFExtensionParser.ts b/packages/loader/src/gltf/extensions/GLTFExtensionParser.ts index 03315faaa..0ca0a5223 100644 --- a/packages/loader/src/gltf/extensions/GLTFExtensionParser.ts +++ b/packages/loader/src/gltf/extensions/GLTFExtensionParser.ts @@ -1,4 +1,4 @@ -import { EngineObject } from "@galacean/engine-core"; +import { AssetPromise, EngineObject } from "@galacean/engine-core"; import { GLTFExtensionOwnerSchema } from "../GLTFSchema"; import { GLTFParserContext } from "../parser/GLTFParserContext"; import { GLTFExtensionSchema } from "./GLTFExtensionSchema"; @@ -26,7 +26,7 @@ export abstract class GLTFExtensionParser { extensionSchema: GLTFExtensionSchema, extensionOwnerSchema: GLTFExtensionOwnerSchema, ...extra - ): EngineObject | Promise { + ): EngineObject | AssetPromise { throw "Not implemented."; } @@ -44,7 +44,7 @@ export abstract class GLTFExtensionParser { extensionSchema: GLTFExtensionSchema, extensionOwnerSchema: GLTFExtensionOwnerSchema, ...extra - ): void | Promise { + ): void | AssetPromise { throw "Not implemented."; } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_anisotropy.ts b/packages/loader/src/gltf/extensions/KHR_materials_anisotropy.ts index 9f1f90bcf..4a06d2ae0 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_anisotropy.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_anisotropy.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; @@ -16,9 +16,14 @@ class KHR_materials_anisotropy extends GLTFExtensionParser { if (anisotropyTexture) { GLTFMaterialParser._checkOtherTextureTransform(anisotropyTexture, "Anisotropy texture"); - context.get(GLTFParserType.Texture, anisotropyTexture.index).then((texture) => { - material.anisotropyTexture = texture; - }); + context + .get(GLTFParserType.Texture, anisotropyTexture.index) + .then((texture) => { + material.anisotropyTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_anisotropy: anisotropy texture error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts b/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts index 033df8d2a..86b191dce 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_clearcoat.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; @@ -22,23 +22,38 @@ class KHR_materials_clearcoat extends GLTFExtensionParser { if (clearcoatTexture) { GLTFMaterialParser._checkOtherTextureTransform(clearcoatTexture, "Clear coat"); - context.get(GLTFParserType.Texture, clearcoatTexture.index).then((texture) => { - material.clearCoatTexture = texture; - }); + context + .get(GLTFParserType.Texture, clearcoatTexture.index) + .then((texture) => { + material.clearCoatTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_clearcoat: clearcoat texture error", e); + }); } if (clearcoatRoughnessTexture) { GLTFMaterialParser._checkOtherTextureTransform(clearcoatRoughnessTexture, "Clear coat roughness"); - context.get(GLTFParserType.Texture, clearcoatRoughnessTexture.index).then((texture) => { - material.clearCoatRoughnessTexture = texture; - }); + context + .get(GLTFParserType.Texture, clearcoatRoughnessTexture.index) + .then((texture) => { + material.clearCoatRoughnessTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_clearcoat: clearcoat roughness texture error", e); + }); } if (clearcoatNormalTexture) { GLTFMaterialParser._checkOtherTextureTransform(clearcoatNormalTexture, "Clear coat normal"); - context.get(GLTFParserType.Texture, clearcoatNormalTexture.index).then((texture) => { - material.clearCoatNormalTexture = texture; - }); + context + .get(GLTFParserType.Texture, clearcoatNormalTexture.index) + .then((texture) => { + material.clearCoatNormalTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_clearcoat: clearcoat normal texture error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_iridescence.ts b/packages/loader/src/gltf/extensions/KHR_materials_iridescence.ts index 6b7b1cb95..5430bc0db 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_iridescence.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_iridescence.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; @@ -24,16 +24,26 @@ class KHR_materials_iridescence extends GLTFExtensionParser { if (iridescenceTexture) { GLTFMaterialParser._checkOtherTextureTransform(iridescenceTexture, "Iridescence texture"); - context.get(GLTFParserType.Texture, iridescenceTexture.index).then((texture) => { - material.iridescenceTexture = texture; - }); + context + .get(GLTFParserType.Texture, iridescenceTexture.index) + .then((texture) => { + material.iridescenceTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_iridescence: iridescence texture error", e); + }); } if (iridescenceThicknessTexture) { GLTFMaterialParser._checkOtherTextureTransform(iridescenceThicknessTexture, "IridescenceThickness texture"); - context.get(GLTFParserType.Texture, iridescenceThicknessTexture.index).then((texture) => { - material.iridescenceThicknessTexture = texture; - }); + context + .get(GLTFParserType.Texture, iridescenceThicknessTexture.index) + .then((texture) => { + material.iridescenceThicknessTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_iridescence: iridescence thickness error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts b/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts index 1502ed2e6..3b8194b8d 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_pbrSpecularGlossiness.ts @@ -1,4 +1,4 @@ -import { PBRSpecularMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRSpecularMaterial, Texture2D } from "@galacean/engine-core"; import { Color } from "@galacean/engine-math"; import type { IMaterial } from "../GLTFSchema"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; @@ -28,10 +28,15 @@ class KHR_materials_pbrSpecularGlossiness extends GLTFExtensionParser { } if (diffuseTexture) { - context.get(GLTFParserType.Texture, diffuseTexture.index).then((texture) => { - material.baseTexture = texture; - GLTFParser.executeExtensionsAdditiveAndParse(diffuseTexture.extensions, context, material, diffuseTexture); - }); + context + .get(GLTFParserType.Texture, diffuseTexture.index) + .then((texture) => { + material.baseTexture = texture; + GLTFParser.executeExtensionsAdditiveAndParse(diffuseTexture.extensions, context, material, diffuseTexture); + }) + .catch((e) => { + Logger.error("KHR_materials_pbrSpecularGlossiness: diffuse texture error", e); + }); } if (specularFactor) { @@ -49,9 +54,14 @@ class KHR_materials_pbrSpecularGlossiness extends GLTFExtensionParser { if (specularGlossinessTexture) { GLTFMaterialParser._checkOtherTextureTransform(specularGlossinessTexture, "Specular glossiness"); - context.get(GLTFParserType.Texture, specularGlossinessTexture.index).then((texture) => { - material.specularGlossinessTexture = texture; - }); + context + .get(GLTFParserType.Texture, specularGlossinessTexture.index) + .then((texture) => { + material.specularGlossinessTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_pbrSpecularGlossiness: specular glossiness texture error", e); + }); } material.name = ownerSchema.name; diff --git a/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts b/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts index 46f4bb6b0..0b4232d73 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_sheen.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { Color } from "@galacean/engine-math"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; @@ -25,17 +25,27 @@ class KHR_materials_sheen extends GLTFExtensionParser { if (sheenColorTexture) { GLTFMaterialParser._checkOtherTextureTransform(sheenColorTexture, "Sheen texture"); - context.get(GLTFParserType.Texture, sheenColorTexture.index).then((texture) => { - material.sheenColorTexture = texture; - }); + context + .get(GLTFParserType.Texture, sheenColorTexture.index) + .then((texture) => { + material.sheenColorTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_sheen: sheenColorTexture error", e); + }); } if (sheenRoughnessTexture) { GLTFMaterialParser._checkOtherTextureTransform(sheenRoughnessTexture, "SheenRoughness texture"); - context.get(GLTFParserType.Texture, sheenRoughnessTexture.index).then((texture) => { - material.sheenRoughnessTexture = texture; - }); + context + .get(GLTFParserType.Texture, sheenRoughnessTexture.index) + .then((texture) => { + material.sheenRoughnessTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_sheen: sheenRoughnessTexture error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts b/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts index a1eeec850..e5ee31141 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_transmission.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; @@ -13,9 +13,14 @@ class KHR_materials_transmission extends GLTFExtensionParser { if (transmissionTexture) { GLTFMaterialParser._checkOtherTextureTransform(transmissionTexture, "Transmission texture"); - context.get(GLTFParserType.Texture, transmissionTexture.index).then((texture) => { - material.transmissionTexture = texture; - }); + context + .get(GLTFParserType.Texture, transmissionTexture.index) + .then((texture) => { + material.transmissionTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_transmission: transmission texture error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_variants.ts b/packages/loader/src/gltf/extensions/KHR_materials_variants.ts index f8e334f44..2e3e35376 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_variants.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_variants.ts @@ -1,4 +1,4 @@ -import { Material, Renderer } from "@galacean/engine-core"; +import { Logger, Material, Renderer } from "@galacean/engine-core"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; @@ -29,13 +29,18 @@ class KHR_materials_variants extends GLTFExtensionParser { for (let i = 0; i < mappings.length; i++) { const { material: materialIndex, variants } = mappings[i]; - context.get(GLTFParserType.Material, materialIndex).then((material) => { - extensionData.push({ - renderer, - material, - variants: variants.map((index) => variantNames[index].name) + context + .get(GLTFParserType.Material, materialIndex) + .then((material) => { + extensionData.push({ + renderer, + material, + variants: variants.map((index) => variantNames[index].name) + }); + }) + .catch((e) => { + Logger.error("KHR_materials_variants: material error", e); }); - }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_materials_volume.ts b/packages/loader/src/gltf/extensions/KHR_materials_volume.ts index a44c52b18..690f9eb0e 100644 --- a/packages/loader/src/gltf/extensions/KHR_materials_volume.ts +++ b/packages/loader/src/gltf/extensions/KHR_materials_volume.ts @@ -1,4 +1,4 @@ -import { PBRMaterial, Texture2D } from "@galacean/engine-core"; +import { Logger, PBRMaterial, Texture2D } from "@galacean/engine-core"; import { Color } from "@galacean/engine-math"; import { GLTFMaterialParser } from "../parser/GLTFMaterialParser"; import { registerGLTFExtension } from "../parser/GLTFParser"; @@ -25,9 +25,14 @@ class KHR_materials_volume extends GLTFExtensionParser { if (thicknessTexture) { GLTFMaterialParser._checkOtherTextureTransform(thicknessTexture, "Thickness texture"); - context.get(GLTFParserType.Texture, thicknessTexture.index).then((texture) => { - material.thicknessTexture = texture; - }); + context + .get(GLTFParserType.Texture, thicknessTexture.index) + .then((texture) => { + material.thicknessTexture = texture; + }) + .catch((e) => { + Logger.error("KHR_materials_volume: thickness texture error", e); + }); } } } diff --git a/packages/loader/src/gltf/extensions/KHR_texture_basisu.ts b/packages/loader/src/gltf/extensions/KHR_texture_basisu.ts index 3679920f8..ca7673c18 100644 --- a/packages/loader/src/gltf/extensions/KHR_texture_basisu.ts +++ b/packages/loader/src/gltf/extensions/KHR_texture_basisu.ts @@ -1,11 +1,11 @@ -import { AssetType, Texture2D, Utils } from "@galacean/engine-core"; +import { AssetPromise, AssetType, Logger, Texture2D, Utils } from "@galacean/engine-core"; +import { BufferTextureRestoreInfo } from "../../GLTFContentRestorer"; +import { KTX2Loader } from "../../ktx2/KTX2Loader"; import type { ITexture } from "../GLTFSchema"; +import { GLTFUtils } from "../GLTFUtils"; import { registerGLTFExtension } from "../parser/GLTFParser"; import { GLTFParserContext, GLTFParserType } from "../parser/GLTFParserContext"; import { GLTFExtensionMode, GLTFExtensionParser } from "./GLTFExtensionParser"; -import { GLTFUtils } from "../GLTFUtils"; -import { BufferTextureRestoreInfo } from "../../GLTFContentRestorer"; -import { KTX2Loader } from "../../ktx2/KTX2Loader"; interface KHRBasisSchema { source: number; @@ -13,11 +13,11 @@ interface KHRBasisSchema { @registerGLTFExtension("KHR_texture_basisu", GLTFExtensionMode.CreateAndParse) class KHR_texture_basisu extends GLTFExtensionParser { - override async createAndParse( + override createAndParse( context: GLTFParserContext, schema: KHRBasisSchema, textureInfo: ITexture - ): Promise { + ): AssetPromise { const { glTF, glTFResource } = context; const { engine, url } = glTFResource; @@ -48,23 +48,28 @@ class KHR_texture_basisu extends GLTFExtensionParser { } else { const bufferView = glTF.bufferViews[bufferViewIndex]; - return context.get(GLTFParserType.Buffer, bufferView.buffer).then((buffer) => { - const imageBuffer = new Uint8Array(buffer, bufferView.byteOffset, bufferView.byteLength); + return context + .get(GLTFParserType.Buffer, bufferView.buffer) + .then((buffer) => { + const imageBuffer = new Uint8Array(buffer, bufferView.byteOffset, bufferView.byteLength); - return KTX2Loader._parseBuffer(imageBuffer, engine) - .then(({ ktx2Container, engine, result, targetFormat, params }) => - KTX2Loader._createTextureByBuffer(engine, ktx2Container.isSRGB, result, targetFormat, params) - ) - .then((texture: Texture2D) => { - texture.name = textureName || imageName || `texture_${bufferViewIndex}`; - if (sampler !== undefined) { - GLTFUtils.parseSampler(texture, samplerInfo); - } - const bufferTextureRestoreInfo = new BufferTextureRestoreInfo(texture, bufferView, mimeType); - context.contentRestorer.bufferTextures.push(bufferTextureRestoreInfo); - return texture; - }); - }); + return KTX2Loader._parseBuffer(imageBuffer, engine) + .then(({ ktx2Container, engine, result, targetFormat, params }) => + KTX2Loader._createTextureByBuffer(engine, ktx2Container.isSRGB, result, targetFormat, params) + ) + .then((texture: Texture2D) => { + texture.name = textureName || imageName || `texture_${bufferViewIndex}`; + if (sampler !== undefined) { + GLTFUtils.parseSampler(texture, samplerInfo); + } + const bufferTextureRestoreInfo = new BufferTextureRestoreInfo(texture, bufferView, mimeType); + context.contentRestorer.bufferTextures.push(bufferTextureRestoreInfo); + return texture; + }); + }) + .catch((e) => { + Logger.error("KHR_texture_basisu: buffer error", e); + }); } } } diff --git a/packages/loader/src/gltf/parser/GLTFAnimationParser.ts b/packages/loader/src/gltf/parser/GLTFAnimationParser.ts index 3b41d0f83..0bb6cf37e 100644 --- a/packages/loader/src/gltf/parser/GLTFAnimationParser.ts +++ b/packages/loader/src/gltf/parser/GLTFAnimationParser.ts @@ -3,6 +3,7 @@ import { AnimationFloatArrayCurve, AnimationQuaternionCurve, AnimationVector3Curve, + AssetPromise, Component, Entity, InterpolationType, @@ -32,7 +33,7 @@ export class GLTFAnimationParser extends GLTFParser { context: GLTFParserContext, animationClip: AnimationClip, animationInfo: IAnimation - ): Promise { + ): AssetPromise { const { glTF } = context; const { accessors, bufferViews } = glTF; const { channels, samplers } = animationInfo; @@ -41,7 +42,7 @@ export class GLTFAnimationParser extends GLTFParser { const entities = context.get(GLTFParserType.Entity); let duration = -1; - let promises = new Array>(); + let promises = new Array>(); // parse samplers for (let j = 0, m = len; j < m; j++) { @@ -49,7 +50,7 @@ export class GLTFAnimationParser extends GLTFParser { const inputAccessor = accessors[glTFSampler.input]; const outputAccessor = accessors[glTFSampler.output]; - const promise = Promise.all([ + const promise = AssetPromise.all([ GLTFUtils.getAccessorBuffer(context, bufferViews, inputAccessor), GLTFUtils.getAccessorBuffer(context, bufferViews, outputAccessor) ]).then((bufferInfos) => { @@ -98,7 +99,7 @@ export class GLTFAnimationParser extends GLTFParser { promises.push(context.get(GLTFParserType.Scene)); - return Promise.all(promises).then(() => { + return AssetPromise.all(promises).then(() => { for (let j = 0, m = channels.length; j < m; j++) { const glTFChannel = channels[j]; const { target } = glTFChannel; @@ -227,16 +228,16 @@ export class GLTFAnimationParser extends GLTFParser { } } - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const animationInfo = context.glTF.animations[index]; const { name = `AnimationClip${index}` } = animationInfo; const animationClipPromise = - | AnimationClip>( + | AnimationClip>( GLTFParser.executeExtensionsCreateAndParse(animationInfo.extensions, context, animationInfo) ) || GLTFAnimationParser._parseStandardProperty(context, new AnimationClip(name), animationInfo); - return Promise.resolve(animationClipPromise).then((animationClip) => { + return AssetPromise.resolve(animationClipPromise).then((animationClip) => { GLTFParser.executeExtensionsAdditiveAndParse(animationInfo.extensions, context, animationClip, animationInfo); return animationClip; }); diff --git a/packages/loader/src/gltf/parser/GLTFAnimatorControllerParser.ts b/packages/loader/src/gltf/parser/GLTFAnimatorControllerParser.ts index 73be27718..c37921e29 100644 --- a/packages/loader/src/gltf/parser/GLTFAnimatorControllerParser.ts +++ b/packages/loader/src/gltf/parser/GLTFAnimatorControllerParser.ts @@ -2,22 +2,29 @@ import { AnimationClip, AnimatorController, AnimatorControllerLayer, - AnimatorStateMachine + AnimatorStateMachine, + AssetPromise, + Logger } from "@galacean/engine-core"; import { GLTFParser } from "./GLTFParser"; import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFParserContext"; @registerGLTFParser(GLTFParserType.AnimatorController) export class GLTFAnimatorControllerParser extends GLTFParser { - parse(context: GLTFParserContext): Promise { + parse(context: GLTFParserContext): AssetPromise { if (!context.needAnimatorController) { - return Promise.resolve(null); + return AssetPromise.resolve(null); } - return context.get(GLTFParserType.Animation).then((animations) => { - const animatorController = this._createAnimatorController(context, animations); - return Promise.resolve(animatorController); - }); + return context + .get(GLTFParserType.Animation) + .then((animations) => { + const animatorController = this._createAnimatorController(context, animations); + return AssetPromise.resolve(animatorController); + }) + .catch((e) => { + Logger.error("GLTFAnimatorControllerParser: animator controller error", e); + }); } private _createAnimatorController(context: GLTFParserContext, animations: AnimationClip[]): AnimatorController { diff --git a/packages/loader/src/gltf/parser/GLTFBufferParser.ts b/packages/loader/src/gltf/parser/GLTFBufferParser.ts index e99039d2c..7a23b2275 100644 --- a/packages/loader/src/gltf/parser/GLTFBufferParser.ts +++ b/packages/loader/src/gltf/parser/GLTFBufferParser.ts @@ -1,4 +1,4 @@ -import { RequestConfig, Utils } from "@galacean/engine-core"; +import { AssetPromise, RequestConfig, Utils } from "@galacean/engine-core"; import { BufferRequestInfo } from "../../GLTFContentRestorer"; import type { IBuffer } from "../GLTFSchema"; import { GLTFParser } from "./GLTFParser"; @@ -6,13 +6,15 @@ import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFPar @registerGLTFParser(GLTFParserType.Buffer) export class GLTFBufferParser extends GLTFParser { - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const buffers = context.glTF.buffers; - return context.buffers ? Promise.resolve(context.buffers[index]) : this._parseSingleBuffer(context, buffers[index]); + return context.buffers + ? AssetPromise.resolve(context.buffers[index]) + : this._parseSingleBuffer(context, buffers[index]); } - private _parseSingleBuffer(context: GLTFParserContext, bufferInfo: IBuffer): Promise { + private _parseSingleBuffer(context: GLTFParserContext, bufferInfo: IBuffer): AssetPromise { const { glTFResource, contentRestorer, resourceManager } = context; const url = glTFResource.url; // @ts-ignore diff --git a/packages/loader/src/gltf/parser/GLTFBufferViewParser.ts b/packages/loader/src/gltf/parser/GLTFBufferViewParser.ts index a85d5b735..771b1ac69 100644 --- a/packages/loader/src/gltf/parser/GLTFBufferViewParser.ts +++ b/packages/loader/src/gltf/parser/GLTFBufferViewParser.ts @@ -1,15 +1,19 @@ -import { registerGLTFParser, GLTFParserType, GLTFParserContext } from "./GLTFParserContext"; +import { AssetPromise, Logger } from "@galacean/engine-core"; import { GLTFParser } from "./GLTFParser"; +import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFParserContext"; @registerGLTFParser(GLTFParserType.BufferView) export class GLTFBufferViewParser extends GLTFParser { - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const bufferView = context.glTF.bufferViews[index]; const { extensions, byteOffset = 0, byteLength, buffer: bufferIndex } = bufferView; return extensions - ? >GLTFParser.executeExtensionsCreateAndParse(extensions, context, bufferView) + ? >GLTFParser.executeExtensionsCreateAndParse(extensions, context, bufferView) : context .get(GLTFParserType.Buffer, bufferIndex) - .then((buffer) => new Uint8Array(buffer, byteOffset, byteLength)); + .then((buffer) => new Uint8Array(buffer, byteOffset, byteLength)) + .catch((e) => { + Logger.error("GLTFBufferViewParser: buffer error", e); + }); } } diff --git a/packages/loader/src/gltf/parser/GLTFMaterialParser.ts b/packages/loader/src/gltf/parser/GLTFMaterialParser.ts index 40bdf3afc..49197fc88 100644 --- a/packages/loader/src/gltf/parser/GLTFMaterialParser.ts +++ b/packages/loader/src/gltf/parser/GLTFMaterialParser.ts @@ -1,6 +1,5 @@ import { - BlinnPhongMaterial, - Engine, + AssetPromise, Logger, Material, PBRMaterial, @@ -17,12 +16,6 @@ import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFPar @registerGLTFParser(GLTFParserType.Material) export class GLTFMaterialParser extends GLTFParser { - /** @internal */ - static _getDefaultMaterial(engine: Engine): BlinnPhongMaterial { - return (GLTFMaterialParser._defaultMaterial ||= new BlinnPhongMaterial(engine)); - } - private static _defaultMaterial: BlinnPhongMaterial; - /** * @internal */ @@ -64,15 +57,20 @@ export class GLTFMaterialParser extends GLTFParser { ); } if (baseColorTexture) { - context.get(GLTFParserType.Texture, baseColorTexture.index).then((texture) => { - material.baseTexture = texture; - GLTFParser.executeExtensionsAdditiveAndParse( - baseColorTexture.extensions, - context, - material, - baseColorTexture - ); - }); + context + .get(GLTFParserType.Texture, baseColorTexture.index) + .then((texture) => { + material.baseTexture = texture; + GLTFParser.executeExtensionsAdditiveAndParse( + baseColorTexture.extensions, + context, + material, + baseColorTexture + ); + }) + .catch((e) => { + Logger.error("GLTFMaterialParser: baseColorTexture error", e); + }); } if (material.constructor === PBRMaterial) { @@ -80,10 +78,14 @@ export class GLTFMaterialParser extends GLTFParser { material.roughness = roughnessFactor ?? 1; if (metallicRoughnessTexture) { GLTFMaterialParser._checkOtherTextureTransform(metallicRoughnessTexture, "Roughness metallic"); - - context.get(GLTFParserType.Texture, metallicRoughnessTexture.index).then((texture) => { - material.roughnessMetallicTexture = texture; - }); + context + .get(GLTFParserType.Texture, metallicRoughnessTexture.index) + .then((texture) => { + material.roughnessMetallicTexture = texture; + }) + .catch((e) => { + Logger.error("GLTFMaterialParser: metallicRoughnessTexture error", e); + }); } } } @@ -91,10 +93,14 @@ export class GLTFMaterialParser extends GLTFParser { if (material.constructor === PBRMaterial || material.constructor === PBRSpecularMaterial) { if (emissiveTexture) { GLTFMaterialParser._checkOtherTextureTransform(emissiveTexture, "Emissive"); - - context.get(GLTFParserType.Texture, emissiveTexture.index).then((texture) => { - material.emissiveTexture = texture; - }); + context + .get(GLTFParserType.Texture, emissiveTexture.index) + .then((texture) => { + material.emissiveTexture = texture; + }) + .catch((e) => { + Logger.error("GLTFMaterialParser: emissiveTexture error", e); + }); } if (emissiveFactor) { @@ -109,9 +115,14 @@ export class GLTFMaterialParser extends GLTFParser { const { index, scale } = normalTexture; GLTFMaterialParser._checkOtherTextureTransform(normalTexture, "Normal"); - context.get(GLTFParserType.Texture, index).then((texture) => { - material.normalTexture = texture; - }); + context + .get(GLTFParserType.Texture, index) + .then((texture) => { + material.normalTexture = texture; + }) + .catch((e) => { + Logger.error("GLTFMaterialParser: emissiveTexture error", e); + }); if (scale !== undefined) { material.normalTextureIntensity = scale; @@ -122,9 +133,14 @@ export class GLTFMaterialParser extends GLTFParser { const { index, strength, texCoord } = occlusionTexture; GLTFMaterialParser._checkOtherTextureTransform(occlusionTexture, "Occlusion"); - context.get(GLTFParserType.Texture, index).then((texture) => { - material.occlusionTexture = texture; - }); + context + .get(GLTFParserType.Texture, index) + .then((texture) => { + material.occlusionTexture = texture; + }) + .catch((e) => { + Logger.error("GLTFMaterialParser: occlusionTexture error", e); + }); if (strength !== undefined) { material.occlusionTextureIntensity = strength; @@ -156,7 +172,7 @@ export class GLTFMaterialParser extends GLTFParser { } } - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const materialInfo = context.glTF.materials[index]; const glTFResource = context.glTFResource; const engine = glTFResource.engine; @@ -171,8 +187,9 @@ export class GLTFMaterialParser extends GLTFParser { GLTFMaterialParser._parseStandardProperty(context, material as PBRMaterial, materialInfo); } - return Promise.resolve(material).then((material) => { - material ||= GLTFMaterialParser._getDefaultMaterial(engine); + return AssetPromise.resolve(material).then((material) => { + // @ts-ignore + material ||= engine._basicResources._getBlinnPhongMaterial(); GLTFParser.executeExtensionsAdditiveAndParse(materialInfo.extensions, context, material, materialInfo); // @ts-ignore material._associationSuperResource(glTFResource); diff --git a/packages/loader/src/gltf/parser/GLTFMeshParser.ts b/packages/loader/src/gltf/parser/GLTFMeshParser.ts index 61f252560..ecd037898 100644 --- a/packages/loader/src/gltf/parser/GLTFMeshParser.ts +++ b/packages/loader/src/gltf/parser/GLTFMeshParser.ts @@ -1,4 +1,12 @@ -import { BlendShape, Buffer, BufferBindFlag, BufferUsage, ModelMesh, VertexElement } from "@galacean/engine-core"; +import { + AssetPromise, + BlendShape, + Buffer, + BufferBindFlag, + BufferUsage, + ModelMesh, + VertexElement +} from "@galacean/engine-core"; import { Vector3 } from "@galacean/engine-math"; import { BlendShapeDataRestoreInfo, @@ -26,7 +34,7 @@ export class GLTFMeshParser extends GLTFParser { gltfPrimitive: IMeshPrimitive, gltf: IGLTF, keepMeshData: boolean - ): Promise { + ): AssetPromise { const { accessors } = gltf; const { attributes, targets, indices, mode } = gltfPrimitive; const engine = mesh.engine; @@ -35,7 +43,7 @@ export class GLTFMeshParser extends GLTFParser { let vertexCount: number; let bufferBindIndex = 0; - const promises = new Array>(); + const promises = new Array>(); for (const attribute in attributes) { const accessor = accessors[attributes[attribute]]; const promise = GLTFUtils.getAccessorBuffer(context, gltf.bufferViews, accessor).then((accessorBuffer) => { @@ -121,7 +129,7 @@ export class GLTFMeshParser extends GLTFParser { promises.push(promise); } - return Promise.all(promises).then(() => { + return AssetPromise.all(promises).then(() => { mesh.setVertexElements(vertexElements); // Indices @@ -155,7 +163,7 @@ export class GLTFMeshParser extends GLTFParser { context: GLTFParserContext, glTF: IGLTF, accessor: IAccessor - ): Promise<{ vertices: Vector3[]; restoreInfo: BlendShapeDataRestoreInfo }> { + ): AssetPromise<{ vertices: Vector3[]; restoreInfo: BlendShapeDataRestoreInfo }> { return GLTFUtils.getAccessorBuffer(context, glTF.bufferViews, accessor).then((bufferInfo) => { const buffer = bufferInfo.data; const byteOffset = bufferInfo.interleaved ? (accessor.byteOffset ?? 0) % bufferInfo.stride : 0; @@ -186,11 +194,11 @@ export class GLTFMeshParser extends GLTFParser { glTFTargets: { [name: string]: number; }[] - ): Promise { + ): AssetPromise { const glTF = context.glTF; const accessors = glTF.accessors; const blendShapeNames = glTFMesh.extras ? glTFMesh.extras.targetNames : null; - let promises = new Array>(); + let promises = new Array>(); const blendShapeCount = glTFTargets.length; const blendShapeCollection = new Array(blendShapeCount); @@ -206,13 +214,12 @@ export class GLTFMeshParser extends GLTFParser { const hasNormal = normalTarget !== undefined; const hasTangent = tangentTarget !== undefined; - const promise = Promise.all([ + const promise = AssetPromise.all([ this._getBlendShapeData(context, glTF, accessors[targets["POSITION"]]), hasNormal ? this._getBlendShapeData(context, glTF, accessors[normalTarget]) : null, hasTangent ? this._getBlendShapeData(context, glTF, accessors[tangentTarget]) : null ]).then((vertices) => { const [positionData, normalData, tangentData] = vertices; - const blendShape = new BlendShape(name); blendShape.addFrame( 1.0, @@ -232,7 +239,7 @@ export class GLTFMeshParser extends GLTFParser { promises.push(promise); } - return Promise.all(promises).then(() => { + return AssetPromise.all(promises).then(() => { for (const blendShape of blendShapeCollection) { mesh.addBlendShape(blendShape.blendShape); meshRestoreInfo.blendShapes.push(blendShape.restoreInfo); @@ -240,18 +247,17 @@ export class GLTFMeshParser extends GLTFParser { }); } - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const meshInfo = context.glTF.meshes[index]; const { glTF, glTFResource } = context; const engine = glTFResource.engine; - const primitivePromises = new Array>(); + const primitivePromises = new Array>(); for (let i = 0, length = meshInfo.primitives.length; i < length; i++) { const gltfPrimitive = meshInfo.primitives[i]; - - primitivePromises[i] = new Promise((resolve) => { - const mesh = >( + primitivePromises[i] = new AssetPromise((resolve, reject) => { + const mesh = >( GLTFParser.executeExtensionsCreateAndParse(gltfPrimitive.extensions, context, gltfPrimitive, meshInfo) ); @@ -284,12 +290,12 @@ export class GLTFMeshParser extends GLTFParser { gltfPrimitive, glTF, context.params.keepMeshData - ).then(resolve); + ).then(resolve, reject); } }); } - return Promise.all(primitivePromises); + return AssetPromise.all(primitivePromises); } } diff --git a/packages/loader/src/gltf/parser/GLTFParser.ts b/packages/loader/src/gltf/parser/GLTFParser.ts index c8ff1992b..f95935ea5 100644 --- a/packages/loader/src/gltf/parser/GLTFParser.ts +++ b/packages/loader/src/gltf/parser/GLTFParser.ts @@ -1,4 +1,4 @@ -import { AnimationClip, EngineObject, Material, Mesh } from "@galacean/engine-core"; +import { AssetPromise, EngineObject } from "@galacean/engine-core"; import type { GLTFExtensionOwnerSchema } from "../GLTFSchema"; import { GLTFExtensionMode, GLTFExtensionParser } from "../extensions/GLTFExtensionParser"; import { GLTFExtensionSchema } from "../extensions/GLTFExtensionSchema"; @@ -23,15 +23,15 @@ export abstract class GLTFParser { context: GLTFParserContext, ownerSchema: GLTFExtensionOwnerSchema, ...extra - ): EngineObject | void | Promise { - let resource: EngineObject | Promise = null; + ): EngineObject | void | AssetPromise { + let resource: EngineObject | AssetPromise = null; const extensionArray = Object.keys(extensions); for (let i = extensionArray.length - 1; i >= 0; --i) { const extensionName = extensionArray[i]; const extensionSchema = extensions[extensionName]; - resource = >( + resource = >( GLTFParser._createAndParse(extensionName, context, extensionSchema, ownerSchema, ...extra) ); if (resource) { @@ -107,7 +107,7 @@ export abstract class GLTFParser { extensionSchema: GLTFExtensionSchema, ownerSchema: GLTFExtensionOwnerSchema, ...extra - ): EngineObject | Uint8Array | Promise { + ): EngineObject | Uint8Array | AssetPromise { const parser = GLTFParser.getExtensionParser(extensionName, GLTFExtensionMode.CreateAndParse); if (parser) { @@ -124,10 +124,7 @@ export abstract class GLTFParser { ...extra ): void { const parser = GLTFParser.getExtensionParser(extensionName, GLTFExtensionMode.AdditiveParse); - - if (parser) { - parser.additiveParse(context, parseResource, extensionSchema, ownerSchema, ...extra); - } + parser?.additiveParse(context, parseResource, extensionSchema, ownerSchema, ...extra); } abstract parse(context: GLTFParserContext, index?: number); diff --git a/packages/loader/src/gltf/parser/GLTFParserContext.ts b/packages/loader/src/gltf/parser/GLTFParserContext.ts index 2558db672..954fdf011 100644 --- a/packages/loader/src/gltf/parser/GLTFParserContext.ts +++ b/packages/loader/src/gltf/parser/GLTFParserContext.ts @@ -2,8 +2,10 @@ import { AnimationClip, Animator, AnimatorController, + AssetPromise, Buffer, Entity, + Logger, Material, ModelMesh, ResourceManager, @@ -33,6 +35,9 @@ export class GLTFParserContext { buffers?: ArrayBuffer[]; needAnimatorController = false; + /** @internal */ + _getPromises: AssetPromise[] = []; + private _resourceCache = new Map(); private _progress = { taskDetail: {}, @@ -54,21 +59,21 @@ export class GLTFParserContext { get(type: GLTFParserType.Entity, index: number): Entity; get(type: GLTFParserType.Entity): Entity[]; - get(type: GLTFParserType.Schema): Promise; - get(type: GLTFParserType.Validator): Promise; - get(type: GLTFParserType.AnimatorController): Promise; - get(type: GLTFParserType, index: number): Promise; - get(type: GLTFParserType): Promise; - get(type: GLTFParserType, index?: number): Entity | Entity[] | Promise | Promise { + get(type: GLTFParserType.Schema): AssetPromise; + get(type: GLTFParserType.Validator): AssetPromise; + get(type: GLTFParserType.AnimatorController): AssetPromise; + get(type: GLTFParserType, index: number): AssetPromise; + get(type: GLTFParserType): AssetPromise; + get(type: GLTFParserType, index?: number): Entity | Entity[] | AssetPromise | AssetPromise { const parser = GLTFParserContext._parsers[type]; if (!parser) { - return Promise.resolve(null); + return AssetPromise.resolve(null); } const cache = this._resourceCache; const cacheKey = index === undefined ? `${type}` : `${type}:${index}`; - let resource: Entity | Entity[] | Promise | Promise = cache.get(cacheKey); + let resource: Entity | Entity[] | AssetPromise | AssetPromise = cache.get(cacheKey); if (resource) { return resource; @@ -84,29 +89,32 @@ export class GLTFParserContext { resource = type === GLTFParserType.Entity ? glTFItems.map((_, index) => this.get(type, index)) - : Promise.all(glTFItems.map((_, index) => this.get(type, index))); + : AssetPromise.all(glTFItems.map((_, index) => this.get(type, index))); } else { resource = parser.parse(this, index); isSubAsset && this._handleSubAsset(resource, type, index); } } else { - resource = Promise.resolve(null); + resource = AssetPromise.resolve(null); } } else { resource = parser.parse(this, index); isSubAsset && this._handleSubAsset(resource, type, index); } + if (resource instanceof AssetPromise) { + this._getPromises.push(resource); + } cache.set(cacheKey, resource); return resource; } - parse(): Promise { + parse(): AssetPromise { const promise = this.get(GLTFParserType.Schema).then((json) => { this.glTF = json; this.needAnimatorController = !!(json.skins || json.animations); - return Promise.all([ + return AssetPromise.all([ this.get(GLTFParserType.Validator), this.get(GLTFParserType.Texture), this.get(GLTFParserType.Material), @@ -147,7 +155,7 @@ export class GLTFParserContext { /** * @internal */ - _addTaskCompletePromise(taskPromise: Promise): void { + _addTaskCompletePromise(taskPromise: PromiseLike): void { const task = this._progress.taskComplete; task.total += 1; taskPromise.then( @@ -159,7 +167,7 @@ export class GLTFParserContext { } private _handleSubAsset( - resource: Entity | Entity[] | Promise | Promise, + resource: Entity | Entity[] | AssetPromise | AssetPromise, type: GLTFParserType, index?: number ): void { @@ -170,33 +178,37 @@ export class GLTFParserContext { } else { const url = this.glTFResource.url; - (>resource).then((item: T) => { - if (index == undefined) { - this.glTFResource[glTFResourceKey] = item; - } else { - (this.glTFResource[glTFResourceKey] ||= [])[index] = item; - } - - if (type === GLTFParserType.Mesh) { - for (let i = 0, length = (item).length; i < length; i++) { - const mesh = item[i] as ModelMesh; - // @ts-ignore - this.resourceManager._onSubAssetSuccess(url, `${glTFResourceKey}[${index}][${i}]`, mesh); + (>resource) + .then((item: T) => { + if (index == undefined) { + this.glTFResource[glTFResourceKey] = item; + } else { + (this.glTFResource[glTFResourceKey] ||= [])[index] = item; } - } else { - // @ts-ignore - this.resourceManager._onSubAssetSuccess( - url, - `${glTFResourceKey}${index === undefined ? "" : `[${index}]`}`, - item - ); - if (type === GLTFParserType.Scene && (this.glTF.scene ?? 0) === index) { + if (type === GLTFParserType.Mesh) { + for (let i = 0, length = (item).length; i < length; i++) { + const mesh = item[i] as ModelMesh; + // @ts-ignore + this.resourceManager._onSubAssetSuccess(url, `${glTFResourceKey}[${index}][${i}]`, mesh); + } + } else { // @ts-ignore - this.resourceManager._onSubAssetSuccess(url, `defaultSceneRoot`, item as Entity); + this.resourceManager._onSubAssetSuccess( + url, + `${glTFResourceKey}${index === undefined ? "" : `[${index}]`}`, + item + ); + + if (type === GLTFParserType.Scene && (this.glTF.scene ?? 0) === index) { + // @ts-ignore + this.resourceManager._onSubAssetSuccess(url, `defaultSceneRoot`, item as Entity); + } } - } - }); + }) + .catch((e) => { + Logger.error("GLTFParserContext", `Failed to load ${glTFResourceKey} ${index}: ${e}`); + }); } } } diff --git a/packages/loader/src/gltf/parser/GLTFSceneParser.ts b/packages/loader/src/gltf/parser/GLTFSceneParser.ts index 0f40f44ca..6517356c9 100644 --- a/packages/loader/src/gltf/parser/GLTFSceneParser.ts +++ b/packages/loader/src/gltf/parser/GLTFSceneParser.ts @@ -1,6 +1,8 @@ import { + AssetPromise, Camera, Entity, + Logger, Material, Mesh, MeshRenderer, @@ -11,13 +13,12 @@ import { import { BoundingBox, Matrix } from "@galacean/engine-math"; import { GLTFResource } from "../GLTFResource"; import { CameraType, ICamera, INode } from "../GLTFSchema"; -import { GLTFMaterialParser } from "./GLTFMaterialParser"; import { GLTFParser } from "./GLTFParser"; import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFParserContext"; @registerGLTFParser(GLTFParserType.Scene) export class GLTFSceneParser extends GLTFParser { - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const { glTF: { scenes, scene = 0 }, glTFResource @@ -46,24 +47,24 @@ export class GLTFSceneParser extends GLTFParser { glTFResource._defaultSceneRoot = sceneRoot; } - const promises = new Array>(); + const promises = new Array>(); for (let i = 0; i < sceneNodes.length; i++) { promises.push(this._parseEntityComponent(context, sceneNodes[i])); } - return Promise.all(promises).then(() => { + return AssetPromise.all(promises).then(() => { GLTFParser.executeExtensionsAdditiveAndParse(sceneExtensions, context, sceneRoot, sceneInfo); return sceneRoot; }); } - private _parseEntityComponent(context: GLTFParserContext, index: number): Promise { + private _parseEntityComponent(context: GLTFParserContext, index: number): AssetPromise { const { glTF, glTFResource } = context; const entityInfo = glTF.nodes[index]; const { camera: cameraID, mesh: meshID } = entityInfo; const entity = context.get(GLTFParserType.Entity, index); - let promise: Promise; + let promise: AssetPromise; if (cameraID !== undefined) { this._createCamera(glTFResource, glTF.cameras[cameraID], entity); @@ -73,7 +74,7 @@ export class GLTFSceneParser extends GLTFParser { promise = this._createRenderer(context, entityInfo, entity); } - return Promise.resolve(promise).then(() => { + return AssetPromise.resolve(promise).then(() => { const promises = []; const children = entityInfo.children; @@ -83,7 +84,7 @@ export class GLTFSceneParser extends GLTFParser { } } - return Promise.all(promises); + return AssetPromise.all(promises); }); } @@ -127,59 +128,65 @@ export class GLTFSceneParser extends GLTFParser { camera.enabled = false; } - private _createRenderer(context: GLTFParserContext, entityInfo: INode, entity: Entity): Promise { + private _createRenderer(context: GLTFParserContext, entityInfo: INode, entity: Entity): AssetPromise { const { mesh: meshID, skin: skinID } = entityInfo; const glTFMesh = context.glTF.meshes[meshID]; const glTFMeshPrimitives = glTFMesh.primitives; const rendererCount = glTFMeshPrimitives.length; const blendShapeWeights = entityInfo.weights || glTFMesh.weights; - const materialPromises = new Array>(rendererCount); + const materialPromises = new Array>(rendererCount); for (let i = 0; i < rendererCount; i++) { materialPromises[i] = context.get(GLTFParserType.Material, glTFMeshPrimitives[i].material ?? -1); } - return Promise.all([ + return AssetPromise.all([ context.get(GLTFParserType.Mesh, meshID), skinID !== undefined && context.get(GLTFParserType.Skin, skinID), - Promise.all(materialPromises) - ]).then(([meshes, skin, materials]) => { - for (let i = 0; i < rendererCount; i++) { - const material = materials[i] || GLTFMaterialParser._getDefaultMaterial(context.glTFResource.engine); - const glTFPrimitive = glTFMeshPrimitives[i]; - const mesh = meshes[i]; + AssetPromise.all(materialPromises) + ]) + .then(([meshes, skin, materials]) => { + // @ts-ignore + const basicResources = context.glTFResource.engine._basicResources; + for (let i = 0; i < rendererCount; i++) { + const material = materials[i] || basicResources._getBlinnPhongMaterial(); + const glTFPrimitive = glTFMeshPrimitives[i]; + const mesh = meshes[i]; - let renderer: MeshRenderer | SkinnedMeshRenderer; + let renderer: MeshRenderer | SkinnedMeshRenderer; - if (skin || blendShapeWeights) { - const skinRenderer = entity.addComponent(SkinnedMeshRenderer); - skinRenderer.mesh = mesh; - if (skin) { - this._computeLocalBounds(skinRenderer, mesh, skin.bones, skin.rootBone, skin.inverseBindMatrices); - skinRenderer.skin = skin; + if (skin || blendShapeWeights) { + const skinRenderer = entity.addComponent(SkinnedMeshRenderer); + skinRenderer.mesh = mesh; + if (skin) { + this._computeLocalBounds(skinRenderer, mesh, skin.bones, skin.rootBone, skin.inverseBindMatrices); + skinRenderer.skin = skin; + } + if (blendShapeWeights) { + skinRenderer.blendShapeWeights = new Float32Array(blendShapeWeights); + } + renderer = skinRenderer; + } else { + renderer = entity.addComponent(MeshRenderer); + renderer.mesh = mesh; } - if (blendShapeWeights) { - skinRenderer.blendShapeWeights = new Float32Array(blendShapeWeights); - } - renderer = skinRenderer; - } else { - renderer = entity.addComponent(MeshRenderer); - renderer.mesh = mesh; + + renderer.setMaterial(material); + + // Enable vertex color if mesh has COLOR_0 vertex element + mesh.vertexElements.forEach((element) => { + if (element.semantic === "COLOR_0") { + renderer.enableVertexColor = true; + } + }); + + GLTFParser.executeExtensionsAdditiveAndParse(glTFPrimitive.extensions, context, renderer, glTFPrimitive); } - - renderer.setMaterial(material); - - // Enable vertex color if mesh has COLOR_0 vertex element - mesh.vertexElements.forEach((element) => { - if (element.semantic === "COLOR_0") { - renderer.enableVertexColor = true; - } - }); - - GLTFParser.executeExtensionsAdditiveAndParse(glTFPrimitive.extensions, context, renderer, glTFPrimitive); - } - }); + }) + .catch((e) => { + Logger.error("GLTFSceneParser: create renderer error", e); + }); } private _computeLocalBounds( diff --git a/packages/loader/src/gltf/parser/GLTFSkinParser.ts b/packages/loader/src/gltf/parser/GLTFSkinParser.ts index ab640e194..7c1580e4c 100644 --- a/packages/loader/src/gltf/parser/GLTFSkinParser.ts +++ b/packages/loader/src/gltf/parser/GLTFSkinParser.ts @@ -1,4 +1,4 @@ -import { Entity, Skin } from "@galacean/engine-core"; +import { AssetPromise, Entity, Skin } from "@galacean/engine-core"; import { Matrix } from "@galacean/engine-math"; import { GLTFUtils } from "../GLTFUtils"; import { GLTFParser } from "./GLTFParser"; @@ -6,7 +6,7 @@ import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFPar @registerGLTFParser(GLTFParserType.Skin) export class GLTFSkinParser extends GLTFParser { - parse(context: GLTFParserContext, index: number): Promise { + parse(context: GLTFParserContext, index: number): AssetPromise { const glTF = context.glTF; const skinInfo = glTF.skins[index]; const { inverseBindMatrices, skeleton, joints, name = `SKIN_${index}` } = skinInfo; @@ -50,7 +50,7 @@ export class GLTFSkinParser extends GLTFParser { return skin; }); - return Promise.resolve(skinPromise); + return AssetPromise.resolve(skinPromise); } private _findSkeletonRootBone(joints: number[], entities: Entity[]): Entity { diff --git a/packages/loader/src/gltf/parser/GLTFTextureParser.ts b/packages/loader/src/gltf/parser/GLTFTextureParser.ts index 9c62977d7..ed1226beb 100644 --- a/packages/loader/src/gltf/parser/GLTFTextureParser.ts +++ b/packages/loader/src/gltf/parser/GLTFTextureParser.ts @@ -1,4 +1,4 @@ -import { AssetType, Texture, Texture2D, TextureWrapMode, Utils } from "@galacean/engine-core"; +import { AssetPromise, AssetType, Logger, Texture, Texture2D, TextureWrapMode, Utils } from "@galacean/engine-core"; import { BufferTextureRestoreInfo } from "../../GLTFContentRestorer"; import { TextureWrapMode as GLTFTextureWrapMode, IMaterial } from "../GLTFSchema"; import { GLTFUtils } from "../GLTFUtils"; @@ -22,14 +22,14 @@ export class GLTFTextureParser extends GLTFParser { sampler?: number, textureName?: string, isSRGBColorSpace?: boolean - ): Promise { + ): AssetPromise { const { glTFResource, glTF } = context; const { engine, url } = glTFResource; const { uri, bufferView: bufferViewIndex, mimeType, name: imageName } = glTF.images[imageIndex]; const useSampler = sampler !== undefined; const samplerInfo = useSampler && GLTFUtils.getSamplerInfo(glTF.samplers[sampler]); - let texture: Promise; + let texture: AssetPromise; if (uri) { const extIndex = uri.lastIndexOf("."); @@ -54,44 +54,47 @@ export class GLTFTextureParser extends GLTFParser { context._addTaskCompletePromise(texture); } else { const bufferView = glTF.bufferViews[bufferViewIndex]; + texture = context + .get(GLTFParserType.Buffer) + .then((buffers) => { + const buffer = buffers[bufferView.buffer]; + const imageBuffer = new Uint8Array(buffer, bufferView.byteOffset, bufferView.byteLength); + return GLTFUtils.loadImageBuffer(imageBuffer, mimeType).then((image) => { + const texture = new Texture2D( + engine, + image.width, + image.height, + undefined, + samplerInfo?.mipmap, + isSRGBColorSpace + ); + texture.setImageSource(image); + texture.generateMipmaps(); - texture = context.get(GLTFParserType.Buffer).then((buffers) => { - const buffer = buffers[bufferView.buffer]; - const imageBuffer = new Uint8Array(buffer, bufferView.byteOffset, bufferView.byteLength); + texture.name = textureName || imageName || `texture_${textureIndex}`; + useSampler && GLTFUtils.parseSampler(texture, samplerInfo); - return GLTFUtils.loadImageBuffer(imageBuffer, mimeType).then((image) => { - const texture = new Texture2D( - engine, - image.width, - image.height, - undefined, - samplerInfo?.mipmap, - isSRGBColorSpace - ); - texture.setImageSource(image); - texture.generateMipmaps(); + const bufferTextureRestoreInfo = new BufferTextureRestoreInfo(texture, bufferView, mimeType); + context.contentRestorer.bufferTextures.push(bufferTextureRestoreInfo); - texture.name = textureName || imageName || `texture_${textureIndex}`; - useSampler && GLTFUtils.parseSampler(texture, samplerInfo); - - const bufferTextureRestoreInfo = new BufferTextureRestoreInfo(texture, bufferView, mimeType); - context.contentRestorer.bufferTextures.push(bufferTextureRestoreInfo); - - return texture; + return texture; + }); + }) + .catch((e) => { + Logger.error("GLTFTextureParser: image buffer error", e); }); - }); } return texture; } - parse(context: GLTFParserContext, textureIndex: number): Promise { + parse(context: GLTFParserContext, textureIndex: number): AssetPromise { const textureInfo = context.glTF.textures[textureIndex]; const glTFResource = context.glTFResource; const { sampler, source: imageIndex = 0, name: textureName, extensions } = textureInfo; const isSRGBColorSpace = this._isSRGBColorSpace(textureIndex, context.glTF.materials); - let texture = >( + let texture = >( GLTFParser.executeExtensionsCreateAndParse(extensions, context, textureInfo, textureIndex, isSRGBColorSpace) ); @@ -106,7 +109,7 @@ export class GLTFTextureParser extends GLTFParser { ); } - return Promise.resolve(texture).then((texture) => { + return AssetPromise.resolve(texture).then((texture) => { GLTFParser.executeExtensionsAdditiveAndParse(extensions, context, texture, textureInfo); // @ts-ignore texture._associationSuperResource(glTFResource); diff --git a/packages/loader/src/gltf/parser/GLTFValidator.ts b/packages/loader/src/gltf/parser/GLTFValidator.ts index 7cbd96f92..6a81331de 100644 --- a/packages/loader/src/gltf/parser/GLTFValidator.ts +++ b/packages/loader/src/gltf/parser/GLTFValidator.ts @@ -1,10 +1,10 @@ -import { Logger } from "@galacean/engine-core"; +import { AssetPromise, Logger } from "@galacean/engine-core"; import { GLTFParser } from "./GLTFParser"; import { GLTFParserContext, GLTFParserType, registerGLTFParser } from "./GLTFParserContext"; @registerGLTFParser(GLTFParserType.Validator) export class GLTFValidator extends GLTFParser { - parse(context: GLTFParserContext): Promise { + parse(context: GLTFParserContext): AssetPromise { const { asset: { version }, extensionsUsed, @@ -37,6 +37,6 @@ export class GLTFValidator extends GLTFParser { } } - return Promise.resolve(null); + return AssetPromise.resolve(null); } } diff --git a/packages/loader/src/resource-deserialize/index.ts b/packages/loader/src/resource-deserialize/index.ts index b73932203..e154ebe11 100644 --- a/packages/loader/src/resource-deserialize/index.ts +++ b/packages/loader/src/resource-deserialize/index.ts @@ -1,4 +1,4 @@ -import { Engine } from "@galacean/engine-core"; +import { AssetPromise, Engine } from "@galacean/engine-core"; import { BufferReader } from "./utils/BufferReader"; import { decoderMap } from "./utils/Decorator"; import { FileHeader } from "./utils/FileHeader"; @@ -15,7 +15,7 @@ export type { IModelMesh } from "./resources/mesh/IModelMesh"; * @param engine - engine * @returns */ -export function decode(arrayBuffer: ArrayBuffer, engine: Engine): Promise { +export function decode(arrayBuffer: ArrayBuffer, engine: Engine): AssetPromise { const header = FileHeader.decode(arrayBuffer); const bufferReader = new BufferReader(new Uint8Array(arrayBuffer), header.headerLength, header.dataLength); return decoderMap[header.type].decode(engine, bufferReader).then((object) => { diff --git a/packages/loader/src/resource-deserialize/resources/animationClip/AnimationClipDecoder.ts b/packages/loader/src/resource-deserialize/resources/animationClip/AnimationClipDecoder.ts index 6fd482d69..4086019fc 100644 --- a/packages/loader/src/resource-deserialize/resources/animationClip/AnimationClipDecoder.ts +++ b/packages/loader/src/resource-deserialize/resources/animationClip/AnimationClipDecoder.ts @@ -1,5 +1,6 @@ import { AnimationArrayCurve, + AnimationBoolCurve, AnimationClip, AnimationColorCurve, AnimationCurve, @@ -7,17 +8,17 @@ import { AnimationFloatArrayCurve, AnimationFloatCurve, AnimationQuaternionCurve, + AnimationRefCurve, + AnimationStringCurve, AnimationVector2Curve, AnimationVector3Curve, AnimationVector4Curve, - AnimationRefCurve, + AssetPromise, Engine, Keyframe, KeyframeValueType, - ReferResource, - AnimationStringCurve, - AnimationBoolCurve, - Loader + Loader, + ReferResource } from "@galacean/engine-core"; import { Color, Quaternion, Vector2, Vector3, Vector4 } from "@galacean/engine-math"; import type { BufferReader } from "../../utils/BufferReader"; @@ -39,8 +40,8 @@ export enum InterpolableValueType { @decoder("AnimationClip") export class AnimationClipDecoder { - public static decode(engine: Engine, bufferReader: BufferReader): Promise { - return new Promise((resolve) => { + public static decode(engine: Engine, bufferReader: BufferReader): AssetPromise { + return new AssetPromise((resolve) => { const name = bufferReader.nextStr(); const clip = new AnimationClip(name); const eventsLen = bufferReader.nextUint16(); diff --git a/packages/loader/src/resource-deserialize/resources/mesh/MeshDecoder.ts b/packages/loader/src/resource-deserialize/resources/mesh/MeshDecoder.ts index 642aeb954..e3bcce213 100644 --- a/packages/loader/src/resource-deserialize/resources/mesh/MeshDecoder.ts +++ b/packages/loader/src/resource-deserialize/resources/mesh/MeshDecoder.ts @@ -1,4 +1,4 @@ -import type { Engine } from "@galacean/engine-core"; +import { AssetPromise, Engine } from "@galacean/engine-core"; import { BlendShape, ModelMesh } from "@galacean/engine-core"; import { Color, Vector2, Vector3, Vector4 } from "@galacean/engine-math"; import type { BufferReader } from "../../utils/BufferReader"; @@ -10,8 +10,8 @@ import type { IEncodedModelMesh } from "./IModelMesh"; */ @decoder("Mesh") export class MeshDecoder { - public static decode(engine: Engine, bufferReader: BufferReader): Promise { - return new Promise((resolve) => { + public static decode(engine: Engine, bufferReader: BufferReader): AssetPromise { + return new AssetPromise((resolve) => { const modelMesh = new ModelMesh(engine); const jsonDataString = bufferReader.nextStr(); const encodedMeshData: IEncodedModelMesh = JSON.parse(jsonDataString); diff --git a/packages/loader/src/resource-deserialize/resources/texture2D/TextureDecoder.ts b/packages/loader/src/resource-deserialize/resources/texture2D/TextureDecoder.ts index 9d4230caa..b9ced4f7c 100644 --- a/packages/loader/src/resource-deserialize/resources/texture2D/TextureDecoder.ts +++ b/packages/loader/src/resource-deserialize/resources/texture2D/TextureDecoder.ts @@ -1,11 +1,11 @@ -import { Engine, Texture2D } from "@galacean/engine-core"; +import { AssetPromise, Engine, Texture2D } from "@galacean/engine-core"; import { BufferReader } from "../../utils/BufferReader"; import { decoder } from "../../utils/Decorator"; @decoder("Texture2D") export class Texture2DDecoder { - static decode(engine: Engine, bufferReader: BufferReader): Promise { - return new Promise((resolve, reject) => { + static decode(engine: Engine, bufferReader: BufferReader): AssetPromise { + return new AssetPromise((resolve, reject) => { const objectId = bufferReader.nextStr(); const mipmap = !!bufferReader.nextUint8(); const filterMode = bufferReader.nextUint8(); diff --git a/packages/loader/src/resource-deserialize/utils/Decorator.ts b/packages/loader/src/resource-deserialize/utils/Decorator.ts index 76ae26e42..2cdc5d3e7 100644 --- a/packages/loader/src/resource-deserialize/utils/Decorator.ts +++ b/packages/loader/src/resource-deserialize/utils/Decorator.ts @@ -1,10 +1,10 @@ -import { Engine } from "@galacean/engine-core"; +import { AssetPromise, Engine } from "@galacean/engine-core"; import type { BufferReader } from "./BufferReader"; export const decoderMap: Record< string, { - decode: (engine: Engine, bufferReader: BufferReader) => Promise; + decode: (engine: Engine, bufferReader: BufferReader) => AssetPromise; } > = {}; diff --git a/packages/math/package.json b/packages/math/package.json index e4137bd76..912385e5a 100644 --- a/packages/math/package.json +++ b/packages/math/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-math", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/physics-lite/package.json b/packages/physics-lite/package.json index ff0c71e02..7a3bb67d1 100644 --- a/packages/physics-lite/package.json +++ b/packages/physics-lite/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-physics-lite", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/physics-physx/package.json b/packages/physics-physx/package.json index 488a2d310..21c820741 100644 --- a/packages/physics-physx/package.json +++ b/packages/physics-physx/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-physics-physx", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/rhi-webgl/package.json b/packages/rhi-webgl/package.json index 04bce177e..2bfa600f5 100644 --- a/packages/rhi-webgl/package.json +++ b/packages/rhi-webgl/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-rhi-webgl", - "version": "1.4.12", + "version": "1.4.15", "repository": { "url": "https://github.com/galacean/engine.git" }, diff --git a/packages/shader-lab/package.json b/packages/shader-lab/package.json index e6ed7eaf0..2ca69d269 100644 --- a/packages/shader-lab/package.json +++ b/packages/shader-lab/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-shaderlab", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/shader-lab/src/lexer/Utils.ts b/packages/shader-lab/src/lexer/Utils.ts index 8512abb53..cdd12a506 100644 --- a/packages/shader-lab/src/lexer/Utils.ts +++ b/packages/shader-lab/src/lexer/Utils.ts @@ -23,7 +23,6 @@ export default class LexerUtils { static isPpCharacters(charCode: number) { return ( charCode === 35 || // # - charCode === 46 || // . charCode === 95 || // _ (charCode >= 48 && charCode <= 57) || // 0 - 9 (charCode >= 65 && charCode <= 90) || // A - Z diff --git a/packages/shader-shaderlab/package.json b/packages/shader-shaderlab/package.json index a1446cfc9..e154229da 100644 --- a/packages/shader-shaderlab/package.json +++ b/packages/shader-shaderlab/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-shader-shaderlab", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/ui/package.json b/packages/ui/package.json index 0505a71ab..262e29b32 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-ui", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/ui/src/component/UICanvas.ts b/packages/ui/src/component/UICanvas.ts index 45dc1109d..6ab7c82ad 100644 --- a/packages/ui/src/component/UICanvas.ts +++ b/packages/ui/src/component/UICanvas.ts @@ -13,6 +13,7 @@ import { Ray, Vector2, Vector3, + assignmentClone, deepClone, dependentComponents, ignoreClone @@ -77,19 +78,19 @@ export class UICanvas extends Component implements IElement { @ignoreClone private _renderMode = CanvasRenderMode.WorldSpace; - @ignoreClone + @assignmentClone private _renderCamera: Camera; @ignoreClone private _cameraObserver: Camera; - @ignoreClone + @assignmentClone private _resolutionAdaptationMode = ResolutionAdaptationMode.HeightAdaptation; - @ignoreClone + @assignmentClone private _sortOrder: number = 0; - @ignoreClone + @assignmentClone private _distance: number = 10; @deepClone private _referenceResolution: Vector2 = new Vector2(800, 600); - @deepClone + @assignmentClone private _referenceResolutionPerUnit: number = 100; @ignoreClone private _hierarchyVersion: number = -1; @@ -154,7 +155,8 @@ export class UICanvas extends Component implements IElement { set renderCamera(value: Camera) { const preCamera = this._renderCamera; if (preCamera !== value) { - this._isSameOrChildEntity(value.entity) && + value && + this._isSameOrChildEntity(value.entity) && Logger.warn( "Camera entity matching or nested within the canvas entity disables canvas auto-adaptation in ScreenSpaceCamera mode." ); @@ -381,6 +383,13 @@ export class UICanvas extends Component implements IElement { } } + /** + * @internal + */ + _cloneTo(target: UICanvas, srcRoot: Entity, targetRoot: Entity): void { + target.renderMode = this._renderMode; + } + private _getRenderers(): UIRenderer[] { const { _orderedRenderers: renderers, entity } = this; const uiHierarchyVersion = entity._uiHierarchyVersion; diff --git a/packages/ui/src/component/advanced/Image.ts b/packages/ui/src/component/advanced/Image.ts index a4031347b..404f248a0 100644 --- a/packages/ui/src/component/advanced/Image.ts +++ b/packages/ui/src/component/advanced/Image.ts @@ -29,7 +29,7 @@ export class Image extends UIRenderer implements ISpriteRenderer { private _sprite: Sprite = null; @ignoreClone private _drawMode: SpriteDrawMode; - @assignmentClone + @ignoreClone private _assembler: ISpriteAssembler; @assignmentClone private _tileMode: SpriteTileMode = SpriteTileMode.Continuous; @@ -149,6 +149,16 @@ export class Image extends UIRenderer implements ISpriteRenderer { } } + /** + * @internal + */ + _cloneTo(target: Image, srcRoot: Entity, targetRoot: Entity): void { + // @ts-ignore + super._cloneTo(target, srcRoot, targetRoot); + target.sprite = this._sprite; + target.drawMode = this._drawMode; + } + protected override _updateBounds(worldBounds: BoundingBox): void { const sprite = this._sprite; const rootCanvas = this._getRootCanvas(); diff --git a/packages/ui/src/input/UIPointerEventEmitter.ts b/packages/ui/src/input/UIPointerEventEmitter.ts index f4684b95c..936e28ff4 100644 --- a/packages/ui/src/input/UIPointerEventEmitter.ts +++ b/packages/ui/src/input/UIPointerEventEmitter.ts @@ -190,6 +190,7 @@ export class UIPointerEventEmitter extends PointerEventEmitter { } add.length = del.length = 0; } + curPath.length = 0; } private _composedPath(element: UIRenderer, path: Entity[]): Entity[] { diff --git a/packages/xr-webxr/package.json b/packages/xr-webxr/package.json index 0b1f73fe5..f3534ce54 100644 --- a/packages/xr-webxr/package.json +++ b/packages/xr-webxr/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-xr-webxr", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/xr/package.json b/packages/xr/package.json index 79784eb44..dc47943e4 100644 --- a/packages/xr/package.json +++ b/packages/xr/package.json @@ -1,6 +1,6 @@ { "name": "@galacean/engine-xr", - "version": "1.4.12", + "version": "1.4.15", "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org" diff --git a/packages/xr/src/loader/XRReferenceImageDecoder.ts b/packages/xr/src/loader/XRReferenceImageDecoder.ts index b40eaed3f..e7d394dd2 100644 --- a/packages/xr/src/loader/XRReferenceImageDecoder.ts +++ b/packages/xr/src/loader/XRReferenceImageDecoder.ts @@ -1,10 +1,10 @@ -import { BufferReader, Engine, decoder } from "@galacean/engine"; +import { AssetPromise, BufferReader, Engine, decoder } from "@galacean/engine"; import { XRReferenceImage } from "../feature/trackable/image/XRReferenceImage"; @decoder("XRReferenceImage") export class XRReferenceImageDecoder { - static decode(engine: Engine, bufferReader: BufferReader): Promise { - return new Promise((resolve, reject) => { + static decode(engine: Engine, bufferReader: BufferReader): AssetPromise { + return new AssetPromise((resolve, reject) => { const physicalWidth = bufferReader.nextFloat32(); bufferReader.nextUint8(); const img = new Image(); diff --git a/tests/package.json b/tests/package.json index 9c5174927..d7c53a4fa 100644 --- a/tests/package.json +++ b/tests/package.json @@ -1,7 +1,7 @@ { "name": "@galacean/engine-tests", "private": true, - "version": "1.4.12", + "version": "1.4.15", "license": "MIT", "main": "dist/main.js", "module": "dist/module.js", diff --git a/tests/src/core/Entity.test.ts b/tests/src/core/Entity.test.ts index f1772a54b..ecd5f99a8 100644 --- a/tests/src/core/Entity.test.ts +++ b/tests/src/core/Entity.test.ts @@ -1,4 +1,4 @@ -import { Entity, Script } from "@galacean/engine-core"; +import { Entity, EntityModifyFlags, Scene, Script } from "@galacean/engine-core"; import { WebGLEngine } from "@galacean/engine-rhi-webgl"; import { beforeEach, describe, expect, it, vi } from "vitest"; @@ -30,7 +30,6 @@ describe("Entity", async () => { const child2 = new Entity(engine, "child2"); child2.parent = parentY; - expect(scene.findEntityByPath("")).eq(null); expect(scene.findEntityByPath("root")).eq(root); @@ -401,6 +400,118 @@ describe("Entity", async () => { }; expect(lonelyBadFn).to.throw(); }); + + it("isRoot", () => { + const parent = scene.createRootEntity("parent"); + const child = scene.createRootEntity("child"); + parent.addChild(child); + scene.addRootEntity(child); + // @ts-ignore + expect(child._isRoot).eq(true); + child.parent = parent; + // @ts-ignore + expect(child._isRoot).eq(false); + scene.addRootEntity(child); + // @ts-ignore + expect(child._isRoot).eq(true); + }); + + it("InActiveAndActive", () => { + const parentA = scene.createRootEntity("parentA"); + const parentB = new Entity(engine, "parentB"); + const parentC = scene.createRootEntity("parentC"); + const sceneA = new Scene(engine, "sceneA"); + const sceneB = new Scene(engine, "sceneB"); + const child = new Entity(engine, "child"); + + let enableCount = 0; + let disableCount = 0; + let enableInSceneCount = 0; + let disableInSceneCount = 0; + + child.addComponent( + class extends Script { + _onEnable(): void { + ++enableCount; + } + + _onDisable(): void { + ++disableCount; + } + + _onEnableInScene(): void { + ++enableInSceneCount; + } + + _onDisableInScene(): void { + ++disableInSceneCount; + } + } + ); + + expect(child.isActive).eq(true); + expect(child.isActiveInHierarchy).eq(false); + child.isActive = false; + expect(child.isActive).eq(false); + + parentB.addChild(child); + expect(child.isActive).eq(false); + expect(child.isActiveInHierarchy).eq(false); + expect(enableCount).eq(0); + expect(disableCount).eq(0); + expect(enableInSceneCount).eq(0); + expect(disableInSceneCount).eq(0); + child.isActive = true; + expect(child.isActive).eq(true); + expect(child.isActiveInHierarchy).eq(false); + expect(enableCount).eq(0); + expect(disableCount).eq(0); + expect(enableInSceneCount).eq(0); + expect(disableInSceneCount).eq(0); + + parentA.addChild(child); + expect(child.isActiveInHierarchy).eq(true); + expect(enableCount).eq(1); + expect(disableCount).eq(0); + expect(enableInSceneCount).eq(1); + expect(disableInSceneCount).eq(0); + + parentA.addChild(child); + expect(child.isActiveInHierarchy).eq(true); + expect(enableCount).eq(1); + expect(disableCount).eq(0); + expect(enableInSceneCount).eq(1); + expect(disableInSceneCount).eq(0); + + parentC.addChild(child); + expect(child.isActiveInHierarchy).eq(true); + expect(enableCount).eq(1); + expect(disableCount).eq(0); + expect(enableInSceneCount).eq(1); + expect(disableInSceneCount).eq(0); + + sceneA.addRootEntity(child); + expect(child.isActiveInHierarchy).eq(false); + expect(enableCount).eq(1); + expect(disableCount).eq(1); + expect(enableInSceneCount).eq(2); + expect(disableInSceneCount).eq(1); + + engine.sceneManager.addScene(sceneB); + sceneB.addRootEntity(child); + expect(child.isActiveInHierarchy).eq(true); + expect(enableCount).eq(2); + expect(disableCount).eq(1); + expect(enableInSceneCount).eq(3); + expect(disableInSceneCount).eq(2); + + sceneB.removeRootEntity(child); + expect(child.isActiveInHierarchy).eq(false); + expect(enableCount).eq(2); + expect(disableCount).eq(2); + expect(enableInSceneCount).eq(3); + expect(disableInSceneCount).eq(3); + }); }); describe("clone", () => { @@ -417,6 +528,65 @@ describe("Entity", async () => { }); }); + describe("modify", () => { + it("ParentAndChild", () => { + const parentA = scene.createRootEntity("parentA"); + const parentB = scene.createRootEntity("parentB"); + const child = new Entity(engine, "child"); + + let modifyParentACount = [0, 0, 0]; + let modifyParentBCount = [0, 0, 0]; + let modifyChildCount = [0, 0, 0]; + + const modifyParentA = (flag: EntityModifyFlags, child: Entity) => { + ++modifyParentACount[flag]; + }; + const modifyParentB = (flag: EntityModifyFlags, child: Entity) => { + ++modifyParentBCount[flag]; + }; + const modifyChild = (flag: EntityModifyFlags, child: Entity) => { + ++modifyChildCount[flag]; + }; + // @ts-ignore + parentA._registerModifyListener(modifyParentA); + // @ts-ignore + parentB._registerModifyListener(modifyParentB); + // @ts-ignore + child._registerModifyListener(modifyChild); + + expect(modifyParentACount[EntityModifyFlags.Child]).eq(0); + expect(modifyParentACount[EntityModifyFlags.Parent]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Child]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Parent]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Child]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Parent]).eq(0); + parentA.addChild(child); + + expect(modifyParentACount[EntityModifyFlags.Child]).eq(1); + expect(modifyParentACount[EntityModifyFlags.Parent]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Child]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Parent]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Child]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Parent]).eq(1); + + child.siblingIndex = 2; + expect(modifyParentACount[EntityModifyFlags.Child]).eq(2); + expect(modifyParentACount[EntityModifyFlags.Parent]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Child]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Parent]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Child]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Parent]).eq(1); + + parentB.addChild(child); + expect(modifyParentACount[EntityModifyFlags.Child]).eq(3); + expect(modifyParentACount[EntityModifyFlags.Parent]).eq(0); + expect(modifyParentBCount[EntityModifyFlags.Child]).eq(1); + expect(modifyParentBCount[EntityModifyFlags.Parent]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Child]).eq(0); + expect(modifyChildCount[EntityModifyFlags.Parent]).eq(2); + }); + }); + describe("destroy", () => { it("normal", () => { const parent = new Entity(engine, "parent"); diff --git a/tests/src/shader-lab/test-case/compare/macro.txt b/tests/src/shader-lab/test-case/compare/macro.txt new file mode 100644 index 000000000..e46e8511f --- /dev/null +++ b/tests/src/shader-lab/test-case/compare/macro.txt @@ -0,0 +1,4 @@ + + +vec2 q = vec2(uv.x * iResolution.x, uv.y * iResolution.y).xy / iResolution.xy; +vec2 p = ( 2.0 * vec2(uv.x * iResolution.x, uv.y * iResolution.y).xy - iResolution.xy ) / min( iResolution.y, iResolution.x ); \ No newline at end of file diff --git a/tests/src/shader-lab/test-case/index.ts b/tests/src/shader-lab/test-case/index.ts index 9a3b3b6ea..6c7e80254 100644 --- a/tests/src/shader-lab/test-case/index.ts +++ b/tests/src/shader-lab/test-case/index.ts @@ -4,7 +4,7 @@ const { readFile } = server.commands; const sourceDir = "test-case/source/"; const cmpDir = "test-case/compare/"; -const files = ["frag.txt", "frag2.txt"]; +const files = ["frag.txt", "frag2.txt", "macro.txt"]; const testCaseList: { source: string; compare: string; name: string }[] = []; for (const f of files) { const cmpFilePath = `${cmpDir}${f}`; diff --git a/tests/src/shader-lab/test-case/source/macro.txt b/tests/src/shader-lab/test-case/source/macro.txt new file mode 100644 index 000000000..9345b2f7b --- /dev/null +++ b/tests/src/shader-lab/test-case/source/macro.txt @@ -0,0 +1,4 @@ +#define fragCoord vec2(uv.x * iResolution.x, uv.y * iResolution.y) + +vec2 q = fragCoord.xy / iResolution.xy; +vec2 p = ( 2.0 * fragCoord.xy - iResolution.xy ) / min( iResolution.y, iResolution.x ); \ No newline at end of file diff --git a/tests/src/ui/Image.test.ts b/tests/src/ui/Image.test.ts index cef3cad4f..e1da58bfc 100644 --- a/tests/src/ui/Image.test.ts +++ b/tests/src/ui/Image.test.ts @@ -48,4 +48,18 @@ describe("Image", async () => { image.tiledAdaptiveThreshold = 1.5; expect(image.tiledAdaptiveThreshold).to.eq(1); }); + + it("Clone", () => { + const imageEntity = canvasEntity.createChild("Image"); + const image = imageEntity.addComponent(Image); + const sprite = new Sprite(engine, new Texture2D(engine, 100, 100)); + image.sprite = sprite; + image.drawMode = SpriteDrawMode.Sliced; + + const cloneEntity = imageEntity.clone(); + const cloneImage = cloneEntity.getComponent(Image); + + expect(cloneImage.sprite).to.eq(sprite); + expect(cloneImage.drawMode).to.eq(SpriteDrawMode.Sliced); + }); }); diff --git a/tests/src/ui/Text.test.ts b/tests/src/ui/Text.test.ts index 15498414f..6a0b0d101 100644 --- a/tests/src/ui/Text.test.ts +++ b/tests/src/ui/Text.test.ts @@ -1,4 +1,4 @@ -import { WebGLEngine } from "@galacean/engine"; +import { Font, WebGLEngine } from "@galacean/engine"; import { Text, UITransform } from "@galacean/engine-ui"; import { describe, expect, it } from "vitest"; @@ -100,4 +100,23 @@ describe("Text", async () => { label3.enableWrapping = true; label3.text = "hello world\nddl\nsdfjdslfsdfdssdfsdf"; }); + + it("Clone", () => { + const textEntity = canvasEntity.createChild("Image"); + const text = textEntity.addComponent(Text); + text.text = "hello world"; + text.fontSize = 30; + text.lineSpacing = 1; + text.enableWrapping = true; + text.font = Font.createFromOS(engine, "AlibabaSans"); + + const cloneEntity = textEntity.clone(); + const cloneText = cloneEntity.getComponent(Text); + + expect(cloneText.text).to.eq("hello world"); + expect(cloneText.fontSize).to.eq(30); + expect(cloneText.lineSpacing).to.eq(1); + expect(cloneText.enableWrapping).to.eq(true); + expect(cloneText.font).to.eq(text.font); + }); }); diff --git a/tests/src/ui/UICanvas.test.ts b/tests/src/ui/UICanvas.test.ts index 618fd6a40..aa9cb9b89 100644 --- a/tests/src/ui/UICanvas.test.ts +++ b/tests/src/ui/UICanvas.test.ts @@ -41,6 +41,10 @@ describe("UICanvas", async () => { expect(!!rootCanvas.renderCamera).to.eq(false); rootCanvas.renderCamera = camera; expect(rootCanvas.renderCamera).to.eq(camera); + rootCanvas.renderCamera = null; + expect(!!rootCanvas.renderCamera).to.eq(false); + rootCanvas.renderCamera = camera; + expect(rootCanvas.renderCamera).to.eq(camera); // Resolution Adaptation Strategy rootCanvas.resolutionAdaptationMode = ResolutionAdaptationMode.WidthAdaptation; @@ -265,4 +269,30 @@ describe("UICanvas", async () => { expect(Math.floor(canvasSize.x)).to.eq(168); expect(Math.floor(canvasSize.y)).to.eq(600); }); + + it("Clone", () => { + rootCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera; + rootCanvas.renderCamera = camera; + rootCanvas.distance = 10; + rootCanvas.referenceResolution = new Vector2(800, 600); + rootCanvas.resolutionAdaptationMode = ResolutionAdaptationMode.WidthAdaptation; + rootCanvas.referenceResolutionPerUnit = 100; + rootCanvas.sortOrder = 10; + const cloneEntity = canvasEntity.clone(); + const cloneCanvas = cloneEntity.getComponent(UICanvas); + console.log(cloneCanvas.entity.parent); + + expect(cloneCanvas.renderMode).to.eq(CanvasRenderMode.ScreenSpaceCamera); + expect(cloneCanvas.renderCamera).to.eq(camera); + expect(cloneCanvas.distance).to.eq(10); + expect(cloneCanvas.referenceResolution).to.deep.include({ x: 800, y: 600 }); + expect(cloneCanvas.resolutionAdaptationMode).to.eq(ResolutionAdaptationMode.WidthAdaptation); + expect(cloneCanvas.referenceResolutionPerUnit).to.eq(100); + expect(cloneCanvas.sortOrder).to.eq(10); + // @ts-ignore + expect(cloneCanvas._isRootCanvas).to.eq(false); + root.addChild(cloneEntity); + // @ts-ignore + expect(cloneCanvas._isRootCanvas).to.eq(true); + }); });