• CSharpGL(5)解析3DS文件并用CSharpGL渲染


    CSharpGL(5)解析3DS文件并用CSharpGL渲染

    我曾经写过一个简单的*.3ds文件的解析器,但是只能解析最基本的顶点、索引信息,且此解析器是仿照别人的C++代码改写的,设计的也不好,不方便扩展。

    现在我重新设计实现了一个*.3ds文件的解析器,它能解析的Chunk类型更多,且容易扩展。以后需要解析更多类型的Chunk时比较简单。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    下载

    这个3DS解析器现在不是CSharpGL的一部分,CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

    本文代码可在(https://github.com/bitzhuwei/CSharpGL2)找到。

    本文所用的3ds文件您可以在此(http://www.cgrealm.org/d/downpage.php?n=2&id=15764::1326768548)下载,由于文件比较大我就不上传了。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    3DS文件格式

    3ds文件是二进制的。3ds格式的基本单元叫块(chunk)。我们就是读这样一块一块的信息。目录树如下,缩进风格体现了块的父子关系。可见3ds模型文件和XML文件类似,都是只有1个根结点的树状结构。

     1 0x4D4D // Main Chunk
     2 ├─ 0x0002 // M3D Version
     3 ├─ 0x3D3D // 3D Editor Chunk
     4 │  ├─ 0x4000 // Object Block
     5 │  │  ├─ 0x4100 // Triangular Mesh
     6 │  │  │  ├─ 0x4110 // Vertices List
     7 │  │  │  ├─ 0x4120 // Faces Description
     8 │  │  │  │  ├─ 0x4130 // Faces Material
     9 │  │  │  │  └─ 0x4150 // Smoothing Group List
    10 │  │  │  ├─ 0x4140 // Mapping Coordinates List
    11 │  │  │  └─ 0x4160 // Local Coordinates System
    12 │  │  ├─ 0x4600 // Light
    13 │  │  │  └─ 0x4610 // Spotlight
    14 │  │  └─ 0x4700 // Camera
    15 │  └─ 0xAFFF // Material Block
    16 │     ├─ 0xA000 // Material Name
    17 │     ├─ 0xA010 // Ambient Color
    18 │     ├─ 0xA020 // Diffuse Color
    19 │     ├─ 0xA030 // Specular Color
    20 │     ├─ 0xA200 // Texture Map 1
    21 │     ├─ 0xA230 // Bump Map
    22 │     └─ 0xA220 // Reflection Map
    23 │        │  // Sub Chunks For Each Map 
    24 │        ├─ 0xA300 // Mapping Filename
    25 │        └─ 0xA351 // Mapping Parameters
    26 └─ 0xB000 // Keyframer Chunk
    27    ├─ 0xB002 // Mesh Information Block
    28    ├─ 0xB007 // Spot Light Information Block
    29    └─ 0xB008 // Frames (Start and End)
    30       ├─ 0xB010 // Object Name
    31       ├─ 0xB013 // Object Pivot Point
    32       ├─ 0xB020 // Position Track
    33       ├─ 0xB021 // Rotation Track
    34       ├─ 0xB022 // Scale Track
    35       └─ 0xB030 // Hierarchy Position
    Chunk树

    实际上完整的chunk列表有上千种类型,我们只需解析其中的顶点列表、面列表和纹理UV列表就行了。

    以类型标识为0x4D4D的MAIN CHUNK为例,整个3ds文件的前两个byte必须是0x4D4D,否则就说明这个文件不是3ds模型文件。然后从第3到第6个byte是一个Uint32型的数值,表示整个MAIN CHUNK的长度。由于MAIN CHUNK是整个3ds文件的根结点,它的长度也即整个3ds文件的长度。

    块(Chunk)的结构

    每一个“chunk”的结构如下所示:

    偏移量

    长度

    0

    2

    块标识符

    2

    4

    块长: 块数据 + 子块内容

    6

    n

    块数据

    6+n

    m

    S子块

    文件内容

    一个3DS文件,其中包含若干材质对象,材质对象里有材质参数和贴图文件名;还有若干子模型,每个子模型都由顶点位置、UV位置、三角形索引和分组索引构成。分组索引是这么一个东西:它由若干三角形索引的编号和一个材质对象名组成。这个分组索引似乎暗示着:渲染过程应根据分组索引描绘的顺序进行,即取出一个分组索引,绑定它指定的材质和贴图,渲染它指定的三角形,然后取出下一个分组索引继续上述渲染操作。我们将在后文进行验证。

    2016-01-21

    今天发现有的3ds文件是没有分组索引这个玩意的。所以要特殊处理一下。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    解析器设计思路

    之前写的解析器中使用的思路是:首先根据偏移量和长度找到一个块的标识符,然后据此来判断它是什么块,遇到我们需要的块,就进一步读取,如果不需要,直接跳过这一块,读取下面的块。这没有用到面向对象的思想,只有面向过程编程。如果需要添加一个新的Chunk类型,修改起来是比较困难的。

    我重新设计的解析器的思路如下:

    递归读取各个块

    读取一个块,然后依次读取它的各个子块。鉴于各个块之间的树状关系,这是一个递归的过程。

    各个类型的块都应该继承自同一基类型ChunkBase。对于具体的Chunk类型,只需override掉Process方法即可实现自己的解析过程。

     1     public abstract class ChunkBase
     2     {
     3         public ChunkBase Parent;
     4         public List<ChunkBase> Childern;
     5 
     6         public uint Length;
     7         public uint BytesRead;
     8 
     9         public ChunkBase()
    10         {
    11             this.Childern = new List<ChunkBase>();
    12         }
    13 
    14         internal virtual void Process(ParsingContext context)
    15         {
    16             var chunk = this;
    17             var reader = context.reader;
    18 
    19             while (chunk.BytesRead < chunk.Length)
    20             {
    21                 ChunkBase child = reader.ReadChunk();
    22                 child.Parent = this;
    23                 this.Childern.Add(child);
    24 
    25                 child.Process(context);
    26 
    27                 chunk.BytesRead += child.BytesRead;
    28             }
    29         }
    30     }

    数据字典

    各个类型的Chunk都用一个具体的class类型表达,为了方便这些class类型与用ushort表达的的Chunk类型相互转换,我们需要2个字典。

      1     public static partial class ChunkBaseHelper
      2     {
      3 
      4         private static readonly Dictionary<Type, ushort> chunkTypeDict = new Dictionary<Type, ushort>();
      5         private static readonly Dictionary<ushort, Type> chunkIDDict = new Dictionary<ushort, Type>();
      6 
      7         /// <summary>
      8         /// 开发者必须了解的东西。
      9         /// </summary>
     10         static ChunkBaseHelper()
     11         {
     12             chunkTypeDict.Add(typeof(MainChunk), 0x4D4D);
     13             {
     14                 chunkTypeDict.Add(typeof(VersionChunk), 0x0002);
     15                 chunkTypeDict.Add(typeof(_3DEditorChunk), 0x3D3D);
     16                 {
     17                     chunkTypeDict.Add(typeof(ObjectBlockChunk), 0x4000);
     18                     {
     19                         chunkTypeDict.Add(typeof(TriangularMeshChunk), 0x4100);
     20                         {
     21                             chunkTypeDict.Add(typeof(VerticesListChunk), 0x4110);
     22                             chunkTypeDict.Add(typeof(FacesDescriptionChunk), 0x4120);
     23                             {
     24                                 chunkTypeDict.Add(typeof(FacesMaterialChunk), 0x4130);
     25                                 chunkTypeDict.Add(typeof(SmoothingGroupListChunk), 0x4150);
     26                             }
     27                             chunkTypeDict.Add(typeof(MappingCoordinatesListChunk), 0x4140);
     28                             chunkTypeDict.Add(typeof(LocalCoordinatesSystemChunk), 0x4160);
     29                         }
     30                         chunkTypeDict.Add(typeof(LightChunk), 0x4600);
     31                         {
     32                             chunkTypeDict.Add(typeof(SpotlightChunk), 0x4610);
     33                         }
     34                         chunkTypeDict.Add(typeof(CameraChunk), 0x4700);
     35                     }
     36                     chunkTypeDict.Add(typeof(MaterialBlockChunk), 0xAFFF);
     37                     {
     38                         chunkTypeDict.Add(typeof(MaterialNameChunk), 0xA000);
     39                         chunkTypeDict.Add(typeof(AmbientColorChunk), 0xA010);
     40                         chunkTypeDict.Add(typeof(DiffuseColorChunk), 0xA020);
     41                         chunkTypeDict.Add(typeof(SpecularColorChunk), 0xA030);
     42                         chunkTypeDict.Add(typeof(MatShininessChunk), 0xA040);
     43                         chunkTypeDict.Add(typeof(TextureMapChunk), 0xA200);
     44                         chunkTypeDict.Add(typeof(BumpMapChunk), 0xA230);
     45                         chunkTypeDict.Add(typeof(ReflectionMapChunk), 0xA220);
     46                         {
     47                             chunkTypeDict.Add(typeof(MappingFilenameChunk), 0xA300);
     48                             chunkTypeDict.Add(typeof(MappingParametersChunk), 0xA351);
     49                         }
     50                     }
     51                 }
     52                 chunkTypeDict.Add(typeof(KeyframeChunk), 0xB000);
     53                 {
     54                     chunkTypeDict.Add(typeof(MeshInformationBlockChunk), 0xB002);
     55                     chunkTypeDict.Add(typeof(SpotLightInformationBlockChunk), 0xB007);
     56                     chunkTypeDict.Add(typeof(FramesChunk), 0xB008);
     57                     {
     58                         chunkTypeDict.Add(typeof(ObjectNameChunk), 0xB010);
     59                         chunkTypeDict.Add(typeof(ObjectPivotPointChunk), 0xB013);
     60                         chunkTypeDict.Add(typeof(PositionTrackChunk), 0xB020);
     61                         chunkTypeDict.Add(typeof(RotationTrackChunk), 0xB021);
     62                         chunkTypeDict.Add(typeof(ScaleTrackChunk), 0xB022);
     63                         chunkTypeDict.Add(typeof(HierarchyPositionChunk), 0xB030);
     64                     }
     65                 }
     66             }
     67 
     68             chunkIDDict.Add(0x4D4D, typeof(MainChunk));
     69             {
     70                 chunkIDDict.Add(0x0002, typeof(VersionChunk));
     71                 chunkIDDict.Add(0x3D3D, typeof(_3DEditorChunk));
     72                 {
     73                     chunkIDDict.Add(0x4000, typeof(ObjectBlockChunk));
     74                     {
     75                         chunkIDDict.Add(0x4100, typeof(TriangularMeshChunk));
     76                         {
     77                             chunkIDDict.Add(0x4110, typeof(VerticesListChunk));
     78                             chunkIDDict.Add(0x4120, typeof(FacesDescriptionChunk));
     79                             {
     80                                 chunkIDDict.Add(0x4130, typeof(FacesMaterialChunk));
     81                                 chunkIDDict.Add(0x4150, typeof(SmoothingGroupListChunk));
     82                             }
     83                             chunkIDDict.Add(0x4140, typeof(MappingCoordinatesListChunk));
     84                             chunkIDDict.Add(0x4160, typeof(LocalCoordinatesSystemChunk));
     85                         }
     86                         chunkIDDict.Add(0x4600, typeof(LightChunk));
     87                         {
     88                             chunkIDDict.Add(0x4610, typeof(SpotlightChunk));
     89                         }
     90                         chunkIDDict.Add(0x4700, typeof(CameraChunk));
     91                     }
     92                     chunkIDDict.Add(0xAFFF, typeof(MaterialBlockChunk));
     93                     {
     94                         chunkIDDict.Add(0xA000, typeof(MaterialNameChunk));
     95                         chunkIDDict.Add(0xA010, typeof(AmbientColorChunk));
     96                         chunkIDDict.Add(0xA020, typeof(DiffuseColorChunk));
     97                         chunkIDDict.Add(0xA030, typeof(SpecularColorChunk));
     98                         chunkIDDict.Add(0xA040, typeof(MatShininessChunk));
     99                         chunkIDDict.Add(0xA200, typeof(TextureMapChunk));
    100                         chunkIDDict.Add(0xA230, typeof(BumpMapChunk));
    101                         chunkIDDict.Add(0xA220, typeof(ReflectionMapChunk));
    102                         {
    103                             chunkIDDict.Add(0xA300, typeof(MappingFilenameChunk));
    104                             chunkIDDict.Add(0xA351, typeof(MappingParametersChunk));
    105                         }
    106                     }
    107                 }
    108                 chunkIDDict.Add(0xB000, typeof(KeyframeChunk));
    109                 {
    110                     chunkIDDict.Add(0xB002, typeof(MeshInformationBlockChunk));
    111                     chunkIDDict.Add(0xB007, typeof(SpotLightInformationBlockChunk));
    112                     chunkIDDict.Add(0xB008, typeof(FramesChunk));
    113                     {
    114                         chunkIDDict.Add(0xB010, typeof(ObjectNameChunk));
    115                         chunkIDDict.Add(0xB013, typeof(ObjectPivotPointChunk));
    116                         chunkIDDict.Add(0xB020, typeof(PositionTrackChunk));
    117                         chunkIDDict.Add(0xB021, typeof(RotationTrackChunk));
    118                         chunkIDDict.Add(0xB022, typeof(ScaleTrackChunk));
    119                         chunkIDDict.Add(0xB030, typeof(HierarchyPositionChunk));
    120                     }
    121                 }
    122             }
    123         }
    124     }
    数据字典

    未定义的Chunk

    3ds文件有上千种Chunk,我们暂时不会都解析出来(也没必要全解析出来)。所以我们用一个“未定义的Chunk”类型来代表那些我们不想解析的Chunk类型。

     1     /// <summary>
     2     /// 3ds文件有上千种Chunk,我们暂时不会都解析出来(也没必要全解析出来)。所以我们用一个“未定义的Chunk”类型来代表那些我们不想解析的Chunk类型。
     3     /// </summary>
     4     public class UndefinedChunk : ChunkBase
     5     {
     6         public ushort ID;
     7         public bool IsChunk { get; private set; }
     8 
     9         public UndefinedChunk()
    10         {
    11             this.IsChunk = true;
    12         }
    13 
    14         public override string ToString()
    15         {
    16             return string.Format("{0}(0x{1:X4}), position: {2}, length: {3}, read bytes: {4}",
    17                 this.IsChunk ? "Unknown Chunk" : "Fake Chunk", ID, Position, Length, BytesRead);
    18         }
    19 
    20         internal override void Process(ParsingContext context)
    21         {
    22             var chunk = this;
    23             var reader = context.reader;
    24             var parent = this.Parent;
    25 
    26             uint length = this.Length - this.BytesRead;
    27 
    28             if ((parent != null))
    29             {
    30                 var another = parent.Length - parent.BytesRead - this.BytesRead;
    31                 length = Math.Min(length, another);
    32             }
    33 
    34             reader.BaseStream.Position += length;
    35             chunk.BytesRead += length;
    36             if (chunk.Length != chunk.BytesRead)
    37             {
    38                 chunk.Length = chunk.BytesRead;
    39                 this.IsChunk = false;
    40             }
    41         }
    42     }

    注意:这里获取到的UndefinedChunk对象,不一定代表真的有这样一个未被解析的Chunk,它也可能是其父Chunk的一部分数据内容。所以,我们要结合这里的another值来判断到底应该继续读取多少字节,并且修补好可能出错的chunk.Length。

    读出一个Chunk的扩展方法

    每次获取一个Chunk对象时,都是借助BinaryReader得到Chunk类型和长度的,所以我们给它一个扩展方法,用于“读出一个Chunk”。

     1     public static partial class ChunkBaseHelper
     2     {
     3         public static ChunkBase ReadChunk(this BinaryReader reader)
     4         {
     5             // 2 byte ID
     6             ushort id = reader.ReadUInt16();
     7             // 4 byte length
     8             uint length = reader.ReadUInt32();
     9             // 2 + 4 = 6
    10             uint bytesRead = 6;
    11 
    12             Type type;
    13             if (chunkIDDict.TryGetValue(id, out type))
    14             {
    15                 object obj = Activator.CreateInstance(type);
    16                 ChunkBase result = obj as ChunkBase;
    17                 //result.ID = id;//不再需要记录ID,此对象的类型就指明了它的ID。
    18                 result.Length = length;
    19                 result.BytesRead = bytesRead;
    20                 return result;
    21             }
    22             else
    23             {
    24                 return new UndefinedChunk() { ID = id, Length = length, BytesRead = bytesRead, };
    25             }
    26         }
    27     }

    获取Chunk类型的ushort值

    得到一个Chunk对象后,可能会需要获取此对象代表的Chunk类型。

     1     public static partial class ChunkBaseHelper
     2     {
     3         public static ushort GetID(this ChunkBase chunk)
     4         {
     5             ushort value;
     6 
     7             if (chunk is UndefinedChunk)
     8             {
     9                 value = (chunk as UndefinedChunk).ID;
    10             }
    11             else
    12             {
    13                 Type type = chunk.GetType();
    14                 value = chunkTypeDict[type];//如果此处不存在此type的key,说明static构造函数需要添加此类型的字典信息。
    15             }
    16 
    17             return value;
    18         }
    19     }
    +BIT祝威+悄悄在此留下版了个权的信息说:

    解析器输出:Chunk树

    我们用TreeView控件来展示解析出来的Chunk树。

    如果不想看那些未定义的Chunk类型,可以隐藏之。

    如果需要,你可以将此Chunk树导出为文本格式:

    +BIT祝威+悄悄在此留下版了个权的信息说:

    从Chunk树到legacy OpenGL

    Dumper

    已经得到了Chunk树,下面需要得到可用于OpenGL渲染的模型。这实际上是一个语义分析和生成中间代码的过程。以根结点MainChunk为例:

     1     public static partial class ChunkDumper
     2     {
     3         public static void Dump(this MainChunk chunk, out ThreeDSModel4LegacyOpenGL model)
     4         {
     5             model = new ThreeDSModel4LegacyOpenGL();
     6 
     7             foreach (var item in chunk.Children)
     8             {
     9                 if(item is VersionChunk)
    10                 {
    11                     (item as VersionChunk).Dump(model);
    12                 }
    13                 else if(item is _3DEditorChunk)
    14                 {
    15                     (item as _3DEditorChunk).Dump(model);
    16                 }
    17                 else if (item is KeyframeChunk)
    18                 {
    19                     (item as KeyframeChunk).Dump(model);
    20                 }
    21                 else if(!(item is UndefinedChunk))
    22                 {
    23                     throw new NotImplementedException(string.Format(
    24                         "not dumper implemented for {0}", item.GetType()));
    25                 }
    26             }
    27         }
    28     }

    我们为每个Chunk类型都编写一个Dumper,在各个Dump过程中收集需要的信息(顶点位置、UV、贴图文件名、材质、光照等),汇总到一个ThreeDSModel4LegacyOpenGL对象,这个对象就可以用来渲染图形了。

    渲染

    根据上文对分组索引的推测,我给出如下的渲染过程。

      1     public class ThreeDSModel4LegacyOpenGL
      2     {
      3         public List<ThreeDSMesh4LegacyOpenGL> Entities = new List<ThreeDSMesh4LegacyOpenGL>();
      4         public Dictionary<string, ThreeDSMaterial4LegacyOpenGL> MaterialDict = new Dictionary<string, ThreeDSMaterial4LegacyOpenGL>();
      5 
      6         public void Render()
      7         {
      8             foreach (ThreeDSMesh4LegacyOpenGL mesh in Entities)
      9             {
     10                 mesh.Render(this);
     11             }
     12         }
     13 }
     14     public class ThreeDSMesh4LegacyOpenGL
     15     {
     16         public List<Tuple<string, ushort[]>> usingMaterialIndexesList = new List<Tuple<string, ushort[]>>();
     17         // TODO: OO this
     18         // fields should be private
     19         // constructor with verts and faces
     20         // normalize in ctor
     21 
     22         //public ThreeDSMaterial material = new ThreeDSMaterial();
     23         //public string UsesMaterial;
     24 
     25         // The stored vertices 
     26         public Vector[] Vertexes;
     27 
     28         // The calculated normals
     29         public Vector[] normals;
     30 
     31         // The indices of the triangles which point to vertices
     32         public Triangle[] TriangleIndexes;
     33 
     34         // The coordinates which map the texture onto the entity
     35         public TexCoord[] TexCoords;
     36 
     37         bool normalized = false;
     38         public ushort[] UsesIndexes;
     39 
     40         public void Render(ThreeDSModel4LegacyOpenGL model)
     41         {
     42             if (TriangleIndexes == null) return;
     43 
     44             // Draw every triangle in the entity
     45             foreach (var item in this.usingMaterialIndexesList)
     46             {
     47                 var material = model.MaterialDict[item.Item1];
     48 
     49                 GL.Materialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, material.Ambient);
     50                 GL.Materialfv(GL.GL_FRONT_AND_BACK, GL.GL_DIFFUSE, material.Diffuse);
     51                 GL.Materialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, material.Specular);
     52                 GL.Materialf(GL.GL_FRONT_AND_BACK, GL.GL_SHININESS, material.Shininess);
     53 
     54                 Texture2D[] textures = new Texture2D[] { material.GetTexture(), material.GetBumpTexture(), material.GetReflectionTexture(), };
     55                 bool drawn = false;
     56                 foreach (var texture in textures)
     57                 {
     58                     if (!(drawn && texture == null)) // 如果没有贴图,就只画一次。
     59                     {
     60                         if (texture != null)
     61                         {
     62                             GL.Enable(GL.GL_TEXTURE_2D);
     63                             texture.Bind();
     64                         }
     65 
     66                         DrawTriangles(item, texture);
     67 
     68                         if (texture != null)
     69                         {
     70                             texture.Unbind();
     71                             GL.Disable(GL.GL_TEXTURE_2D);
     72                         }
     73                     }
     74 
     75                     drawn = true;
     76                 }
     77             }
     78         }
     79 
     80         private void DrawTriangles(Tuple<string, ushort[]> usingMaterialIndexes, Texture2D texture)
     81         {
     82             GL.Begin(GL.GL_TRIANGLES);
     83             foreach (var usingIndex in usingMaterialIndexes.Item2)
     84             {
     85                 Triangle tri = this.TriangleIndexes[usingIndex];
     86                 // Vertex 1
     87                 if (normalized)
     88                 {
     89                     var normal = this.normals[tri.vertex1];
     90                     GL.Normal3d(normal.X, normal.Y, normal.Z);
     91                 }
     92                 if (texture != null)
     93                 {
     94                     var texCoord = this.TexCoords[tri.vertex1];
     95                     GL.TexCoord2f(texCoord.U, texCoord.V);
     96                 }
     97                 {
     98                     var vertex = this.Vertexes[tri.vertex1];
     99                     GL.Vertex3d(vertex.X, vertex.Y, vertex.Z);
    100                 }
    101 
    102                 // Vertex 2
    103                 if (normalized)
    104                 {
    105                     var normal = this.normals[tri.vertex2];
    106                     GL.Normal3d(normal.X, normal.Y, normal.Z);
    107                 }
    108                 if (texture != null)
    109                 {
    110                     var texCoord = this.TexCoords[tri.vertex2];
    111                     GL.TexCoord2f(texCoord.U, texCoord.V);
    112                 }
    113                 {
    114                     var vertex = this.Vertexes[tri.vertex2];
    115                     GL.Vertex3d(vertex.X, vertex.Y, vertex.Z);
    116                 }
    117 
    118                 // Vertex 3
    119                 if (normalized)
    120                 {
    121                     var normal = this.normals[tri.vertex3];
    122                     GL.Normal3d(normal.X, normal.Y, normal.Z);
    123                 }
    124                 if (texture != null)
    125                 {
    126                     var texCoord = this.TexCoords[tri.vertex3];
    127                     GL.TexCoord2f(texCoord.U, texCoord.V);
    128                 }
    129                 {
    130                     var vertex = this.Vertexes[tri.vertex3];
    131                     GL.Vertex3d(vertex.X, vertex.Y, vertex.Z);
    132                 }
    133             }
    134             GL.End();
    135         }
    136 }
    针对分组索引的渲染
    +BIT祝威+悄悄在此留下版了个权的信息说:

    验证分组索引的功能

    上文中我们发现了分组索引的存在,根据它的内容推测了它的功能,现在来验证一下。我找到一个3ds文件,用A3dsViewer打开是这样的:

    这个3ds文件附带多个贴图:

    这个是树皮。

    这是花盆里的石头。

    这是花盆里的苔藓(某种绿色植物?)

    这是盆景的红叶。

    现在再用我制作的3DSViewer渲染看看:

    整体上是对了,分组索引成功地将各个贴图附到了对应的三角形上。

    但是花盆不应该是白的,这是某些光照没有解析的原因。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    从Chunk树到modern OpenGL

    有了legacy OpenGL探路,modern OpenGL的渲染就容易多了,这里暂时不详述。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    总结

    目前这个3ds解析器算是可用了,以后需要扩展时也很容易。如果能找到更多的3ds文件来测试,就能知道还需要解析哪些类型的Chunk了。

  • 相关阅读:
    《软件需求十步走》阅读笔记一
    《探索需求》读书笔记三
    2018.9.26 随笔
    2018.9.09 随笔
    日期随笔,目录
    2018.9.03 随笔
    linux signal函数遇到的问题
    关于子线程执行两次的问题
    本科四年的一点经验
    linux 网络编程 3---(io多路复用,tcp并发)
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-2-parse-3ds-file-and-render-using-CSharpGL.html
Copyright © 2020-2023  润新知