教你如何使用GPA导出模型,另送一个 GPA CSV2MESH Tool in unity


以前写过一篇:Unity - RenderDoc 抓帧导出 FBX(带UV)


吐槽

我估计GPA是怕收律师函,因为如果 GPA 将所有资源一键提取,一键导出,那么可能很多开发商会告他


GPA Geometry Output 没有 UV

可以看到也好几个帖子问 GPA 官方,都是被官方忽悠回答了:

(除了这个,我自己还搜索过还几个类似有人问这个问题,结果同样被官方忽悠)



既然GPA不整模型导出带UV,那么今天我们实现一个 GPA 中的模型导出 UV 的工具


问题实例

如果直接将 input gemoetry 导出
会得到一个除了 position 之外,没有其他数据的网格,如下:
在这里插入图片描述

然后,对应的 shader vs input 有6个 register
在这里插入图片描述

如果我们要向将这些顶点数据都导出来,直接使用 GPA 功能默认的Geometry output是导不出来的


开始实现


提取 VBV, IBV

上面的 VS_INPUT 可以看到 input attribute 的定义

但是这些数据是从哪些 VBV(vertex buffer viewer) 输入的呢?

我们可以点击 Resource 中的 Shader,选择:Vertex shader 后

然后查看使用到的数据有哪些,如下图:
在这里插入图片描述

OK,留意:
在这里插入图片描述

然后我们可以将每个 VBV 个、单个 IBV 设置导出 CSV Titles 格式,下面以导出 position.csv 为例

  • 根据shader input attribute 定义,定位使用的 vbv序号
  • 在 resource 中,找到对应的 vbv
  • 给 vbv 的 titles 设置好格式
  • 最后 导出 csv

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

其他的 tangent, normal, uv0, uv1, color0 都可以使用类似的方式导出,如上图,这个模型的数据我是使用下面的格式导出的
因为我们使用的是 unity mesh,那么TEXCOORD0 的 position 我们需要将其 semantic 修改为 POSITION,同理,normal 和 uv0 都使用对应的:NORMAL, TEXCOORD0 来替代

参考如下(注意每个 shader 的 VBV 是干嘛用的,需要自行去查看 shader 怎么使用,以此分析他们的所属的 semantic,因为每个 shader 都有可能不一样):

VBV, IBV 导出的 csv format:

  • position : VBV1 - float POSITION.x;float POSITION.y;float POSITION.z;

  • tangent : VBV2 - float TANGENT.x;float TANGENT.y;float TANGENT.z;float TANGENT.w;

    • half 精度的 tangent : half TANGENT.x;half TANGENT.y;half TANGENT.z;half TANGENT.w; - 有些精度低一些,为了性能考虑的是有的,抓帧逆向效果都可以看到很多地方有类似的做法
  • normal : VBV3 - float NORMAL.x;float NORMAL.y;float NORMAL.z;

    • half 精度的 tangent : half NORMAL.x;half NORMAL.y;half NORMAL.z;
  • uv0 : VBV0 - half TEXCOORD0.x;half TEXCOORD0.y;byte4p

  • uv1 : VBV0 - byte4p; half TEXCOORD4.x;half TEXCOORD4.y;

  • color0 : VBV1

    • float TEXCOORD5.x;float TEXCOORD5.y;float TEXCOORD5.z;float TEXCOORD5.w; byte4p
    • ubyte COLOR0.x;ubyte COLOR0.y;ubyte COLOR0.z;ubyte COLOR0.w; byte4p
    • ubyte TEXCOORD5.x;ubyte TEXCOORD5.y;ubyte TEXCOORD5.z;ubyte TEXCOORD5.w; byte4p - 这个会使用比较多,但是这个有些问题:没有 normalized
    • nubyte TEXCOORD5.x;nubyte TEXCOORD5.y;nubyte TEXCOORD5.z;nubyte TEXCOORD5.w; byte4p - 然后添加了前缀 n 后,还是不能 normalized,所以这个我在工具中特殊弄了一些开关,可是设置 VBV 是否需要 normalized
  • index : IBV - 不需要格式,直接导出,只要确保第二列是 index 的值即可

另外要记得[type][n]p的padding设置,这样才能stride到对应的vertex data 长度

比如下面的法线,使用的是 half 精度的,而且有 36 个 byte 的 padding
在这里插入图片描述

那么我们使用: half NORMAL.x;half NORMAL.y;half NORMAL.z;half NORMAL.w;byte36p; list view 格式即可

在这里插入图片描述


GPA buffer list view format 攻略

上面我是提取: 《CFDG》 的方式,可以罗列出下面的 buffer view format,基本上 CFDG 的 MRA 流 PBR 都是这套规则即可

// jave.lin : 下面提供 list view format 的样例,可以用于CTRL+C,V到 GPA list view 中
// 注意 : padding 不要处理
// 注意 : semantic 要设置对 (依赖 shader 分析 对应的作用后,才能正确设置 semantic)

// position-float3
float POSITION.x;float POSITION.y;float POSITION.z;

// tangent-float4
float TANGENT.x;float TANGENT.y;float TANGENT.z;float TANGENT.w;
// tangent-half4
half TANGENT.x;half TANGENT.y;half TANGENT.z;half TANGENT.w;

// normal-float4
float NORMAL.x;float NORMAL.y;float NORMAL.z;float NORMAL.w;
// normal-half4
half NORMAL.x;half NORMAL.y;half NORMAL.z;half NORMAL.w;

// normal-float3
float NORMAL.x;float NORMAL.y;float NORMAL.z;
// normal-half3
half NORMAL.x;half NORMAL.y;half NORMAL.z;

// uv0-float2
float TEXCOORD0.x;float TEXCOORD0.y;
// uv0-half2
half TEXCOORD0.x;half TEXCOORD0.y;

// uv1-float2
float TEXCOORD1.x;float TEXCOORD1.y;
// uv1-half2
half TEXCOORD1.x;half TEXCOORD1.y;

// color0-ubyte4
ubyte COLOR0.x;ubyte COLOR0.y;ubyte COLOR0.z;ubyte COLOR0.w;


工具演示导出

请添加图片描述


GPA 的 VBV,IBV的 BUG

我之前就怀疑还原模型的话,如果这些 IBV, VBV 长度都不对的话,除了浪费数据,还可能出问题

因为很多 VBV 和 IBV 的元素数量是对不上的

今天在抓帧某个角色的 卧蚕(眼部底下部分) 模型时候就出现这个问题

从下面的 IBV 可以看到1091,1092 之间的索引跨度很大,是有问题的

23901 的索引直接就超出了 VBV 顶点总数的数量
在这里插入图片描述

从下面的数据分析, Primitive 总数才 364 个三角形
在这里插入图片描述

VBV position 数量才 10313,但是 IBV 的索引已经远超这个范围了,这个很明显是 GPA 的数据显示上的 BUG
在这里插入图片描述

所以部分模型的导出可能遇到:

  • Fail setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 2046, VertexCount: 10314
  • Fail setting triangles. The number of supplied triangle indices must be a multiple of 3.

在这里插入图片描述
在这里插入图片描述


must be a multiple of 3 的解决办法

在我的工具中,是这么来处理的:

因为是在 set triangles 的时候发生的错误

那么我们只要将 indices 的长度保持在 缓存元素数量可以整除3即可:indices.Count % 3 == 0

        // jave.lin : push padding or remove padding
        // jave.lin : 这么处理可以避免 https://blog.csdn.net/linjf520/article/details/127066726 文章中提及的 BUG:
        //  - Fail setting triangles. Some indices are referencing out of bounds vertices. IndexCount: xxx, VertexCount: xxx
        //  - Fail setting triangles. The number of supplied triangle indices must be a multiple of 3.
        if (indices.Count > 0 && indices.Count % 3 != 0)
        {
            var loopCount = 0;
            var lastOneVal = indices[indices.Count - 1];
            var lastOneIsZeroVal = lastOneVal == 0;
            while (indices.Count > 0 && indices.Count % 3 != 0)
            {
                if (loopCount > 10)
                    break;
                loopCount++;
                if (lastOneIsZeroVal)
                {
                    // jave.lin : remove padding 如果尾部是 0 索引,我们可以用删除 padding 的方式
                    indices.RemoveAt(indices.Count - 1);
                }
                else
                {
                    // jave.lin : push padding, 否则我们使用 push 最后一个顶点作为 padding 的方式
                    indices.Add(lastOneVal);
                }
            }
        }

indices out of bounds vertices - 暂无解决方法

比如:Failed setting triangles. Some indices are referencing out of bounds vertices. IndexCount: 2049, VertexCount: 281

这个问题是 GPA 的 BUG
暂时无解,连 index 的数值都错了,我们是不可能知道正确的数值的,只能猜:比如,可能是 数据类型溢出?
就是新版本的 fbx 模型,可能使用了 index buffer 的索引值的压缩技术,可能在 GPA 中没有对应实现解析,那么肯定会有报错的

这个 index 索引值的压缩技术大概是这么个思路:

  • 如果 index < byte.MaxValue,那么 index 使用 byte 类型解析
  • 如果 index < ushort.MaxValue,那么 使用 ushort 类型解析
  • 如果 index < uint.MaxValue,那么 使用 uint 类型解析

indices out of bounds vertices - 尝试解决的方法

可以尝试将后续开始 indices 是很大的数值统统删除,保留前面的数据,95% 是OK的,就可以导出来了


Invalid worldAABB.Object is too large or too far away from the origin - 解决方法

看起来是 AABB 太大导致,很有可能是坐标数值出问题
我直接 google : Invalid worldAABB.Object is too large or too far away from the origin
找到一篇: Assertion failed: Invalid worldAABB. Object is too large or too far away from the origin

然后我添加了检测 local position 大于 1000 的都打印出来 (一般来说说不会整这么大的 local pos)
在这里插入图片描述

发现输出在: 12506 的 idx position 数值非常大

其实也可以打开 position.csv 使用正则搜索: \b\d+,\d{2},一样可以搜索到 大于 2 位以上的数值
如果向定位数值位数,之久修改,比如,超过 百位可以修改为: \b\d+,\d{3}
超过千位: \b\d+,\d{4},以此类推,修改后面的 \d{n} 里面的 n 数值即可

OK,如下图,我们找到了异常的数据
在这里插入图片描述

我们要思考一下,为何是正常的呢?
很明显,这些是优化 VBV 的数据复用了一些 内存块
然后 GPA 里面没有 resize 这个大小,或是显示的时候,没丢对应的大小进去
导致 GPA 显示一丢后续无用的数据
那我们到底要怎么处理这些无用的数据呢?
怎么判断他是无用的呢?

方法就是: 根据 indices 的数量来定范围即可
比如,indices 里面最大的有效索引值,我们需要先知道,假如叫: var indices_max_val = indices.Max() + 1;
只要遍历顶点属性数据 (vertex attribute) 的时候 (position, color, uv等数据的时候) 的时候
如果这次的遍历次数 大于或等于 indices_max_val 我们就停止,不再处理

在这里插入图片描述

但是注意,indices 里面同样有可能有多余的数据,因此我们要看一下索引跨度很大的情况,删除后续无用的数据
我将之前的截图 挪到这再瞄一下,如下图
在这里插入图片描述

这样一般就不会出现: Invalid worldAABB.Object is too large or too far away from the origin 的问题

但如果你的模型本身 local pos 就很大的数值,那你应该考虑制作方法是否除了问题
因为一般模型的 local pos 不会很大,否则会有精度问题
如果实在是有这类需求,可以考虑,模型切块,分块加载 (streaming load)
比如,大世界里面的 超大地形的切块,或是 地表模型的切块


另一个问题: 有些TEXCOORD[n].y 是不需要翻转的,因为这些数据不是采样用的

如果我们直接对所有的 TEXCOORD[n].y 都判断是 dx 的就 1- y 的处理的话,那么对于一些模型中,将部分数据存于 TEXCOORD[n].y 的话,就会出错

因此我们额外有添加了其他的选项来确定要不要翻转这些数据
在这里插入图片描述


另一个问题:导出的 FBX 中,UV 无法保存 vector3 或是 vector4 的数据 (已有解决方法)

具体参考另一篇: Unity - 导出的FBX模型,无法将 vector4 保存在 uv 中(使用 Unity Mesh 保存即可)

