
Hi, Habr!
We all have heard many times the phrase that 24 frames per second is the maximum possible value that human vision is capable of perceiving. Therefore, if you speed up the video sequence by just one extra frame, you can embed any information into the viewer's subconscious. And we all, of course, know that this is
not true . Just like the photodiodes of a matrix of digital cameras, retinal neurons do not fix the instantaneous illumination at a given point, but the total luminous flux for a certain short time interval, as a result of which fast-moving objects seem “blurry” to us. Moreover, our brain has become accustomed to this particular view, so the video, made up of individual photographs of the object, seems unnatural to us. The same goes for computer animation. Artists-animators have long learned to draw blurry trains behind their characters - this technique is called “Motion blur” and is available in all modern packages of 2d and 3d animations. But how to be ordinary desktop programmers? In this article I will try to talk about how I screwed Motion Blur to a WPF application to give a responsive effect when pop-up windows appear.
To begin with, I suggest to look at two pictures. The number of frames in them is the same.
Plain animation | Motion blur |
| |
If you don’t see the difference, you can take it that I spent a few evenings wasted. But I want to believe that the difference is still noticeable :)
And this is how it looks in frame-by-frame scanning:
The attentive reader probably noticed that each frame from the bottom row consists of fifteen translucent images superimposed on each other, therefore, with a slight stretch, we can say that we increased the FPS 15 times. Joke.
Look under the hood?
')
Pixel shaders
Implementing a non-slowing motion blur effect is hardly possible even on the most powerful modern CPUs, so the GPU plays a key role in drawing the blurred “trace” in my example. Thanks to pixel shaders in WPF, we can apply various effects to visual elements, including shadows, distortion effects (magnifying glass, twisting, rippling), changing color balance, blurring, etc.
If it seems to you that shaders are something terrible and complex, I completely agree with you. Until recently, I was sure that I would never come across them in my life unless I went to game-dev. But it turned out that they can also be useful in application programming. At the same time, it is not necessary to know specialized languages ​​for writing shaders, such as
GLSL ,
HLSL ,
etc. On the Internet, there are already many ready-made examples of shaders, one of which I used. It is called ZoomBlurEffect and is included in the demo shaders free editor “Shazzam Shader Editor”. Here is his code:
Even without knowing the HLSL language on which this shader is written, one can easily understand the algorithm of its operation: for each point of the final image, the average color value of 15 points located on a line between the point and the center of the blur, stored in the C0 register, is calculated. The distance of averaged points from this point is controlled by the BlurAmount parameter stored in register C1. In our example, the blur comes from the center of the image, so C0 is (0.5; 0.5), and the value of the BlurAmount parameter depends on how much the current frame differs from the previous one, but more on that later.
Of course, in this form, the shader cannot be used - it needs to be compiled using the fxc.exe utility included with the DirectX SDK. The result of compiling a pixel shader is a file with the ".ps" extension, which can be used in our WPF application. To do this, add it to our project as a resource and create the ZoomBlurEffect class:
(Actually, the Shazzam Shader Editor itself can generate similar wrapper classes for shaders, which I used.)Animation when a window appears
Any visual element has a property RenderTransform, which is used by the graphics subsystem to transform an element during its drawing. These transformations include scaling, rotation and tilt. In our example, we will change the scale of the window content from zero (the content is “minimized” to a point) to one (the content is stretched to the whole window). The window itself at the same time has a transparent background, and its chrome (frame with title) is disabled.
For animation in WPF, the so-called “smoothness functions” are traditionally used. We can use predefined functions or write our own, inheriting from the class
EasingFunctionBase .
In the example from this article, I used the ElasticEase function, which gives the window the effect of a “released spring” - at first it drastically expands to sizes slightly larger than those set, and then gradually decreases.
Pseudo-code for animating a window without a motion blur effect double t = 0.0; int = ; while (t < 1.0) { (ElasticEase(t)); t = ( - ) / ; } (1.0);
Here t varies from 0 to 1, where 0 is the moment of the beginning of the animation, and 1 is the moment of its end. The value of the ElasticEase (t) function changes approximately according to the following law:
Add motion-blur to our animation. To do this, use the Effect property of the child window control:
content.Effect = new ZoomBlurEffect { Center = new Point(0.5, 0.5) };
Pseudocode animation with motion blur effect double t = 0.0; double prevEase = 0.0; int = ; (new ZoomBlurEffect { Center = new Point(0.5, 0.5) }); while (t < 1.0) { var ease = ElasticEase(t); (ease); content.Effect.BlurAmount = ease - prevEase; prevEase = ease; t = ( - ) / ; } (1.0); (null);
This code differs from the previous one in that at each step we change the value of BlurAmount depending on how much the current value of the ElasticEase function differs from the value in the previous step. At the beginning of the animation, the function grows quickly, BlurAmount is quite important, therefore the window “blurring” is big. In the end - BlurAmount is almost zero, and therefore the "blurring" is almost not noticeable.
About disadvantages
Unfortunately, the use of the Motion Blur effect in WPF applications causes some problems. Here are some of them:
- Performance. As practice has shown, even the GPU is not omnipotent. According to the test results, adding a zoom effect to the window slows down rendering by about 1.5-2 times (on my video card). However, since visually it seems that FPS has increased significantly, it does not seem to me a big problem.
- It is not clear why this is needed at all :) I conducted a survey among friends, whether they see the difference between behavior with and without effect. And they all said at first that there was no difference. After specifying what exactly to look at, most expressed the wow-effect, but the other part said that without effect it is much better and sharper. Their opinion also needs to be taken into account.
Conclusion
Unfortunately, I never brought the idea of ​​using the effect of Motion Blur to the state of production-code, since it is hardly applicable in those applications that I have to deal with. He did, so to speak, for the soul. I hope that this material will be useful to someone in his work.
Download the demo project from here:
github.com/misupov/motion-blur