対象指定モザイクエフェクト【MME】
指定したオブジェクト周辺にモザイクをかけるエフェクト
(Update ) MikuMikuDanceMikuMikuEffectCGhttp://arithmeticoverflow.blog.fc2.com/blog-entry-76.html から移行
https://bowlroll.net/file/65697
製作時点で VPVPwiki を見て材質を指定してそこだけにモザイクをかけるものがなかったので作りました。しかし,作り終わった後によく調べると既にわたりさんの AutoMosaic/SubsetMosaic がありました。それでもモザイクのかかり方が違うからいいじゃん?と公開したのが冒頭のイラストです。
SubsetMosaic はそぼろさんの ShapeMosaic と同様にモザイクの正方形を各領域内の任意のピクセルの色で塗りつぶすものです。ピクセルの選択はランダムで時間によって揺らぎが加えられていたりします。
公開した MaskMosaic は領域内のピクセルの平均を用いています。他エフェクトと比べてなんと考えのないシンプルな方法。正直これはサムネにするとモザイクになっていなかったりする。
スクリプト
// アクセサリ操作で値を設定したい場合は1に
#define USE_CONTROLLER 0
#if USE_CONTROLLER == 0
// パラメータ
float strength = 4.0; // モザイク強度(3から5が良い)
float3 col_bias = {0.0,0.0,0.0}; // RGBで色を加算,マイナス値も可(あまり使えない)
float spread = 2.0; // モザイク適用領域の拡張範囲(SAMP_NUMを増やすほうが重くなるけど綺麗)
#endif
#define SAMP_NUM 3 // 整数値
// パラメータここまで
float Script : STANDARDSGLOBAL <
string ScriptOutput = "color";
string ScriptClass = "scene";
string ScriptOrder = "postprocess";
> = 0.8;
////////////////////////////////////////////////////////////////
// コントローラ
#if USE_CONTROLLER == 1
float strength : CONTROLOBJECT < string name = "(self)"; string item = "X"; >; // 荒さ
float3 col_bias : CONTROLOBJECT < string name = "(self)"; string item = "Rxyz"; >; // 色(加算)
float spread : CONTROLOBJECT < string name = "(self)"; string item = "Z"; >; // 広がり
#endif
// スクリーンサイズ
float2 ViewportSize : VIEWPORTPIXELSIZE;
static float2 ViewportOffset = (float2(0.5,0.5)/ViewportSize);
static float2 SampStep = ViewportOffset*float2(2.0*spread,2.0*spread);
// レンダリングターゲットのクリア値
const float4 ClearColor = {1.0,1.0,1.0,1.0};
const float ClearDepth = 1.0;
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// 深度バッファ
texture2D DepthBuffer : RENDERDEPTHSTENCILTARGET <
float2 ViewPortRatio = {1.0,1.0};
string Format = "D24S8";
>;
// オリジナル
texture2D ScnMap : RENDERCOLORTARGET <
float2 ViewPortRatio = {1.0,1.0};
string Format = "A8R8G8B8" ;
int MipLevels = 0;
>;
sampler2D ScnSamp = sampler_state {
texture = <ScnMap>;
MinFilter = NONE;
MagFilter = NONE;
MipFilter = LINEAR;
AddressU = CLAMP;
AddressV = CLAMP;
};
// オフスクリーンレンダー
texture MaskRT : OFFSCREENRENDERTARGET <
string Description = "Masking for mask_mosaic.fx";
string Format = "R16F";
float2 ViewPortRatio = {1.0,1.0};
float4 ClearColor = { 0, 0, 0, 0 };
float ClearDepth = 1.0;
int Miplevels = 1;
string DefaultEffect =
"self = hide;"
"* = _none.fx;"
;
>;
sampler2D MaskSamp = sampler_state {
texture = <MaskRT>;
MinFilter = NONE;
MagFilter = NONE;
MipFilter = NONE;
AddressU = CLAMP;
AddressV = CLAMP;
};
// 範囲広げるためのバッファ
// X
texture MaskRT_X : RENDERCOLORTARGET <
float2 ViewPortRatio = {1.0,1.0};
string Format = "R16F";
int MipLevels = 1;
>;
sampler2D MaskSamp_X = sampler_state{
texture = <MaskRT_X>;
MinFilter = NONE;
MagFilter = NONE;
MipFilter = NONE;
AddressU = CLAMP;
AddressV = CLAMP;
};
// Y
texture MaskRT_Y : RENDERCOLORTARGET <
float2 ViewPortRatio = {1.0,1.0};
string Format = "R16F";
int MipLevels = 1;
>;
sampler2D MaskSamp_Y = sampler_state{
texture = <MaskRT_Y>;
MinFilter = NONE;
MagFilter = NONE;
MipFilter = NONE;
AddressU = CLAMP;
AddressV = CLAMP;
};
////////////////////////////////////////////////////////////////
// 共通
struct VS_OUTPUT {
float4 Pos : POSITION;
float2 TexUV : TEXCOORD0;
};
VS_OUTPUT VS_Draw( float4 Pos : POSITION, float2 TexUV : TEXCOORD0 ) {
VS_OUTPUT Out = (VS_OUTPUT)0;
Out.Pos = Pos;
Out.TexUV = TexUV + ViewportOffset;
return Out;
}
float4 PS_MaskSpreadX( float2 TexUV : TEXCOORD0 ) : COLOR0 {
float a = tex2D(MaskSamp, TexUV).r;
[unroll]
for (int i=1; i<=SAMP_NUM; i++) {
a += tex2D( MaskSamp, TexUV-float2(SampStep.x*i,0) ).r;
a += tex2D( MaskSamp, TexUV+float2(SampStep.x*i,0) ).r;
}
float col = (a>0.0)?1.0:0.0;
return float4(col,col,col,1.0);
}
float4 PS_MaskSpreadY( float2 TexUV : TEXCOORD0 ) : COLOR0 {
float a = tex2D(MaskSamp_X, TexUV).r;
[unroll]
for (int i=1; i<=SAMP_NUM; i++) {
a += tex2D( MaskSamp_X, TexUV-float2(0,SampStep.y*i) ).r;
a += tex2D( MaskSamp_X, TexUV+float2(0,SampStep.y*i) ).r;
}
float col = (a>0.0)?1.0:0.0;
return float4(col,col,col,1.0);
}
float4 PS_Draw( float2 TexUV: TEXCOORD0 ) : COLOR0 {
// float4 color;
// if(tex2D(MaskSamp_Y,TexUV).r == 1.0){
// color = tex2Dlod(ScnSamp, float4(TexUV,0,strength))
// +float4(col_bias,1.0);
// }else{
// color = tex2D(ScnSamp, TexUV);
// }
float4 color = (tex2D(MaskSamp_Y,TexUV).r == 1.0)
?tex2Dlod(ScnSamp, float4(TexUV,0,strength))+float4(col_bias,1.0)
:tex2D(ScnSamp, TexUV);
// color = tex2D(MaskSamp_X,TexUV);
return color;
}
////////////////////////////////////////////////////////////////////////////////////////////////
technique MaskMosaic <
string Script =
"RenderColorTarget0=ScnMap;"
"RenderDepthStencilTarget=DepthBuffer;"
"ClearSetColor=ClearColor;"
"ClearSetDepth=ClearDepth;"
"Clear=Color;"
"Clear=Depth;"
"ScriptExternal=Color;"
"RenderColorTarget0=MaskRT_X;"
"RenderDepthStencilTarget=DepthBuffer;"
"ClearSetColor=ClearColor;"
"ClearSetDepth=ClearDepth;"
"Clear=Color;"
"Clear=Depth;"
"Pass=MaskSpreadX;"
"RenderColorTarget0=MaskRT_Y;"
"RenderDepthStencilTarget=DepthBuffer;"
"ClearSetColor=ClearColor;"
"ClearSetDepth=ClearDepth;"
"Clear=Color;"
"Clear=Depth;"
"Pass=MaskSpreadY;"
"RenderColorTarget0=;"
"RenderDepthStencilTarget=;"
"ClearSetColor=ClearColor;"
"ClearSetDepth=ClearDepth;"
"Clear=Color;"
"Clear=Depth;"
"Pass=DrawPass;"
;
> {
pass MaskSpreadX < string Script= "Draw=Buffer;"; > {
AlphaBlendEnable = false;
VertexShader = compile vs_3_0 VS_Draw();
PixelShader = compile ps_3_0 PS_MaskSpreadX();
}
pass MaskSpreadY < string Script= "Draw=Buffer;"; > {
AlphaBlendEnable = false;
VertexShader = compile vs_3_0 VS_Draw();
PixelShader = compile ps_3_0 PS_MaskSpreadY();
}
pass DrawPass < string Script= "Draw=Buffer;"; > {
AlphaBlendEnable = false;
VertexShader = compile vs_3_0 VS_Draw();
PixelShader = compile ps_3_0 PS_Draw();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
平均の計算は面倒なのでミップマップを使用しています。ミップマップなのでモザイクのサイズが 2 倍ずつ大きくなるためにパラメータの有効範囲が狭い。すぐ大きくなっちゃう(´Д⊂ ヽ
あとで気づきましたがミップマップなので領域の形が正方形ではなくアス比に従った長方形になります。(ま,大して気にならないでしょう。)
範囲を広げるのはよくあるガウシアンブラー(ぼかし)と同じ方法です。このせいで無駄に重くなっています。