; 本文档由晨星制作
; 我的B站链接:https://space.bilibili.com/95747099
; 我的Youtube:https://www.youtube.com/channel/UC9maLE-2mYDlZbDNWrX5dxA
; 我的爱发电:https://afdian.com/a/MorningStar?tab=home (现在也没发啥,应该也没人支持吧(@_@;))
; 我的香蕉网账号:https://gamebanana.com/members/2798606
; 非我的渠道下载 还收费的,那你就是被骗了! 记得帮我点点举报(免费分享且标注链接的没有关系)
; 我的QQ群:1029406436(欢迎一起聊天吹牛逼,一起玩 ~)

基本知识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
;表示这行是注释,不会加载

;[]表示一个对象(更像是一个语句块)? 差不多也是一个变量,是可以传递的

;drawindexed表示你的模型组件(一个组件可以包含多个模型 .001 .002等等)

;global $key = 0 全局临时变量
;global persis $key = 0 全局存储变量(变量存储在XXMI Launcher\WWMI\d3dx_user.ini文件中)

;$\XXX\xxx
;第一个XXX标识命名空间(有C语言经验的应该很容易明白)
;会有一个对应的INI文件头部会有下面这句话
namespace = XXX
;第二个xxx表示那个命名空间对应的变量名

材质/按键

透明材质替换

全透明的材质替换成RabbitFX,不要使用这个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;任意位置新建这段内容
;[括号里的标识符内容是可以改的,因为你可能不止一个透明材质]
[CustomShaderTransparency]
blend = ADD BLEND_FACTOR INV_BLEND_FACTOR
blend_factor[0] = 0.0-1.0
blend_factor[1] = 0.0-1.0
blend_factor[2] = 0.0-1.0
blend_factor[3] = 1
handling = skip
;要替换的模型,可以在下面[TextureOverrideComponentX]里找到这些变量
drawindexed = 66666, 666, 666

;;;替换材质步骤
;找到你刚才的那行数据[drawindexed = 66666, 666, 666]
;替换成对应的run=CustomShaderTransparency(你刚才命名的数据)


RabbitFX&透明贴图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
;创建原始的光照贴图(RGB控制颜色,A控制亮度)
[ResourceLegEffectMap]
filename = Textures/light.dds

;创建原始的透明贴图(根据透明度来判断)
[ResourceTransparencyMask]
filename = Textures/Transparency.dds

;找到你要发光的组件
[TextureOverrideComponentX]
...
run = CommandListTriggerResourceOverrides
run = CommandListOverrideSharedResources

;主要添加的语句块(HSV调色板,有兴趣可以看一下,很多调色软件也有这玩意)
$\rabbitfx\h = 0
$\rabbitfx\s = 0
$\rabbitfx\v = 0
$\rabbitfx\brightness = 10
$\rabbitfx\interpolate = 1
Resource\RabbitFX\GlowMap = ref ResourceLegEffectMap
;这是透明效果的写法
;Resource\RabbitFX\FXMap = ref ResourceTransparencyMask
run = CommandList\RabbitFX\Run
;------------------------------------------

;加载模型
drawindexed = 28770, 106563, 0
run = CommandListCleanupSharedResources
endif
endif


按键切换模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
;新建变量
global persis $key1 = 0

;任意位置新建这段内容
;[括号里的标识符内容是可以改的,因为你可能不止一个切换按钮]
[Key key1]
condition = $object_detected
;按键可以是英文也可以是特殊按键,像手柄之类的
key = NO_CTRL NO_ALT VK_DOWN
type = cycle
;切换的数量:可以往后写 2 3 4 5……
$key1 = 0,1



;切换步骤
;找到你要切换的模型,可以在[TextureOverrideComponentX]里找到这些模型
;找到组件渲染的那行代码 drawindexed = 66666, 666, 666
if $key1 == 0
; Draw Component 2
drawindexed = 66666, 666, 666
; 也可以像透明材质一样写这句话
; run=CustomShaderTransparency
else if $key1 == 1
; Draw Component 2.001
drawindexed = 77777, 777, 777
endif
;这里很重要,endif不加,语序会识别错误,会导致模型加载混乱抽搐,很多时候找不到问题就是这个原因

按键切换贴图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
global $active = 0
;新建变量
global persis $key = 0

[Key key]
;切换条件,可以是别的条件,也可以不加
;condition = global $active = 1
key = NO_CTRL NO_ALT VK_UP
type = cycle
;更多的切换和模型切换同理
$key = 0,1

[ResourceTextureX]
filename = Textures/Components-2 t=xxx.dds

[ResourceTextureX-1]
filename = Textures/xxx.dds
;这里的xxx.dds是你自己新建的贴图放在Textures文件夹里的

[TextureOverrideTextureX]
hash = 225aad5a
match_priority = 0
if $key == 0
this = ResourceTextureX
;我知道你想说什么,没有打错,就是elif
elif $key == 1
this = ResourceTextureX-1
endif

按键触发次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;攻击检测键(实际就是鼠标左键)
global $key1 = 0
;记录上一帧的 $key1 状态
global $key1_last = 0
;触发次数
global $count = 0


[Key key1]
condition = $object_detected
key = NO_CTRL NO_ALT VK_LBUTTON
type = cycle
$key1 = 0,1

[Present]
; 检测 key1 从 0 变成 1
; 直接检测$key1 == 1 $count就会不停的增加
if $key1 == 1 && $key1_last == 0
$count = $count + 1
endif
; 更新上一帧状态
$key1_last = $key1

按键长按触发

1
2
3
4
5
6
7
8
9
10
11
12
[Constants]
global $hold = 0

[KeyClick1]
;启动条件(可以是检测变量等)
condition = $\Mod009\enable_mods
;切换键,别的也行(此处是鼠标左键)
key = no_ctrl no_shift alt VK_LBUTTON
;关键,切换的类型(长按)
type = hold
;切换的变量
$hold = 1

模型检测切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
;新建切换变量
persist global $key = 0
;新建检测变量
global $attack = 0

[Key key]
;条件是检测到物体生成
condition = $object_detected == 1
key = NO_CTRL NO_ALT VK_DOWN
type = cycle
;要切换的部件使用的变量(使用参考“切换模型”篇)
$key = 0,1

;找到[Present]代码
;帧计算代码
[Present]
if $object_detected
if $mod_enabled
post $object_detected = 0
run = CommandListUpdateMergedSkeleton
else
if $mod_id == -1000
run = CommandListRegisterMod
endif
endif
endif
;加上这两句,即模型加载完毕后执行变量赋值操作
$key = $attack
$attack = 0

;新建TextureOverri语句块
;IBHash)通过游戏中手动筛选查找
[TextureOverrideA]
hash = xxx
;出现模型对应的hash就会影响变量,key也会随之改变
;直接赋值key的话,物品消失后就无法重置
$attack = 1

[TextureOverrideB]
hash = xxx
$attack = 1

;索引切换代码(参考模型切换或者贴图切换)
[TextureOverrideComponentX]

控制模型权重

1
2
3
4
5
6
[TextureOverrideComponentX]
;控制这句话是否执行即可(应该是合并骨骼?)
;注意不执行后会保留那一瞬间的状态(所以不能一开始就关闭,不然模型会出问题)
;这行代码注释只能控制这个模型组件的权重影响
run = CommandListMergeSkeleton

合并用到的贴图

在需要烘焙的模型上新建一个UV(名称需一样)

选中所有的需要烘焙的物体,再选中你新建的UV,然后在UV视图点击顶部的UV选项,选择重排孤岛,或者智能UV投射,别的都行,这步就是让UV不重叠,手动排都行

模型的材质中都需要新建一个纹理贴图(其中的图片就是最终输出的材质,所以必须都是同一张贴图)

然后右边选中渲染选项(小电视图标),选中Cycles模式,
采样选项卡中的渲染选项,将降噪关了,采样改成10(不改也没事,就是慢一点);烘焙类型选择漫射(漫反射贴图,需要什么选什么),将直接光、间接光关闭(会渲染环境光,看你的需求)边距给8-32px(看你贴图的大小,8K的建议32,1K就8就行了)

最后烘焙之前一定要保证3D视图选中所有的模型,所有模型的材质视图选中新建的那个纹理节点,然后选中新建的UV,点击烘焙按钮等待一个个烘焙到那个贴图即可,然后保存贴图,使用新的UV和材质即可


UI

显示文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
; 打印文本(默认屏幕中央且为黑底白字)
[CommandListPrintText]
; Input:
; Resource\WWMIv1\Text
; Resource\WWMIv1\TextParams
;
; 使用示例
Resource\WWMIv1\Text = ref ResourceTextExample
Resource\WWMIv1\TextParams = ref ResourceTextParamsExample
run = CommandList\WWMIv1\PrintText

;文本内容
[ResourceTextExample]
type = Buffer
data = "Hello World!"

;文本格式
[ResourceTextParamsExample]
type = StructuredBuffer
array = 1
data = R32_FLOAT -0.25 +0.15 +0.25 -0.05 1.00 1.00 1.00 1.00 0.00 0.00 0.00 0.95 0.02 0.05 2 1 1 1.0
; x1-^ y1-^ x2-^ y2-^ | R-^ G-^ B-^ A-^ | R-^ G-^ B-^ A-^ | ^-H V-^ | ^ ^ | ^- text alignment: 0=left 1=center 2=right | ^- font scale
; Rectangle (range -1:+1) | Text Color | Background Color | Border | ^- h/v-anchor: 0=none 1=left/top 2=center 3=right/bottom

;运行语句
run = CommandListPrintText

;代码案例来自文件: ..\XXMI Launcher\WWMI\Core\WWMI\WuWa-Model-Importer.ini

滑块UI使用

默认滑块的使用需要按住ALT键拖动(当然你也可以改成别的键)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
;此滑块工具主要代码来自momo制作
;使用方式

;滑块工具的INI---------------------------------------------------
[Present]
;是否启动滑块页面检测
if ($\ModXXX\enable_mods)
……
;输出变量参数到模组INI中(乘几可以自己改,slider_value变量值为[0 - 1])
$\ModXXX\mod_val = $slider_value * 2



;模组INI---------------------------------------------------------
;顶部中添加命名空间(名称随意,这里只是方便区分)
namespace=ModXXX

;启动滑块变量
global $enable_mods=0
;接收滑块的参数(形态键的值)
global $mod_val=0

[Key enable_mods]
condition = $object_detected
;按键可以是英文也可以是特殊按键,像手柄之类的
key = NO_CTRL NO_ALT VK_DOWN
type = cycle
$enable_mods = 0,1

;参考上面的自定义形态键
[CommandListSetShapeKey]
……
;形态键的值使用传入的变量
$\WWMIv1\shapekey_value = $mod_val
……



模型

模型大小

1
2
3
4
5
6
7
8
9
10
11
12
;WWMI 的核心机制之一: 模型是通过骨架合并,在 GPU 中统一缩放到对应的顶点位置

[TextureOverrideMyCharacter]
;需要指定放大物品的 Hash
hash = c7aae00c
;放大倍率
$\WWMIv1\custom_mesh_scale = 0.5
run = $\WWMIv1\CustomShaderSkeletonMerger

;记得把这里的这句话删掉(改这里也能实现这个效果,但是是整体的)
[CommandListMergeSkeleton]
;$\WWMIv1\custom_mesh_scale = 1

自定义形态键

有形态键的组件才能自定义形态键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;根据WuWa-Model-Importer.ini给出的[CommandListSetShapeKey]参考
;得出如下使用方式(名称可以自定义,不一定是CommandListSetShapeKey
[CommandListSetShapeKey]
; Usage example
; 这里是形态键对应的ID(blender中形如 Deform XXX
; 序号记得改大点,不要和原模型的重复(一般都是105以后就没了)
$\WWMIv1\shapekey_id = 105
; 给的形态键的值,与blender中形态键的值一样
$\WWMIv1\shapekey_value = 2
cs-u5 = ResourceCustomShapeKeyValuesRW
run = CommandList\WWMIv1\SetShapeKey
$shapekey_mode = 0
run = CustomShaderShapeKeySetter

[Present]
if $object_detected
if $mod_enabled
post $object_detected = 0
run = CommandListUpdateMergedSkeleton
;每一帧都检测形态键的变化?(反正就是运行它)
run = CommandListSetShapeKey
else
……

留下指定的形态键

需复制出一个你已经调整好的物体

然后将原来的物体应用所有的形态键

选中复制物体中 你需要导出的形态键,然后选中复制的物体,和原来的物体,在原来物体的形态键选项框右边点击“V”小三角,再选择传递形态键即可将你选中的形态键传递过来

角色检测:模型替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[Constants]
;检测变量
global $ModActive = 0

[TextureOverrideYangyangPosition]
;检测语句,加载贴图的Hash(角色里随便选一个就行)
hash = aa467d8c
$ModActive = 2
match_priority = 1

[Present]
; 保证只存活 1
post $ModActive = $ModActive - 1
; 只有当 $ModActive > 0 时才打开总开关【检测-关闭模组】
if $ModActive > 0
$object_detected = 1
else
$object_detected = 0
endif
; …………原来的逻辑

; 【检测-关闭模组】
; 如果说要关闭模组需要在所有的[TextureOverrideComponentX]中添加下面的代码
[TextureOverrideComponent0]】
; $object_detected = 1 这行改成下面的代码
; 先判断是不是目标角色
if $Mod010Active > 0
$object_detected = 1
endif
; if $mod_enabled 这行改成下面的
if $mod_enabled && $object_detected
; …………原来的逻辑不变

; 【检测-替换贴图】
; 找到你要替换贴图的地方
[TextureOverrideTexture3]
hash = 8c2f4684
match_priority = 0
; 这里写 == 1 也是可以的
if $ModActive > 0
this = ResourceTexture3
endif

动画制作

这是针对不改变模型面数的动画制作

复杂的模型只能通过大量的IF去判断帧数执行对应的渲染了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[Constants] 
global $frame = 0
;速度变量(等会要用)
global persist $speed = 0.5
;起始帧
global $start_frame = 0
;结束帧
global $end_frame = 30
;规整化(等会要用)
global $frame_value = 0

[Present]
;在帧运算中加入如下代码:
if $frame > $end_frame
$frame = $start_frame
endif
; 规整化,防止出现渲染半帧的情况(如果不影响整体效果,则不添加也没事)
if $frame_value >= 1
$frame = $frame + 1
$frame_value = 0
endif
$frame_value = $frame_value + $speed
;可以设置切换键,暂停动画播放(可以参考按键切换模型内容)
if $key == 1
$frame = $start_frame
endif

[TextureOverrideComponentN]
;找到加载的模型语句
; Draw Component 2.XXX_001
drawindexed = 2304, 137607, 0
; Draw Component 2.烟雾1_002
drawindexed = 2304, 139911, 0
……
;你会发现它们的面数是等差数列
;所以就有了如下写法
;使用第一个模型为模板,然后通过帧数和面数(差值)的乘积逐级递增
;即可做到模型按顺序渲染的效果(动画)
drawindexed = 2304, 137607 + $frame * 2304, 0


; 带按键切换动画且能倒放的制作 --------------------------------------------------------
[Present]
;在帧运算中加入如下代码:
;初始态:按下按键,且动画未播放完,则进入语句播放至结尾
if $key1 == 1 && $frame < $end_frame
; 规整化,防止出现渲染半帧的情况
if $frame_value >= 1
$frame = $frame + $frame_value
$frame_value = 0
endif
$frame_value = $frame_value + $speed
endif

;倒放态:按下按键,且动画已经播放一段时间,则进入语句倒放至开头
if $key1 == 0 && $frame > 0
; 规整化,防止出现渲染半帧的情况
if $frame_value >= 1
$frame = $frame - 1
$frame_value = 0
endif
$frame_value = $frame_value + $speed
endif

自定义骨骼


着色器

着色器颜色修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
float4 v1 :  XXXX
out float4 o0 : XXXX
;float 代表 参数类型:4 代表其有四个组件(x,y,z,w)
;x,y,z:代表RGB | w 代表着色范围?
;v1(vX): 输入值
;o0(oX): 输出值

;赋值语句
o0.x = 0.2
o0.x -= 0.1
o0.xyz = float3(10,10,10)

;t0: 纹理
;r0: 寄存器——存放临时变量
;cb0: 常量缓冲区——游戏内部的值,如时间、位置等

o0.x = 1
o0.y = 0
o0.z = 0

着色器检测触发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 着色器代码 ---------------------------------------------------------------
// 注册变量“ACTIVE”在第160的位置上(只有一个x组件)
#define ACTIVE IniParams[160].x
// 注意main方法的位置
void main(
……

// 最底下(要在return的前面)
if (ACTIVE == 0){
// 根据你想要的输出参数来写
o0.xyzw = float4(0,0,0,0);
}else{
o0.xyzw = float4(1,0,0,1);
}
return;


;模组INI代码 ---------------------------------------------------------------
[Constants]
;特效着色器检测键
global $ShaderKey = 0

[ShaderOverride]
;你的着色器的Hash(如 55ee3565231ff874-ps_replace Hash就是前面那串字符)
hash = 55ee3565231ff874
;刚才你注册的变量位置
x160 = $ShaderKey

着色器指定dump

1
2
3
4
5
6
7
8
9
10
11
12
13
;在\XXMI Launcher\WWMI\d3dx.ini文件中将下面这行话注释掉
;这句话是转储所有的纹理和缓冲
;analyse_options = dump_rt dump_tex dump_cb dump_vb dump_ib buf txt

;在Mod文件夹任意位置中创建一个INI
[ShaderOverrideGlider]
;通过小键盘0 开启狩猎模式 1 / 2 控制着色器 3 复制着色器Hash
;获取到你需要的着色器(对应的物体消失,或是没有贴图)
hash = 7191d68ad7775895
;加上转储的语句,就会将此着色器影响的物体dump下来
analyse_options = dump_rt dump_tex dump_cb dump_vb dump_ib buf txt dds
;如果发现dump都不完整,可以将多个着色器一起启用
;或者直接恢复d3dx文件的语句执;行全dump

其它内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
;这句话控制模组的执行
run = CommandListUpdateMergedSkeleton

;可以直接通过cursor_x和cursor_y获取鼠标的位置

;time表示游戏开始后经过的秒数

[present]
;表示每帧都进行运算

[TextureOverrideXXX]
;游戏运行的物体hash
hash = xxx
;运行的内容: 如着色器等
run = xxx
;替换的内容: 如贴图等
this = xxx
;跳过绘制
handling = skip

;转储的文件,文件名开头的 6 位数字字符串,代表纹理绘制的顺序
;deduped中的文件: 结构为哈希-文件类型


;常用例:
;[Constants]--每一个ini为独立的命名空间,该段配置该空间内的全局变量

;[Present]--每一帧都会执行该段内的代码,用于变量初始化和动态变化

;[Key*]--按键检测,触发相应按键时执行代码

;[TextureOverride*]--用于匹配和覆盖贴图、模型资源,匹配到相应资源时执行

;[ShaderOverride*]--用于匹配和覆盖着色器资源,匹配到时执行.......等等

;[CustomShader*]--用于加载和配置自定义着色器文件,需用run=*段名*执行

;[CommandList*]--用于自定义代码块,可以将需要多次执行的代码块变量定义、 初始化、判断等等
;操作放到一个段内,需要的时候用run=*段名*执行

;有两种着色器
;VS(顶点着色器):控制物体绘制的位置
;PS(像素着色器):控制物体绘制的外观


;ini顶部的[Constants]定义模型的面数等数据不要修改
;修改模型后一定要自动生成INI,保证下面数据的准确
[Constants]
global $required_wwmi_version = 0.91
global $object_guid = 200604
global $mesh_vertex_count = 56382
global $shapekey_vertex_count = 30463
[TextureOverrideComponentX]
match_first_index = 14907
match_index_count = 13692

; 使用一个未定义的变量默认值是1(很多时候切换键有问题就是因为这个)

VK虚拟键码

为什么要用这个?个人觉得还是主要因为这个区分度高?

实际上直接写 up 与 VK_UP 的效果是一样的(不过我没试过手柄,可能和这有关系)

https://learn.microsoft.com/zh-cn/windows/win32/inputdev/virtual-key-codes

WWMI-Tools

工具对应的中文名称

  1. 为具有形状键的对象应用修改器(脸部模型删除骨骼时使用)
  2. 合并顶点组
  3. 填充顶点组中的间隙
  4. 删除未使用的顶点组(方便刷权重)
  5. 删除所有顶点组(点一下就好了,挺快的)
  6. 创建合并对象
  7. 应用合并的对象雕刻

导出选项

导出选项对应的中文名称

  1. Partial Export——部分导出
  2. Mirror Mesh——镜面网格导出(那导入也要镜像)
  3. Ignore Nested Collections ——忽略嵌套集合
  4. Ignore Hidden Objects——忽略隐藏的对象
  5. Ignore Muted Shape Keys——忽略形态键
  6. Apply All Modifiers——应用所有修改器

鼠标指针

1
.\Wuthering Waves Game\Client\Content\Aki\Cursor

像素大小:48 60 72 96

blender默认布局

在顶部“文件”选项中的“默认”选项中,找到“保存启动文件”按钮,点击即可保存当前布局

; 本文档由晨星制作
; 我的B站链接:https://space.bilibili.com/95747099
; 我的Youtube:https://www.youtube.com/channel/UC9maLE-2mYDlZbDNWrX5dxA
; 我的爱发电:https://afdian.com/a/MorningStar?tab=home (现在也没发啥,应该也没人支持吧(@_@;))
; 我的香蕉网账号:https://gamebanana.com/members/2798606
; 非我的渠道下载 还收费的,那你就是被骗了! 记得帮我点点举报(免费分享且标注链接的没有关系)
; 我的QQ群:1029406436(欢迎一起聊天吹牛逼,一起玩 ~)