目前我个人想到的比较好的3D模型转2D像素的渲染方法有:
1.通过后处理的方式将整个摄像机画面转为像素风格[1]。
2.将摄像机画面渲染到低分辨率的RenderTexture后再拉伸显示。
3.将3D模型预渲染成像素风格序列帧[2]。
当然,我标题都这么说了,肯定本文的主题就是第三种方法。前两种方法实际上是一个方向的变体,好处是不需要更改游戏素材设计,像素分布对齐,不方便的大概是对于素材的使用和掌控力度会小很多。
《死亡细胞》中美术师使用一款名为FBXCrouncherGUI的程序,将FBX的动画制作成序列帧后使用。经过一番搜索我并未发现这款软件被公开,所以只能手动重现。
我将3D模型转2D像素序列帧的功能罗列如下:
支持将静态的模型渲染到一张带透明通道的像素图片上。
支持像素图片的尺寸自定义。
支持预览——所见即所得。
支持将模型的一段动画输出到一组像素图片中。
支持动画进度的调节。
相对的界面支持*。
支持预览输出后的像素动画*。(划掉)
思路
使用射线检测方法,发出射线阵列获取碰撞点,然后根据碰撞点信息输出贴图。之所以要碰撞点信息而不是直接采样颜色,是因为我另外打算实现2D像素动画中的光照效果,需要法线信息。
准备工作
既然是带动画的模型,导入到Unity中之后多半离不开SkinnedMeshRenderer,因此不能直接使用MeshCollider,而是先要将每帧的Mesh烘焙出来后再使用。
编写MeshBaker.cs:
随后编写BodyPart.cs,用于统一色彩的采样。因为像素风格的素材对纹理精细度要求不高,有些小部件用纯色替代更为恰当,所以采样模式分为纯色和纹理两种模式:
然后新建一个UnlitShader,命名为Model,并创建使用该Shader的Model材质。由于渲染的时候只需要颜色信息,所以无需做其他处理。
使用的动画模型来自于AssetStore的免费资源[3]。
射线检测
新建RaySample.cs,在这里进行采样操作。当然,下述代码仅仅是将射线检测结果直接转化成了图片,但实际上射线检测信息还有其他作用,当然因为我是边写代码边写这篇文章的
然后我们堆砌好场景:
然后渲染!
排序之前
然后我们就会发现,剑呢?这么大一把剑呢?哦,原来我忘了对RaycastHit进行排序,由于我规定射线的采样方向是Vector3.forward,所以只需要取z轴最小的检测结果即可:
排序之后实时预览
其实到这一步,fbxanim转pixel的基本功能也就差不多了。但是既然都说了是工作流程,当然功能不是最重要的,保证开发流程的便利才是本文的重点。
实时预览也很简单,用UGUI的RawImage即可,需要注意的是生成Texture2D的时候要更改其过滤模式,保证点阵的清楚。
进度调节
原本打算用Animator的Record[4],但是发现它只能作用在默认动画状态上,随作罢。虽然ForceStateNormalizedTime显示已被弃用,但是还是挺好用的。
动画导出
既然能够自然调节进度,那么也就自然能够导出,使用一个协程在每帧渲染结束后导出当前帧的动画:
导出的截图色调替换
色调的替换也是一个非常有利于开发的功能。这里我约定了8种颜色,分别是RGB通道的0/1组合,一般来说8种颜色已经够用了。
多重采样
目前而言我们只对一个像素采样一次,但是这是不够充分的,甚至在极端情况下是偏颇的。因此根据蒙特卡洛法,我们将对像素进行多次采样,并予以混合。
单次采样
5次采样
可以看到,原本的黑色边缘被淡化,受到了更多来自其他像素的干扰。
2D动态光照
这一部分我的想法是,通过将法线信息储存在另外一张序列帧中,在游戏运行中通过计算法线和光线的点积,对明暗面分别进行RGB调整。
不过最后的效果不尽如人意,过渡比较突兀,调节光照的过程不够直观,而且说实话,光是为了动态光照而需要多上一倍的资源占用,显然不是那么的……“足够产品”。
Shader代码如下,是拿Unity自带的SpriteShaderDefault改的:
效果如下:
结语
这篇文章并无技术难点,主要是还原了《死亡细胞》美术师的工作流程。
当然,这里要多嘴提一句,并不是所有3D模型都适合用这种方法制作出像素素材的,因为《死亡细胞》的模型在设计之初就是为了像素表现,突出强调了角色身上的形状,而没有精雕细琢。
我认为适于像素化的3D模型最好也是为此重新制作的,3D模型像素化的目的不是表现效果,而是开发速度。
知乎
破晓破晓赞赏