为何方便导出,我将工具扩展了,可以导出 FBX,也可以选择导出 Unity Mesh

对于一些带有自定义的 TEXCOORD[n] 的数据,建议使用 Unity Mesh 导出
对于 *.obj 数据同样有不能保存 vector3, vector4 的问题

选项如下图
在这里插入图片描述


增加了 readable 的选项便于导出后就自动设置

对于导出 FBX,unity mesh ,此选项同样都有作用
在这里插入图片描述


关于 must be a multiple of 3; indices out of bounds vertices; AABB too large 的终极解决方法

这些这些问题的原因:我这个工具 并没有处理 buffer bounds 也就是没有处理 buffer 的 start offset 和 length

就是对这个 buffer 的数据读取没有做很好 起始idx 和 读取的长度控制,导致读取了 其他的范围的数据,导致无法正确转换
要处理这个问题,可以参考这篇文章: GPA提取模型数据

后续有时间,我会再给这个工具做一次迭代:增加对 indices 的有效长度的输入。

比方说,下面提取《CFDG》里面的一个角色模型的某一部分网格

在这里插入图片描述


EVENT ID

找到 24 个 Event ID

在这里插入图片描述


找到对应想要的网格

在这里插入图片描述


打开网格fx - 关注: Topology 和 Primitive count,决定 indices buffer 的有效长度,关注 slot 信息,决定使用到的 buffer 是哪个

topology 决定 index buffer 元素的拓补结构
primitive count 决定 图元数据量,比如上面是 triangle list (三角形列表),那就是每个元素就是有三个索引对应的顶点组成的三角形,那么意味着:总的顶点索引数量 = 图元数量 * 3
在这里插入图片描述

显示不完整,我们将 snipaste 截图 pin 到一块
在这里插入图片描述

可以看到,这个网格,有 6 个 slot 输入槽,每个 slot 对应使用到的 buffer 都有显示
比如 slot0

  • buffer : B:191
  • format R32G32B32A32_FLOAT
  • offset : 0

每个 slot 都会对应的信息


Checked - Full API Log

在这里插入图片描述
打开这个开关后,就会多出很多 这次 dc 相关的 图形 API 调用函数
在这里插入图片描述


关注 IASetIndexBuffer - 关注的是 format (格式)

如下图,可以看到 这个网格的 indices buffer 的情况,

  • PIndexBuffer - 使用的是 419 PIndexBuffer 指针的数据,也就是:(PIndexBuffer*)(419)
  • Format : 使用的是 DXGI_FORMAT_R16_UNIT 格式,也就是 也就是 无符号的 16 bit 的 int 类似,也就是 ushort 无符号整型
  • Offset : 这个 (PIndexBuffer*)(419) 指针数据中,byte 从 0 开始读取

这里我们重点关注 format,就是每个 index buffer 里面的元素都是 ushort 格式的
在这里插入图片描述


关注 IASetVertexBuffers - 关注的是 ppVertexBuffers, pStrides, pOffset 决定每个 buffer 的 offset 和 stride (每个元素的 对应格式个数)

下面是正常截图,但是 IASetVertexBuffers 的数据太多,屏幕高度不够截图
在这里插入图片描述

因此使用 Snipaste 工具将一些关注的数据截图 pin 到一起显示
在这里插入图片描述

注意每一个 buffer 指针的格式
比如:

  • ppVertexBuffers 是一个 (ID3D11Buffer**) 的格式
  • pStrides 是 (UINT*)
  • pOffsets 是 (UINT*)

如何截图这块信息
在这里插入图片描述

上面截图可以看到,红绿蓝 三种框选
每种颜色都是一起关联使用的
比如,红色框选这么理解,其他以此类推

  • buffer : 191,这个可以理解为一个指针地址
  • stride : 12,可以理解为,每个元素用到 (UINT) 对应的数量
  • offset : 1345200,可以理解为,从这个 191 buffer 里面偏移 (UNIT*) 指针元素的 1345200个元素后在读取后续数据的意思

这样确定 indices 数量,然后其他顶点数据读取时,安装上面的 buffer, stride, offset 就可以精准读取了


Project


References

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值