疑問
著色器只能訪問控制元件可視區域內的畫素,但是陰影特效出現在控制元件可視區域外部,這是怎麼實現的?
我想起來WPF中有個叫做裝飾器的東西,然而閱讀了一下文件,似乎不行
放置在裝飾器層中的任何內容將呈現在設定的其他任何樣式的頂部。 換言之,裝飾器始終以可見的方式位於頂部,無法使用 z 順序重寫。
而且裝飾器的寫法也和Effect
不一樣,這不行。
一個有趣的屬性
我順著繼承關係往上找,找到了ShaderEffect類的另外幾個屬性PaddingRight
PaddingLeft
PaddingBottom
PaddingTop
,這幾個屬性可以擴大控制元件傳給shader的矩形框Rect
範圍,從而給了shader在在控制元件可視範圍外部進行渲染的能力。
這給了我啟發,我們給shader傳入一個控制元件實際範圍,然後在shader中判斷,如果畫素位於控制元件範圍內,就渲染原來的顏色,否則就計算陰影。類似與這樣
快速驗證
- 演算法驗證
我們現在Shazzam
中快速驗證下這個想法是否可行
float2 shadowPoint : register(C0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color;
//origin color
color= tex2D( input , uv.xy);
float2 checks =1-step(1-shadowPoint,uv );
float2 checke =step(shadowPoint,uv );
float border = max( checke.x , checke.y);
float rightTop=border*checks.x;
float leftBottom=border*checks.y;
float4 tempColor=lerp(color,float4(0,0,0,1),border );
float4 finalColor = lerp(tempColor,float4(0,0,0,0),max(rightTop,leftBottom));
return finalColor;
}
看起來還不錯,但wpf傳入的矩形區域是否如我們所設想的那樣?
- 引數驗證
internal class MyShadowEffect:ShaderEffect
{
public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(MyShadowEffect), 0);
public static readonly DependencyProperty ShadowPointProperty = DependencyProperty.Register("ShadowPoint", typeof(Point), typeof(MyShadowEffect), new UIPropertyMetadata(new Point(0D, 0D), PixelShaderConstantCallback(0)));
public MyShadowEffect()
{
PixelShader pixelShader = new PixelShader();
pixelShader.UriSource = new Uri("pack://application:,,,/WpfApp1;component/x3D/ShadowEffect.ps", UriKind.Absolute);
this.PixelShader = pixelShader;
this.UpdateShaderValue(InputProperty);
this.UpdateShaderValue(ShadowPointProperty);
ShadowPoint = new Point(0.8, 0.8);
PaddingRight=50;
PaddingBottom=50;
}
public Brush Input
{
get
{
return ((Brush)(this.GetValue(InputProperty)));
}
set
{
this.SetValue(InputProperty, value);
}
}
public Point ShadowPoint
{
get
{
return ((Point)(this.GetValue(ShadowPointProperty)));
}
set
{
this.SetValue(ShadowPointProperty, value);
}
}
}
<Button Content="Btn" FontSize="28" Margin="0" Width="200" Height="200">
<Button.Effect>
<local:MyShadowEffect/>
</Button.Effect>
</Button>
效果看起來還不錯!
問題
- 實現這種陰影效果,需要我們瞭解控制元件尺寸,並計算控制元件左下角的紋理座標
\[\frac{button.width}{button.width+PaddingRight}
\]
然而shaderEffect
獲取並不天然支援獲取控制元件尺寸,所以實現方式並不很好看。