シェーダの最適化を楽しむ(2)

 id:ABAさんのシェーダに続き、id:erakanさんのシェーダについて、使用スロット数上の最適化を行ってみました。

  • http://d.hatena.ne.jp/erakan/20070612より
  • ShaderOriginal→ShaderXELFMod
  • こちらも同じ話題に基づくシェーダです。
  • 提供されているzipのEdge.fxを置き換えることで動作します。
float4x4 g_mWorldViewProjection;    // World * View * Projection matrix
float    g_fEdgeWidth;					// Width of edge

struct VS_INPUT {
	float4 Position : POSITION;
	float4 Diffuse  : COLOR0;
	float2 Texcoord : TEXCOORD0;
};

struct VS_OUTPUT {
	float4 Position : POSITION;
	float4 Diffuse  : COLOR0;
	float2 Texcoord : TEXCOORD0;
};

struct PS_OUTPUT {
	float4 Color : COLOR0;
};

VS_OUTPUT VS(VS_INPUT Input) {
    VS_OUTPUT Output;
    Output.Position = mul(Input.Position, g_mWorldViewProjection);
    Output.Diffuse = float4(1.0f, 1.0f, 1.0f, 1.0f);
    Output.Texcoord = Input.Texcoord;
    return Output;
}

PS_OUTPUT PS(VS_OUTPUT Input) {
    PS_OUTPUT Output;

	// make 'screen to texcoord' matrix
	float a = ddx(Input.Texcoord.x);
	float b = ddx(Input.Texcoord.y);
	float c = ddy(Input.Texcoord.x);
	float d = ddy(Input.Texcoord.y);
	// make inverse('texcoord to screen') matrix
	float det = a*d - b*c;
	float idet = (det != 0.0f) ? 1.0f / det : 0.0f;
	float ia = d * idet;
	float ib = -b * idet;
	float ic = -c * idet;
	float id = a * idet;

	float2 screenXY;
	float screenLength;

	// convert uv(1,0) to screenXY
	screenXY     = float2(ia, ib);
	screenLength = length(screenXY);
	// calculate U where screenLength = g_fEdgeWidth
	float edgeU = g_fEdgeWidth / screenLength;

	// convert uv(0,1) to screenXY
	screenXY     = float2(ic, id);
	screenLength = length(screenXY);
	// calculate V where screen length = g_fEdgeWidth
	float edgeV = g_fEdgeWidth / screenLength;

	float edge = (
		Input.Texcoord.x <= edgeU ||
		Input.Texcoord.x >= 1.0f - edgeU ||
		Input.Texcoord.y <= edgeV ||
		Input.Texcoord.y >= 1.0f - edgeV) ? 0.0f : 1.0f;

	Output.Color = float4(edge, edge, edge, 1.0f);
	return Output;
}

PS_OUTPUT PSX(VS_OUTPUT Input) { 
    PS_OUTPUT Output;

	// make 'screen to texcoord' matrix
	float4 m = float4(ddx(Input.Texcoord.xy), ddy(Input.Texcoord.xy));
	// make inverse('texcoord to screen') matrix
	float det = dot(m.xy * float2(+1, -1), m.wz); // a*d - b*c;
	float idet = (det != 0.0f) ? 1.0f / det : 0.0f;
	float4 screen = m.wyzx * float4(+1, -1, -1, +1) * idet;

	float2 screenLength = float2(length(screen.xy), length(screen.zw));
	
	// calculate U,V where screenLength = g_fEdgeWidth
	float2 edge = g_fEdgeWidth / screenLength;

	float2 bound = (Input.Texcoord.xy >= edge) * (Input.Texcoord.xy <= float2(1, 1) - edge);
	float mask = bound.x * bound.y;

	Output.Color = float4(mask, mask, mask, 1.0f);
	return Output;
}

technique ShaderXELFMod {
	pass P0 {
		VertexShader = compile vs_3_0 VS(); 
		// XELF's mod / ps_3_0: 25-slots @ D3D10 Shader Compiler 9.19.949.0046
		PixelShader = compile ps_3_0 PSX(); 
	}
}

technique ShaderOriginal {
	pass P0 {
		VertexShader = compile vs_3_0 VS(); 
		// original   / ps_3_0: 36-slots @ D3D10 Shader Compiler 9.19.949.0046
		PixelShader = compile ps_3_0 PS(); 
	}
}