We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
将模型提取出来放到网页中并查看模型的动画
解包的方法就不赘述了,由于碧蓝档案(下称ba)是基于Unity开发的,所以相关的解包工具网上是可以找到的。
Unity
由于我们是需要在线查看模型和动画,以AssetStudioGUI为例,需要找到对应人物的Animator以及相关的AnimationClip,这里有一点需要注意的是,导出的模型可能会缺少光环。这里以Hina为例,两个与模型有关的文件分别是Hina_Original和Hina_Original_Mesh,前者是包含光环的。
AssetStudioGUI
Animator
AnimationClip
Hina
Hina_Original
Hina_Original_Mesh
提取出来的模型文件是由贴图Texture以及白模组成的,其中白模是Fbx格式中。
Texture
Fbx
这里我使用的是three.js v0.131.3。
three.js v0.131.3
模型提取出来后要做的就是在three.js中加载,这块官方有提供FBXLoader以及相关的使用案例。
const loader = new FBXLoader(); loader.load( '/Animator/Hina_Original/Hina_Original.fbx', function ( object ) { mixer = new THREE.AnimationMixer( object ); const action = mixer.clipAction( object.animations[ 0 ] ); action.play(); object.traverse( function ( child ) { if ( child.isMesh ) { child.castShadow = true; child.receiveShadow = true; } } ); scene.add( object ); } );
看上去这样可以实现模型的在线查看了,但是实际运行的时候会出现两个问题
可以看到控制台上有警告THREE.Material: 'map' parameter is undefined.
THREE.Material: 'map' parameter is undefined.
而three.js中材质贴图是通过map关联的,那么问题很显然了,在加载fbx模型的时候贴图没有关联上
three.js
map
FBXLoader: can't check image until loaded
这个问题的根本原因就是在关联贴图的时候会先检查是否存在,但是如果贴图是远程加载的就会导致返回undefined, 进而出现上面的警告。
undefined
比较尴尬的一点是,这个问题在131之前不存在,而在132这个版本被修复了。
关于这一点,因为之前从来没接触过,所以并不知道导致这个问题的原因是什么。只能大胆猜测AssetStudioGUI导出的数据格式有问题。
我的解决方法是将解包出的Fbx模型先导入到Unity中,再通过插件gltf-exporter作为Gltf导出即可。
Gltf
可以看到嘴部周围有一圈很规则的白色区域。
这是因为hina眼睛和嘴的贴图(eyeMouth)就是这样的
因为嘴巴有不同的口型,因此是放在另一张独立的贴图中,根据不同的需求渲染不同位置的口型。
根据eyeMouth的贴图不难判断左下角就是嘴部使用贴图,只要将这块位置扣掉,再叠加上嘴型的贴图就可以了。但是实际上不论是Unity还是Threejs都没有直接提供纹理叠加的功能,ue5好像是支持材质中的贴图叠加的。
eyeMouth
Threejs
ue5
由于看不到代码,只能猜测ba是通过着色器来实现贴图叠加的。而关于Threejs的实现可以参考How to apply two texture in single face of cube
ba
This should help. You can also use CanvasTexture and mix textures there - if it’s one-time only, it shouldn’t hurt performance too much.
简单的说就是可以先在canvas中进行贴图的叠加,再把它通过CanvasTexture转换成纹理绑定到材质上。由于纹理是通过UV映射到模型的对应位置上的,所以可以直接把eyeMouth覆盖到嘴型贴图上。
canvas
CanvasTexture
UV
但是实际操作的时候会发现嘴型确实改变了,但是嘴部的颜色不是预想中皮肤的颜色而是黑色。这是由于Threejs默认会将透明的地方渲染成黑色。
material.transparent = true
通知Threejs将其视为透明材质。
但是实际渲染的结果却很奇怪。 可以看到眼睛有很明显的透明度渐变的现象。
这是因为模型的geomertry上设置了顶点颜色,而顶点颜色中指定了眼睛的部分顶点是存在透明度的,所以当transparent设置为true时,看上去是部分透明的。
geomertry
transparent
解决方法很简单
const eyeMouth = obj.getObjectByName('Hina_Original_Body_3') eyeMouth.material.vertexColors = false
至于出现这个问题的根本原因应该是在Fbx转GLTF的时候出了问题。
gltf-exporter导出的模型
gltf-exporter
assetStudio导出的模型
assetStudio
GLTF格式中合成了顶点颜色,而FBX格式中却是直接使用的纹理贴图,同时绑定的法线贴图和混合贴图也消失了。
GLTF
FBX
因此需要在导入模型的时候关闭顶点颜色的渲染
gltf.scene.traverse( function ( object ) { if ( object.isMesh ) { object.castShadow = true object.material.vertexColors = false } })
游戏中小人实际上在不同的场景下有不同的口型,甚至在EX动画的时候会使用多个口型实现类似逐帧动画的效果。这种时候如果通过CanvasTexture来不断的生成新的纹理很明显不太现实。
这种时候可以考虑使用ShaderMaterial来自定义眼睛与嘴巴的渲染方式。
ShaderMaterial
// vertex #include <skinning_pars_vertex> varying vec2 vUv; void main() { #include <skinbase_vertex> #include <begin_vertex> #include <skinning_vertex> #include <project_vertex> vUv = uv; }
// fragment varying vec2 vUv; uniform vec2 offset; uniform sampler2D eyeMouthTex; uniform sampler2D mouthTex; void main() { vec2 fUv = vUv; vec4 mouth = texture2D(mouthTex, fUv + offset); // 这里没有理解,为什么从unity导出后eyeMouth做了y轴镜像翻转 // 但是手动导入后发现uv坐标还是原来的 // 为什么使用原来的uv坐标却使用y轴镜像后的贴图,而且映射的位置还是对的 fUv.y = 1.0 - fUv.y; vec4 eyeMouth = texture2D(eyeMouthTex, fUv); float alpha = eyeMouth.a; if (alpha == 0.0) { gl_FragColor = mouth; } else { gl_FragColor = eyeMouth; } }
顶点着色器中需要额外引入骨骼相关的预处理指令,不然在播放动画的时候会出现头动眼睛不动的情况
对于一般的材质可以这么做,但是导入的模型使用的是物理材质,简单的说就是贴图的实际颜色会受金属度、粗糙度、光照等的影响。
不难看出来相较于没有物理效果的,第二组没那么鲜艳
至于为什么第一组会表现出不同的颜色,这个涉及到色彩空间这个概念。
色彩空间
所以为了保留物理效果,就需要对MeshStandardMaterial的片元着色器进行扩展。
MeshStandardMaterial
const fragmentParmas = ` uniform vec2 mouth_offset; uniform sampler2D mouth_texture; ` const fragmentStart = ` vec4 mouthColor = diffuseColor * texture2D(mouth_texture, vMapUv + mouth_offset); ` const fragmentEnd = ` float alpha = diffuseColor.a; if (alpha == 0.0) { diffuseColor = mouthColor; } ` THREE.ShaderChunk['face_mix_pars_fragment'] = fragmentParmas THREE.ShaderChunk['face_mix_fragment_start'] = fragmentStart THREE.ShaderChunk['face_mix_fragment_end'] = fragmentEnd material.onBeforeCompile = (shader) => { Object.assign(shader.uniforms, uniforms) const shaderList = shader.fragmentShader.split('\n') // 变量声明 shaderList.splice(0, 0, '#include <face_mix_pars_fragment>') // 在diffuseColor被纹理贴图的颜色污染之前计算 const index = shaderList.findIndex(item => item.includes('#include <map_fragment>')) shaderList.splice(index, 0, '#include <face_mix_fragment_start>') // 在计算完法线贴图后 const i = shaderList.findIndex(item => item.includes('#include <alphamap_fragment>')) shaderList.splice(i + 1, 0, '#include <face_mix_fragment_end>') shader.fragmentShader = shaderList.join('\n') }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
目的
将模型提取出来放到网页中并查看模型的动画
解包的方法就不赘述了,由于碧蓝档案(下称ba)是基于
Unity
开发的,所以相关的解包工具网上是可以找到的。模型提取
由于我们是需要在线查看模型和动画,以
AssetStudioGUI
为例,需要找到对应人物的Animator
以及相关的AnimationClip
,这里有一点需要注意的是,导出的模型可能会缺少光环。这里以Hina
为例,两个与模型有关的文件分别是Hina_Original
和Hina_Original_Mesh
,前者是包含光环的。提取出来的模型文件是由贴图
Texture
以及白模组成的,其中白模是Fbx
格式中。网页渲染
这里我使用的是
three.js v0.131.3
。模型提取出来后要做的就是在three.js中加载,这块官方有提供FBXLoader以及相关的使用案例。
看上去这样可以实现模型的在线查看了,但是实际运行的时候会出现两个问题
1. 贴图丢失
可以看到控制台上有警告
THREE.Material: 'map' parameter is undefined.
而
three.js
中材质贴图是通过map
关联的,那么问题很显然了,在加载fbx模型的时候贴图没有关联上FBXLoader: can't check image until loaded
这个问题的根本原因就是在关联贴图的时候会先检查是否存在,但是如果贴图是远程加载的就会导致返回
undefined
, 进而出现上面的警告。比较尴尬的一点是,这个问题在131之前不存在,而在132这个版本被修复了。
2. 骨骼动画错误
关于这一点,因为之前从来没接触过,所以并不知道导致这个问题的原因是什么。只能大胆猜测
AssetStudioGUI
导出的数据格式有问题。我的解决方法是将解包出的Fbx模型先导入到
Unity
中,再通过插件gltf-exporter作为Gltf
导出即可。嘴部贴图
可以看到嘴部周围有一圈很规则的白色区域。
这是因为hina眼睛和嘴的贴图(eyeMouth)就是这样的
因为嘴巴有不同的口型,因此是放在另一张独立的贴图中,根据不同的需求渲染不同位置的口型。
根据
eyeMouth
的贴图不难判断左下角就是嘴部使用贴图,只要将这块位置扣掉,再叠加上嘴型的贴图就可以了。但是实际上不论是Unity
还是Threejs
都没有直接提供纹理叠加的功能,ue5
好像是支持材质中的贴图叠加的。由于看不到代码,只能猜测
ba
是通过着色器来实现贴图叠加的。而关于Threejs
的实现可以参考How to apply two texture in single face of cube简单的说就是可以先在
canvas
中进行贴图的叠加,再把它通过CanvasTexture
转换成纹理绑定到材质上。由于纹理是通过UV
映射到模型的对应位置上的,所以可以直接把eyeMouth
覆盖到嘴型贴图上。但是实际操作的时候会发现嘴型确实改变了,但是嘴部的颜色不是预想中皮肤的颜色而是黑色。这是由于
Threejs
默认会将透明的地方渲染成黑色。通知
Threejs
将其视为透明材质。但是实际渲染的结果却很奇怪。
可以看到眼睛有很明显的透明度渐变的现象。
这是因为模型的
geomertry
上设置了顶点颜色,而顶点颜色中指定了眼睛的部分顶点是存在透明度的,所以当transparent
设置为true时,看上去是部分透明的。解决方法很简单
至于出现这个问题的根本原因应该是在Fbx转GLTF的时候出了问题。
gltf-exporter
导出的模型assetStudio
导出的模型GLTF
格式中合成了顶点颜色,而FBX
格式中却是直接使用的纹理贴图,同时绑定的法线贴图和混合贴图也消失了。因此需要在导入模型的时候关闭顶点颜色的渲染
不同口型的切换
游戏中小人实际上在不同的场景下有不同的口型,甚至在EX动画的时候会使用多个口型实现类似逐帧动画的效果。这种时候如果通过
CanvasTexture
来不断的生成新的纹理很明显不太现实。这种时候可以考虑使用
ShaderMaterial
来自定义眼睛与嘴巴的渲染方式。顶点着色器中需要额外引入骨骼相关的预处理指令,不然在播放动画的时候会出现头动眼睛不动的情况
对于一般的材质可以这么做,但是导入的模型使用的是物理材质,简单的说就是贴图的实际颜色会受金属度、粗糙度、光照等的影响。
不难看出来相较于没有物理效果的,第二组没那么鲜艳
至于为什么第一组会表现出不同的颜色,这个涉及到
色彩空间
这个概念。所以为了保留物理效果,就需要对
MeshStandardMaterial
的片元着色器进行扩展。The text was updated successfully, but these errors were encountered: