Smart DropShadowEffect

Can DropShadowEffect ignore certain colors when rendering a shadow? To have a hidden (color) shadow?

My problem is that the shadow can be assigned to the entire visual element (graph). It looks like this:

I want too

Pay attention to the grid lines without a shadow (except 0,0 ). This can be achieved with 2 synchronized scaling / displacement graphs, one with no shadow effect containing a grid and the other with a shadow containing the rest. But I am not very happy with this solution (I predict many problems in the future with this solution). Therefore, I would rather change DropShadowEffect .

I can create and use ShaderEffect , but I don’t know how to program shaders to have a real shadow effect (if it can be created by shaders at all).

Perhaps there is a much simpler way to do something with DropShadowEffect itself? Is anyone

Edit

I tried to make a 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 not all.

Edit

Here is a screenshot of the appearance of the graph that I like (for those who are trying to convince me not to do this):

Currently, this is achieved using a special Graph that has a 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 displayed on PART_ImageFront (with shadow), and the grid is displayed on PART_ImageBack (without shadow). In terms of performance, it's still good.

+5
source share
2 answers

I have no experience with pixel shaders, but here is my quick and dirty attempt at a shadow effect that ignores "unpainted" 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's the action against the (fully legitimate) diagram I drew in Blend with a pencil tool:

Chart

The shader effect is applied to the root panel, which contains axes, grid lines, and rows of lines, and generates a shadow only for consecutive lines.

I do not find it realistic to expect the shader to be able to apply shadow to the axes and labels, ignoring the grid lines; text smoothing is associated with the intersection of the color range / saturation of the grid lines. I think applying shadow only to series of lines is cleaner and more aesthetically pleasing anyway.

+2
source
 sampler2D input : register(s0); float4 main(float2 uv : TEXCOORD) : COLOR { float4 Color; Color = tex2D( input , uv.xy); return Color; } 

This is the basic "do nothing" shader. the line with the call to text2D takes the color that will usually be displayed at the current location. (And in this case, just returns it)

Instead of fetching uv.xy, you can add an offset vector to uv.xy and return that color. This will shift the entire image in the direction of the displacement vector.

You can combine these two:

  • uv.xy example, if set to a visible color chart that will contain color (this will keep all lines visible in the right place)
  • if it is transparent, swipe a little in the upper left. if it is set to the color you want in the shadow: return the color of the shadow.

Step 2. can be changed to: if it is set to a color that you do not want to have in shadow, return the transparent color.

The offset and colors to check and use as the color of the shadow can be effect parameters.

I highly recommend playing with Shazzam , this will let you test your shader and it will generate you C # code.

Please note that the uv coordinates are not in pixels, but are scaled to 0.0 to 1.0.

Adding

Poor human blurring (smoothing) can be obtained by sampling more pixels around the offset and calculating the average number of colors found that should cause the shadow. this will make more pixels get shadow.

To calculate the color of the shadow, you can simply darken the existing color by multiplying it by a factor between 0.0 (black) and 1.0 (original color)

Using the average of the blur, you can multiply the shadow color again so that the blur blends with the original color.

More accurate (and expensive) would be to translate the rgb values ​​into hls values ​​and use the “official” dimming formulas to determine the color of the shadow.

+1
source

Source: https://habr.com/ru/post/1204102/


All Articles