我正在学习如何使用hlsl为wpf创建效果。 我目前正在尝试制作一种简单的效果来标记图像中的边缘。 我也想为此使用Sobel运算符,因此我在hlsl代码中设置了一个公共float2x3,但似乎无法访问该矩阵中的元素。
我尝试手动输入正确的值,它可以工作,但在使用循环时无效。
sampler2D imageSampler : register(s0);
float imageWidth : register(c0);
float imageHeight : register(c1);
float threshold : register(c2);
float2x3 op =
{
1.0f, 2.0f, 1.0f,
-1.0f, -2.0f, -1.0f
};
float grayScale(float3 color)
{
return (color.r + color.g + color.b) / 3;
}
float4 GetEdgeLoop(float2 coord, float2 pixelSize)
{
float2 current;
float avrg = 0;
float holder;
float gsHolder;
current.x = coord.x - pixelSize.x;
for (int x = 0; x < 2; x++)
{
current.y = coord.y - pixelSize.y;
for (int y = 0; y < 3; y++)
{
holder = op[x][y];
gsHolder = grayScale(tex2D(imageSampler, current).rgb);
avrg += gsHolder * holder;
current.y += pixelSize.y;
}
current.x += pixelSize.x * 2;
}
avrg = abs(avrg / 8);
if (avrg > threshold)
return float4(1, 0, 0, 1);
return tex2D(imageSampler, coord);
}
float4 main(float2 uv : TEXCOORD) : COLOR
{
float2 pixelSize = (1 / imageWidth, 1 / imageHeight);
return GetEdgeLoop(uv, pixelSize);
}
此方法应返回红色以获得足够强的边缘。 有时确实会返回红色,但显然不会返回边缘。
我还有另一种方法来检测实际可行的边缘,但它会手动采样所需的像素:
float4 GetEdge(float2 coord, float2 pixelSize)
{
float avrg = 0;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y - pixelSize.y)).rgb) * 1;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y)).rgb) * 2;
avrg += grayScale(tex2D(imageSampler, float2(coord.x - pixelSize.x, coord.y + pixelSize.y)).rgb) * 1;
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y - pixelSize.y)).rgb) * (-1);
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y)).rgb) * (-2);
avrg += grayScale(tex2D(imageSampler, float2(coord.x + pixelSize.x, coord.y + pixelSize.y)).rgb) * (-1);
avrg = abs(avrg / 8);
if (avrg > threshold)
return float4(1, 0, 0, 1);
return tex2D(imageSampler, coord);
}
此方法不是很好,我想替换它。
答案 0 :(得分:0)
我不是WPF中着色器的专家,但是在您提供的代码中,您没有为输入指定任何寄存器,因此无法从像素着色器代码访问它。我希望这样的事情成为您代码的一部分:
sampler2D implicitInputSampler : register(S0);
float opacity : register(C0);
(第一个定义是纹理采样器,第二个定义是浮点数寄存器。)
您需要“声明”将传递给着色器函数的对象。 Microsoft的着色器效果编写指南在https://docs.microsoft.com/en-us/dotnet/api/system.windows.media.effects.shadereffect?view=netframework-4.8上有一个很好的例子。这是后代的代码:
// Threshold shader
// Object Declarations
sampler2D implicitInput : register(s0);
float threshold : register(c0);
float4 blankColor : register(c1);
//------------------------------------------------------------------------------------
// Pixel Shader
//------------------------------------------------------------------------------------
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = tex2D(implicitInput, uv);
float intensity = (color.r + color.g + color.b) / 3;
float4 result;
if (intensity > threshold)
{
result = color;
}
else
{
result = blankColor;
}
return result;
}
您可以看到代码声明了一个纹理采样器,一个float和一个float4(具有4个float的向量)作为输入。然后,您可以像这样通过DependencyProperties绑定到它们:
#region Input dependency property
public Brush Input
{
get { return (Brush)GetValue(InputProperty); }
set { SetValue(InputProperty, value); }
}
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ThresholdEffect), 0);
#endregion
///////////////////////////////////////////////////////////////////////
#region Threshold dependency property
public double Threshold
{
get { return (double)GetValue(ThresholdProperty); }
set { SetValue(ThresholdProperty, value); }
}
public static readonly DependencyProperty ThresholdProperty =
DependencyProperty.Register("Threshold", typeof(double), typeof(ThresholdEffect),
new UIPropertyMetadata(0.5, PixelShaderConstantCallback(0)));
#endregion
///////////////////////////////////////////////////////////////////////
#region BlankColor dependency property
public Color BlankColor
{
get { return (Color)GetValue(BlankColorProperty); }
set { SetValue(BlankColorProperty, value); }
}
public static readonly DependencyProperty BlankColorProperty =
DependencyProperty.Register("BlankColor", typeof(Color), typeof(ThresholdEffect),
new UIPropertyMetadata(Colors.Transparent, PixelShaderConstantCallback(1)));
#endregion
请注意,PixelShaderConstantCallback具有一个int参数,可让您指定要将值绑定到的寄存器的索引。
编辑:好的,所以我已经阅读了更多有关此主题的内容,使用了您的代码并使其正常工作。以下是我未按特定顺序进行的更改:
以下是相关代码:
static const float3x3 laplace =
{
-1.0f, -1.0f, -1.0f,
-1.0f, 8.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
};
float grayScaleByLumino(float3 color)
{
return (0.299 * color.r + 0.587 * color.g + 0.114 * color.b);
}
float4 GetEdgeGeorge(float2 coord, float2 pixelSize)
{
float2 current = coord;
float avrg = 0;
float kernelValue;
float4 currentColor;
float grayScale;
float4 result;
current.x = coord.x - pixelSize.x;
for (int x = 0; x < 3; x++)
{
current.y = coord.y - pixelSize.y;
for (int y = 0; y < 3; y++)
{
kernelValue = laplace[x][y];
grayScale = grayScaleByLumino(tex2D(imageSampler, current).rgb);
avrg += grayScale * kernelValue;
current.y += pixelSize.y;
}
current.x += pixelSize.x;
}
avrg = abs(avrg / 8);
if (avrg > threshold)
{
result = float4(1, 0, 0, 1);
}
else
{
result = tex2D(imageSampler, coord);
}
return result;
}
我创建的整个解决方案可在以下位置找到:https://github.com/georgethejournalist/WPFShaders。我希望这会有所帮助。