ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Draco 位流规范深度解析:顺序连接性解码器(Sequential Connectivity Decoder)的实现与位流语法

Draco 位流规范深度解析:顺序连接性解码器(Sequential Connectivity Decoder)的实现与位流语法 图形学3D渲染【免费下载链接】dracoDraco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.项目地址https://gitcode.com/gh_mirrors/draco1/draco点击查看免费下载导读本文以 Draco 官方位流规范文档 docs/spec/sequential.decoder.md 为骨架完整解析其中定义的顺序Sequential连接性解码流程从num_faces/num_points/connectivity_method三个头部字段的解析到压缩与非压缩两种索引恢复路径的逐位级语法。结合仓库中 mesh_sequential_decoder.cc 与 mesh_sequential_encoder.cc 的真实实现读者将掌握 Draco 顺序编码位流的字段布局、索引精度选择规则、ZigZag 差分重建原理及其背后的边界校验逻辑。一、顺序连接性解码器在 Draco 解码流程中的位置Draco 位流解码的顶层入口定义在 draco.decoder.mdvoid Decode() { ParseHeader(); if (flags METADATA_FLAG_MASK) DecodeMetadata(); DecodeConnectivityData(); DecodeAttributeData(); }其中DecodeConnectivityData()见 connectivity.decoder.md根据头部的encoder_method字段分派到两条完全不同的连接性重建路径void DecodeConnectivityData() { if (encoder_method MESH_SEQUENTIAL_ENCODING) DecodeSequentialConnectivityData(); else if (encoder_method MESH_EDGEBREAKER_ENCODING) DecodeEdgebreakerConnectivityData(); }按 variable.descriptions.md 中的常量定义encoder_method取值 0 表示MESH_SEQUENTIAL_ENCODING顺序编码取值 1 表示MESH_EDGEBREAKER_ENCODINGEdgeBreaker 编码。顺序编码不依赖任何拓扑遍历或符号序列直接按面face逐一记录顶点索引因此结构最简单、解码最直接EdgeBreaker 则通过拓扑符号流压缩连接性详见 edgebreaker.decoder.md。本文聚焦前者。二、连接性头部解析ParseSequentialConnectivityData()解码器读入顺序连接性数据的第一个动作是解析三个头部字段规范语法如下void ParseSequentialConnectivityData() { num_faces varUI32 num_points varUI32 connectivity_method UI8 }字段类型含义num_facesvarUI32网格中包含的三角形面数量即待恢复的三角形个数num_pointsvarUI32网格包含的顶点点数量同时决定后续索引的存储精度connectivity_methodUI8索引编码方式选择符见下文常量表对应到真实实现mesh_sequential_decoder.cc 的DecodeConnectivity()首先通过DecodeVarint读取这两个数量值得注意的是源码还保留了向后兼容分支当位流版本低于 2.2DRACO_BITSTREAM_VERSION(2, 2)时num_faces与num_points以固定 4 字节形式读取而非 varint。这与规范中 varUI32 的标注存在版本差异当前默认新位流均采用 varint 编码。源码随后会对num_faces做两项合法性预检faces_64 0xffffffff / 3压缩顺序编码最多只能处理(2^32 - 1) / 3个索引超限直接拒绝faces_64 buffer()-remaining_size() / 3每个面需要 3 个索引若面数超出剩余缓冲区能容纳的索引数量同样拒绝。这两个检查确保后续解码不会因畸形位流产生越界读取或内存放大。三、connectivity_method压缩与未压缩两种索引模式connectivity_method只有一个字节取值定义在 variable.descriptions.md常量值含义SEQUENTIAL_COMPRESSED_INDICES0索引经过熵编码 差分压缩需先解码符号再重建SEQUENTIAL_UNCOMPRESSED_INDICES1索引直接以原始整数形式顺序写入位流编码端的对应选择位于 mesh_sequential_encoder.cc// We encode all attributes in the original (possibly duplicated) format. if (options()-GetGlobalBool(compress_connectivity, false)) { // 0 Encode compressed indices. buffer()-Encode(static_castuint8_t(0)); if (!CompressAndEncodeIndices()) { ... } } else { // 1 Encode indices directly. buffer()-Encode(static_castuint8_t(1)); ... }即当编码端配置项compress_connectivity为true时写入方法 0压缩默认值false时写入方法 1未压缩。这一开关可在编码器选项中通过compress_connectivity全局布尔选项控制。四、未压缩路径DecodeSequentialIndices() 与四种精度解析当connectivity_method SEQUENTIAL_UNCOMPRESSED_INDICES时走未压缩路径。为了在位流紧凑与表示范围之间取得平衡Draco 根据num_points的大小动态选择最小可容纳的整数宽度void DecodeSequentialIndices() { if (num_points 256) { ParseSequentialIndicesUI8(); } else if (num_points (1 16)) { ParseSequentialIndicesUI16(); } else if (num_points (1 21)) { ParseSequentialIndicesVarUI32(); } else { ParseSequentialIndicesUI32(); } }num_points范围调用的解析函数每个索引的编码宽度 256ParseSequentialIndicesUI8()1 字节定长 UI8 65536116ParseSequentialIndicesUI16()2 字节定长 UI16 2097152121ParseSequentialIndicesVarUI32()varint 变长 UI32其余ParseSequentialIndicesUI32()4 字节定长 UI32四种解析函数的结构完全一致只是按位读取的整数宽度不同均按面优先、面内三分量顺序排列。以 UI8 为例void ParseSequentialIndicesUI8() { for (i 0; i num_faces; i) { for (j 0; j 3; j) { face_to_vertex[j][i] UI8 } } }其余三个函数ParseSequentialIndicesUI16、ParseSequentialIndicesVarUI32、ParseSequentialIndicesUI32仅将字段类型替换为UI16、varUI32、UI32循环结构与索引写入顺序完全相同外层循环遍历面i内层循环遍历该面的三个角j最终填满face_to_vertex[3][num_faces]二维结构。源码实现 mesh_sequential_decoder.cc 与规范一一对应并额外增加了索引合法性校验每个解码出的索引值都必须 num_points否则立即返回失败。其中num_points (1 21)的 varint 分支还要求位流版本不低于 2.2旧版本位流直接落入UI32分支。编码端 mesh_sequential_encoder.cc 使用同一套num_points阈值选择写出宽度保证编解码两侧精度一致。五、压缩路径DecodeSequentialCompressedIndices() 的差分重建当connectivity_method SEQUENTIAL_COMPRESSED_INDICES时索引不再直接存储而是经过两步变换先对相邻索引做差值差分编码再把有符号差值映射为无符号整数ZigZag 式符号折叠最后对整个差值序列做熵编码。解码端流程如下void DecodeSequentialCompressedIndices() { DecodeSymbols(num_faces * 3, 1, decoded_symbols); last_index_value 0; for (i 0; i num_faces; i) { for (j 0; j 3; j) { encoded_val decoded_symbols[i * 3 j]; index_diff (encoded_val 1); if (encoded_val 1) index_diff -index_diff; val index_diff last_index_value; face_to_vertex[j][i] val; last_index_value val; } } }其核心语义可拆解为熵解码DecodeSymbols(num_faces * 3, 1, decoded_symbols)一次性解出num_faces * 3个符号每面 3 个索引各对应一个符号。该函数定义在 symbol_decoding.h负责从位流中恢复经熵编码的符号序列与之对应的编码端入口是 symbol_encoding.h 中的EncodeSymbols。符号折叠还原每个encoded_val的最低位LSB存放符号位——为 1 表示负差值数值部分存放在encoded_val 1。因此index_diff encoded_val 1若最低位为 1 则取负。这正是编码端 ZigZag 变换的逆操作。差分累加val index_diff last_index_value把差值恢复为绝对索引并更新last_index_value作为下一个差值的基准。初始基准值为 0。编码端佐证差值如何被折叠在 mesh_sequential_encoder.cc 的CompressAndEncodeIndices()中可以看到完全对称的正向过程const int32_t index_value face[j].value(); const int32_t index_diff index_value - last_index_value; // Encode signed value to an unsigned one (put the sign to lsb pos). const uint32_t encoded_val (abs(index_diff) 1) | (index_diff 0 ? 1 : 0); indices_buffer.push_back(encoded_val); last_index_value index_value;即encoded_val (|diff| 1) | (diff 0 ? 1 : 0)解码端 1取绝对值、 1判断符号后还原diff再与last_index_value累加。这种差分 符号折叠 熵编码的组合使相邻面共享顶点的网格索引序列呈现出大量小差值分布熵编码能显著压缩连接性数据。解码端的安全校验mesh_sequential_decoder.cc 的DecodeAndDecompressIndices()是上述规范流程的落地实现它在重建每个索引时增加了三类溢出/越界防护当符号位为 1负差值且index_diff last_index_value时累加会产生负数索引拒绝当符号位为 0正差值且index_diff INT32_MAX - last_index_value时累加会溢出int32_t拒绝重建出的index_value若 0或 num_points拒绝。这些检查保证了压缩路径与未压缩路径一样任何解码索引最终都落在合法顶点范围内。六、总入口DecodeSequentialConnectivityData()顺序连接性解码的调度核心如下void DecodeSequentialConnectivityData() { ParseSequentialConnectivityData(); if (connectivity_method SEQUENTIAL_COMPRESSED_INDICES) { DecodeSequentialCompressedIndices(); } else if (connectivity_method SEQUENTIAL_UNCOMPRESSED_INDICES) { DecodeSequentialIndices(); } }整个流程可归纳为三段式读头部ParseSequentialConnectivityData()取得num_faces、num_points与connectivity_method按方法分派方法 0 走DecodeSequentialCompressedIndices()熵解码 差分重建方法 1 走DecodeSequentialIndices()按num_points选择 UI8/UI16/VarUI32/UI32 直接读取产出两种路径最终都把face_to_vertex[3][num_faces]填满即完成DecodeConnectivityData()的职责随后解码器进入属性Attribute数据阶段。在 mesh_sequential_decoder.cc 中这一分派体现为对connectivity_method字节的if/else判断connectivity_method 0调用DecodeAndDecompressIndices()否则按num_points阈值进入四种未压缩读取分支解码完成后以point_cloud()-set_num_points(num_points)记录最终点数。属性阶段则由CreateAttributesDecoder()配合LinearSequencersequential_attribute_decoders_controller.cc按顶点线性顺序解码各属性与连接性解码器共同完成整个顺序编码位流的还原。七、小结与进一步阅读顺序连接性解码器是 Draco 全部连接性重建路径中最简单的一条其要点可概括为头部三元组num_facesvarUI32num_pointsvarUI32connectivity_methodUI8构成解码起点双模式分派connectivity_method 0走熵解码差分重建 1走原始整数直读自适应精度未压缩路径依据num_points在 256 / 2^16 / 2^21 三个阈值间切换 UI8、UI16、varint、UI32 四种宽度压缩原理ZigZag 符号折叠 差分累加 DecodeSymbols熵解码与编码端CompressAndEncodeIndices()严格对称健壮性源码实现额外包含面数合法性预检、索引范围与溢出校验确保畸形位流被安全拒绝。想继续深入 Draco 位流规范可依次阅读仓库内以下文档与源码解码总流程draco.decoder.md连接性解码分派connectivity.decoder.md另一条连接性路径拓扑符号流edgebreaker.decoder.md常量与变量定义variable.descriptions.md顺序解码器实现mesh_sequential_decoder.cc顺序编码器实现差分与 ZigZag 正向变换mesh_sequential_encoder.cc熵符号解码入口symbol_decoding.h顺序编码端到端测试point_cloud_sequential_encoding_test.cc赞分享图形学3D渲染【免费下载链接】dracoDraco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.项目地址https://gitcode.com/gh_mirrors/draco1/draco点击查看免费下载相关推荐Draco 顺序量化属性解码器Sequential Quantization Attribute Decoder深度解析位流格式、反量化原理与源码实现Draco 顺序量化属性解码器Sequential Quantization Attribute Decoder深度解析位流格式、反量化原理与源码实现 本图形学3D渲染Draco 位流规范深度解读属性解码器Attributes Decoder完整解码流程剖析Draco 位流规范深度解读属性解码器Attributes Decoder完整解码流程剖析 导读 本文以 Draco 官方位流规范文档 docs/spe图形学3D渲染Draco 比特流规范精读顺序整数属性解码器Sequential Integer Attribute Decoder的伪码解析与源码印证Draco 比特流规范精读顺序整数属性解码器Sequential Integer Attribute Decoder的伪码解析与源码印证 本文是 Drac图形学3D渲染上一篇VidBee 便携版怎么用3 分钟跑起来简单免安装下载视频下一篇5分钟精通暗黑破坏神2存档修改Diablo Edit2完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表