改造MMM用HLShader << 上一篇下一篇 >>

MikuMikuMoving在很多方面都要比MMD好用一些,但是目前最大的问题就是有些很重要的MME在MMM里面没法使用,比如HalfLambertShader Ver0.301,以下略称HLShader。
但是根据官方给出的文档和范例,可以对其进行改造。

参考文档:https://sites.google.com/site/mikumikumoving/manual/effect-1/mmekaranokonbajon

PS1:发布完整修改的代码是不可能的,第一我不太清楚改造或者二配之类的问题,第二因为我完全不懂着色器语言,所以里面还存留着很多无意义的代码和小BUG,虽然好像不影响使用,但是我实在拿不出手……我只是分享一下方法给有需要的人,网上哪里都找不到这方面的教程……
PS2:推荐使用Notepad++修改,因为fx内注释多为日文,中文系统会乱码,用Notepad++可以很方便地修改编码,并且可以左右对照。当然其他类似的文本编辑器都行。

那开始吧,请各位做好代码冲击准备!

首先我们把Cook-Torrance.fx加载进MMM,应用在模型上,摆好姿势,设置好灯光,会发现两个主要的问题:

1. 在地面阴影模式下HLShader不起作用

在地面阴影模式下HLShader不起作用,而在本影模式下虽然起作用,但是动作却不起作用了。

这是因为MMM改变了读取顶点位置的方式,先来看一下HLShader的这一段代码:

// 頂点シェーダ
BufferShadow_OUTPUT BufferShadow_VS(float4 Pos : POSITION, float3 Normal : NORMAL, float2 Tex : TEXCOORD0, uniform bool useTexture)
{
    BufferShadow_OUTPUT Out = (BufferShadow_OUTPUT)0;

    // カメラ視点のワールドビュー射影変換
    Out.Pos = mul( Pos, WorldViewProjMatrix );

    // ワールド位置
    Out.WPos= mul( Pos, WorldMatrix );
    
    // カメラとの相対位置
    Out.Eye = CameraPosition - Out.WPos;

    // 頂点法線
    Out.Normal = mul( Normal, (float3x3)WorldMatrix );

    // ライト視点によるワールドビュー射影変換
    Out.ZCalcTex = mul( Pos, LightWorldViewProjMatrix );
       
    // テクスチャ座標
    Out.Tex = Tex;
    
    return Out;
}

很明显,这个函数可以接收几个关于顶点位置的参数,我们需要这样改:

首先是接收参数的方式需要用到MMM内建的结构体:MMM_SKINNING_INPUT IN
然后使用MMM_SkinnedPositionNormal函数将IN的数据传入临时创建的MMM_SKINNING_OUTPUT SkinOut结构体。
接下来把所有以前的Pos和Normal都改成SkinOut.Position及SkinOut.Normal。
但是Tex要改成:Out.Tex = IN.Tex;

完整代码如下:

// 頂点シェーダ
BufferShadow_OUTPUT BufferShadow_VS(MMM_SKINNING_INPUT IN, uniform bool useTexture)
{
	BufferShadow_OUTPUT Out = (BufferShadow_OUTPUT)0;

	MMM_SKINNING_OUTPUT SkinOut = MMM_SkinnedPositionNormal(IN.Pos, IN.Normal, IN.BlendWeight, IN.BlendIndices, IN.SdefC, IN.SdefR0, IN.SdefR1);

    // カメラ視点のワールドビュー射影変換
    Out.Pos = mul( SkinOUt.Position, WorldViewProjMatrix );

    // ワールド位置
    Out.WPos= mul( SkinOUt.Position, WorldMatrix );
    
    // カメラとの相対位置
    Out.Eye = CameraPosition - Out.WPos;

    // 頂点法線
    Out.Normal = mul( SkinOUt.Normal, (float3x3)WorldMatrix );

    // ライト視点によるワールドビュー射影変換
    Out.ZCalcTex = mul( SkinOUt.Position, LightWorldViewProjMatrix );
       
    // テクスチャ座標
    Out.Tex = IN.Tex;
    
    return Out;
}

全部改完之后进MMM测试一下,问题应该已经解决了。

2. 某些模型的某些部位HLShader不起作用

代码的最后面,有两段绘制用的方法:

// オブジェクト描画用テクニック
technique MainTecBS0  < string MMDPass = "object_ss"; bool UseTexture = false;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(false);
        PixelShader  = compile ps_3_0 BufferShadow_PS(false);
    }
}

technique MainTecBS1  < string MMDPass = "object_ss"; bool UseTexture = true;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(true);
        PixelShader  = compile ps_3_0 BufferShadow_PS(true);
    }

把这两段复制一遍,将object_ss改成object,把前面的MainTecBS序号也改一下,像这样:

// オブジェクト描画用テクニック
technique MainTecBS0  < string MMDPass = "object"; bool UseTexture = false;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(false);
        PixelShader  = compile ps_3_0 BufferShadow_PS(false);
    }
}
technique MainTecBS1  < string MMDPass = "object"; bool UseTexture = true;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(true);
        PixelShader  = compile ps_3_0 BufferShadow_PS(true);
    }
}
technique MainTecBS2  < string MMDPass = "object_ss"; bool UseTexture = false;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(false);
        PixelShader  = compile ps_3_0 BufferShadow_PS(false);
    }
}
technique MainTecBS3  < string MMDPass = "object_ss"; bool UseTexture = true;> {
    pass DrawObject {
        VertexShader = compile vs_3_0 BufferShadow_VS(true);
        PixelShader  = compile ps_3_0 BufferShadow_PS(true);
    }
}

关于object和object_ss的区别,我也不是很清楚(日语无能…),引用一段MME的文档:

string MMDPass
そのテクニックを適用する描画対象を指定する。以下のうちいずれかを指定する。この区分は、MMDの描画手順に由来する。

“object” オブジェクト本体(セルフシャドウOFF)
“zplot” セルフシャドウ用Z値プロット
“object_ss” オブジェクト本体(セルフシャドウON)
“shadow” 影(セルフシャドウではない単純な影)
“edge” 輪郭(PMDモデルのみ)

3. 全面修改

到目前为止我们已经解决了两个比较致命的问题,现在已经可以在地面阴影模式下使用HLShader,但是效果和在MMD里面还是有一些差别。同样的灯光配置,上图是MMD的效果,下图是MMM的效果:

选用这只模型的原因是因为每个材质都有toon材质,其他地方可能区别很细微,但是最明显的地方就是头发,原因似乎是因为MMM修改了渲染toon材质的方式,并且改变和增加了很多全局变量。

在MMM安装目录的Shader文件夹下有一份SampleBase.fxm,这是一份官方给出的shader模板,我们要做的就是参照那份文档逐行检查和修改HLShader的代码,并且添加在本影和柔影模式下渲染的方法。这就是为什么我推荐使用Notepad++的原因。

另外MMM官方也有一些修改好的特效文件可以下载
我建议可以先对比一下以前MME用的版本和官方修改的版本,在我们不知道该怎么修改的时候会有很大的启发,我对比的是AdultShader。

其实这个过程不难,但是需要的是耐心和仔细,只要有点c/c++基础的,哪怕不懂HLSL编程(比如我就是),也可以修改。我这里也给大家一些例子:

首先是全局变量的修改:

float3   LightDirection : DIRECTION < string Object = "Light"; >;

修改成

float3   LightDirection[MMM_LightCount] : LIGHTDIRECTIONS;

MMM中自带3个灯光,所以需要一个数组来存放,而默认情况下只有一个灯光是开启的,所以某些代码修改的时候我们只需要用到第一个灯光就行,比如:

comp = min(max(0,dot(Nn,-LightDirection[0]))*Toon,comp); 

关于floatN:

这些是HLSL使用的特殊数据类型,表示float型N维向量,N最大为4,其他诸如intN同理。
比如float3可以存放3个float类型的数据,一般用于存放xyz或者rgb;而float4则还可以再加一个alpha,很好理解。修改的时候务必注意这些数据类型。

再比如原来的结构体:

struct BufferShadow_OUTPUT {
    float4 Pos      : POSITION;     // 射影変換座標
    float4 ZCalcTex : TEXCOORD0;    // Z値
    float2 Tex      : TEXCOORD1;    // テクスチャ
    float3 Normal   : TEXCOORD2;    // 法線
    float3 Eye      : TEXCOORD3;    // カメラとの相対位置
    float4 WPos     : TEXCOORD4;    // ワールド位置
};

最终会需要修改成:

struct BufferShadow_OUTPUT {
    float4 Pos      : POSITION; 		// 射影変換座標
    float4 ZCalcTex : TEXCOORD0;		// Z値
    float2 Tex      : TEXCOORD1;		// テクスチャ
    float3 Normal   : TEXCOORD2;		// 法線
    float3 Eye      : TEXCOORD3;		// カメラとの相対位置
	float4 SS_UV1   : TEXCOORD4;		// セルフシャドウテクスチャ座標
	float4 SS_UV2   : TEXCOORD5;		// セルフシャドウテクスチャ座標
	float4 SS_UV3   : TEXCOORD6;		// セルフシャドウテクスチャ座標
	float4 SubTex	: TEXCOORD7;		// サブテクスチャ/スフィアマップテクスチャ座標
	float4 WPos     : TEXCOORD8;		// ワールド位置
	float4 Color	: COLOR0;			// ライト0による色
};

注意保留原有的WPos!我之前把它替换成了SS_UV1,把所有后面用到的也都替换了,结果渲染变得很奇怪……

之前修改过的那一段顶点位置的计算:

Out.Pos = mul( SkinOut.Position, WorldViewProjMatrix );

按照官方的文档修改(虽然没什么太大作用…):

// カメラ視点のワールドビュー射影変換( 頂点座標)
if (MMM_IsDinamicProjection)
{
	float4x4 wvpmat = mul(mul(WorldMatrix, ViewMatrix), MMM_DynamicFov(ProjMatrix, length(Out.Eye)));
	Out.Pos = mul( SkinOut.Position, wvpmat );
}
else
{
	Out.Pos = mul( SkinOut.Position, WorldViewProjMatrix );
}

然后像这一段修改好的代码就是关于渲染toon材质的一段(直接从sample里复制的,而且这段很关键):

// セルフシャドウなしのトゥーン適用
float3 color;
if (!useSelfShadow && useToon && usetoontexturemap ) {
	color = MMM_GetToonColor(MaterialToon, IN.Normal, LightDirection[0], LightDirection[1], LightDirection[2]);
	Color.rgb *= color;
}
if (useSelfShadow) {
	if (useToon && usetoontexturemap) {
		float3 shadow = MMM_GetToonColor(MaterialToon, IN.Normal, LightDirection[0], LightDirection[1], LightDirection[2]);
		color = MMM_GetSelfShadowToonColor(MaterialToon, IN.Normal, IN.SS_UV1, IN.SS_UV2, IN.SS_UV3, false, useToon);
		Color.rgb *= min(shadow, color);
	}
	else {
		Color.rgb *= MMM_GetSelfShadowToonColor(MaterialToon, IN.Normal, IN.SS_UV1, IN.SS_UV2, IN.SS_UV3, false, useToon);
	}
}

我是懒得修改最后的渲染方法,所以保留了官方推荐的这些形式,但是在某些函数的开始自己的定义了一些布尔值,以便以后再修改,比如:

// ピクセルシェーダ
float4 BufferShadow_PS(BufferShadow_OUTPUT IN, uniform bool useTexture) : COLOR0
{
	bool useSphereMap = false;
	bool useToon = true;
	bool useSelfShadow = true;

但是切记一定要保留HLShader原本的计算方法,比如这是sample当中的一段:

static float3 AmbientColor[3]  = { 
	saturate(MaterialAmbient * LightAmbients[0]) + MaterialEmmisive,
	saturate(MaterialAmbient * LightAmbients[1]) + MaterialEmmisive,
	saturate(MaterialAmbient * LightAmbients[2]) + MaterialEmmisive};

而这是原来HLShader的计算方法:

static float4 AmbientColor  = float4( MaterialEmmisive*MaterialToon*LightAmbient/0.60, MaterialDiffuse.a+0.01);

所以我们要在保留原计算方法的同时把它修改成数组的形式:

static float4 AmbientColor[3]  = {
	float4( MaterialEmmisive*MaterialToon*LightAmbients[0]/0.60, MaterialDiffuse.a+0.01),
	float4( MaterialEmmisive*MaterialToon*LightAmbients[1]/0.60, MaterialDiffuse.a+0.01),
	float4( MaterialEmmisive*MaterialToon*LightAmbients[2]/0.60, MaterialDiffuse.a+0.01)};

成果

有没有头晕掉……我可以晕了整整两天才摸索出来然后全部修改完啊!

全部修改完之后可以就拖进MMM中进行调试,它报哪里错就修改哪里,只要足够仔细,应该没问题的。另外ShadowDraw.fx也要修改,但是加载的时候是加载ShadowMap.fx,因为是调用关系。同样的,其他涉及到shader方面的MME应该也都是这样改造。

最后这是我改造完的成果,祝各位成功~

<< 上一篇:2.4小记 下一篇:说说做网页那些事 >>