3

在WPF中使用PlaneProjection模拟动态3D效果

 3 years ago
source link: https://hypersharp.net/coding/33-wpf-3d-effects-by-plane-projection/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在WPF中使用PlaneProjection模拟动态3D效果

2013年3月12日 / 编程

  虽然在WPF中也集成了3D呈现的功能,在简单的3D应用中,有时候并不需要真实光影的3D场景。毕竟使用3D引擎会消耗很多资源,有时候使用各种变换和假的阴影贴图也能设计出既省资源,又有很好用户体验的“伪”3D界面。

  在Silverlight中,因为性能问题,一般并不使用真3D引擎,微软为Silverlight提供了System.Windows.Media.PlaneProjection 类,用投影变换来模拟3D的效果。

  下面让我们看下一个 Microsoft Expression Blend 4 提供的示例 Wall3D (位于帮助>欢迎屏幕>示例)。

blend-4-wall3d-project-1024x614.jpg

大家不要被这个可以流畅滚动的3D图片墙所迷惑,其实这只是一个ListBox控件。MainPage中给ListBox定义了一个ItemsPanelTemplate,使用新的控件来作为ListBox中Items的布局控件,这个控件就是这个项目最核心的类:CircularPanel3D。

CircularPanel3D类继承自System.Windows.Controls.Panel,它实现了一种新的布局方式,效果大家在上一张图片中都看到了。这种华丽的效果实际上都是由这个最重要的类中的最重要的方法: private void Refresh() 完成的。

private void Refresh()
//几个计数器,看名字就功能很明了
int count = 0;
int col = 0;
int row = 0;
int zLevel = 0;
//开始遍历子元素
foreach (FrameworkElement childElement in this.Children)
//AngleItem是指单个元素的旋转角度,算法是360除以列数
//这个方法的布局方式是先布满一圈,再下一环的,角度总是可以取模的
//所以这边直接AngleItem和count相乘了
//InitialAngle这个属性是用来确定整个圆环的偏转角度的,每次这个依赖属性变化就会重新计算布局(调用这个方法)
double angle = (this.AngleItem * count++) - this.InitialAngle;
//下面两个变量用来确定元素在屏幕上的位置,用到了三角函数,数学不好的请问高中数学老师
double x = this.Radius * Math.Cos(Math.PI * angle / 180);
double z = this.Radius * Math.Sin(Math.PI * angle / 180);
//创建个PlaneProjection对象,并赋值
PlaneProjection projection = new PlaneProjection();
if (projection != null)
projection.CenterOfRotationX = 0.5;
projection.CenterOfRotationY = 0.5;
projection.CenterOfRotationZ = 0.5;
projection.RotationY = angle + 90;
projection.GlobalOffsetX = x;
//Distance实际上就是模拟的镜头距离
projection.GlobalOffsetZ = z - this.Distance;
//-330。。。坑爹的硬编码,实际上就是两行元素的间距,OffsetY是纵向的偏移量,用于调整环在屏幕上的位置
projection.GlobalOffsetY = row * (-330) + this.OffsetY;
//实际上是让double数变成int数,但是又不会丧失区别性,下面要用到
int depth = (int)(z * 100);
double pDist = (this.Distance - 1000) / 2000;
double pZ = ((z + 1000) / 2000) + 0.5;
//让太远的和太近的变透明
double opacity = (pZ - pDist) + 0.4;
if (opacity >= 1)
childElement.Opacity = (2 - opacity);
else if (opacity < 0)
childElement.Opacity = 0;
childElement.Opacity = opacity;
// 嗯这边有原版的英文注释,不解释
// Variable zLevel changes value of ZIndex for each item in the ListBox.
// This way the reflex of elements at the top will be placed behind the item below it.
Canvas.SetZIndex(childElement, depth - (++zLevel * 10));
//根据Align属性设置对齐方式,不是很重要
double alignX = 0;
double alignY = 0;
switch (this.Align)
case AlignmentOptions.Left:
alignX = 0;
alignY = 0;
break;
case AlignmentOptions.Center:
alignX = childElement.DesiredSize.Width / 2;
alignY = childElement.DesiredSize.Height / 2;
break;
case AlignmentOptions.Right:
alignX = childElement.DesiredSize.Width;
alignY = childElement.DesiredSize.Height;
break;
//将PlaneProjection对象赋给子元素的Projection属性
childElement.Projection = projection;
//定位子元素
childElement.Arrange(new Rect(this.Width / 2 - alignX, this.Height / 2 - alignY, childElement.DesiredSize.Width, childElement.DesiredSize.Height));
//换行,又见坑爹的硬编码14。。这个代表有十四列
col++;
if (col > 14)
col = 0;
row++;
private void Refresh()
{
    //几个计数器,看名字就功能很明了
    int count = 0;
    int col = 0;
    int row = 0;
    int zLevel = 0;

    //开始遍历子元素
    foreach (FrameworkElement childElement in this.Children)
    {
        //AngleItem是指单个元素的旋转角度,算法是360除以列数
        //这个方法的布局方式是先布满一圈,再下一环的,角度总是可以取模的
        //所以这边直接AngleItem和count相乘了
        //InitialAngle这个属性是用来确定整个圆环的偏转角度的,每次这个依赖属性变化就会重新计算布局(调用这个方法)
        double angle = (this.AngleItem * count++) - this.InitialAngle;
        //下面两个变量用来确定元素在屏幕上的位置,用到了三角函数,数学不好的请问高中数学老师
        double x = this.Radius * Math.Cos(Math.PI * angle / 180);
        double z = this.Radius * Math.Sin(Math.PI * angle / 180);
        //创建个PlaneProjection对象,并赋值
        PlaneProjection projection = new PlaneProjection();
        if (projection != null)
        {
            projection.CenterOfRotationX = 0.5;
            projection.CenterOfRotationY = 0.5;
            projection.CenterOfRotationZ = 0.5;
            projection.RotationY = angle + 90;
            projection.GlobalOffsetX = x;
            //Distance实际上就是模拟的镜头距离
            projection.GlobalOffsetZ = z - this.Distance;
            //-330。。。坑爹的硬编码,实际上就是两行元素的间距,OffsetY是纵向的偏移量,用于调整环在屏幕上的位置
            projection.GlobalOffsetY = row * (-330) + this.OffsetY;
        }
        //实际上是让double数变成int数,但是又不会丧失区别性,下面要用到
        int depth = (int)(z * 100);

        double pDist = (this.Distance - 1000) / 2000;
        double pZ = ((z + 1000) / 2000) + 0.5;

        //让太远的和太近的变透明
        double opacity = (pZ - pDist) + 0.4;
        if (opacity >= 1)
        {
            childElement.Opacity = (2 - opacity);
        }
        else if (opacity < 0)
        {
            childElement.Opacity = 0;
        }
        else
        {
            childElement.Opacity = opacity;
        }

        // 嗯这边有原版的英文注释,不解释
        // Variable zLevel changes value of ZIndex for each item in the ListBox.
        // This way the reflex of elements at the top will be placed behind the item below it.
        Canvas.SetZIndex(childElement, depth - (++zLevel * 10));

        //根据Align属性设置对齐方式,不是很重要
        double alignX = 0;
        double alignY = 0;
        switch (this.Align)
        {
            case AlignmentOptions.Left:
                alignX = 0;
                alignY = 0;
                break;
            case AlignmentOptions.Center:
                alignX = childElement.DesiredSize.Width / 2;
                alignY = childElement.DesiredSize.Height / 2;
                break;
            case AlignmentOptions.Right:
                alignX = childElement.DesiredSize.Width;
                alignY = childElement.DesiredSize.Height;
                break;
        }
        //将PlaneProjection对象赋给子元素的Projection属性
        childElement.Projection = projection;
        //定位子元素
        childElement.Arrange(new Rect(this.Width / 2 - alignX, this.Height / 2 - alignY, childElement.DesiredSize.Width, childElement.DesiredSize.Height));

        //换行,又见坑爹的硬编码14。。这个代表有十四列
        col++;
        if (col > 14)
        {
            col = 0;
            row++;
        }
    }
}




Recommend

  • 9
    • www.cnblogs.com 4 years ago
    • Cache

    给WPF程序增加玻璃效果

    给WPF程序增加玻璃效果 在Vista中增加了一种Aero新界面,也就是我们所说的玻璃效果,这种效果比较...

  • 23

    WPF 下拉框选项做鼠标 Hover 预览效果本文来告诉大家如何在 WPF 中,在 下拉框 ComboBox 里面,鼠标移动到 ComboBoxItem 上时,自动触发对应的事件,用来预览此选项值。例如我在实现一个颜色下拉框,此时我可以通过点击下拉框显示多个不同的颜色,鼠标移动...

  • 8

    WPF将窗口置于桌面下方(可用于动态桌面) 先来看一下效果:

  • 8

    WPF 模拟 WPFMediaKit 的 D3D 配置用来测试4k性能 本文告诉大家我在测试 WPFMediaKit 的 D3D 配置性能影响在 4k 分辨率设备下采用高清摄像头的性能 测试效果是 10 代 i3 带 4G 内存和集显 UHD 630 在 4k 下,跑满 36 Hz 不卡。以下...

  • 9
    • www.cnblogs.com 3 years ago
    • Cache

    [WPF] 用 Effect 实现线条光影效果

    1. 前言# 几个月前 ChokCoco 大佬发布了一篇文章: CSS 奇技淫巧 | 妙用 drop-shadow 实现线条光...

  • 3
    • www.cnblogs.com 2 years ago
    • Cache

    在 WPF 中实现融合效果 - dino.c

    1. 融合效果# 融合效果是指对两个接近的元素进行高斯模糊后再提高对比度,使它们看上去“粘”在一起。在之前的

  • 5

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10889 鑫空间-鑫生活 本文欢迎分享与聚合,全文转载...

  • 4
    • www.cnblogs.com 1 year ago
    • Cache

    WPF实现跳动的字符效果 - czwy

    本文将介绍一个好玩但实际作用可能不太大的动画效果:跳动的字符。为了提高动画效果的可重用性以及调用的灵活性,通过Behavior实现跳动的字符动画。先看下效果:

  • 11

    【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】         很多时候,我们并不希望所有的程序都放到一个exe里面。因为这样相当于把所有的风险都放在了一个文件里里面,既不利于程序的升级,也不利于程序的模块化切割和复...

  • 6

    C# WPF上位机开发(动画效果) ...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK