Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart DropShadowEffect

Tags:

wpf

Is it possible to have DropShadowEffect to ignore certain colors when rendering shadow? To have sort of masked (color selective) shadow?

My problem is what shadow can be assigned to whole visual element (graph). It looks like this:

And I want

Notice grid lines without shadow (except 0,0 ones). This can be achieved by having 2 synchronized in zooming/offset graphs, one without shadow effect containing grid and another with shadow containing the rest. But I am not very happy about this solution (I predict lots of problems in the future with that solution). So I'd rather prefer to modify DropShadowEffect somehow.

I can create and use ShaderEffect, but I have no knowledge of how to program shaders to have actual shadow effect (if it can be produced by shaders at all).

Perhaps there is much easier way of doing something with DropShadowEffect itself? Anyone?

Edit

I tried to make shader effect:

sampler2D _input : register(s0);
float _width : register(C0);
float _height : register(C1);
float _depth : register(C2); // shadow depth

float4 main(float2 uv : TEXCOORD) : COLOR
{
    // get pixel size
    float2 pixel = {1 / _width, 1 / _height};

    // find color at offset
    float2 offset = float2(uv.x - pixel.x * _depth, uv.y - pixel.y * _depth);
    float4 color = tex2D(_input, offset);

    // convert to gray?
    //float gray = dot(color, float4(0.1, 0.1, 0.1, 0));
    //color = float4(gray, gray, gray, 1);

    // saturate?
    //color = saturate(color);

    return tex2D(_input, uv) + color;
}

But fail at everything.

Edit

Here is screenshot of graph appearance, which I like (to those, who try to convince me not to do this):

Currently it is achieved by having special Graph which has template

<Border x:Name="PART_Border" BorderThickness="1" BorderBrush="Gray" CornerRadius="4" Background="White">
    <Grid>
        <Image x:Name="PART_ImageBack" Stretch="None"/>
        <Image x:Name="PART_ImageFront" Stretch="None">
            <Image.Effect>
                <DropShadowEffect Opacity="0.3"/>
            </Image.Effect>
        </Image>
    </Grid>
</Border>

Everything is rendered onto PART_ImageFront (with shadow), while grid is rendered onto PART_ImageBack (without shadow). Performance-wise it is still good.

like image 984
Sinatr Avatar asked Oct 20 '22 00:10

Sinatr


1 Answers

I have zero experience with pixel shaders, but here's my quick and dirty attempt at a shadow effect that ignores "uncolored" pixels:

sampler2D _input : register(s0);
float _width : register(C0);
float _height : register(C1);
float _depth : register(C2);
float _opacity : register(C3);

float3 rgb_to_hsv(float3 RGB) {
    float r = RGB.x;
    float g = RGB.y;
    float b = RGB.z;

    float minChannel = min(r, min(g, b));
    float maxChannel = max(r, max(g, b));

    float h = 0;
    float s = 0;
    float v = maxChannel;

    float delta = maxChannel - minChannel;

    if (delta != 0) {
        s = delta / v;

        if (r == v) h = (g - b) / delta;
        else if (g == v) h = 2 + (b - r) / delta;
        else if (b == v) h = 4 + (r - g) / delta;
    }

    return float3(h, s, v);
}

float4 main(float2 uv : TEXCOORD) : COLOR {
    float width = _width; // 512;
    float height = _height; // 512;
    float depth = _depth; // 3;
    float opacity = _opacity; // 0.25;

    float2 pixel = { 1 / width, 1 / height };

    float2 offset = float2(uv.x - pixel.x * depth, uv.y - pixel.y * depth);
    float4 srcColor = tex2D(_input, offset);

    float3 srcHsv = rgb_to_hsv(srcColor);
    float4 dstColor = tex2D(_input, uv);

    // add shadow for colored pixels only
    // tweak saturation threshold as necessary
    if (srcHsv.y >= 0.1) {
        float gray = dot(srcColor, float4(0.1, 0.1, 0.1, 0.0));
        float4 multiplier = float4(gray, gray, gray, opacity * srcColor.a);
        return dstColor + (float4(0.1, 0.1, 0.1, 1.0) * multiplier);
    } 

    return dstColor;
}

Here it is in action against a (totally legit) chart that I drew in Blend with the pencil tool:

Chart

The shader effect is applied on the root panel containing the axes, grid lines, and series lines, and it generates a shadow only for the series lines.

I don't think it's realistic to expect a shader to be able to apply a shadow to the axes and labels while ignoring the grid lines; the antialiasing on the text is bound to intersect the color/saturation range of the grid lines. I think applying the shadow to just the series lines is cleaner and more aesthetically pleasing anyway.

like image 53
Mike Strobel Avatar answered Feb 26 '23 17:02

Mike Strobel