安装BlenderMMD导入插件
方法1 Github下载
https://github.com/UuuNyaa/blender_mmd_tools/tree/v4.2.2?tab=readme-ov-file
方法2 Blender内置下载
内置下载或者到Blender插件社区下载 社区地址:https://extensions.blender.org/

模型处理
导入

删除无用
joints(关节点)和rigidbodies(刚体)

多余材质合并
面,齿,舌,口合并,编辑模式下材质面板点一下直接选择就行,最后选择面,点击指定

合并后物体模式下删除材质槽

眼睛合并到一起

眉毛合并到一起

眼睛高光和透贴不要删除

头发(刘海)需要拆下来,做眼睛透过头发效果

不需要分离,选出来之后直接新建一个材质头发指定上

将头发,肌肤,黑丝都合并到饰品里,然后删掉其他的

生成一套平滑法线用于描边
可以存在UV里面或者在定点色里面


新建python脚本
注意:mesh = bpy.data.meshes[‘星見雅’],这里的名称是网格体名称

因为是UV存值,所以需要将点位置从3维转换到2维,使用八面体映射

import bpy
from mathutils import *
from math import *
import numpy as np
#创建空字典列表list
dict = {}
#获取模型Mesh
mesh = bpy.data.meshes['星見雅']
#calc_tangents:
#在网格中计算每个顶点的切线(tangent)、双切线(bitangent)和法线(normal),以便后续的方向相关计算。
mesh.calc_tangents(uvmap = 'UVMap')
#计算两个向量之间的夹角
# a·b = |a||b|cosθ
# θ = arccos(a·b / (|a||b|))
# 这里返回的弧度值 θ * (π/180) ≈ xxx弧度
def included_angle(v0, v1):
return np.arccos(v0.dot(v1)/(v0.length * v1.length))
# 3维降为2维
def unitVectorToOct(v):
# 步骤1:计算L1范数(曼哈顿距离)
d = abs(v.x) + abs(v.y) + abs(v.z)
# 步骤2:通过除以L1范数进行投影到八面体表面
# o 是八面体的坐标
o = Vector((v.x / d, v.y / d))
# 步骤3:如果z是负数,需要进行特殊处理以保持连续性
if v.z <= 0:
# 使用折叠技术处理八面体的下半部分
o.x = (1 - abs(o.y)) * (1 if o.x >= 0 else -1)
o.y = (1 - abs(o.x)) * (1 if o.y >= 0 else -1)
return o
#.co是coordinate的缩写,表示顶点的坐标。
# 取出模型的每一个顶点坐标,然后清空置空列表,后续存入新的坐标数据
for vertex in mesh.vertices:
# 初始化 "<Vector (1.0, 2.0, 3.0)>": []
dict[str(vertex.co)] = []
# 获取模型Mesh的每一个面
# l0,l1,l2分别表示模型Mesh的每一个面中的每一个顶点数据
# l0,l1,l2这样的写法是Blender的写法,表示loop0,loop1,loop2的循环体
# mesh.loops 存储了多边形顶点的循环信息
# poly.loop_start 表示多边形顶点的循环开始索引
for poly in mesh.polygons:
#获取模型Mesh的每一个三角面中的每一个顶点数据
l0 = mesh.loops[poly.loop_start]
l1 = mesh.loops[poly.loop_start + 1]
l2 = mesh.loops[poly.loop_start + 2]
#获取模型Mesh的每一个面中的每一个顶点数据
## 顶点的主要属性:
#vertex.co 顶点的3D坐标 (Vector类型,包含x,y,z)
#vertex.normal 顶点的法线方向
#vertex.index 顶点在mesh.vertices数组中的索引号
#vertex.groups 顶点组信息(用于骨骼绑定等)
v0 = mesh.vertices[l0.vertex_index]
v1 = mesh.vertices[l1.vertex_index]
v2 = mesh.vertices[l2.vertex_index]
#计算向量,三角形两条边向量
vec0 = v1.co - v0.co
vec1 = v2.co - v0.co
#计算向量叉积,三角形两条边向量叉乘,得到法线
n = vec0.cross(vec1)
n = n.normalized()
#v0.co 是一个 Vector 类型,包含 (x,y,z) 坐标值
#Python的字典要求键(key)必须是"可哈希的"(hashable)
#Vector 类型不能直接用作字典的键
#这里将顶点坐标转换为字符串,便于后续使用
#k0,k1,k2是字典的Key
k0 = str(v0.co) # 将顶点0的坐标转换为字符串
k1 = str(v1.co) # 将顶点1的坐标转换为字符串
k2 = str(v2.co) # 将顶点2的坐标转换为字符串
# 计算三角形三个顶点的权重
if k0 in dict:
#计算顶点0的权重,w实际上是两向量夹角角度
w = included_angle(v2.co - v0.co, v1.co - v0.co)
# 添加数据
dict[k0].append({"n" : n, "w" : w})
if k1 in dict:
w = included_angle(v0.co - v1.co, v2.co - v1.co)
dict[k1].append({"n" : n, "w" : w})
if k2 in dict:
w = included_angle(v1.co - v2.co, v0.co - v2.co)
dict[k2].append({"n" : n, "w" : w})
for poly in mesh.polygons:
# 获取每一个三角面数据
#range(poly.loop_start, poly.loop_start + 3)遍历3个点
for loop_index in range(poly.loop_start, poly.loop_start + 3):
l = mesh.loops[loop_index]
vertex_index = l.vertex_index
v = mesh.vertices[vertex_index]
#smoothNormal初始化
smoothNormal = Vector((0,0,0))
weightSum = 0
k = str(v.co)
if k in dict:
a = dict[k]
for d in a:
n = d['n']
w = d['w']
#加权平均法线的计算公式:
#例如:smoothNormal = (n1 * w1 + n2 * w2 + n3 * w3) / (w1 + w2 + w3)
smoothNormal += n * w
weightSum += w
if smoothNormal.length != 0:
smoothNormal /= weightSum
smoothNormal = smoothNormal.normalized()
else:
# 如果计算出的平滑法线无效(长度为0),
# 使用原始顶点法线作为后备方案
smoothNormal = l.normal
normal = l.normal # 获取顶点的原始法线
tangent = l.tangent # 获取顶点的原始切线
bitangent = l.bitangent # 获取顶点的原始副切线
# 计算投影
# 使用原始的切线空间基底,计算平滑法线在这个空间中的表示
x = tangent.dot(smoothNormal) # 平滑法线在原始切线方向上的投影
y = bitangent.dot(smoothNormal) # 平滑法线在原始副切线方向上的投影
z = normal.dot(smoothNormal) # 平滑法线在原始法线方向上的投影
#UV数组的大小总是等于loops的数量
uv1 = mesh.uv_layers['UVMap.001'].uv[loop_index]
uv1.vector = unitVectorToOct(Vector((x,y,z)))
如果使用顶点色传入的话不需要进行维度转换
# 存入顶点色而不是UV
vertex_color_layer = mesh.vertex_colors.active.data[loop_index]
# 直接将平滑法线的分量存入顶点色
vertex_color_layer.color = (smoothNormal.x, smoothNormal.y, smoothNormal.z)
模型拆分
将有形态键的面部单独拆一个网格,包含下面的目影一起拆



清理材质

面部一样清理一下

去除原网格体形态键

给虚幻引擎需要将模型拆分多一些,刘海拆分下来

饰品和衣服也拆分下来

因为涉及到半透明排序还有覆层材质,眼影等全部都要拆分

拆完记得重命名和清理材质

Blender内模型可以不需要拆
合并之前的模型,刘海记得单独一个材质插槽