diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..f59a9a2 Binary files /dev/null and b/.DS_Store differ diff --git a/gltfTutorial_cn/README.md b/gltfTutorial_cn/README.md new file mode 100644 index 0000000..d6b0363 --- /dev/null +++ b/gltfTutorial_cn/README.md @@ -0,0 +1,36 @@ +# glTF 教程 + +作者:Marco Hutter, [@javagl](https://github.com/javagl) + +本教程介绍了 [glTF](https://www.khronos.org/gltf),即 GL 传输格式。它总结了 glTF 最重要的特性和应用场景,并描述了与 glTF 相关的文件结构。它解释了如何读取、处理 glTF 资产以及如何高效地使用它们显示 3D 图形。 + +假设您已经具备 [JSON](https://json.org/)(JavaScript 对象表示法)的一些基本知识。此外,还需要对常见的图形 API(如 OpenGL 或 WebGL)有基本的了解。本教程重点关注 [glTF 2.0 版本](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html),该版本引入了对*基于物理的渲染*的支持,但这里解释的其他概念与 [glTF 1.0 版本](https://github.com/KhronosGroup/glTF/tree/main/specification/1.0) 中的实现方式类似。 + + +- [介绍](gltfTutorial_001_Introduction.md) +- [基本 glTF 结构](gltfTutorial_002_BasicGltfStructure.md) +- [示例:最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) +- [场景和节点](gltfTutorial_004_ScenesNodes.md) +- [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) +- [示例:简单的动画](gltfTutorial_006_SimpleAnimation.md) +- [动画](gltfTutorial_007_Animations.md) +- [示例:简单的网格](gltfTutorial_008_SimpleMeshes.md) +- [网格](gltfTutorial_009_Meshes.md) +- [材质](gltfTutorial_010_Materials.md) +- [示例:简单的材质](gltfTutorial_011_SimpleMaterial.md) +- [纹理、图像和采样器](gltfTutorial_012_TexturesImagesSamplers.md) +- [示例:简单的纹理](gltfTutorial_013_SimpleTexture.md) +- [示例:高级材质](gltfTutorial_014_AdvancedMaterial.md) +- [示例:简单的相机](gltfTutorial_015_SimpleCameras.md) +- [相机](gltfTutorial_016_Cameras.md) +- [示例:简单的变形目标](gltfTutorial_017_SimpleMorphTarget.md) +- [变形目标](gltfTutorial_018_MorphTargets.md) +- [示例:简单的蒙皮](gltfTutorial_019_SimpleSkin.md) +- [蒙皮](gltfTutorial_020_Skins.md) + + +**致谢:** + +- Patrick Cozzi, Cesium, [@pjcozzi](https://twitter.com/pjcozzi) +- Alexey Knyazev, [@lexaknyazev](https://github.com/lexaknyazev) +- Sarah Chow, [@slchow](https://github.com/slchow) diff --git a/gltfTutorial_cn/gltfTutorial_001_Introduction.md b/gltfTutorial_cn/gltfTutorial_001_Introduction.md new file mode 100644 index 0000000..9c39316 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_001_Introduction.md @@ -0,0 +1,47 @@ +[目录](README.md) | 下一章: [基本 glTF 结构](gltfTutorial_002_BasicGltfStructure.md) + +# 使用 WebGL 的 glTF 介绍 + +越来越多的应用程序和服务都基于 3D 内容。在线商店提供带有 3D 预览的产品配置器。博物馆对其文物进行 3D 扫描,并允许游客在虚拟画廊中探索他们的收藏品。城市规划师使用 3D 城市模型进行规划和信息可视化。教育工作者创建人体的交互式动画 3D 模型。许多这些应用程序直接在网络浏览器中运行,这是可能的,因为所有现代浏览器都支持通过 WebGL 进行高效渲染。 + +

+
+图 1a: 展示 3D 模型的各种网站和应用程序的截图。 +

+ +各种应用程序对 3D 内容的需求不断增长。在许多情况下,3D 内容必须通过网络传输,并且必须在客户端高效地渲染。但直到现在,3D 内容创建和在运行时应用程序中高效渲染之间仍存在差距。 + +## 3D 内容管道 + +在客户端应用程序中渲染的 3D 内容来自不同的源,并存储在不同的文件格式中。[维基百科上的 3D 图形文件格式列表](https://en.wikipedia.org/wiki/List_of_file_formats#3D_graphics) 显示了一个庞大的数量,有超过 70 种不同的 3D 数据文件格式,服务于不同的目的和应用场景。 + +例如,原始 3D 数据可以通过 3D 扫描仪获取。这些扫描仪通常提供单个对象的几何数据,这些数据存储在 [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file)、[PLY](https://en.wikipedia.org/wiki/PLY_(file_format)) 或 [STL](https://en.wikipedia.org/wiki/STL_(file_format)) 文件中。这些文件格式不包含关于场景结构或对象应如何渲染的信息。 + +更复杂的 3D 场景可以使用创作工具创建。这些工具允许编辑场景的结构、灯光设置、相机、动画,以及当然还有场景中出现的对象的 3D 几何体。应用程序以自己的自定义文件格式存储这些信息。例如,[Blender](https://www.blender.org/) 将场景存储在 `.blend` 文件中,[LightWave3D](https://www.lightwave3d.com/) 使用 `.lws` 文件格式,[3ds Max](https://www.autodesk.com/3dsmax) 使用 `.max` 文件格式,而 [Maya](https://www.autodesk.com/maya) 使用 `.ma` 文件。 + +为了渲染这些 3D 内容,运行时应用程序必须能够读取不同的输入文件格式。必须解析场景结构,3D 几何数据必须转换为图形 API 所需的格式。3D 数据必须传输到图形卡内存,然后可以用图形 API 调用序列描述渲染过程。因此,每个运行时应用程序都必须为其支持的所有文件格式创建导入器、加载器或转换器,如 [图 1b](#contentPipeline-png) 所示。 + +

+
+图 1b: 当今的 3D 内容管道。 +

+ +## glTF: 3D 场景的传输格式 + +glTF 的目标是为 3D 内容定义一个标准,以适合在运行时应用程序中使用的形式。现有的文件格式不适合这种用例:有些不包含任何场景信息,只有几何数据;其他一些则是为了在创作应用程序之间交换数据而设计的,它们的主要目标是保留关于 3D 场景的尽可能多的信息,这导致文件通常很大、很复杂且难以解析。此外,几何数据可能需要预处理,以便可以用客户端应用程序渲染。 + +现有的文件格式都不是为通过网络高效传输 3D 场景并尽可能高效地渲染它们而设计的。但 glTF 不是"又一种文件格式"。它是 3D 场景*传输*格式的定义: + +- 场景结构用 JSON 描述,它非常紧凑且易于解析。 +- 对象的 3D 数据以可以被常见图形 API 直接使用的形式存储,因此没有解码或预处理 3D 数据的开销。 + +现在,不同的内容创建工具可以提供 glTF 格式的 3D 内容。越来越多的客户端应用程序能够使用和渲染 glTF。[图 1a](#applications-png) 中显示了其中一些应用程序。因此,glTF 可能有助于弥合内容创建和渲染之间的差距,如 [图 1c](#contentPipelineWithGltf-png) 所示。 + +

+
+图 1c: 使用 glTF 的 3D 内容管道。 +

+ +越来越多的内容创建工具直接提供 glTF 导入和导出功能。例如,Blender 手册记录了[如何使用 glTF 导入和导出 PBR 材质](https://docs.blender.org/manual/en/latest/addons/import_export/scene_gltf2.html)。或者,其他文件格式可以使用 [glTF Project Explorer](https://github.khronos.org/glTF-Project-Explorer/) 中列出的开源转换工具之一创建 glTF 资产。转换器和导出器的输出可以使用 [Khronos glTF Validator](https://github.khronos.org/glTF-Validator/) 进行验证。 + +[目录](README.md) | 下一章: [基本 glTF 结构](gltfTutorial_002_BasicGltfStructure.md) diff --git a/gltfTutorial_cn/gltfTutorial_002_BasicGltfStructure.md b/gltfTutorial_cn/gltfTutorial_002_BasicGltfStructure.md new file mode 100644 index 0000000..6b71245 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_002_BasicGltfStructure.md @@ -0,0 +1,95 @@ +上一章: [介绍](gltfTutorial_001_Introduction.md) | [目录](README.md) | 下一章: [最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) + +# glTF 的基本结构 + +glTF 的核心是一个 JSON 文件。这个文件描述了整个 3D 场景的内容。它包含场景结构本身的描述,该结构由定义场景图的节点层次结构给出。场景中出现的 3D 对象使用附加到节点的网格来定义。材质(Materials)定义了对象的外观。动画(Animations)描述了 3D 对象如何随时间变换(例如,旋转或平移),而蒙皮(Skins)定义了对象的几何形状如何基于骨架姿势进行变形。相机(Cameras)描述了渲染器的视图配置。 + +## JSON 结构 + +场景对象存储在 JSON 文件中的数组中。可以使用对象在数组中的索引来访问它们: + +```javascript +"meshes" : +[ + { ... } + { ... } + ... +], +``` + +这些索引也用于定义对象之间的*关系*。上面的例子定义了多个网格,一个节点可以使用网格索引引用其中一个网格,表明该网格应该附加到这个节点: + +```javascript +"nodes": +[ + { "mesh": 0, ... }, + { "mesh": 5, ... }, + ... +] +``` + +下图(改编自 [glTF 概念部分](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#concepts))概述了 glTF 资产的 JSON 部分的顶级元素: + +

+
+图 2a: glTF JSON 结构 +

+ +这里快速总结了这些元素,以提供一个概述,并附有 glTF 规范相应部分的链接。在以下章节中将对这些元素之间的关系进行更详细的解释。 + +- [`scene`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-scene)(场景)是存储在 glTF 中的场景描述的入口点。它引用定义场景图的 `node`(节点)。 +- [`node`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-node)(节点)是场景图层次结构中的一个节点。它可以包含变换(例如,旋转或平移),并且可以引用更多(子)节点。此外,它可以引用"附加"到节点的 `mesh`(网格)或 `camera`(相机)实例,或者引用描述网格变形的 `skin`(蒙皮)。 +- [`camera`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-camera)(相机)定义了渲染场景的视图配置。 +- [`mesh`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh)(网格)描述出现在场景中的几何对象。它引用用于访问实际几何数据的 `accessor`(访问器)对象,以及定义渲染对象时外观的 `material`(材质)。 +- [`skin`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-skin)(蒙皮)定义了顶点蒙皮所需的参数,顶点蒙皮允许基于虚拟角色的姿势对网格进行变形。这些参数的值从 `accessor` 获取。 +- [`animation`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation)(动画)描述了某些节点的变换(例如,旋转或平移)如何随时间变化。 +- [`accessor`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-accessor)(访问器)被用作任意数据的抽象源。它被 `mesh`、`skin` 和 `animation` 使用,并提供几何数据、蒙皮参数和时间相关的动画值。它引用 [`bufferView`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-bufferview)(缓冲区视图),它是包含实际原始二进制数据的 [`buffer`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer)(缓冲区)的一部分。 +- [`material`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-material)(材质)包含定义对象外观的参数。它通常引用将应用于渲染几何体的 `texture`(纹理)对象。 +- [`texture`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-texture)(纹理)由 [`sampler`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-sampler)(采样器)和 [`image`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-image)(图像)定义。`sampler` 定义了 `image` 纹理应该如何放置在对象上。 + +## 对外部数据的引用 + +3D 对象的二进制数据,如几何体和纹理,通常不包含在 JSON 文件中。相反,它们存储在专用文件中,JSON 部分只包含指向这些文件的链接。这使得二进制数据可以以非常紧凑的形式存储,并且可以通过网络高效传输。此外,数据可以以可以直接在渲染器中使用的格式存储,而无需解析、解码或预处理数据。 + +

+
+图 2b: glTF 结构 +

+ +如上图所示,有两种类型的对象可能包含这样的外部资源链接,即 `buffers`(缓冲区)和 `images`(图像)。这些对象将在后面更详细地解释。 + +## 读取和管理外部数据 + +读取和处理 glTF 资产从解析 JSON 结构开始。结构解析后,[`buffer`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer) 和 [`image`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-image) 对象分别在顶级 `buffers` 和 `images` 数组中可用。这些对象中的每一个都可能引用二进制数据块。为了进一步处理,这些数据被读入内存。通常,数据将存储在一个数组中,以便可以使用与引用它们所属的 `buffer` 或 `image` 对象相同的索引查找它们。 + +## `buffers` 中的二进制数据 + +[`buffer`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer) 包含一个指向包含原始二进制缓冲数据的文件的 URI: + +```javascript +"buffer01": { + "byteLength": 12352, + "type": "arraybuffer", + "uri": "buffer01.bin" +} +``` + +这个二进制数据只是从 `buffer` 的 URI 读取的原始内存块,没有固有的含义或结构。[缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) 部分将展示如何用关于数据类型和数据布局的信息扩展这些原始数据。有了这些信息,一部分数据可能会被解释为动画数据,而另一部分可能会被解释为几何数据。以二进制形式存储数据可以使其通过网络传输比 JSON 格式更高效,而且二进制数据可以直接传递给渲染器,无需解码或预处理。 + +## `images` 中的图像数据 + +[`image`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-image) 可以引用可以用作渲染对象纹理的外部图像文件: + +```javascript +"image01": { + "uri": "image01.png" +} +``` + +引用以 URI 的形式给出,通常指向 PNG 或 JPG 文件。这些格式显著减小了文件的大小,以便可以通过网络高效传输。在某些情况下,`image` 对象可能不引用外部文件,而是引用存储在 `buffer` 中的数据。这种间接的细节将在 [纹理、图像和采样器](gltfTutorial_012_TexturesImagesSamplers.md) 部分解释。 + +## 数据 URI 中的二进制数据 + +通常,`buffer` 和 `image` 对象中包含的 URI 将指向包含实际数据的文件。作为替代方案,数据可以使用 [数据 URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) 以二进制格式*嵌入*到 JSON 中。 + +上一章: [介绍](gltfTutorial_001_Introduction.md) | [目录](README.md) | 下一章: [最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) diff --git a/gltfTutorial_cn/gltfTutorial_003_MinimalGltfFile.md b/gltfTutorial_cn/gltfTutorial_003_MinimalGltfFile.md new file mode 100644 index 0000000..c27abcf --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_003_MinimalGltfFile.md @@ -0,0 +1,248 @@ +上一章: [基本 glTF 结构](gltfTutorial_002_BasicGltfStructure.md) | [目录](README.md) | 下一章: [场景和节点](gltfTutorial_004_ScenesNodes.md) + + +# 最小的 glTF 文件 + +以下是一个最小但完整的 glTF 资产,包含一个单独的、索引化的三角形。您可以将它复制并粘贴到一个 `gltf` 文件中,每个基于 glTF 的应用程序都应该能够加载并渲染它。本节将基于此示例解释 glTF 的基本概念。 + +```javascript +{ + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} +``` + +

+
+图 3a: 一个单独的三角形。 +

+ + +## `scene` 和 `nodes` 结构 + +[`scenes`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-scene) 数组是存储在 glTF 中的场景描述的入口点。在解析 glTF JSON 文件时,场景结构的遍历将从这里开始。每个场景包含一个名为 `nodes` 的数组,其中包含 [`node`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-node) 对象的索引。这些节点是场景图层次结构的根节点。 + +此示例由一个单独的场景组成。`scene` 属性表示该场景是加载资产时应显示的默认场景。场景引用了本示例中唯一的节点,即索引为 0 的节点。该节点反过来引用唯一的网格,索引为 0: + + +```javascript + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], +``` + +关于场景和节点及其属性的更多详细信息将在 [场景和节点](gltfTutorial_004_ScenesNodes.md) 部分给出。 + + +## `meshes`(网格) + +[`mesh`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh)(网格)表示出现在场景中的实际几何对象。网格本身通常没有任何属性,只包含一个 [`mesh.primitive`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh-primitive)(网格图元)对象数组,作为更大模型的构建块。每个网格图元都包含网格所包含的几何数据的描述。 + +该示例由一个单独的网格组成,并有一个单独的 `mesh.primitive` 对象。网格图元有一个 `attributes` 数组。这些是网格几何体顶点的属性,在这种情况下,这只是描述顶点位置的 `POSITION` 属性。网格图元描述了一个*索引化*的几何体,这由 `indices` 属性指示。默认情况下,假定它描述了一组三角形,以便三个连续的索引是一个三角形顶点的索引。 + +网格图元的实际几何数据由 `attributes` 和 `indices` 给出。这两者都引用 `accessor` 对象,将在下面解释。 + +```javascript + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], +``` + +关于网格和网格图元的更详细描述可以在 [网格](gltfTutorial_009_Meshes.md) 部分找到。 + + +## `buffer`、`bufferView` 和 `accessor` 概念 + +`buffer`、`bufferView` 和 `accessor` 对象提供了关于网格图元组成的几何数据的信息。基于特定示例,这里将简要介绍它们。这些概念的更详细描述将在 [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) 部分给出。 + +### 缓冲区 + +[`buffer`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer) 定义了一个没有固有含义的原始、非结构化数据块。它包含一个 `uri`,可以指向包含数据的外部文件,也可以是直接在 JSON 文件中编码二进制数据的 [数据 URI](gltfTutorial_002_BasicGltfStructure.md#数据-uri-中的二进制数据)。 + +在示例文件中,使用了第二种方法:有一个包含 44 字节的单一缓冲区,这个缓冲区的数据被编码为数据 URI: + +```javascript + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + } + ], +``` + +这些数据包含三角形的索引和三角形的顶点位置。但是为了实际使用这些数据作为网格图元的几何数据,需要关于这些数据*结构*的额外信息。这个关于结构的信息在 `bufferView` 和 `accessor` 对象中编码。 + +### 缓冲区视图 + +[`bufferView`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-bufferview) 描述了整个原始缓冲区数据的一个"块"或"片段"。在给定的示例中,有两个缓冲区视图。它们都引用同一个缓冲区。第一个缓冲区视图引用包含索引数据的缓冲区部分:它有一个 `byteOffset` 为 0,引用整个缓冲区数据,以及一个 `byteLength` 为 6。第二个缓冲区视图引用包含顶点位置的缓冲区部分。它从 `byteOffset` 为 8 的位置开始,并有一个 `byteLength` 为 36;也就是说,它延伸到整个缓冲区的末尾。 + +```javascript + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], +``` + + +### 访问器 + +数据结构化的第二步是通过 [`accessor`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-accessor) 对象完成的。它们通过提供关于数据类型和布局的信息来定义如何解释 `bufferView` 的数据。 + +在这个示例中,有两个访问器对象。 + +第一个访问器描述了几何数据的索引。它引用索引为 0 的 `bufferView`,这是包含索引原始数据的 `buffer` 部分。此外,它还指定了元素的 `count` 和 `type` 以及它们的 `componentType`。在这种情况下,有 3 个标量元素,它们的组件类型由代表 `unsigned short` 类型的常量给出。 + +第二个访问器描述了顶点位置。它通过索引为 1 的 `bufferView` 包含对缓冲区数据相关部分的引用,其 `count`、`type` 和 `componentType` 属性表明有三个 3D 向量元素,每个都有 `float` 组件。 + + +```javascript + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], +``` + +如上所述,`mesh.primitive` 现在可以使用这些访问器的索引引用它们: + +```javascript + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], +``` + +当需要渲染这个 `mesh.primitive` 时,渲染器可以解析底层的缓冲区视图和缓冲区,并将缓冲区的所需部分与关于数据类型和布局的信息一起发送到渲染器。关于访问器数据如何被渲染器获取和处理的更详细描述在 [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) 部分给出。 + + + + +## `asset` 描述 + +在 glTF 1.0 中,这个属性仍然是可选的,但在后续的 glTF 版本中,JSON 文件必须包含包含 `version` 编号的 `asset` 属性。这里的示例表明该资产符合 glTF 2.0 版本: + +```javascript + "asset" : { + "version" : "2.0" + } +``` + +`asset` 属性可能包含 [`asset` 规范](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-asset) 中描述的额外元数据。 + + + + +上一章: [基本 glTF 结构](gltfTutorial_002_BasicGltfStructure.md) | [目录](README.md) | 下一章: [场景和节点](gltfTutorial_004_ScenesNodes.md) \ No newline at end of file diff --git a/gltfTutorial_cn/gltfTutorial_004_ScenesNodes.md b/gltfTutorial_cn/gltfTutorial_004_ScenesNodes.md new file mode 100644 index 0000000..4c084a3 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_004_ScenesNodes.md @@ -0,0 +1,151 @@ +上一章: [最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) | [目录](README.md) | 下一章: [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) + +# 场景和节点 + +## 场景 + +一个 glTF 文件中可能存储多个场景。`scene` 属性指示这些场景中的哪一个应该作为加载资产时显示的默认场景。每个场景包含一个 `nodes` 数组,这是场景图的根节点的索引。同样,可能有多个根节点,形成不同的层次结构,但在许多情况下,场景将有一个单一的根节点。上一节中已经展示了最简单的场景描述,它由一个具有单个节点的场景组成: + +```javascript + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], +``` + + +## 形成场景图的节点 + +每个 [`node`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-node) 可以包含一个称为 `children` 的数组,该数组包含其子节点的索引。因此,每个节点都是节点层次结构中的一个元素,它们共同定义了场景的结构作为一个场景图。 + +

+
+图 4a: 存储在 glTF JSON 中的场景图表示。 +

+ +可以遍历场景中给出的每个节点,递归访问其所有子节点,以处理附加到节点的所有元素。这种遍历的简化伪代码可能如下所示: + +``` +traverse(node) { + // 处理附加到此节点的网格、相机等 + // - 将在后面讨论 + processElements(node); + + // 递归处理所有子节点 + for each (child in node.children) { + traverse(child); + } +} +``` + +在实践中,遍历需要一些额外的信息:处理附加到节点的某些元素将需要有关它们附加到*哪个*节点的信息。此外,在遍历期间必须累积有关节点变换的信息。 + + +### 局部和全局变换 + +每个节点可以有一个变换。这种变换将定义平移、旋转和/或缩放。这个变换将应用于附加到节点本身的所有元素以及其所有子节点。因此,节点层次结构使得可以构建应用于场景元素的平移、旋转和缩放。 + + +#### 节点的局部变换 + +节点的局部变换有不同的可能表示方式。变换可以由节点的 `matrix` 属性直接给出。这是一个包含 16 个浮点数的数组,它以列主序描述矩阵。例如,以下矩阵描述了关于 (2,1,0.5) 的缩放,围绕 x 轴旋转 30 度,以及关于 (10,20,30) 的平移: + +```javascript +"node0": { + "matrix": [ + 2.0, 0.0, 0.0, 0.0, + 0.0, 0.866, 0.5, 0.0, + 0.0, -0.25, 0.433, 0.0, + 10.0, 20.0, 30.0, 1.0 + ] +} +``` + +这里定义的矩阵如图 4b 所示。 + +

+
+图 4b: 一个示例矩阵。 +

+ + +节点的变换也可以使用节点的 `translation`、`rotation` 和 `scale` 属性给出,有时缩写为 *TRS*: + +```javascript +"node0": { + "translation": [ 10.0, 20.0, 30.0 ], + "rotation": [ 0.259, 0.0, 0.0, 0.966 ], + "scale": [ 2.0, 1.0, 0.5 ] +} +``` + +这些属性中的每一个都可以用来创建一个矩阵,这些矩阵的乘积就是节点的局部变换: + +- `translation` 只包含 x、y 和 z 方向上的平移。例如,从 `[ 10.0, 20.0, 30.0 ]` 的平移中,可以创建一个平移矩阵,它在最后一列中包含这个平移,如图 4c 所示。 + +

+
+图 4c: 一个平移矩阵。 +

+ + +- `rotation` 以 [四元数](https://en.wikipedia.org/wiki/Quaternion) 的形式给出。四元数的数学背景超出了本教程的范围。目前,最重要的信息是四元数是围绕任意角度和任意轴的旋转的紧凑表示。它存储为一个元组 `(x,y,z,w)`,其中 `w` 分量是旋转角度一半的余弦。例如,四元数 `[ 0.259, 0.0, 0.0, 0.966 ]` 描述了围绕 x 轴旋转 30 度。所以这个四元数可以转换为一个旋转矩阵,如图 4d 所示。 + +

+
+图 4d: 一个旋转矩阵。 +

+ + +- `scale` 包含沿 x、y 和 z 轴的缩放因子。相应的矩阵可以通过将这些缩放因子用作矩阵对角线上的条目来创建。例如,缩放因子 `[ 2.0, 1.0, 0.5 ]` 的缩放矩阵如图 4e 所示。 + +

+
+图 4e: 一个缩放矩阵。 +

+ +计算节点的最终局部变换矩阵时,这些矩阵会相乘。按正确的顺序执行这些矩阵的乘法很重要。局部变换矩阵必须始终计算为 `M = T * R * S`,其中 `T` 是 `translation` 部分的矩阵,`R` 是 `rotation` 部分的矩阵,`S` 是 `scale` 部分的矩阵。因此,计算的伪代码是 + +``` +translationMatrix = createTranslationMatrix(node.translation); +rotationMatrix = createRotationMatrix(node.rotation); +scaleMatrix = createScaleMatrix(node.scale); +localTransform = translationMatrix * rotationMatrix * scaleMatrix; +``` + +对于上面给出的示例矩阵,节点的最终局部变换矩阵将如图 4f 所示。 + +

+
+图 4f: 从 TRS 属性计算的最终局部变换矩阵。 +

+ +该矩阵将使网格的顶点按照节点中给出的 `scale`、`rotation` 和 `translation` 属性进行缩放、旋转和平移。 + +当未给出三个属性中的任何一个时,将使用恒等矩阵。同样,当节点既不包含 `matrix` 属性也不包含 TRS 属性时,其局部变换将是恒等矩阵。 + + + +#### 节点的全局变换 + +无论 JSON 文件中的表示如何,节点的局部变换都可以存储为一个 4×4 矩阵。节点的*全局*变换由从根到相应节点的路径上所有局部变换的乘积给出: + + 结构: 局部变换 全局变换 + root R R + +- nodeA A R*A + +- nodeB B R*A*B + +- nodeC C R*A*C + +需要指出的是,在文件加载后,这些全局变换*不能*只计算一次。后面将展示*动画*如何修改单个节点的局部变换。这些修改将影响所有后代节点的全局变换。因此,当需要节点的全局变换时,必须直接从所有节点的当前局部变换计算它。或者,作为潜在的性能改进,实现可以缓存全局变换,检测祖先节点的局部变换的变化,并仅在必要时更新全局变换。这方面的不同实现选项将取决于编程语言和客户端应用程序的要求,因此超出了本教程的范围。 + + + +上一章: [最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) | [目录](README.md) | 下一章: [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) diff --git a/gltfTutorial_cn/gltfTutorial_005_BuffersBufferViewsAccessors.md b/gltfTutorial_cn/gltfTutorial_005_BuffersBufferViewsAccessors.md new file mode 100644 index 0000000..afb03c2 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_005_BuffersBufferViewsAccessors.md @@ -0,0 +1,262 @@ +上一章: [场景和节点](gltfTutorial_004_ScenesNodes.md) | [目录](README.md) | 下一章: [简单动画](gltfTutorial_006_SimpleAnimation.md) + +# 缓冲区、缓冲区视图和访问器 + +`buffer`、`bufferView` 和 `accessor` 对象的示例已经在 [最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) 部分给出。本节将更详细地解释这些概念。 + +## 缓冲区 + +[`buffer`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-buffer) 表示一块原始二进制数据,没有固有的结构或含义。缓冲区使用其 `uri` 引用这些数据。这个 URI 可以指向外部文件,也可以是 [数据 URI](gltfTutorial_002_BasicGltfStructure.md#数据-uri-中的二进制数据),它直接在 JSON 文件中编码二进制数据。[最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) 包含了一个 `buffer` 的示例,其中有 44 字节的数据,编码为数据 URI: + +```javascript + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + } + ], +``` + +

+
+图 5a: 缓冲区数据,由 44 字节组成。 +

+ +`buffer` 数据的部分可能必须作为顶点属性或索引传递给渲染器,或者数据可能包含蒙皮信息或动画关键帧。为了能够使用这些数据,需要有关此数据结构和类型的额外信息。 + +## 缓冲区视图 + +使用 [`bufferView`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-bufferview) 对象是结构化 `buffer` 数据的第一步。`bufferView` 表示一个缓冲区数据的"切片"。这个切片使用偏移量和长度(以字节为单位)来定义。[最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) 定义了两个 `bufferView` 对象: + +```javascript + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], +``` + +第一个 `bufferView` 引用缓冲区数据的前 6 个字节。第二个引用缓冲区的 36 个字节,偏移量为 8 字节,如下图所示: + +

+
+图 5b: 缓冲区视图,引用缓冲区的部分。 +

+ +浅灰色显示的字节是填充字节,这些字节是为了正确对齐访问器所必需的,如下所述。 + +每个 `bufferView` 还包含一个 `target` 属性。渲染器稍后可以使用此属性来分类缓冲区视图引用的数据的类型或性质。`target` 可以是一个常量,表示数据用于顶点属性(`34962`,代表 `ARRAY_BUFFER`),或者数据用于顶点索引(`34963`,代表 `ELEMENT_ARRAY_BUFFER`)。 + +在这一点上,`buffer` 数据已被分成多个部分,每个部分由一个 `bufferView` 描述。但是为了在渲染器中真正使用这些数据,还需要有关数据类型和布局的额外信息。 + +## 访问器 + +[`accessor`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-accessor) 对象引用一个 `bufferView` 并包含定义此 `bufferView` 数据类型和布局的属性。 + +### 数据类型 + +访问器数据的类型编码在 `type` 和 `componentType` 属性中。`type` 属性的值是一个字符串,指定数据元素是标量、向量还是矩阵。例如,值可能是 `"SCALAR"` 表示标量值,`"VEC3"` 表示 3D 向量,或者 `"MAT4"` 表示 4×4 矩阵。 + +`componentType` 指定这些数据元素的组件类型。这是一个 GL 常量,可能是例如 `5126`(`FLOAT`)或 `5123`(`UNSIGNED_SHORT`),分别表示元素具有 `float` 或 `unsigned short` 组件。 + +这些属性的不同组合可用于描述任意数据类型。例如,[最小的 glTF 文件](gltfTutorial_003_MinimalGltfFile.md) 包含两个访问器: + +```javascript + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], +``` + +第一个访问器引用索引为 0 的 `bufferView`,它定义了包含索引的 `buffer` 数据部分。其 `type` 是 `"SCALAR"`,其 `componentType` 是 `5123`(`UNSIGNED_SHORT`)。这意味着索引存储为标量 `unsigned short` 值。 + +第二个访问器引用索引为 1 的 `bufferView`,它定义了包含顶点属性的 `buffer` 数据部分 - 特别是顶点位置。其 `type` 是 `"VEC3"`,其 `componentType` 是 `5126`(`FLOAT`)。所以这个访问器描述了具有浮点组件的 3D 向量。 + +### 数据布局 + +访问器的其他属性进一步指定数据的布局。访问器的 `count` 属性表示它由多少个数据元素组成。在上面的例子中,两个访问器的计数都是 `3`,分别代表三角形的三个索引和三个顶点。每个访问器还有一个 `byteOffset` 属性。对于上面的例子,两个访问器的偏移量都是 `0`,因为每个 `bufferView` 只有一个 `accessor`。但是当多个访问器引用同一个 `bufferView` 时,`byteOffset` 描述了访问器的数据相对于它引用的 `bufferView` 从哪里开始。 + +### 数据对齐 + +`accessor` 引用的数据可能被发送到图形卡进行渲染,或者在主机端作为动画或蒙皮数据使用。因此,`accessor` 的数据必须根据数据的*类型*进行对齐。例如,当 `accessor` 的 `componentType` 是 `5126`(`FLOAT`)时,数据必须在 4 字节边界上对齐,因为单个 `float` 值由四个字节组成。访问器的这种对齐要求涉及其 `bufferView` 和底层的 `buffer`。特别是,对齐要求如下: + +- `accessor` 的 `byteOffset` 必须能被其 `componentType` 的大小整除。 +- 访问器的 `byteOffset` 和它引用的 `bufferView` 的 `byteOffset` 之和必须能被其 `componentType` 的大小整除。 + +在上面的例子中,索引为 1 的 `bufferView`(引用顶点属性)的 `byteOffset` 被选为 `8`,以便将顶点位置访问器的数据对齐到 4 字节边界。因此,`buffer` 的第 `6` 和 `7` 字节是*填充*字节,不包含相关数据。 + +图 5c 说明了如何使用 `bufferView` 对象结构化 `buffer` 的原始数据,并使用 `accessor` 对象增加数据类型信息。 + +

+
+图 5c: 访问器定义如何解释缓冲区视图的数据。 +

+ +### 数据交错 + +存储在单个 `bufferView` 中的属性数据可以作为*结构数组*存储。例如,单个 `bufferView` 可能以交错方式包含顶点位置和顶点法线的数据。在这种情况下,访问器的 `byteOffset` 定义相应属性的第一个相关数据元素的开始,而 `bufferView` 定义了额外的 `byteStride` 属性。这是其访问器的一个元素的开始与下一个元素的开始之间的字节数。图 5d 显示了交错的位置和法线属性如何存储在 `bufferView` 中的示例。 + +

+
+图 5d: 一个缓冲区视图中的交错访问器。 +

+ +### 数据内容 + +`accessor` 还包含 `min` 和 `max` 属性,这些属性汇总了它们数据的内容。它们是访问器中包含的所有数据元素的分量最小值和最大值。在顶点位置的情况下,`min` 和 `max` 属性因此定义了对象的*边界框*。这对于优先下载或可见性检测可能很有用。一般来说,这些信息对于存储和处理*量化*数据也很有用,这些数据在运行时由渲染器进行反量化,但是这种量化的细节超出了本教程的范围。 + +## 稀疏访问器(Sparse accessors) + +在 glTF 2.0 版本中,引入了*稀疏访问器*的概念。这是一种特殊的数据表示,允许非常紧凑地存储仅有少数不同条目的多个数据块。例如,当存在包含顶点位置的几何数据时,这些几何数据可能用于多个对象。这可以通过从两个对象引用同一个 `accessor` 来实现。如果两个对象的顶点位置大部分相同,仅对少数顶点有所不同,那么就不必将整个几何数据存储两次。相反,可以只存储一次数据,并使用稀疏访问器仅存储第二个对象有所不同的顶点位置。 + +以下是一个完整的 glTF 资产,以嵌入式表示形式展示了稀疏访问器的示例: + +```javascript +{ + "scenes" : [ { + "nodes" : [ 0 ] + } ], + + "nodes" : [ { + "mesh" : 0 + } ], + + "meshes" : [ { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } ], + + "buffers" : [ { + "uri" : "data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=", + "byteLength" : 284 + } ], + + "bufferViews" : [ { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 72, + "target" : 34963 + }, { + "buffer" : 0, + "byteOffset" : 72, + "byteLength" : 168 + }, { + "buffer" : 0, + "byteOffset" : 240, + "byteLength" : 6 + }, { + "buffer" : 0, + "byteOffset" : 248, + "byteLength" : 36 + } ], + + "accessors" : [ { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 36, + "type" : "SCALAR", + "max" : [ 13 ], + "min" : [ 0 ] + }, { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 14, + "type" : "VEC3", + "max" : [ 6.0, 4.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ], + "sparse" : { + "count" : 3, + "indices" : { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5123 + }, + "values" : { + "bufferView" : 3, + "byteOffset" : 0 + } + } + } ], + + "asset" : { + "version" : "2.0" + } +} +``` + +渲染此资产的结果如图 5e 所示: + +

+
+图 5e: 渲染简单稀疏访问器资产的结果。 +

+ +示例包含两个访问器:一个用于网格的索引,一个用于顶点位置。引用顶点位置的访问器定义了一个额外的 `accessor.sparse` 属性,其中包含有关应该应用的稀疏数据替换的信息: + +```javascript + "accessors" : [ + ... + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 14, + "type" : "VEC3", + "max" : [ 6.0, 4.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ], + "sparse" : { + "count" : 3, + "indices" : { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5123 + }, + "values" : { + "bufferView" : 3, + "byteOffset" : 0 + } + } + } ], +``` + +这个 `sparse` 对象本身定义了将受替换影响的元素的 `count`。`sparse.indices` 属性引用一个 `bufferView`,其中包含将被替换的元素的索引。`sparse.values` 引用一个 `bufferView`,其中包含实际数据。 + +在示例中,原始几何数据存储在索引为 1 的 `bufferView` 中。它描述了一个矩形顶点数组。`sparse.indices` 引用索引为 2 的 `bufferView`,其中包含索引 `[8, 10, 12]`。`sparse.values` 引用索引为 3 的 `bufferView`,其中包含新的顶点位置,即 `[(1,2,0), (3,3,0), (5,4,0)]`。应用相应替换的效果如图 5f 所示。 + +

+
+图 5f: 使用稀疏访问器进行的替换。 +

+ +上一章: [场景和节点](gltfTutorial_004_ScenesNodes.md) | [目录](README.md) | 下一章: [简单动画](gltfTutorial_006_SimpleAnimation.md) diff --git a/gltfTutorial_cn/gltfTutorial_006_SimpleAnimation.md b/gltfTutorial_cn/gltfTutorial_006_SimpleAnimation.md new file mode 100644 index 0000000..4ef85da --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_006_SimpleAnimation.md @@ -0,0 +1,277 @@ +上一章: [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) | [目录](README.md) | 下一章: [动画](gltfTutorial_007_Animations.md) + + +# 简单动画 + +如 [场景和节点](gltfTutorial_004_ScenesNodes.md) 部分所示,每个节点都可以有一个局部变换。这个变换可以通过节点的 `matrix` 属性给出,或者使用 `translation`、`rotation` 和 `scale`(TRS)属性。 + +当变换通过 TRS 属性给出时,可以使用 [`animation`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation) 来描述节点的 `translation`、`rotation` 或 `scale` 如何随时间变化。 + +以下是之前展示的[最小 glTF 文件](gltfTutorial_003_MinimalGltfFile.md),但扩展了一个动画。本节将解释为添加此动画所做的更改和扩展。 + + +```javascript +{ + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0, + "rotation" : [ 0.0, 0.0, 0.0, 1.0 ] + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], + + "animations": [ + { + "samplers" : [ + { + "input" : 2, + "interpolation" : "LINEAR", + "output" : 3 + } + ], + "channels" : [ { + "sampler" : 0, + "target" : { + "node" : 0, + "path" : "rotation" + } + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + }, + { + "uri" : "data:application/octet-stream;base64,AAAAAAAAgD4AAAA/AABAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAPT9ND/0/TS/AAAAAAAAAAAAAAAAAACAPw==", + "byteLength" : 100 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + }, + { + "buffer" : 1, + "byteOffset" : 0, + "byteLength" : 100 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, + { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 5, + "type" : "SCALAR", + "max" : [ 1.0 ], + "min" : [ 0.0 ] + }, + { + "bufferView" : 2, + "byteOffset" : 20, + "componentType" : 5126, + "count" : 5, + "type" : "VEC4", + "max" : [ 0.0, 0.0, 1.0, 1.0 ], + "min" : [ 0.0, 0.0, 0.0, -0.707 ] + } + ], + + "asset" : { + "version" : "2.0" + } + +} +``` + +

+
+图 6a: 一个单独的、带动画的三角形。 +

+ + +## 节点的 `rotation` 属性 + +示例中的唯一节点现在有一个 `rotation` 属性。这是一个包含描述旋转的[四元数](https://en.wikipedia.org/wiki/Quaternion)的四个浮点值的数组: + +```javascript + "nodes" : [ + { + "mesh" : 0, + "rotation" : [ 0.0, 0.0, 0.0, 1.0 ] + } + ], +``` + +给定的值是描述"旋转 0 度"的四元数,因此三角形将以其初始方向显示。 + + +## 动画数据 + +在 glTF JSON 的顶层数组中添加了三个元素来编码动画数据: + +- 一个包含原始动画数据的新 `buffer`; +- 一个引用该缓冲区的新 `bufferView`; +- 两个新的 `accessor` 对象,为动画数据添加结构信息。 + +### 原始动画数据的 `buffer` 和 `bufferView` + +已添加一个新的 `buffer`,其中包含原始动画数据。这个缓冲区也使用[数据 URI](gltfTutorial_002_BasicGltfStructure.md#数据-uri-中的二进制数据)来编码组成动画数据的 100 个字节: + +```javascript + "buffers" : [ + ... + { + "uri" : "data:application/octet-stream;base64,AAAAAAAAgD4AAAA/AABAPwAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAPT9ND/0/TS/AAAAAAAAAAAAAAAAAACAPw==", + "byteLength" : 100 + } + ], + + "bufferViews" : [ + ... + { + "buffer" : 1, + "byteOffset" : 0, + "byteLength" : 100 + } + ], +``` + +还有一个新的 `bufferView`,它在这里简单地引用包含整个动画缓冲区数据的索引为 1 的新 `buffer`。更多的结构信息通过下面描述的 `accessor` 对象添加。 + +请注意,也可以将动画数据附加到已经包含三角形几何数据的现有缓冲区中。在这种情况下,新的缓冲区视图将引用索引为 0 的 `buffer`,并使用适当的 `byteOffset` 来引用包含动画数据的缓冲区部分。 + +在这里显示的示例中,动画数据作为新缓冲区添加,以保持几何数据和动画数据分离。 + + +### 动画数据的 `accessor` 对象 + +添加了两个新的 `accessor` 对象,描述如何解释动画数据。第一个访问器描述动画关键帧的*时间*。有五个元素(由 `count` 5 表示),每个元素都是一个标量 `float` 值(总共 20 个字节)。第二个访问器表示,在前 20 个字节之后,有五个元素,每个元素都是具有 `float` 组件的 4D 向量。这些是对应于动画五个关键帧的*旋转*,以四元数给出。 + +```javascript + "accessors" : [ + ... + { + "bufferView" : 2, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 5, + "type" : "SCALAR", + "max" : [ 1.0 ], + "min" : [ 0.0 ] + }, + { + "bufferView" : 2, + "byteOffset" : 20, + "componentType" : 5126, + "count" : 5, + "type" : "VEC4", + "max" : [ 0.0, 0.0, 1.0, 1.0 ], + "min" : [ 0.0, 0.0, 0.0, -0.707 ] + } + ], + +``` + +使用示例中缓冲区的数据,*时间*访问器和*旋转*访问器提供的实际数据如下表所示: + +|*时间*访问器|*旋转*访问器|含义| +|---|---|---| +|0.0| (0.0, 0.0, 0.0, 1.0 )| 在 0.0 秒时,三角形的旋转为 0 度 | +|0.25| (0.0, 0.0, 0.707, 0.707)| 在 0.25 秒时,它围绕 z 轴旋转 90 度 +|0.5| (0.0, 0.0, 1.0, 0.0)| 在 0.5 秒时,它围绕 z 轴旋转 180 度 | +|0.75| (0.0, 0.0, 0.707, -0.707)| 在 0.75 秒时,它围绕 z 轴旋转 270 (= -90) 度 | +|1.0| (0.0, 0.0, 0.0, 1.0)| 在 1.0 秒时,它围绕 z 轴旋转 360 (= 0) 度 | + +所以这个动画描述了围绕 z 轴的 360 度旋转,持续 1 秒。 + + +## `animation`(动画) + +最后,这是添加实际动画的部分。顶层 `animations` 数组包含一个单独的 `animation` 对象。它由两个元素组成: + +- `samplers`,描述动画数据的来源; +- `channels`,可以想象为连接动画数据的"源"和"目标"。 + +在给定的示例中,有一个采样器。每个采样器定义了 `input` 和 `output` 属性。它们都引用访问器对象。在这里,这些是上面描述的*时间*访问器(索引为 2)和*旋转*访问器(索引为 3)。此外,采样器定义了一个 `interpolation` 类型,在本例中为 `"LINEAR"`。 + +这个例子中也有一个 `channel`。这个通道将唯一的采样器(索引为 0)作为动画数据的来源。动画的目标在 `channel.target` 对象中编码:它包含一个引用其属性应该被动画化的节点的 `id`。实际的节点属性在 `path` 中命名。因此,给定示例中的通道目标表示节点索引为 0 的 `"rotation"` 属性应该被动画化。 + + +```javascript + "animations": [ + { + "samplers" : [ + { + "input" : 2, + "interpolation" : "LINEAR", + "output" : 3 + } + ], + "channels" : [ { + "sampler" : 0, + "target" : { + "node" : 0, + "path" : "rotation" + } + } ] + } + ], +``` + +结合所有这些信息,给定的动画对象表示以下内容: + +> 在动画期间,动画值从*旋转*访问器获取。它们根据当前模拟时间和*时间*访问器提供的关键帧时间进行线性插值。然后将插值后的值写入索引为 0 的节点的 `"rotation"` 属性中。 + +关于插值和相关计算的更详细描述和实际示例可以在[动画](gltfTutorial_007_Animations.md)部分找到。 + + + +上一章: [缓冲区、缓冲区视图和访问器](gltfTutorial_005_BuffersBufferViewsAccessors.md) | [目录](README.md) | 下一章: [动画](gltfTutorial_007_Animations.md) diff --git a/gltfTutorial_cn/gltfTutorial_007_Animations.md b/gltfTutorial_cn/gltfTutorial_007_Animations.md new file mode 100644 index 0000000..bf9355a --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_007_Animations.md @@ -0,0 +1,222 @@ +Previous: [Simple Animation](gltfTutorial_006_SimpleAnimation.md) | [Table of Contents](README.md) | Next: [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) + +# Animations + +As shown in the [Simple Animation](gltfTutorial_006_SimpleAnimation.md) example, an [`animation`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation) can be used to describe how the `translation`, `rotation`, or `scale` properties of nodes change over time. + +The following is another example of an `animation`. This time, the animation contains two channels. One animates the translation, and the other animates the rotation of a node: + +```javascript + "animations": [ + { + "samplers" : [ + { + "input" : 2, + "interpolation" : "LINEAR", + "output" : 3 + }, + { + "input" : 2, + "interpolation" : "LINEAR", + "output" : 4 + } + ], + "channels" : [ + { + "sampler" : 0, + "target" : { + "node" : 0, + "path" : "rotation" + } + }, + { + "sampler" : 1, + "target" : { + "node" : 0, + "path" : "translation" + } + } + ] + } + ], +``` + + +## Animation samplers + +The `samplers` array contains [`animation.sampler`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation-sampler) objects that define how the values that are provided by the accessors have to be interpolated between the key frames, as shown in Image 7a. + +

+
+Image 7a: Animation samplers. +

+ +In order to compute the value of the translation for the current animation time, the following algorithm can be used: + +* Let the current animation time be given as `currentTime`. +* Compute the next smaller and the next larger element of the *times* accessor: + + `previousTime` = The largest element from the *times* accessor that is smaller than the `currentTime` + + `nextTime` = The smallest element from the *times* accessor that is larger than the `currentTime` + +* Obtain the elements from the *translations* accessor that correspond to these times: + + `previousTranslation` = The element from the *translations* accessor that corresponds to the `previousTime` + + `nextTranslation` = The element from the *translations* accessor that corresponds to the `nextTime` + +* Compute the interpolation value. This is a value between 0.0 and 1.0 that describes the *relative* position of the `currentTime`, between the `previousTime` and the `nextTime`: + + `interpolationValue = (currentTime - previousTime) / (nextTime - previousTime)` + +* Use the interpolation value to compute the translation for the current time: + + `currentTranslation = previousTranslation + interpolationValue * (nextTranslation - previousTranslation)` + + +### Example: + +Imagine the `currentTime` is **1.2**. The next smaller element from the *times* accessor is **0.8**. The next larger element is **1.6**. So + + previousTime = 0.8 + nextTime = 1.6 + +The corresponding values from the *translations* accessor can be looked up: + + previousTranslation = (14.0, 3.0, -2.0) + nextTranslation = (18.0, 1.0, 1.0) + +The interpolation value can be computed: + + interpolationValue = (currentTime - previousTime) / (nextTime - previousTime) + = (1.2 - 0.8) / (1.6 - 0.8) + = 0.4 / 0.8 + = 0.5 + +From the interpolation value, the current translation can be computed: + + currentTranslation = previousTranslation + interpolationValue * (nextTranslation - previousTranslation) + = (14.0, 3.0, -2.0) + 0.5 * ( (18.0, 1.0, 1.0) - (14.0, 3.0, -2.0) ) + = (14.0, 3.0, -2.0) + 0.5 * (4.0, -2.0, 3.0) + = (16.0, 2.0, -0.5) + +So when the current time is **1.2**, then the `translation` of the node is **(16.0, 2.0, -0.5)**. + + + +## Animation channels + +The animations contain an array of [`animation.channel`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation-channel) objects. The channels establish the connection between the input, which is the value that is computed from the sampler, and the output, which is the animated node property. Therefore, each channel refers to one sampler, using the index of the sampler, and contains an [`animation.channel.target`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-animation-channel-target). The `target` refers to a node, using the index of the node, and contains a `path` that defines the property of the node that should be animated. The value from the sampler will be written into this property. + +In the example above, there are two channels for the animation. Both refer to the same node. The path of the first channel refers to the `translation` of the node, and the path of the second channel refers to the `rotation` of the node. So all objects (meshes) that are attached to the node will be translated and rotated by the animation, as shown in Image 7b. + +

+
+Image 7b: Animation channels. +

+ +## Interpolation + +The above example only covers `LINEAR` interpolation. Animations in a glTF asset can use three interpolation modes : + + - `STEP` + - `LINEAR` + - `CUBICSPLINE` + +### Step + +The `STEP` interpolation is not really an interpolation mode, it makes objects jump from keyframe to keyframe *without any sort of interpolation*. When a sampler defines a step interpolation, just apply the transformation from the keyframe corresponding to `previousTime`. + +### Linear + +Linear interpolation exactly corresponds to the above example. The general case is : + +Calculate the `interpolationValue`: + +``` + interpolationValue = (currentTime - previousTime) / (nextTime - previousTime) +``` + +For scalar and vector types, use a linear interpolation (generally called `lerp` in mathematics libraries). Here's a "pseudo code" implementation for reference + +``` + Point lerp(previousPoint, nextPoint, interpolationValue) + return previousPoint + interpolationValue * (nextPoint - previousPoint) +``` + +In the case of rotations expressed as quaternions, you need to perform a spherical linear interpolation (`slerp`) between the previous and next values: + +``` + Quat slerp(previousQuat, nextQuat, interpolationValue) + var dotProduct = dot(previousQuat, nextQuat) + + //make sure we take the shortest path in case dot Product is negative + if(dotProduct < 0.0) + nextQuat = -nextQuat + dotProduct = -dotProduct + + //if the two quaternions are too close to each other, just linear interpolate between the 4D vector + if(dotProduct > 0.9995) + return normalize(previousQuat + interpolationValue(nextQuat - previousQuat)) + + //perform the spherical linear interpolation + var theta_0 = acos(dotProduct) + var theta = interpolationValue * theta_0 + var sin_theta = sin(theta) + var sin_theta_0 = sin(theta_0) + + var scalePreviousQuat = cos(theta) - dotproduct * sin_theta / sin_theta_0 + var scaleNextQuat = sin_theta / sin_theta_0 + return scalePreviousQuat * previousQuat + scaleNextQuat * nextQuat +``` + +This example implementation is inspired from this [Wikipedia article](https://en.wikipedia.org/wiki/Slerp) + +### Cubic Spline interpolation + +Cubic spline interpolation needs more data than just the previous and next keyframe time and values, it also need for each keyframe a couple of tangent vectors that act to smooth out the curve around the keyframe points. + +These tangent are stored in the animation channel. For each keyframe described by the animation sampler, the animation channel contains 3 elements : + + - The input tangent of the keyframe + - The keyframe value + - The output tangent + + The input and output tangents are normalized vectors that will need to be scaled by the duration of the keyframe, we call that the deltaTime + + ``` + deltaTime = nextTime - previousTime + ``` + + To calculate the value for `currentTime`, you will need to fetch from the animation channel : + + - The output tangent direction of `previousTime` keyframe + - The value of `previousTime` keyframe + - The value of `nextTime` keyframe + - The input tangent direction of `nextTime` keyframe + +*note: the input tangent of the first keyframe and the output tangent of the last keyframe are totally ignored* + +To calculate the actual tangents of the keyframe, you need to multiply the direction vectors you got from the channel by `deltaTime` + +``` + previousTangent = deltaTime * previousOutputTangent + nextTangent = deltaTime * nextInputTangent +``` + +The mathematical function is described in the [Appendix C](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#appendix-c-interpolation) of the glTF 2.0 specification. + +Here's a corresponding pseudocode snippet : + +``` + Point cubicSpline(previousPoint, previousTangent, nextPoint, nextTangent, interpolationValue) + t = interpolationValue + t2 = t * t + t3 = t2 * t + + return (2 * t3 - 3 * t2 + 1) * previousPoint + (t3 - 2 * t2 + t) * previousTangent + (-2 * t3 + 3 * t2) * nextPoint + (t3 - t2) * nextTangent; +``` + + +Previous: [Simple Animation](gltfTutorial_006_SimpleAnimation.md) | [Table of Contents](README.md) | Next: [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) diff --git a/gltfTutorial_cn/gltfTutorial_008_SimpleMeshes.md b/gltfTutorial_cn/gltfTutorial_008_SimpleMeshes.md new file mode 100644 index 0000000..3b0ef65 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_008_SimpleMeshes.md @@ -0,0 +1,146 @@ +Previous: [Animations](gltfTutorial_007_Animations.md) | [Table of Contents](README.md) | Next: [Meshes](gltfTutorial_009_Meshes.md) + +# Simple Meshes + +A [`mesh`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh) represents a geometric object that appears in a scene. An example of a `mesh` has already been shown in the [minimal glTF file](gltfTutorial_003_MinimalGltfFile.md). This example had a single `mesh` attached to a single `node`, and the mesh consisted of a single [`mesh.primitive`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh-primitive) that contained only a single attribute—namely, the attribute for the vertex positions. But usually, the mesh primitives will contain more attributes. These attributes may, for example, be the vertex normals or texture coordinates. + +The following is a glTF asset that contains a simple mesh with multiple attributes, which will serve as the basis for explaining the related concepts: + +```javascript +{ + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0, 1] + } + ], + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8=", + "byteLength" : 80 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 72, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 36, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 0.0, 0.0, 1.0 ], + "min" : [ 0.0, 0.0, 1.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} +``` + +Image 8a shows the rendered glTF asset. + +

+
+Image 8a: A simple mesh, attached to two nodes. +

+ + +## The mesh definition + +The given example still contains a single mesh that has a single mesh primitive. But this mesh primitive contains multiple attributes: + +```javascript + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], +``` + +In addition to the `"POSITION"` attribute, it has a `"NORMAL"` attribute. This refers to the `accessor` object that provides the vertex normals, as described in the [Buffers, BufferViews, and Accessors](gltfTutorial_005_BuffersBufferViewsAccessors.md) section. + + +## The rendered mesh instances + +As can be seen in Image 8a, the mesh is rendered *twice*. This is accomplished by attaching the mesh to two different nodes: + +```javascript + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], +``` + +The `mesh` property of each node refers to the mesh that is attached to the node, using the index of the mesh. One of the nodes has a `translation` that causes the attached mesh to be rendered at a different position. + +The [next section](gltfTutorial_009_Meshes.md) will explain meshes and mesh primitives in more detail. + + + +Previous: [Animations](gltfTutorial_007_Animations.md) | [Table of Contents](README.md) | Next: [Meshes](gltfTutorial_009_Meshes.md) diff --git a/gltfTutorial_cn/gltfTutorial_009_Meshes.md b/gltfTutorial_cn/gltfTutorial_009_Meshes.md new file mode 100644 index 0000000..900f00a --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_009_Meshes.md @@ -0,0 +1,88 @@ +Previous: [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) | [Table of Contents](README.md) | Next: [Materials](gltfTutorial_010_Materials.md) + +# Meshes + +The [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) example from the previous section showed a basic example of a [`mesh`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh) with a [`mesh.primitive`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-mesh-primitive) object that contained several attributes. This section will explain the meaning and usage of mesh primitives, how meshes may be attached to nodes of the scene graph, and how they can be rendered with different materials. + + +## Mesh primitives + +Each `mesh` contains an array of `mesh.primitive` objects. These mesh primitive objects are smaller parts or building blocks of a larger object. A mesh primitive summarizes all information about how the respective part of the object will be rendered. + + +### Mesh primitive attributes + +A mesh primitive defines the geometry data of the object using its `attributes` dictionary. This geometry data is given by references to `accessor` objects that contain the data of vertex attributes. The details of the `accessor` concept are explained in the [Buffers, BufferViews, and Accessors](gltfTutorial_005_BuffersBufferViewsAccessors.md) section. + +In the given example, there are two entries in the `attributes` dictionary. The entries refer to the `positionsAccessor` and the `normalsAccessor`: + +```javascript + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "NORMAL" : 2 + }, + "indices" : 0 + } ] + } + ], +``` + +Together, the elements of these accessors define the attributes that belong to the individual vertices, as shown in Image 9a. + +

+
+Image 9a: Mesh primitive accessors containing the data of vertices. +

+ + +### Indexed and non-indexed geometry + +The geometry data of a `mesh.primitive` may be either *indexed* geometry or geometry without indices. In the given example, the `mesh.primitive` contains *indexed* geometry. This is indicated by the `indices` property, which refers to the accessor with index 0, defining the data for the indices. For non-indexed geometry, this property is omitted. + + +### Mesh primitive mode + +By default, the geometry data is assumed to describe a triangle mesh. For the case of *indexed* geometry, this means that three consecutive elements of the `indices` accessor are assumed to contain the indices of a single triangle. For non-indexed geometry, three elements of the vertex attribute accessors are assumed to contain the attributes of the three vertices of a triangle. + +Other rendering modes are possible: the geometry data may also describe individual points, lines, or triangle strips. This is indicated by the `mode` that may be stored in the mesh primitive. Its value is a constant that indicates how the geometry data has to be interpreted. The mode may, for example, be `0` when the geometry consists of points, or `4` when it consists of triangles. These constants correspond to the GL constants `POINTS` or `TRIANGLES`, respectively. See the [`primitive.mode` specification](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode) for a list of available modes. + +### Mesh primitive material + +The mesh primitive may also refer to the `material` that should be used for rendering, using the index of this material. In the given example, no `material` is defined, causing the objects to be rendered with a default material that just defines the objects to have a uniform 50% gray color. A detailed explanation of materials and the related concepts will be given in the [Materials](gltfTutorial_010_Materials.md) section. + + +## Meshes attached to nodes + +In the example from the [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) section, there is a single `scene`, which contains two nodes, and both nodes refer to the same `mesh` instance, which has the index 0: + +```javascript + "scenes" : [ + { + "nodes" : [ 0, 1] + } + ], + "nodes" : [ + { + "mesh" : 0 + }, + { + "mesh" : 0, + "translation" : [ 1.0, 0.0, 0.0 ] + } + ], + + "meshes" : [ + { ... } + ], +``` + +The second node has a `translation` property. As shown in the [Scenes and Nodes](gltfTutorial_004_ScenesNodes.md) section, this will be used to compute the local transform matrix of this node. In this case, the matrix will cause a translation of 1.0 along the x-axis. The product of all local transforms of the nodes will yield the [global transform](gltfTutorial_004_ScenesNodes.md#global-transforms-of-nodes). And all elements that are attached to the nodes will be rendered with this global transform. + +So in this example, the mesh will be rendered twice because it is attached to two nodes: once with the global transform of the first node, which is the identity transform, and once with the global transform of the second node, which is a translation of 1.0 along the x-axis. + + + +Previous: [Simple Meshes](gltfTutorial_008_SimpleMeshes.md) | [Table of Contents](README.md) | Next: [Materials](gltfTutorial_010_Materials.md) diff --git a/gltfTutorial_cn/gltfTutorial_010_Materials.md b/gltfTutorial_cn/gltfTutorial_010_Materials.md new file mode 100644 index 0000000..66b0d35 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_010_Materials.md @@ -0,0 +1,46 @@ +Previous: [Meshes](gltfTutorial_009_Meshes.md) | [Table of Contents](README.md) | Next: [Simple Material](gltfTutorial_011_SimpleMaterial.md) + +# Materials + +## Introduction + +The purpose of glTF is to define a transmission format for 3D assets. As shown in the previous sections, this includes information about the scene structure and the geometric objects that appear in the scene. But a glTF asset can also contain information about the *appearance* of the objects; that is, how these objects should be rendered on the screen. + +There are different possible representations for the properties of a material, and the *shading model* describes how these properties are processed. Simple shading models, like the [Phong](https://en.wikipedia.org/wiki/Phong_reflection_model) or [Blinn-Phong](https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model), are directly supported by common graphics APIs like OpenGL or WebGL. These shading models are built on a set of basic material properties. For example, the material properties involve information about the color of diffusely reflected light (often in the form of a texture), the color of specularly reflected light, and a shininess parameter. Many file formats contain exactly these parameters. For example, [Wavefront OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file) files are combined with `MTL` files that contain this texture and color information. Renderers can read this information and render the objects accordingly. But in order to describe more realistic materials, more sophisticated shading and material models are required. + +## Physically-Based Rendering (PBR) + +To allow renderers to display objects with a realistic appearance under different lighting conditions, the shading model has to take the *physical* properties of the object surface into account. There are different representations of these physical material properties. One that is frequently used is the *metallic-roughness-model*. Here, the information about the object surface is encoded with three main parameters: + +- The *base color*, which is the "main" color of the object surface. +- The *metallic* value. This is a parameter that describes how much the reflective behavior of the material resembles that of a metal. +- The *roughness* value, indicating how rough the surface is, affecting the light scattering. + +The metallic-roughness model is the representation that is used in glTF. Other material representations, like the *specular-glossiness-model*, are supported via extensions. + +The effects of different metallic- and roughness values are illustrated in this image: + +

+
+Image 10a: Spheres with different metallic- and roughness values. +

+ +The base color, metallic, and roughness properties may be given as single values and are then applied to the whole object. In order to assign different material properties to different parts of the object surface, these properties may also be given in the form of textures. This makes it possible to model a wide range of real-world materials with a realistic appearance. + +Depending on the shading model, additional effects can be applied to the object surface. These are usually given as a combination of a texture and a scaling factor: + +- An *emissive* texture describes the parts of the object surface that emit light with a certain color. +- The *occlusion* texture can be used to simulate the effect of objects self-shadowing each other. +- The *normal map* is a texture applied to modulate the surface normal in a way that makes it possible to simulate finer geometric details without the cost of a higher mesh resolution. + +glTF supports all of these additional properties, and defines sensible default values for the cases that these properties are omitted. + +The following sections will show how these material properties are encoded in a glTF asset, including various examples of materials: + +- [A Simple Material](gltfTutorial_011_SimpleMaterial.md) +- [Textures, Images, and Samplers](gltfTutorial_012_TexturesImagesSamplers.md) that serve as a basis for defining material properties +- [A Simple Texture](gltfTutorial_013_SimpleTexture.md) showing an example of how to use a texture for a material +- [An Advanced Material](gltfTutorial_014_AdvancedMaterial.md) combining multiple textures to achieve a sophisticated surface appearance for the objects + + +Previous: [Meshes](gltfTutorial_009_Meshes.md) | [Table of Contents](README.md) | Next: [Simple Material](gltfTutorial_011_SimpleMaterial.md) diff --git a/gltfTutorial_cn/gltfTutorial_011_SimpleMaterial.md b/gltfTutorial_cn/gltfTutorial_011_SimpleMaterial.md new file mode 100644 index 0000000..635bc5d --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_011_SimpleMaterial.md @@ -0,0 +1,140 @@ +Previous: [Materials](gltfTutorial_010_Materials.md) | [Table of Contents](README.md) | Next: [Textures, Images, Samplers](gltfTutorial_012_TexturesImagesSamplers.md) + +# A Simple Material + +The examples of glTF assets that have been given in the previous sections contained a basic scene structure and simple geometric objects. But they did not contain information about the appearance of the objects. When no such information is given, viewers are encouraged to render the objects with a "default" material. And as shown in the screenshot of the [minimal glTF file](gltfTutorial_003_MinimalGltfFile.md), depending on the light conditions in the scene, this default material causes the object to be rendered with a uniformly white or light gray color. + +This section will start with an example of a very simple material and explain the effect of the different material properties. + +This is a minimal glTF asset with a simple material: + +```javascript +{ + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0 ] + } + ], + + "nodes" : [ + { + "mesh" : 0 + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0, + "material" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=", + "byteLength" : 44 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 6, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 8, + "byteLength" : 36, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 3, + "type" : "SCALAR", + "max" : [ 2 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 3, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "materials" : [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ 1.000, 0.766, 0.336, 1.0 ], + "metallicFactor": 0.5, + "roughnessFactor": 0.1 + } + } + ], + "asset" : { + "version" : "2.0" + } +} +``` + +When rendered, this asset will show the triangle with a new material, as shown in Image 11a. + +

+
+Image 11a: A triangle with a simple material. +

+ + +## Material definition + + +A new top-level array has been added to the glTF JSON to define this material: The `materials` array contains a single element that defines the material and its properties: + +```javascript + "materials" : [ + { + "pbrMetallicRoughness": { + "baseColorFactor": [ 1.000, 0.766, 0.336, 1.0 ], + "metallicFactor": 0.5, + "roughnessFactor": 0.1 + } + } + ], +``` + +The actual definition of the [`material`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-material) here only consists of the [`pbrMetallicRoughness`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-material-pbrmetallicroughness) object, which defines the basic properties of a material in the *metallic-roughness-model*. (All other material properties will therefore have default values, which will be explained later.) The `baseColorFactor` contains the red, green, blue, and alpha components of the main color of the material - here, a bright orange color. The `metallicFactor` of 0.5 indicates that the material should have reflection characteristics between that of a metal and a non-metal material. The `roughnessFactor` causes the material to not be perfectly mirror-like, but instead scatter the reflected light a bit. + +## Assigning the material to objects + +The material is assigned to the triangle, namely to the `mesh.primitive`, by referring to the material using its index: + +```javascript + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0, + "material" : 0 + } ] + } +``` + +The next section will give a short introduction to how textures are defined in a glTF asset. The use of textures will then allow the definition of more complex and realistic materials. + +Previous: [Materials](gltfTutorial_010_Materials.md) | [Table of Contents](README.md) | Next: [Textures, Images, Samplers](gltfTutorial_012_TexturesImagesSamplers.md) diff --git a/gltfTutorial_cn/gltfTutorial_012_TexturesImagesSamplers.md b/gltfTutorial_cn/gltfTutorial_012_TexturesImagesSamplers.md new file mode 100644 index 0000000..b04de0b --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_012_TexturesImagesSamplers.md @@ -0,0 +1,37 @@ +Previous: [Simple Material](gltfTutorial_011_SimpleMaterial.md) | [Table of Contents](README.md) | Next: [Simple Texture](gltfTutorial_013_SimpleTexture.md) + +# Textures, Images, and Samplers + +Textures are an important aspect of giving objects a realistic appearance. They make it possible to define the main color of the objects, as well as other characteristics that are used in the material definition in order to precisely describe what the rendered object should look like. + +A glTF asset may define multiple [`texture`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-texture) objects, which can be used as the textures of geometric objects during rendering, and which can be used to encode different material properties. Depending on the graphics API, there may be many features and settings that influence the process of texture mapping. Many of these details are beyond the scope of this tutorial. There are dedicated tutorials that explain the exact meaning of all the texture mapping parameters and settings; for example, on [webglfundamentals.org](https://webglfundamentals.org/webgl/lessons/webgl-3d-textures.html), [open.gl](https://open.gl/textures), and others. This section will only summarize how the information about textures is encoded in a glTF asset. + +There are three top-level arrays for the definition of textures in the glTF JSON. The `textures`, `samplers`, and `images` dictionaries contain [`texture`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-texture), [`sampler`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_texture_sampler), and [`image`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-image) objects, respectively. The following is an excerpt from the [Simple Texture](gltfTutorial_013_SimpleTexture.md) example, which will be presented in the next section: + +```javascript +"textures": [ + { + "source": 0, + "sampler": 0 + } +], +"images": [ + { + "uri": "testTexture.png" + } +], +"samplers": [ + { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 33648, + "wrapT": 33648 + } +], +``` + +The `texture` itself uses indices to refer to one `sampler` and one `image`. The most important element here is the reference to the `image`. It contains a URI that links to the actual image file that will be used for the texture. Information about how to read this image data can be found in the section about [image data in `images`](gltfTutorial_002_BasicGltfStructure.md#image-data-in-images). + +The next section will show how such a texture definition may be used inside a material. + +Previous: [Simple Material](gltfTutorial_011_SimpleMaterial.md) | [Table of Contents](README.md) | Next: [Simple Texture](gltfTutorial_013_SimpleTexture.md) diff --git a/gltfTutorial_cn/gltfTutorial_013_SimpleTexture.md b/gltfTutorial_cn/gltfTutorial_013_SimpleTexture.md new file mode 100644 index 0000000..93026a7 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_013_SimpleTexture.md @@ -0,0 +1,143 @@ +Previous: [Textures, Images, and Samplers](gltfTutorial_012_TexturesImagesSamplers.md) | [Table of Contents](README.md) | Next: [Advanced Material](gltfTutorial_014_AdvancedMaterial.md) + +# A Simple Texture + +As shown in the previous sections, the material definition in a glTF asset contains different parameters for the color of the material or the overall appearance of the material under the influence of light. These properties may be given via single values, for example, defining the color or the roughness of the object as a whole. Alternatively, these values may be provided via textures that are mapped on the object surface. The following is a glTF asset that defines a material with a simple, single texture: + +```javascript +{ + "scene": 0, + "scenes" : [ { + "nodes" : [ 0 ] + } ], + "nodes" : [ { + "mesh" : 0 + } ], + "meshes" : [ { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 0, + "material" : 0 + } ] + } ], + + "materials" : [ { + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0, + "roughnessFactor" : 1.0 + } + } ], + + "textures" : [ { + "sampler" : 0, + "source" : 0 + } ], + "images" : [ { + "uri" : "testTexture.png" + } ], + "samplers" : [ { + "magFilter" : 9729, + "minFilter" : 9987, + "wrapS" : 33648, + "wrapT" : 33648 + } ], + + "buffers" : [ { + "uri" : "data:application/gltf-buffer;base64,AAABAAIAAQADAAIAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAA", + "byteLength" : 108 + } ], + "bufferViews" : [ { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 12, + "target" : 34963 + }, { + "buffer" : 0, + "byteOffset" : 12, + "byteLength" : 96, + "byteStride" : 12, + "target" : 34962 + } ], + "accessors" : [ { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 6, + "type" : "SCALAR", + "max" : [ 3 ], + "min" : [ 0 ] + }, { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 4, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + }, { + "bufferView" : 1, + "byteOffset" : 48, + "componentType" : 5126, + "count" : 4, + "type" : "VEC2", + "max" : [ 1.0, 1.0 ], + "min" : [ 0.0, 0.0 ] + } ], + + "asset" : { + "version" : "2.0" + } +} +``` + +The actual image that the texture consists of is stored as a PNG file called `"testTexture.png"` (see Image 13a). + +

+
+Image 13a: The image for the simple texture example. +

+ +Bringing this all together in a renderer will result in the scene rendered in Image 13b. + +

+
+Image 13b: A simple texture on a unit square. +

+ + +## The Textured Material Definition + +The material definition in this example differs from the [Simple Material](gltfTutorial_011_SimpleMaterial.md) that was shown earlier. While the simple material only defined a single color for the whole object, the material definition now refers to the newly added texture: + +```javascript +"materials" : [ { + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0, + "roughnessFactor" : 1.0 + } +} ], +``` + +The `baseColorTexture` is the index of the texture that will be applied to the object surface. The `metallicFactor` and `roughnessFactor` are still single values. A more complex material where these properties are also given via textures will be shown in the next section. + +In order to apply a texture to a mesh primitive, there must be information about the texture coordinates that should be used for each vertex. The texture coordinates are only another attribute for the vertices defined in the `mesh.primitive`. By default, a texture will use the texture coordinates that have the attribute name `TEXCOORD_0`. If there are multiple sets of texture coordinates, the one that should be used for one particular texture may be selected by adding a `texCoord` property to the texture reference: + +```javascript +"baseColorTexture" : { + "index" : 0, + "texCoord": 2 +}, +``` +In this case, the texture would use the texture coordinates that are contained in the attribute called `TEXCOORD_2`. + + +Previous: [Textures, Images, and Samplers](gltfTutorial_012_TexturesImagesSamplers.md) | [Table of Contents](README.md) | Next: [Advanced Material](gltfTutorial_014_AdvancedMaterial.md) diff --git a/gltfTutorial_cn/gltfTutorial_014_AdvancedMaterial.md b/gltfTutorial_cn/gltfTutorial_014_AdvancedMaterial.md new file mode 100644 index 0000000..156b7ec --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_014_AdvancedMaterial.md @@ -0,0 +1,56 @@ +Previous: [Simple Texture](gltfTutorial_013_SimpleTexture.md) | [Table of Contents](README.md) | Next: [Simple Cameras](gltfTutorial_015_SimpleCameras.md) + +# An Advanced Material + +The [Simple Texture](gltfTutorial_013_SimpleTexture.md) example in the previous section showed a material for which the "base color" was defined using a texture. But in addition to the base color, there are other properties of a material that may be defined via textures. These properties have already been summarized in the [Materials](gltfTutorial_010_Materials.md) section: + +- The *base color*, +- The *metallic* value, +- The *roughness* of the surface, +- The *emissive* properties, +- An *occlusion* texture, and +- A *normal map*. + + +The effects of these properties cannot properly be demonstrated with trivial textures. Therefore, they will be shown here using one of the official Khronos PBR sample models, namely, the [WaterBottle](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/main/Models/WaterBottle) model. Image 14a shows an overview of the textures that are involved in this model, and the final rendered object: + +

+
+Image 14a: An example of a material where the surface properties are defined via textures. +

+ +Explaining the implementation of physically based rendering is beyond the scope of this tutorial. The official Khronos [glTF Sample Viewer](https://github.com/KhronosGroup/glTF-Sample-Viewer) contains a reference implementation of a PBR renderer based on WebGL, and provides implementation hints and background information. The following images mainly aim at demonstrating the effects of the different material property textures, under different lighting conditions. + +Image 14b shows the effect of the roughness texture: the main part of the bottle has a low roughness, causing it to appear shiny, compared to the cap, which has a rough surface structure. + +

+
+Image 14b: The influence of the roughness texture. +

+ +Image 14c highlights the effect of the metallic texture: the bottle reflects the light from the surrounding environment map. + +

+
+Image 14c: The influence of the metallic texture. +

+ +Image 14d shows the emissive part of the texture: regardless of the dark environment setting, the text, which is contained in the emissive texture, is clearly visible. + +

+
+Image 14d: The emissive part of the texture. +

+ +Image 14e shows the part of the bottle cap for which a normal map is defined: the text appears to be embossed into the cap. This makes it possible to model finer geometric details on the surface, even though the model itself only has a very coarse geometric resolution. + +

+
+Image 14e: The effect of a normal map. +

+ +Together, these textures and maps allow modeling a wide range of real-world materials. Thanks to the common underlying PBR model - namely, the metallic-roughness model - the objects can be rendered consistently by different renderer implementations. + + + +Previous: [Simple Texture](gltfTutorial_013_SimpleTexture.md) | [Table of Contents](README.md) | Next: [Simple Cameras](gltfTutorial_015_SimpleCameras.md) diff --git a/gltfTutorial_cn/gltfTutorial_015_SimpleCameras.md b/gltfTutorial_cn/gltfTutorial_015_SimpleCameras.md new file mode 100644 index 0000000..b82853f --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_015_SimpleCameras.md @@ -0,0 +1,168 @@ +Previous: [Advanced Material](gltfTutorial_014_AdvancedMaterial.md) | [Table of Contents](README.md) | Next: [Cameras](gltfTutorial_016_Cameras.md) + +# Simple Cameras + +The previous sections showed how a basic scene structure with geometric objects is represented in a glTF asset, and how different materials can be applied to these objects. This did not yet include information about the view configuration that should be used for rendering the scene. This view configuration is usually described as a virtual *camera* that is contained in the scene, at a certain position, and pointing in a certain direction. + +The following is a simple, complete glTF asset. It is similar to the assets that have already been shown: it defines a simple `scene` containing `node` objects and a single geometric object that is given as a `mesh`, attached to one of the nodes. But this asset additionally contains two [`camera`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-camera) objects: + + +```javascript +{ + "scene": 0, + "scenes" : [ + { + "nodes" : [ 0, 1, 2 ] + } + ], + "nodes" : [ + { + "rotation" : [ -0.383, 0.0, 0.0, 0.924 ], + "mesh" : 0 + }, + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 0 + }, + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 1 + } + ], + + "cameras" : [ + { + "type": "perspective", + "perspective": { + "aspectRatio": 1.0, + "yfov": 0.7, + "zfar": 100, + "znear": 0.01 + } + }, + { + "type": "orthographic", + "orthographic": { + "xmag": 1.0, + "ymag": 1.0, + "zfar": 100, + "znear": 0.01 + } + } + ], + + "meshes" : [ + { + "primitives" : [ { + "attributes" : { + "POSITION" : 1 + }, + "indices" : 0 + } ] + } + ], + + "buffers" : [ + { + "uri" : "data:application/octet-stream;base64,AAABAAIAAQADAAIAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAA", + "byteLength" : 60 + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteOffset" : 0, + "byteLength" : 12, + "target" : 34963 + }, + { + "buffer" : 0, + "byteOffset" : 12, + "byteLength" : 48, + "target" : 34962 + } + ], + "accessors" : [ + { + "bufferView" : 0, + "byteOffset" : 0, + "componentType" : 5123, + "count" : 6, + "type" : "SCALAR", + "max" : [ 3 ], + "min" : [ 0 ] + }, + { + "bufferView" : 1, + "byteOffset" : 0, + "componentType" : 5126, + "count" : 4, + "type" : "VEC3", + "max" : [ 1.0, 1.0, 0.0 ], + "min" : [ 0.0, 0.0, 0.0 ] + } + ], + + "asset" : { + "version" : "2.0" + } +} +``` + +The geometry in this asset is a simple unit square. It is rotated by -45 degrees around the x-axis, to emphasize the effect of the different cameras. Image 15a shows three options for rendering this asset. The first examples use the cameras from the asset. The last example shows how the scene looks from an external, user-defined viewpoint. + +

+
+Image 15a: The effect of rendering the scene with different cameras. +

+ + +## Camera definitions + +The new top-level element of this glTF asset is the `cameras` array, which contains the [`camera`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-camera) objects: + +```javascript +"cameras" : [ + { + "type": "perspective", + "perspective": { + "aspectRatio": 1.0, + "yfov": 0.7, + "zfar": 100, + "znear": 0.01 + } + }, + { + "type": "orthographic", + "orthographic": { + "xmag": 1.0, + "ymag": 1.0, + "zfar": 100, + "znear": 0.01 + } + } +], +``` + +When a camera object has been defined, it may be attached to a `node`. This is accomplished by assigning the index of the camera to the `camera` property of a node. In the given example, two new nodes have been added to the scene graph, one for each camera: + +```javascript +"nodes" : { + ... + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 0 + }, + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 1 + } +}, +``` + +The differences between perspective and orthographic cameras and their properties, the effect of attaching the cameras to the nodes, and the management of multiple cameras will be explained in detail in the [Cameras](gltfTutorial_016_Cameras.md) section. + + + + +Previous: [Advanced Material](gltfTutorial_014_AdvancedMaterial.md) | [Table of Contents](README.md) | Next: [Cameras](gltfTutorial_016_Cameras.md) diff --git a/gltfTutorial_cn/gltfTutorial_016_Cameras.md b/gltfTutorial_cn/gltfTutorial_016_Cameras.md new file mode 100644 index 0000000..bc55982 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_016_Cameras.md @@ -0,0 +1,80 @@ +Previous: [Simple Cameras](gltfTutorial_015_SimpleCameras.md) | [Table of Contents](README.md) | Next: [Simple Morph Target](gltfTutorial_017_SimpleMorphTarget.md) + +# Cameras + +The example in the [Simple Cameras](gltfTutorial_017_SimpleCameras.md) section showed how to define perspective and orthographic cameras, and how they can be integrated into a scene by attaching them to nodes. This section will explain the differences between both types of cameras, and the handling of cameras in general. + + +## Perspective and orthographic cameras + +There are two kinds of cameras: *Perspective* cameras, where the viewing volume is a truncated pyramid (often referred to as "viewing frustum"), and *orthographic* cameras, where the viewing volume is a rectangular box. The main difference is that rendering with a *perspective* camera causes a proper perspective distortion, whereas rendering with an *orthographic* camera causes a preservation of lengths and angles. + +The example in the [Simple Cameras](gltfTutorial_015_SimpleCameras.md) section contains one camera of each type, a perspective camera at index 0 and an orthographic camera at index 1: + +```javascript +"cameras" : [ + { + "type": "perspective", + "perspective": { + "aspectRatio": 1.0, + "yfov": 0.7, + "zfar": 100, + "znear": 0.01 + } + }, + { + "type": "orthographic", + "orthographic": { + "xmag": 1.0, + "ymag": 1.0, + "zfar": 100, + "znear": 0.01 + } + } +], +``` + + +The `type` of the camera is given as a string, which can be `"perspective"` or `"orthographic"`. Depending on this type, the `camera` object contains a [`camera.perspective`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-camera-perspective) object or a [`camera.orthographic`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-camera-orthographic) object. These objects contain additional parameters that define the actual viewing volume. + +The `camera.perspective` object contains an `aspectRatio` property that defines the aspect ratio of the viewport. Additionally, it contains a property called `yfov`, which stands for *Field Of View in Y-direction*. It defines the "opening angle" of the camera and is given in radians. + +The `camera.orthographic` object contains `xmag` and `ymag` properties. These define the magnification of the camera in x- and y-direction, and basically describe the width and height of the viewing volume. + +Both camera types additionally contain `znear` and `zfar` properties, which are the coordinates of the near and far clipping plane. For perspective cameras, the `zfar` value is optional. When it is missing, a special "infinite projection matrix" will be used. + +Explaining the details of cameras, viewing, and projections is beyond the scope of this tutorial. The important point is that most graphics APIs offer methods for defining the viewing configuration that are directly based on these parameters. In general, these parameters can be used to compute a *camera matrix*. The camera matrix can be inverted to obtain the *view matrix*, which will later be post-multiplied with the *model matrix* to obtain the *model-view matrix*, which is required by the renderer. + + +# Camera orientation + +A `camera` can be transformed to have a certain orientation and viewing direction in the scene. This is accomplished by attaching the camera to a `node`. Each [`node`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-node) may contain the index of a `camera` that is attached to it. In the simple camera example, there are two nodes for the cameras. The first node refers to the perspective camera with index 0, and the second one refers to the orthographic camera with index 1: + +```javascript +"nodes" : { + ... + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 0 + }, + { + "translation" : [ 0.5, 0.5, 3.0 ], + "camera" : 1 + } +}, +``` + +As shown in the [Scenes and Nodes](gltfTutorial_004_ScenesNodes.md) section, these nodes may have properties that define the transform matrix of the node. The [global transform](gltfTutorial_004_ScenesNodes.md#global-transforms-of-nodes) of a node then defines the actual orientation of the camera in the scene. With the option to apply arbitrary [animations](gltfTutorial_007_Animations.md) to the nodes, it is even possible to define camera flights. + +When the global transform of the camera node is the identity matrix, then the eye point of the camera is at the origin, and the viewing direction is along the negative z-axis. In the given example, the nodes both have a `translation` about `(0.5, 0.5, 3.0)`, which causes the camera to be transformed accordingly: it is translated about 0.5 in the x- and y- direction, to look at the center of the unit square, and about 3.0 along the z-axis, to move it a bit away from the object. + + +## Camera instancing and management + +There may be multiple cameras defined in the JSON part of a glTF. Each camera may be referred to by multiple nodes. Therefore, the cameras as they appear in the glTF asset are really "templates" for actual camera *instances*: Whenever a node refers to one camera, a new instance of this camera is created. + +There is no "default" camera for a glTF asset. Instead, the client application has to keep track of the currently active camera. The client application may, for example, offer a dropdown-menu that allows one to select the active camera and thus to quickly switch between predefined view configurations. With a bit more implementation effort, the client application can also define its own camera and interaction patterns for the camera control (e.g., zooming with the mouse wheel). However, the logic for the navigation and interaction has to be implemented solely by the client application in this case. [Image 15a](gltfTutorial_015_SimpleCameras.md#cameras-png) shows the result of such an implementation, where the user may select either the active camera from the ones that are defined in the glTF asset, or an "external camera" that may be controlled with the mouse. + + + +Previous: [Simple Cameras](gltfTutorial_015_SimpleCameras.md) | [Table of Contents](README.md) | Next: [Simple Morph Target](gltfTutorial_017_SimpleMorphTarget.md) diff --git a/gltfTutorial_cn/gltfTutorial_017_SimpleMorphTarget.md b/gltfTutorial_cn/gltfTutorial_017_SimpleMorphTarget.md new file mode 100644 index 0000000..6d4838b --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_017_SimpleMorphTarget.md @@ -0,0 +1,277 @@ +Previous: [Cameras](gltfTutorial_016_Cameras.md) | [Table of Contents](README.md) | Next: [Morph Targets](gltfTutorial_018_MorphTargets.md) + +# A Simple Morph Target + +Starting with version 2.0, glTF supports the definition of *morph targets* for meshes. A morph target stores displacements or differences for certain mesh attributes. At runtime, these differences may be added to the original mesh, with different weights, in order to animate parts of the mesh. This is often used in character animations, for example, to encode different facial expressions of a virtual character. + +The following is a minimal example that shows a mesh with two morph targets. The new elements will be summarized here, and the broader concept of morph targets and how they are applied at runtime will be explained in the next section. + + +```javascript +{ + "scene": 0, + "scenes":[ + { + "nodes":[ + 0 + ] + } + ], + "nodes":[ + { + "mesh":0 + } + ], + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "POSITION":1 + }, + "targets":[ + { + "POSITION":2 + }, + { + "POSITION":3 + } + ], + "indices":0 + } + ], + "weights":[ + 1.0, + 0.5 + ] + } + ], + + "animations":[ + { + "samplers":[ + { + "input":4, + "interpolation":"LINEAR", + "output":5 + } + ], + "channels":[ + { + "sampler":0, + "target":{ + "node":0, + "path":"weights" + } + } + ] + } + ], + + "buffers":[ + { + "uri":"data:application/gltf-buffer;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAAAA=", + "byteLength":116 + }, + { + "uri":"data:application/gltf-buffer;base64,AAAAAAAAgD8AAABAAABAQAAAgEAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAA", + "byteLength":60 + } + ], + "bufferViews":[ + { + "buffer":0, + "byteOffset":0, + "byteLength":6, + "target":34963 + }, + { + "buffer":0, + "byteOffset":8, + "byteLength":108, + "byteStride":12, + "target":34962 + }, + { + "buffer":1, + "byteOffset":0, + "byteLength":20 + }, + { + "buffer":1, + "byteOffset":20, + "byteLength":40 + } + ], + "accessors":[ + { + "bufferView":0, + "byteOffset":0, + "componentType":5123, + "count":3, + "type":"SCALAR", + "max":[ + 2 + ], + "min":[ + 0 + ] + }, + { + "bufferView":1, + "byteOffset":0, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 1.0, + 0.5, + 0.0 + ], + "min":[ + 0.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":1, + "byteOffset":36, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 0.0, + 1.0, + 0.0 + ], + "min":[ + -1.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":1, + "byteOffset":72, + "componentType":5126, + "count":3, + "type":"VEC3", + "max":[ + 1.0, + 1.0, + 0.0 + ], + "min":[ + 0.0, + 0.0, + 0.0 + ] + }, + { + "bufferView":2, + "byteOffset":0, + "componentType":5126, + "count":5, + "type":"SCALAR", + "max":[ + 4.0 + ], + "min":[ + 0.0 + ] + }, + { + "bufferView":3, + "byteOffset":0, + "componentType":5126, + "count":10, + "type":"SCALAR", + "max":[ + 1.0 + ], + "min":[ + 0.0 + ] + } + ], + + "asset":{ + "version":"2.0" + } +} + +``` + +The asset contains an animation that interpolates between the different morph targets for a single triangle. A screenshot of this asset is shown in Image 17a. + +

+
+Image 17a: A triangle with two morph targets. +

+ + +Most of the elements of this asset have already been explained in the previous sections: It contains a `scene` with a single `node` and a single `mesh`. There are two `buffer` objects, one storing the geometry data and one storing the data for the `animation`, and several `bufferView` and `accessor` objects that provide access to this data. + +The new elements that have been added in order to define the morph targets are contained in the `mesh` and the `animation`: + + +```javascript + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "POSITION":1 + }, + "targets":[ + { + "POSITION":2 + }, + { + "POSITION":3 + } + ], + "indices":0 + } + ], + "weights":[ + 0.5, + 0.5 + ] + } + ], + +``` + +The `mesh.primitive` contains an array of [morph `targets`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_targets). Each morph target is a dictionary that maps attribute names to `accessor` objects. In the example, there are two morph targets, both mapping the `"POSITION"` attribute to accessors that contain the morphed vertex positions. The mesh also contains an array of `weights` that defines the contribution of each morph target to the final, rendered mesh. These weights are also the `channel.target` of the `animation` that is contained in the asset: + +```javascript + "animations":[ + { + "samplers":[ + { + "input":4, + "interpolation":"LINEAR", + "output":5 + } + ], + "channels":[ + { + "sampler":0, + "target":{ + "node":0, + "path":"weights" + } + } + ] + } + ], + +``` + +This means that the animation will modify the `weights` of the mesh that is referred to by the `target.node`. The result of applying the animation to these weights, and the computation of the final, rendered mesh will be explained in more detail in the next section about [Morph Targets](gltfTutorial_018_MorphTargets.md). + + + +Previous: [Cameras](gltfTutorial_016_Cameras.md) | [Table of Contents](README.md) | Next: [Morph Targets](gltfTutorial_018_MorphTargets.md) diff --git a/gltfTutorial_cn/gltfTutorial_018_MorphTargets.md b/gltfTutorial_cn/gltfTutorial_018_MorphTargets.md new file mode 100644 index 0000000..fdb6d22 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_018_MorphTargets.md @@ -0,0 +1,80 @@ +Previous: [Simple Morph Target](gltfTutorial_017_SimpleMorphTarget.md) | [Table of Contents](README.md) | Next: [SimpleSkin](gltfTutorial_019_SimpleSkin.md) + +# Morph Targets + +The example in the previous section contains a mesh that consists of a single triangle with two morph targets: + +```javascript +{ + "meshes":[ + { + "primitives":[ + { + "attributes":{ + "POSITION":1 + }, + "targets":[ + { + "POSITION":2 + }, + { + "POSITION":3 + } + ], + "indices":0 + } + ], + "weights":[ + 1.0, + 0.5 + ] + } + ], +``` + + +The actual base geometry of the mesh, namely the triangle geometry, is defined by the `mesh.primitive` attribute called `"POSITION"`. The morph targets of the `mesh.primitive` are dictionaries that map the attribute name `"POSITION"` to `accessor` objects that contain the *displacements* for each vertex. Image 18a shows the initial triangle geometry in black, and the displacement for the first morph target in red, and the displacement for the second morph target in green. + +

+
+Image 18a: The initial triangle and morph target displacements. +

+ +The `weights` of the mesh determine how these morph target displacements are added to the initial geometry in order to obtain the current state of the geometry. The pseudocode for computing the rendered vertex positions for a mesh `primitive` is as follows: +``` +renderedPrimitive.POSITION = primitive.POSITION + + weights[0] * primitive.targets[0].POSITION + + weights[1] * primitive.targets[1].POSITION; +``` + +This means that the current state of the mesh primitive is computed by taking the initial mesh primitive geometry and adding a linear combination of the morph target displacements, where the `weights` are the factors for the linear combination. + +The asset additionally contains an `animation` that affects the weights for the morph targets. The following table shows the key frames of the animated weights: + +| Time | Weights | +|:----:|:---------:| +| 0.0 | 0.0, 0.0 | +| 1.0 | 0.0, 1.0 | +| 2.0 | 1.0, 1.0 | +| 3.0 | 1.0, 0.0 | +| 4.0 | 0.0, 0.0 | + + +Throughout the animation, the weights are interpolated linearly, and applied to the morph target displacements. At each point, the rendered state of the mesh primitive is updated accordingly. The following is an example of the state that is computed at 1.25 seconds. The weights that are provided by the animation sampler for this animation time are (0.25, 1.0), and they are used for computing the linear combination of the morph target displacements. + +

+
+Image 18b: An intermediate state of the morph target animation. +

+ + + + +Previous: [Simple Morph Target](gltfTutorial_017_SimpleMorphTarget.md) | [Table of Contents](README.md) | Next: [SimpleSkin](gltfTutorial_019_SimpleSkin.md) + + + + + + + diff --git a/gltfTutorial_cn/gltfTutorial_019_SimpleSkin.md b/gltfTutorial_cn/gltfTutorial_019_SimpleSkin.md new file mode 100644 index 0000000..2b86209 --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_019_SimpleSkin.md @@ -0,0 +1,166 @@ +Previous: [Morph Targets](gltfTutorial_018_MorphTargets.md) | [Table of Contents](README.md) | Next: [Skins](gltfTutorial_020_Skins.md) + +# A Simple Skin + +glTF supports *vertex skinning*, which allows the geometry (vertices) of a mesh to be deformed based on the pose of a skeleton. This is essential in order to give animated geometry, for example of virtual characters, a realistic appearance. The core for the definition of vertex skinning in a glTF asset is the [`skin`](https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#reference-skin), but vertex skinning in general implies several interdependencies between the elements of a glTF asset that have been presented so far. + +The following is a glTF asset that shows basic vertex skinning for a simple geometry. The elements of this asset will be summarized quickly in this section, referring to the previous sections where appropriate, and pointing out the new elements that have been added for the vertex skinning functionality. The details and background information for vertex skinning will be given in the next section. + +```javascript +{ + "scene" : 0, + "scenes" : [ { + "nodes" : [ 0, 1 ] + } ], + + "nodes" : [ { + "skin" : 0, + "mesh" : 0 + }, { + "children" : [ 2 ] + }, { + "translation" : [ 0.0, 1.0, 0.0 ], + "rotation" : [ 0.0, 0.0, 0.0, 1.0 ] + } ], + + "meshes" : [ { + "primitives" : [ { + "attributes" : { + "POSITION" : 1, + "JOINTS_0" : 2, + "WEIGHTS_0" : 3 + }, + "indices" : 0 + } ] + } ], + + "skins" : [ { + "inverseBindMatrices" : 4, + "joints" : [ 1, 2 ] + } ], + + "animations" : [ { + "channels" : [ { + "sampler" : 0, + "target" : { + "node" : 2, + "path" : "rotation" + } + } ], + "samplers" : [ { + "input" : 5, + "interpolation" : "LINEAR", + "output" : 6 + } ] + } ], + + "buffers" : [ { + "uri" : "data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAvwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAvwAAAD8AAAAAAAAAPwAAAD8AAAAAAAAAvwAAgD8AAAAAAAAAPwAAgD8AAAAAAAAAvwAAwD8AAAAAAAAAPwAAwD8AAAAAAAAAvwAAAEAAAAAAAAAAPwAAAEAAAAAA", + "byteLength" : 168 + }, { + "uri" : "data:application/gltf-buffer;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=", + "byteLength" : 320 + }, { + "uri" : "data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAgD8=", + "byteLength" : 128 + }, { + "uri" : "data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/", + "byteLength" : 240 + } ], + + "bufferViews" : [ { + "buffer" : 0, + "byteLength" : 48, + "target" : 34963 + }, { + "buffer" : 0, + "byteOffset" : 48, + "byteLength" : 120, + "target" : 34962 + }, { + "buffer" : 1, + "byteLength" : 320, + "byteStride" : 16 + }, { + "buffer" : 2, + "byteLength" : 128 + }, { + "buffer" : 3, + "byteLength" : 240 + } ], + + "accessors" : [ { + "bufferView" : 0, + "componentType" : 5123, + "count" : 24, + "type" : "SCALAR" + }, { + "bufferView" : 1, + "componentType" : 5126, + "count" : 10, + "type" : "VEC3", + "max" : [ 0.5, 2.0, 0.0 ], + "min" : [ -0.5, 0.0, 0.0 ] + }, { + "bufferView" : 2, + "componentType" : 5123, + "count" : 10, + "type" : "VEC4" + }, { + "bufferView" : 2, + "byteOffset" : 160, + "componentType" : 5126, + "count" : 10, + "type" : "VEC4" + }, { + "bufferView" : 3, + "componentType" : 5126, + "count" : 2, + "type" : "MAT4" + }, { + "bufferView" : 4, + "componentType" : 5126, + "count" : 12, + "type" : "SCALAR", + "max" : [ 5.5 ], + "min" : [ 0.0 ] + }, { + "bufferView" : 4, + "byteOffset" : 48, + "componentType" : 5126, + "count" : 12, + "type" : "VEC4", + "max" : [ 0.0, 0.0, 0.707, 1.0 ], + "min" : [ 0.0, 0.0, -0.707, 0.707 ] + } ], + + "asset" : { + "version" : "2.0" + } +} +``` + + + +The result of rendering this asset is shown in Image 19a. + +

+
+Image 19a: A scene with simple vertex skinning. +

+ + +## Elements of the simple skin example + +The elements of the given example are briefly summarized here: + +- The `scenes` and `nodes` elements have been explained in the [Scenes and Nodes](gltfTutorial_004_ScenesNodes.md) section. For the vertex skinning, new nodes have been added: the nodes at index 1 and 2 define a new node hierarchy for the *skeleton*. These nodes can be considered the joints between the "bones" that will eventually cause the deformation of the mesh. +- The new top-level dictionary `skins` contains a single skin in the given example. The properties of this skin object will be explained later. +- The concepts of `animations` has been explained in the [Animations](gltfTutorial_007_Animations.md) section. In the given example, the animation refers to the *skeleton* nodes so that the effect of the vertex skinning is actually visible during the animation. +- The [Meshes](gltfTutorial_009_Meshes.md) section already explained the contents of the `meshes` and `mesh.primitive` objects. In this example, new mesh primitive attributes have been added, which are required for vertex skinning, namely the `"JOINTS_0"` and `"WEIGHTS_0"` attributes. +- There are several new `buffers`, `bufferViews`, and `accessors`. Their basic properties have been described in the [Buffers, BufferViews, and Accessors](gltfTutorial_005_BufferBufferViewsAccessors.md) section. In the given example, they contain the additional data required for vertex skinning. + +Details about how these elements are interconnected to achieve the vertex skinning will be explained in the [Skins](gltfTutorial_020_Skins.md) section. + + +Previous: [Morph Targets](gltfTutorial_018_MorphTargets.md) | [Table of Contents](README.md) | Next: [Skins](gltfTutorial_020_Skins.md) diff --git a/gltfTutorial_cn/gltfTutorial_020_Skins.md b/gltfTutorial_cn/gltfTutorial_020_Skins.md new file mode 100644 index 0000000..e8c3b1d --- /dev/null +++ b/gltfTutorial_cn/gltfTutorial_020_Skins.md @@ -0,0 +1,231 @@ +Previous: [Simple Skin](gltfTutorial_019_SimpleSkin.md) | [Table of Contents](README.md) + + +# Skins + +The process of vertex skinning is a bit complex. It brings together nearly all elements that are contained in a glTF asset. This section will explain the basics of vertex skinning, based on the example in the [Simple Skin](gltfTutorial_019_SimpleSkin.md) section. + + +## The geometry data + +The geometry of the vertex skinning example is an indexed triangle mesh, consisting of 8 triangles and 10 vertices. They form a rectangle in the xy-plane, with a width of 1.0 and a height of 2.0. The bottom center of the rectangle is at the origin (0,0,0). So the positions of the vertices are + + -0.5, 0.0, 0.0, + 0.5, 0.0, 0.0, + -0.5, 0.5, 0.0, + 0.5, 0.5, 0.0, + -0.5, 1.0, 0.0, + 0.5, 1.0, 0.0, + -0.5, 1.5, 0.0, + 0.5, 1.5, 0.0, + -0.5, 2.0, 0.0, + 0.5, 2.0, 0.0 + +and the indices of the triangles are + + 0, 1, 3, + 0, 3, 2, + 2, 3, 5, + 2, 5, 4, + 4, 5, 7, + 4, 7, 6, + 6, 7, 9, + 6, 9, 8, + +The raw data is stored in the first `buffer`. The indices and vertex positions are defined by the `bufferView` objects at index 0 and 1, and the corresponding `accessor` objects at index 0 and 1 offer typed access to these buffer views. Image 20a shows this geometry with outline rendering to better show the structure. + +

+
+Image 20a: The geometry for the skinning example, with outline rendering, in its initial configuration. +

+ +This geometry data is contained in the mesh primitive of the only mesh, which is attached to the main node of the scene. The mesh primitive contains additional attributes, namely the `"JOINTS_0"` and `"WEIGHTS_0"` attributes. The purpose of these attributes will be explained below. + + +## The skeleton structure + +In the given example, there are two nodes that define the skeleton. They are referred to as "skeleton nodes", or "joint nodes", because they can be imagined as the joints between the bones of the skeleton. The `skin` refers to these nodes, by listing their indices in its `joints` property. + +```javascript + "nodes" : [ + ... + { + "children" : [ 2 ] + }, + { + "translation" : [ 0.0, 1.0, 0.0 ], + "rotation" : [ 0.0, 0.0, 0.0, 1.0 ] + } + ], + +``` + +The first joint node is located at the origin, and does not contain any transformations. The second node has a `translation` property, defining a translation about 1.0 along the y-axis, and a `rotation` property that initially describes a rotation about 0 degrees (thus, no rotation at all). This rotation will later be changed by the animation to let the skeleton bend left and right and show the effect of the vertex skinning. + +## The skin + + +The `skin` is the core element of the vertex skinning. In the example, there is a single skin: + +```javascript + "skins" : [ + { + "inverseBindMatrices" : 4, + "joints" : [ 1, 2 ] + } + ], + +``` + +The skin contains an array called `joints`, which lists the indices of the nodes that define the skeleton hierarchy. Additionally, the skin contains a reference to an accessor in the property `inverseBindMatrices`. This accessor provides one matrix for each joint. Each of these matrices transforms the geometry into the space of the respective joint. This means that each matrix is the *inverse* of the global transform of the respective joint, in its initial configuration. + +In the given example, joint `0` does not have an explicit transform, meaning that its global transform is the identity matrix. Therefore, the inverse bind matrix of joint `0` is also the identity matrix. + +Joint `1` contains a translation about 1.0 along the y-axis. The inverse bind matrix of joint `1` is therefore + + 1.0 0.0 0.0 0.0 + 0.0 1.0 0.0 -1.0 + 0.0 0.0 1.0 0.0 + 0.0 0.0 0.0 1.0 + +This matrix translates the mesh about -1.0 along the y-axis, as shown Image 20b. + +

+
+Image 20b: The transformation of the geometry with the inverse bind matrix of joint 1. +

+ +This transformation may look counterintuitive at first glance. But the goal of this transformation is to bring the coordinates of the skinned vertices into the same space as each joint. + + +## Vertex skinning implementation + +Users of existing rendering libraries will hardly ever have to manually process the vertex skinning data contained in a glTF asset: the actual skinning computations usually take place in the vertex shader, which is a low-level implementation detail of the respective library. However, knowing how the vertex skinning data is supposed to be processed may help to create proper, valid models with vertex skinning. So this section will give a short summary of how the vertex skinning is applied, using some pseudocode and examples in GLSL. + +### The joint matrices + +The vertex positions of a skinned mesh are eventually computed by the vertex shader. During these computations, the vertex shader has to take into account the current pose of the skeleton in order to compute the proper vertex position. This information is passed to the vertex shader as an array of matrices, namely as the *joint matrices*. This is an array - that is, a `uniform` variable - that contains one 4×4 matrix for each joint of the skeleton. In the shader, these matrices are combined to compute the actual skinning matrix for each vertex: + + +```glsl +... +uniform mat4 u_jointMat[2]; + +... +void main(void) +{ + mat4 skinMat = + a_weight.x * u_jointMat[int(a_joint.x)] + + a_weight.y * u_jointMat[int(a_joint.y)] + + a_weight.z * u_jointMat[int(a_joint.z)] + + a_weight.w * u_jointMat[int(a_joint.w)]; + .... +} +``` + +The joint matrix for each joint has to perform the following transformations to the vertices: + +- The vertices have to be transformed with the `inverseBindMatrix` of the joint node, to bring them into the same coordinate space as the joint. +- The vertices have to be transformed with the *current* global transform of the joint node. Together with the transformation from the `inverseBindMatrix`, this will cause the vertices to be transformed only based on the current transform of the node, in the coordinate space of the current joint node. + +So the pseudocode for computing the joint matrix of joint `j` may look as follows: + + jointMatrix(j) = + globalTransformOfJointNode(j) * + inverseBindMatrixForJoint(j); + +Note: Vertex skinning in other contexts often involves a matrix that is called "Bind Shape Matrix". This matrix is supposed to transform the geometry of the skinned mesh into the coordinate space of the joints. In glTF, this matrix is omitted, and it is assumed that this transform is either premultiplied with the mesh data, or postmultiplied to the inverse bind matrices. + +Image 20c shows the transformations that are done to the geometry in the [Simple Skin](gltfTutorial_019_SimpleSkin.md) example, using the joint matrix of joint 1. The image shows the transformation for an intermediate state of the animation, namely, when the rotation of the joint node has already been modified by the animation, to describe a rotation about 45 degrees around the z-axis. + +

+
+Image 20c: The transformation of the geometry done for joint 1. +

+ +The last panel of Image 20c shows how the geometry would look like if it were *only* transformed with the joint matrix of joint 1. This state of the geometry is never really visible: The *actual* geometry that is computed in the vertex shader will *combine* the geometries as they are created from the different joint matrices, based on the joints- and weights that are explained below. + + +### The skinning joints and weights + +As mentioned above, the mesh primitive contains new attributes that are required for the vertex skinning. Particularly, these are the `"JOINTS_0"` and the `"WEIGHTS_0"` attributes. Each attribute refers to an accessor that provides one data element for each vertex of the mesh. + +The `"JOINTS_0"` attribute refers to an accessor that contains the indices of the joints that should have an influence on the vertex during the skinning process. For simplicity and efficiency, these indices are usually stored as 4D vectors, limiting the number of joints that may influence a vertex to 4. In the given example, the joints information is very simple: + + Vertex 0: 0, 0, 0, 0, + Vertex 1: 0, 0, 0, 0, + Vertex 2: 0, 1, 0, 0, + Vertex 3: 0, 1, 0, 0, + Vertex 4: 0, 1, 0, 0, + Vertex 5: 0, 1, 0, 0, + Vertex 6: 0, 1, 0, 0, + Vertex 7: 0, 1, 0, 0, + Vertex 8: 0, 1, 0, 0, + Vertex 9: 0, 1, 0, 0, + +This means that every vertex may be influenced by joint 0 and joint 1, except the first two vertices are influenced only by joint 0, and the last two vertices are influenced only by joint 1. The last 2 components of each vector are ignored here. If there were multiple joints, then one entry of this accessor could, for example, contain + + 3, 1, 8, 4, + +meaning that the corresponding vertex should be influenced by the joints 3, 1, 8, and 4. + +The `"WEIGHTS_0"` attribute refers to an accessor that provides information about how strongly each joint should influence each vertex. In the given example, the weights are as follows: + + Vertex 0: 1.00, 0.00, 0.0, 0.0, + Vertex 1: 1.00, 0.00, 0.0, 0.0, + Vertex 2: 0.75, 0.25, 0.0, 0.0, + Vertex 3: 0.75, 0.25, 0.0, 0.0, + Vertex 4: 0.50, 0.50, 0.0, 0.0, + Vertex 5: 0.50, 0.50, 0.0, 0.0, + Vertex 6: 0.25, 0.75, 0.0, 0.0, + Vertex 7: 0.25, 0.75, 0.0, 0.0, + Vertex 8: 0.00, 1.00, 0.0, 0.0, + Vertex 9: 0.00, 1.00, 0.0, 0.0, + +Again, the last two components of each entry are not relevant, because there are only two joints. + +Combining the `"JOINTS_0"` and `"WEIGHTS_0"` attributes yields exact information about the influence that each joint has on each vertex. For example, the given data means that vertex 6 should be influenced to 25% by joint 0 and to 75% by joint 1. + +In the vertex shader, this information is used to create a linear combination of the joint matrices. This matrix is called the *skin matrix* of the respective vertex. Therefore, the data of the `"JOINTS_0"` and `"WEIGHTS_0"` attributes are passed to the shader. In this example, they are given as the `a_joint` and `a_weight` attribute variable, respectively: + +```glsl +... +attribute vec4 a_joint; +attribute vec4 a_weight; + +uniform mat4 u_jointMat[2]; + +... +void main(void) +{ + mat4 skinMat = + a_weight.x * u_jointMat[int(a_joint.x)] + + a_weight.y * u_jointMat[int(a_joint.y)] + + a_weight.z * u_jointMat[int(a_joint.z)] + + a_weight.w * u_jointMat[int(a_joint.w)]; + vec4 worldPosition = skinMat * vec4(a_position,1.0); + vec4 cameraPosition = u_viewMatrix * worldPosition; + gl_Position = u_projectionMatrix * cameraPosition; +} +``` +The skin matrix is then used to transform the original position of the vertex into the world space. The transform of the node that the skin is attached to is ignored. The result of this transformation can be imagined as a weighted transformation of the vertices with the respective joint matrices, as shown in Image 20d. + +

+
+Image 20d: Computation of the skin matrix. +

+ +The result of applying this skin matrix to the vertices for the given example is shown in Image 20e. + +

+
+Image 20e: The geometry for the skinning example, with outline rendering, during the animation. +

+ + + + + + + +Previous: [Simple Skin](gltfTutorial_019_SimpleSkin.md) | [Table of Contents](README.md) diff --git a/gltfTutorial_cn/images/advancedMaterial_emissive.png b/gltfTutorial_cn/images/advancedMaterial_emissive.png new file mode 100644 index 0000000..128834c Binary files /dev/null and b/gltfTutorial_cn/images/advancedMaterial_emissive.png differ diff --git a/gltfTutorial_cn/images/advancedMaterial_metallic.png b/gltfTutorial_cn/images/advancedMaterial_metallic.png new file mode 100644 index 0000000..77ae25d Binary files /dev/null and b/gltfTutorial_cn/images/advancedMaterial_metallic.png differ diff --git a/gltfTutorial_cn/images/advancedMaterial_normal.png b/gltfTutorial_cn/images/advancedMaterial_normal.png new file mode 100644 index 0000000..b589c84 Binary files /dev/null and b/gltfTutorial_cn/images/advancedMaterial_normal.png differ diff --git a/gltfTutorial_cn/images/advancedMaterial_roughness.png b/gltfTutorial_cn/images/advancedMaterial_roughness.png new file mode 100644 index 0000000..39e90e0 Binary files /dev/null and b/gltfTutorial_cn/images/advancedMaterial_roughness.png differ diff --git a/gltfTutorial_cn/images/animatedTriangle.gif b/gltfTutorial_cn/images/animatedTriangle.gif new file mode 100644 index 0000000..b7de2d5 Binary files /dev/null and b/gltfTutorial_cn/images/animatedTriangle.gif differ diff --git a/gltfTutorial_cn/images/animationChannels.png b/gltfTutorial_cn/images/animationChannels.png new file mode 100644 index 0000000..5f59be5 Binary files /dev/null and b/gltfTutorial_cn/images/animationChannels.png differ diff --git a/gltfTutorial_cn/images/animationChannels.svg b/gltfTutorial_cn/images/animationChannels.svg new file mode 100644 index 0000000..6fbae10 --- /dev/null +++ b/gltfTutorial_cn/images/animationChannels.svg @@ -0,0 +1,2362 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + "animations"{ "animation0": { "channels": [ { "sampler": "translationSampler", "target": { "id": "animatedNodeId", "path": "translation" } }, { "sampler": "rotationSampler", "target": { "id": "animatedNodeId", "path": "rotation" } } ], ... }} + + "nodes" { "animatedNodeId" : { "translation": [16.0, 2.0, -0.5 ], "rotation" : [ 0.2, 0.1, 0.5, 0.2], "meshes": [ duckMeshId ] },} + + + + + + + + + + + + + + + + + + + + + + Time: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + accessorForRotation + + Each channel refers to a sampler and defines a target. The sampler provides the values from the accessors, interpolated based on the current time. The channel forwards the values from the sampler to the node. The ID of the node is given as the target.id. The animated property is given as the target.path: + + + + 10.0 + + + + + 5.0 + -5.0 + + + 14.0 + + + + + 3.0 + -2.0 + + 18.0 + + + + + 1.0 + 1.0 + + 24.0 + + + + + -1.0 + 4.0 + + 31.0 + + + + + -3.0 + 7.0 + + + accessorForTranslation + + + diff --git a/gltfTutorial_cn/images/animationSamplers.png b/gltfTutorial_cn/images/animationSamplers.png new file mode 100644 index 0000000..e0ba674 Binary files /dev/null and b/gltfTutorial_cn/images/animationSamplers.png differ diff --git a/gltfTutorial_cn/images/animationSamplers.svg b/gltfTutorial_cn/images/animationSamplers.svg new file mode 100644 index 0000000..d4b33ab --- /dev/null +++ b/gltfTutorial_cn/images/animationSamplers.svg @@ -0,0 +1,1600 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 10.0 + + + + + 5.0 + -5.0 + + + 14.0 + + + + + 3.0 + -2.0 + + 18.0 + + + + + 1.0 + 1.0 + + 24.0 + + + + + -1.0 + 4.0 + + 31.0 + + + + + -3.0 + 7.0 + + + 0.0 + + + + + + 0.8 + + + + 1.6 + + + + 2.4 + + + + 3.2 + + + + + timeAccessor: + translationAccessor: + + + + Current time: 1.2 + + + + + 16.0 2.0 -0.5 + The sampler provides thetranslation, interpolatedbased on the current time: + + diff --git a/gltfTutorial_cn/images/aos.png b/gltfTutorial_cn/images/aos.png new file mode 100644 index 0000000..82400d0 Binary files /dev/null and b/gltfTutorial_cn/images/aos.png differ diff --git a/gltfTutorial_cn/images/aos.svg b/gltfTutorial_cn/images/aos.svg new file mode 100644 index 0000000..452a9bb --- /dev/null +++ b/gltfTutorial_cn/images/aos.svg @@ -0,0 +1,2070 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Array-Of-Structures: One bufferView contains positions and normals, interleaved: + + + + 0 + 4 + 8 + 12 + 16 + 20 + 24 + + + p0x + + + + + + p0y + + + + + + p0z + + + + + + n0x + + + + + + n0y + + + + + + n0z + + + + + + + p1x + + + + + + p1y + + + + + + p1z + + + + + + n1x + + + + + + n1y + + + + + + n1z + + 24 + 32 + 36 + 40 + 44 + 48 + + + vertex 0 + + vertex 1 + + + + 0 + 4 + 8 + 12 + 16 + 20 + 24 + + + p0x + + + + + + p0y + + + + + + p0z + + + + + + p1x + + + + + + p1y + + + + + + p1z + 24 + 32 + 36 + 40 + 44 + 48 + positionsAccessor: + byteOffset : 0componentType : FLOATtype : "VEC3"count: : 2 + 0 + 4 + 8 + 12 + 16 + 20 + 24 + + + + + + n0x + + + + + + n0y + + + + + + n0z + + + + + + n1x + + + + + + n1y + + + + + + n1z + 24 + 32 + 36 + 40 + 44 + 48 + normalsAccessor: + byteOffset : 12 componentType : FLOATtype : "VEC3"count: : 2 + + + byteOffset : 12 + byteStride : 24 + + byteStride : 24 + The bufferView defines a byteStride of 24, which is the difference between the start of one accessor element, and the start of the next one. + + diff --git a/gltfTutorial_cn/images/applications.png b/gltfTutorial_cn/images/applications.png new file mode 100644 index 0000000..5bb63f2 Binary files /dev/null and b/gltfTutorial_cn/images/applications.png differ diff --git a/gltfTutorial_cn/images/applications.svg b/gltfTutorial_cn/images/applications.svg new file mode 100644 index 0000000..00d4478 --- /dev/null +++ b/gltfTutorial_cn/images/applications.svg @@ -0,0 +1,103 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + Screenshots from blend4web.com, sketchfab.com, cesium.com, biodigital.com  + + diff --git a/gltfTutorial_cn/images/buffer.png b/gltfTutorial_cn/images/buffer.png new file mode 100644 index 0000000..128ac4a Binary files /dev/null and b/gltfTutorial_cn/images/buffer.png differ diff --git a/gltfTutorial_cn/images/buffer.svg b/gltfTutorial_cn/images/buffer.svg new file mode 100644 index 0000000..a1f62e2 --- /dev/null +++ b/gltfTutorial_cn/images/buffer.svg @@ -0,0 +1,1062 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gltfTutorial_cn/images/bufferBufferView.png b/gltfTutorial_cn/images/bufferBufferView.png new file mode 100644 index 0000000..ff60114 Binary files /dev/null and b/gltfTutorial_cn/images/bufferBufferView.png differ diff --git a/gltfTutorial_cn/images/bufferBufferView.svg b/gltfTutorial_cn/images/bufferBufferView.svg new file mode 100644 index 0000000..82d5e34 --- /dev/null +++ b/gltfTutorial_cn/images/bufferBufferView.svg @@ -0,0 +1,2080 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gltfTutorial_cn/images/bufferBufferViewAccessor.png b/gltfTutorial_cn/images/bufferBufferViewAccessor.png new file mode 100644 index 0000000..931a424 Binary files /dev/null and b/gltfTutorial_cn/images/bufferBufferViewAccessor.png differ diff --git a/gltfTutorial_cn/images/bufferBufferViewAccessor.svg b/gltfTutorial_cn/images/bufferBufferViewAccessor.svg new file mode 100644 index 0000000..088418f --- /dev/null +++ b/gltfTutorial_cn/images/bufferBufferViewAccessor.svg @@ -0,0 +1,2648 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Three scalars with unsigned short components:The indices of the triangle + Three 3D vectors with float components:The vertex positions of the triangle + diff --git a/gltfTutorial_cn/images/cameras.png b/gltfTutorial_cn/images/cameras.png new file mode 100644 index 0000000..11a098f Binary files /dev/null and b/gltfTutorial_cn/images/cameras.png differ diff --git a/gltfTutorial_cn/images/contentPipeline.png b/gltfTutorial_cn/images/contentPipeline.png new file mode 100644 index 0000000..5b2446a Binary files /dev/null and b/gltfTutorial_cn/images/contentPipeline.png differ diff --git a/gltfTutorial_cn/images/contentPipeline.svg b/gltfTutorial_cn/images/contentPipeline.svg new file mode 100644 index 0000000..ab1db39 --- /dev/null +++ b/gltfTutorial_cn/images/contentPipeline.svg @@ -0,0 +1,1402 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Authoring applications + + Blender + + .blend files + + Runtime applications + Graphics APIs + + + + + + + 3D data sources E.g. laser scanners + + + .obj files + + .ply files + + .stl files + + Maya + + .ma files + + LightWave3D + + .lws files + + 3DSMAX + + .max files + + ... + + other files + + + + + + + + + Application 1 + + Application 2 + + ... + + Application N + + + + + + + + + + + + + + + + + Importers and converters Hundreds of them! + + diff --git a/gltfTutorial_cn/images/contentPipelineWithGltf.png b/gltfTutorial_cn/images/contentPipelineWithGltf.png new file mode 100644 index 0000000..be4761a Binary files /dev/null and b/gltfTutorial_cn/images/contentPipelineWithGltf.png differ diff --git a/gltfTutorial_cn/images/contentPipelineWithGltf.svg b/gltfTutorial_cn/images/contentPipelineWithGltf.svg new file mode 100644 index 0000000..e557d1a --- /dev/null +++ b/gltfTutorial_cn/images/contentPipelineWithGltf.svg @@ -0,0 +1,1545 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Authoring applications + + Blender + + Runtime applications + Graphics APIs + + + + + + + 3D data sources E.g. laser scanners + + .obj files + + .ply files + + .stl files + + Maya + + LightWave3D + + 3DSMAX + + ... + + other files + + + + + + Application 1 + + Application 2 + + ... + + Application N + + + + + + + + + + + + + Custom converter + + COLLADA2GLTF + + + + obj2gltf + + Custom converter + + + + diff --git a/gltfTutorial_cn/images/createPng.bat b/gltfTutorial_cn/images/createPng.bat new file mode 100644 index 0000000..1731d5f --- /dev/null +++ b/gltfTutorial_cn/images/createPng.bat @@ -0,0 +1,4 @@ + +echo Create for %1 + +"C:\Program Files\Inkscape\inkscape.exe" -z -f "%1" -D -e "%~n1.png" \ No newline at end of file diff --git a/gltfTutorial_cn/images/createPngs.bat b/gltfTutorial_cn/images/createPngs.bat new file mode 100644 index 0000000..019f1d9 --- /dev/null +++ b/gltfTutorial_cn/images/createPngs.bat @@ -0,0 +1,2 @@ + +for %%i in (*.svg) do call createPng.bat %%i diff --git a/gltfTutorial_cn/images/gltfJsonStructure.png b/gltfTutorial_cn/images/gltfJsonStructure.png new file mode 100644 index 0000000..5710702 Binary files /dev/null and b/gltfTutorial_cn/images/gltfJsonStructure.png differ diff --git a/gltfTutorial_cn/images/gltfJsonStructure.svg b/gltfTutorial_cn/images/gltfJsonStructure.svg new file mode 100644 index 0000000..8f63777 --- /dev/null +++ b/gltfTutorial_cn/images/gltfJsonStructure.svg @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + scene + + node + + mesh + + accessor + + bufferView + + buffer + + camera + + skin + + animation + + material + + image + + texture + + sampler + + + + + + + + + + + + + + + + diff --git a/gltfTutorial_cn/images/gltfStructure.png b/gltfTutorial_cn/images/gltfStructure.png new file mode 100644 index 0000000..456c370 Binary files /dev/null and b/gltfTutorial_cn/images/gltfStructure.png differ diff --git a/gltfTutorial_cn/images/gltfStructure.svg b/gltfTutorial_cn/images/gltfStructure.svg new file mode 100644 index 0000000..7e839f2 --- /dev/null +++ b/gltfTutorial_cn/images/gltfStructure.svg @@ -0,0 +1,755 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + "scenes": [ ... ],"nodes": [ ... ],"cameras": [ ... ],"animations": [ ... ],..."buffers": [ { "uri": "buffer01.bin", "byteLength": 102040 }],"images": [ { "uri": "image01.png" }], + The JSON part describes the general scene structure, and elements like cameras and animations. Additionally, it contains links to files withbinary data and images: + + .gltf (JSON) file + + + .bin files + Raw data for geometry,animations and skins + + + + + + + + + + + + ... + ... + + + + + 100101011011 + + + + + + .jpg or .png files + Images for the texturesof the models + + + + + + diff --git a/gltfTutorial_cn/images/materials.png b/gltfTutorial_cn/images/materials.png new file mode 100644 index 0000000..37a3c9a Binary files /dev/null and b/gltfTutorial_cn/images/materials.png differ diff --git a/gltfTutorial_cn/images/matrix.png b/gltfTutorial_cn/images/matrix.png new file mode 100644 index 0000000..8dfa36e Binary files /dev/null and b/gltfTutorial_cn/images/matrix.png differ diff --git a/gltfTutorial_cn/images/matrix.tex b/gltfTutorial_cn/images/matrix.tex new file mode 100644 index 0000000..0a58768 --- /dev/null +++ b/gltfTutorial_cn/images/matrix.tex @@ -0,0 +1,11 @@ +\begin{align*} +M = +\[ \left( \begin{array}{cccc} + 2.0 & 0.0 & 0.0 & 10.0 \\ + 0.0 & 0.866 & -0.25 & 20.0 \\ + 0.0 & 0.5 & 0.433 & 30.0 \\ + 0.0 & 0.0 & 0.0, & 1.0 \end{array} +\right)\] +\end{align*} + + diff --git a/gltfTutorial_cn/images/meshPrimitiveAttributes.png b/gltfTutorial_cn/images/meshPrimitiveAttributes.png new file mode 100644 index 0000000..da9c7af Binary files /dev/null and b/gltfTutorial_cn/images/meshPrimitiveAttributes.png differ diff --git a/gltfTutorial_cn/images/meshPrimitiveAttributes.svg b/gltfTutorial_cn/images/meshPrimitiveAttributes.svg new file mode 100644 index 0000000..e6b980f --- /dev/null +++ b/gltfTutorial_cn/images/meshPrimitiveAttributes.svg @@ -0,0 +1,945 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + normalsAccessor: + 0.0 + 1.0 + 0.0 + 0.0 + -1.0 + 0.0 + 0.71 + 0.71 + 0.0 + 1.0 + 1.0 + -5.0 + 4.0 + 3.0 + -2.0 + 8.0 + + 1.8 + -2.0 + ... + + + + + Position:Normal: + (1.0, 1.0, -5.0)(0.0, 1.0, 0.0) + + + + (4.0, 2.0, -2.0)(0.0, -1.0, 0.0) + + + (8.0, 1.8, -2.0)(0.71, 0.71, 0.0) + + + + + + + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + + positionsAccessor: + + diff --git a/gltfTutorial_cn/images/metallicRoughnessSpheres.png b/gltfTutorial_cn/images/metallicRoughnessSpheres.png new file mode 100644 index 0000000..8a60f43 Binary files /dev/null and b/gltfTutorial_cn/images/metallicRoughnessSpheres.png differ diff --git a/gltfTutorial_cn/images/productMatrix.png b/gltfTutorial_cn/images/productMatrix.png new file mode 100644 index 0000000..6ac7f28 Binary files /dev/null and b/gltfTutorial_cn/images/productMatrix.png differ diff --git a/gltfTutorial_cn/images/productMatrix.tex b/gltfTutorial_cn/images/productMatrix.tex new file mode 100644 index 0000000..4f99664 --- /dev/null +++ b/gltfTutorial_cn/images/productMatrix.tex @@ -0,0 +1,11 @@ +\begin{align*} +M = T * R * S = +\[ \left( \begin{array}{cccc} + 2.0 & 0.0 & 0.0 & 10.0 \\ + 0.0 & 0.866 & -0.25 & 20.0 \\ + 0.0 & 0.5 & 0.433 & 30.0 \\ + 0.0 & 0.0 & 0.0, & 1.0 \end{array} +\right)\] +\end{align*} + + diff --git a/gltfTutorial_cn/images/rotationMatrix.png b/gltfTutorial_cn/images/rotationMatrix.png new file mode 100644 index 0000000..1a242e0 Binary files /dev/null and b/gltfTutorial_cn/images/rotationMatrix.png differ diff --git a/gltfTutorial_cn/images/rotationMatrix.tex b/gltfTutorial_cn/images/rotationMatrix.tex new file mode 100644 index 0000000..6ce91dc --- /dev/null +++ b/gltfTutorial_cn/images/rotationMatrix.tex @@ -0,0 +1,9 @@ +\begin{align*} +R = +\[ \left( \begin{array}{cccc} + 1.0 & 0.0 & 0.0 & 0.0 \\ + 0.0 & 0.866 & -0.5 & 0.0 \\ + 0.0 & 0.5 & 0.866 & 0.0 \\ + 0.0 & 0.0 & 0.0 & 1.0 \end{array} +\right)\] +\end{align*} \ No newline at end of file diff --git a/gltfTutorial_cn/images/scaleMatrix.png b/gltfTutorial_cn/images/scaleMatrix.png new file mode 100644 index 0000000..76b106a Binary files /dev/null and b/gltfTutorial_cn/images/scaleMatrix.png differ diff --git a/gltfTutorial_cn/images/scaleMatrix.tex b/gltfTutorial_cn/images/scaleMatrix.tex new file mode 100644 index 0000000..cddf68d --- /dev/null +++ b/gltfTutorial_cn/images/scaleMatrix.tex @@ -0,0 +1,9 @@ +\begin{align*} +S = +\[ \left( \begin{array}{cccc} + 2.0 & 0.0 & 0.0 & 0.0 \\ + 0.0 & 1.0 & 0.0 & 0.0 \\ + 0.0 & 0.0 & 0.5 & 0.0 \\ + 0.0 & 0.0 & 0.0 & 1.0 \end{array} +\right)\] +\end{align*} \ No newline at end of file diff --git a/gltfTutorial_cn/images/sceneGraph.png b/gltfTutorial_cn/images/sceneGraph.png new file mode 100644 index 0000000..720f050 Binary files /dev/null and b/gltfTutorial_cn/images/sceneGraph.png differ diff --git a/gltfTutorial_cn/images/sceneGraph.svg b/gltfTutorial_cn/images/sceneGraph.svg new file mode 100644 index 0000000..927ca2b --- /dev/null +++ b/gltfTutorial_cn/images/sceneGraph.svg @@ -0,0 +1,1038 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + "nodes" : [ { "children": [ 1, 2 ] }, { "camera": 0, "matrix" : [ ... ] } { "mesh": 0, "children": [ 3, 4 ] }, { "mesh": 2 "rotation" : [...], "translation" : [...] }, { "mesh": 2, "rotation" : [...] "translation" : [...] }} + + + + glTF nodes + Scene structure + Scene + + + + + + + + + + + + + root + camera + car + front wheels + rear wheels + + + + + + + + + + + + + + + + + diff --git a/gltfTutorial_cn/images/simpleMaterial.png b/gltfTutorial_cn/images/simpleMaterial.png new file mode 100644 index 0000000..e33bb4a Binary files /dev/null and b/gltfTutorial_cn/images/simpleMaterial.png differ diff --git a/gltfTutorial_cn/images/simpleMeshes.png b/gltfTutorial_cn/images/simpleMeshes.png new file mode 100644 index 0000000..725873f Binary files /dev/null and b/gltfTutorial_cn/images/simpleMeshes.png differ diff --git a/gltfTutorial_cn/images/simpleMorph.png b/gltfTutorial_cn/images/simpleMorph.png new file mode 100644 index 0000000..0143865 Binary files /dev/null and b/gltfTutorial_cn/images/simpleMorph.png differ diff --git a/gltfTutorial_cn/images/simpleMorphInitial.png b/gltfTutorial_cn/images/simpleMorphInitial.png new file mode 100644 index 0000000..a07dc98 Binary files /dev/null and b/gltfTutorial_cn/images/simpleMorphInitial.png differ diff --git a/gltfTutorial_cn/images/simpleMorphInitial.svg b/gltfTutorial_cn/images/simpleMorphInitial.svg new file mode 100644 index 0000000..509368a --- /dev/null +++ b/gltfTutorial_cn/images/simpleMorphInitial.svg @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + (0.0, 0.0, 0.0) + (1.0, 0.0, 0.0) + (0.5, 0.5, 0.0) + (-1.0, 1.0, 0.0) + ( 1.0, 1.0, 0.0) + + diff --git a/gltfTutorial_cn/images/simpleMorphIntermediate.png b/gltfTutorial_cn/images/simpleMorphIntermediate.png new file mode 100644 index 0000000..9ed4ff0 Binary files /dev/null and b/gltfTutorial_cn/images/simpleMorphIntermediate.png differ diff --git a/gltfTutorial_cn/images/simpleMorphIntermediate.svg b/gltfTutorial_cn/images/simpleMorphIntermediate.svg new file mode 100644 index 0000000..a24cca5 --- /dev/null +++ b/gltfTutorial_cn/images/simpleMorphIntermediate.svg @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + (0.0, 0.0, 0.0) + (1.0, 0.0, 0.0) + ( 0.5, 0.5, 0.0)+ 0.25 * (-1.0, 1.0, 0.0)+ 1.0 * ( 1.0, 1.0, 0.0)= ( 1.25, 1.75, 0.0) + (-1.0, 1.0, 0.0) + ( 1.0, 1.0, 0.0) + + diff --git a/gltfTutorial_cn/images/simpleSkin.gif b/gltfTutorial_cn/images/simpleSkin.gif new file mode 100644 index 0000000..6b161ef Binary files /dev/null and b/gltfTutorial_cn/images/simpleSkin.gif differ diff --git a/gltfTutorial_cn/images/simpleSkinOutline01.png b/gltfTutorial_cn/images/simpleSkinOutline01.png new file mode 100644 index 0000000..2c4eb48 Binary files /dev/null and b/gltfTutorial_cn/images/simpleSkinOutline01.png differ diff --git a/gltfTutorial_cn/images/simpleSkinOutline02.png b/gltfTutorial_cn/images/simpleSkinOutline02.png new file mode 100644 index 0000000..d51a49b Binary files /dev/null and b/gltfTutorial_cn/images/simpleSkinOutline02.png differ diff --git a/gltfTutorial_cn/images/simpleSparseAccessor.png b/gltfTutorial_cn/images/simpleSparseAccessor.png new file mode 100644 index 0000000..76cbc3e Binary files /dev/null and b/gltfTutorial_cn/images/simpleSparseAccessor.png differ diff --git a/gltfTutorial_cn/images/simpleSparseAccessorDescription.png b/gltfTutorial_cn/images/simpleSparseAccessorDescription.png new file mode 100644 index 0000000..ff53acf Binary files /dev/null and b/gltfTutorial_cn/images/simpleSparseAccessorDescription.png differ diff --git a/gltfTutorial_cn/images/simpleTexture.png b/gltfTutorial_cn/images/simpleTexture.png new file mode 100644 index 0000000..620bab4 Binary files /dev/null and b/gltfTutorial_cn/images/simpleTexture.png differ diff --git a/gltfTutorial_cn/images/skinInverseBindMatrix.png b/gltfTutorial_cn/images/skinInverseBindMatrix.png new file mode 100644 index 0000000..5f95786 Binary files /dev/null and b/gltfTutorial_cn/images/skinInverseBindMatrix.png differ diff --git a/gltfTutorial_cn/images/skinInverseBindMatrix.svg b/gltfTutorial_cn/images/skinInverseBindMatrix.svg new file mode 100644 index 0000000..b79fd49 --- /dev/null +++ b/gltfTutorial_cn/images/skinInverseBindMatrix.svg @@ -0,0 +1,701 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + Geometry transformed with theinverseBindMatrix of joint 1, prepared for transforming it into the space of joint node 1 + Geometry in the space of theskeleton: + + Translate about -1.0along y-axis with inverseBindMatrixof joint1 + jointNode0 + jointNode1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + jointNode0 + jointNode1 + + diff --git a/gltfTutorial_cn/images/skinJointMatrices.png b/gltfTutorial_cn/images/skinJointMatrices.png new file mode 100644 index 0000000..100ea99 Binary files /dev/null and b/gltfTutorial_cn/images/skinJointMatrices.png differ diff --git a/gltfTutorial_cn/images/skinJointMatrices.svg b/gltfTutorial_cn/images/skinJointMatrices.svg new file mode 100644 index 0000000..ab8d98c --- /dev/null +++ b/gltfTutorial_cn/images/skinJointMatrices.svg @@ -0,0 +1,990 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + joint node 0 + joint node 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + 1. Initial configuration + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + + + + 2. After translating about -1.0 along the y-axis with the inverse bind matrixof joint 1 + + 3. After transforming with the current global transform of joint node 1: translating about 1.0 along the y-axis and rotating about 45 degrees around the z-axis + + joint node 0 + joint node 1 + joint node 0 + joint node 1 + + diff --git a/gltfTutorial_cn/images/skinSkinMatrix.png b/gltfTutorial_cn/images/skinSkinMatrix.png new file mode 100644 index 0000000..b7de14a Binary files /dev/null and b/gltfTutorial_cn/images/skinSkinMatrix.png differ diff --git a/gltfTutorial_cn/images/skinSkinMatrix.svg b/gltfTutorial_cn/images/skinSkinMatrix.svg new file mode 100644 index 0000000..222801a --- /dev/null +++ b/gltfTutorial_cn/images/skinSkinMatrix.svg @@ -0,0 +1,1169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + + + + + + + + + + + + + + + + + + + + + + + Origin + + + + + + + + + + + + Transformation of the geometry with jointMatrix[1] : + Transformation of the geometry with jointMatrix[0] : + skinMatrix = 1.0 * jointMatrix[0] + 0.0 * jointMatrix[1]; + skinMatrix = 0.75 * jointMatrix[0] + 0.25 * jointMatrix[1]; + skinMatrix = 0.5 * jointMatrix[0] + 0.5 * jointMatrix[1]; + skinMatrix = 0.25 * jointMatrix[0] + 0.75 * jointMatrix[1]; + skinMatrix = 0.0 * jointMatrix[0] + 1.0 * jointMatrix[1]; + + + + + + + + Data from "JOINTS" attribute:a_joint.x and a_joint.y + Data from "WEIGHTS" attribute:a_weight.x and a_weight.y + Transformation of the geometry with the skin matrix: + The skin matrix is computed in the vertex shader, for each vertex, as a weighted combination of the joint matrices:- The indices of the joint matrices are given to the vertex shader as the a_joint attribute.- The weights for the combination are given to the vertex shader as the a_weight attribute. + + diff --git a/gltfTutorial_cn/images/testTexture.png b/gltfTutorial_cn/images/testTexture.png new file mode 100644 index 0000000..c0aeb3c Binary files /dev/null and b/gltfTutorial_cn/images/testTexture.png differ diff --git a/gltfTutorial_cn/images/translationMatrix.png b/gltfTutorial_cn/images/translationMatrix.png new file mode 100644 index 0000000..f847c24 Binary files /dev/null and b/gltfTutorial_cn/images/translationMatrix.png differ diff --git a/gltfTutorial_cn/images/translationMatrix.tex b/gltfTutorial_cn/images/translationMatrix.tex new file mode 100644 index 0000000..6b76137 --- /dev/null +++ b/gltfTutorial_cn/images/translationMatrix.tex @@ -0,0 +1,9 @@ +\begin{align*} +T = +\[ \left( \begin{array}{cccc} + 1.0 & 0.0 & 0.0 & 10.0 \\ + 0.0 & 1.0 & 0.0 & 20.0 \\ + 0.0 & 0.0 & 1.0 & 30.0 \\ + 0.0 & 0.0 & 0.0 & 1.0 \end{array} +\right)\] +\end{align*} \ No newline at end of file diff --git a/gltfTutorial_cn/images/triangle.png b/gltfTutorial_cn/images/triangle.png new file mode 100644 index 0000000..89c356c Binary files /dev/null and b/gltfTutorial_cn/images/triangle.png differ diff --git a/gltfTutorial_cn/images/triangleWithSimpleMaterial.png b/gltfTutorial_cn/images/triangleWithSimpleMaterial.png new file mode 100644 index 0000000..ff24ca4 Binary files /dev/null and b/gltfTutorial_cn/images/triangleWithSimpleMaterial.png differ