一.先看看实现效果图

        (左边的2d图片如何运动出右边3d的效果)

                                    

引言:

    对于这个题目,真的很尴尬,不知道取啥,就想了这个题目,涵盖范围很广,很抽象,算是通用知识点吧。想要了解下面几个问题的,可以看看。   

     ①2D图形如何运动出3D空间的效果。

     ②3D物体如何渲染成2D图形到屏幕上。

     ③Unity中模型到世界,世界到相机,相机到屏幕的关系。

     ④如何通过矩阵进行各种风骚(旋转,缩放,平移,投影等)的变换操作。

二.应用知识

    ①向量

    ②矩阵,矩阵变换规则

    ③透视投影

三.实现

      问题:图形不断变换,通过简化,图形的本质是由顶点组合而成,因此,可以简化为顶点不断变换。不断意思大概就是每隔一段时间变换,我们这里

再简化,可以简化为变换一次。即问题可以不断简化如图

      

      对于“顶点变换“,顶点,即是向量,根据矩阵的相关知识,我们可以了解到,变换矩阵可以使向量得到指定的变换。因此就是“顶点通过矩阵变换”。

最后,问题的本质即为顶点和矩阵的之间的交互即可。

1)顶点

      顶点,当然是拥有x,y,z三个分量,根据矩阵变换规则,我们想要使用矩阵对向量进行变换,需要多一个维度,且第四个分量为1。至于具体原因这里不加详述,

详情可见:https://blog.csdn.net/zl_gsyy/article/details/73278742。

      即,需要设定一个拥有四维向量Vector4.cs        

 class Vector4
{
public double a, b, c, d; public Vector4()
{ } public Vector4(double a,double b,double c, double d)
{
this.a = a;
this.b = b;
this.c = c;
this.d = d;
} public Vector4(Vector4 v)
{
this.a = v.a;
this.b = v.b;
this.c = v.c;
this.d = v.d;
}
}

2)矩阵

      因为我们需要变换的是4维向量(顶点,可以看做1X4的矩阵),所以再根据矩阵变换规则,我们需要设定矩阵维4x4的。

        定义 Matrix4x4.cs     

 class Matrix4x4
{
double[,] ts; public Matrix4x4()
{
ts = new double[, ];
} public Matrix4x4(Matrix4x4 m)
{
for(int i=;i<;i++)
{
for(int j=;j<;j++)
{
ts[i, j] = m[i, j];
}
}
} public double this[int x,int y]
{
get{
return ts[x, y];
} set
{
ts[x, y] = value;
}
} /// <summary>
/// 用这个矩阵变换一个四维向量,返回另外一个向量
/// </summary>
/// <param name="v"></param>
/// <returns></returns>
public Vector4 Mul(Vector4 v)
{
Vector4 vNew = new Vector4();
vNew.a = ts[, ] * v.a + ts[, ] * v.b + ts[, ] * v.c + ts[, ] * v.d;
vNew.b = ts[, ] * v.a + ts[, ] * v.b + ts[, ] * v.c + ts[, ] * v.d;
vNew.c = ts[, ] * v.a + ts[, ] * v.b + ts[, ] * v.c + ts[, ] * v.d;
vNew.d = ts[, ] * v.a + ts[, ] * v.b + ts[, ] * v.c + ts[, ] * v.d;
return vNew;
}
}

         根据矩阵变换规则,如果需要变换多次,多次变换可以通过相乘合并为一个矩阵,比如某顶点需要先平移,再缩放,可以平移*缩放=合并矩阵。

所以需要在Matrix4x4.cs 中 增加一个矩阵*矩阵得到另外一个矩阵 方法。        

  public Matrix4x4 Mul(Matrix4x4 m)
{
Matrix4x4 newM = new Matrix4x4();
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
for (int k = ; k < ; k++)
{
newM[i, j] += this[i, k] * m[k, j];
}
}
}
return newM;
}

3)模拟变换过程

   因为我们这边直接使用点来变换显示,不够直观,可以使用由3个顶点组成的三角形,来模拟测试效果。所以,这里再定义一个三角形类Triangles.cs.

 直接使用矩阵对该Triangles进行变换,即是对三个顶点进行变换。 

 class Triangles
{
public Vector4 A, B, C;
private Vector4 a, b, c; //临时变量使用 public Triangles(Vector4 a,Vector4 b,Vector4 c)
{
A =this.a= new Vector4(a);
B =this.b= new Vector4(b);
C =this.c= new Vector4(c);
} /// <summary>
/// 变换过后原始顶点保留(变换针对原始数据)
/// </summary>
/// <param name="m"></param>
public void Transform(Matrix4x4 m)
{
this.a = m.Mul(this.A);
this.b= m.Mul(this.B);
this.c = m.Mul(this.C);
} #region 系统内置方法(这边了解一下就行)
/// <summary>
/// 应用windows窗体应用程序内部方法,根据三个顶点画出三角形
/// </summary>
/// <param name="e"></param>
public void Draw(System.Drawing.Graphics e)
{
e.TranslateTransform(, );//不然会太靠左上角。Windowform的内部功能
e.DrawLines(new Pen(Color.Red, 2f), Get2DPointFArr());
} PointF[] Get2DPointFArr()
{
PointF[] points = new PointF[];
points[] = Get2DPointF(this.a);
points[] = Get2DPointF(this.b);
points[] = Get2DPointF(this.c);
points[] = Get2DPointF(this.a);
//我们这边需要画第四个与第一个顶点一样,系统需求这样做
return points;
} PointF Get2DPointF(Vector4 v)
{
PointF point = new PointF();
point.X = (float)(v.a / v.d);
point.Y = (float)(v.b / v.d);
return point;
}
#endregion
}  

   通过以上,我们可以实现通过变换矩阵(平移,缩放,旋转)来实现简单变换了。当然通过某种变换的时候首先需要明白对应的哪个矩阵。参考https://www.cnblogs.com/u3ddjw/p/10282186.html

======================================================正式进入正题=====================================================================      

4)如何实现3D空间变换效果

        ①首先,我们先声明一个三角形,并且给出其三个顶点的坐标。

         Triangles triangles;
//注意这边屏幕左上角为(0,0)坐标原点
Vector4 v1 = new Vector4(, -0.5, , );
Vector4 v2 = new Vector4(0.5f, 0.5, , );
Vector4 v3 = new Vector4(-0.5f, 0.5f, , );
triangles = new Triangles(v1, v2, v3);

    因为我们这里给出坐标位置是比例坐标(模型坐标),这种大小当然很小,与屏幕(1320,1080)差距很大,所以我们需要把它放大道合适的大小。假设就放大250倍刚好是我们需要的大小。就需要缩放矩阵了,通过https://www.cnblogs.com/u3ddjw/p/10282186.html 查找到缩放矩阵 

故,我声明scale =250,     

       //模型到世界
m_scale = new Matrix4x4();
m_scale[, ] = scale; //scale =250,这个数值可以任意调节,你可以试试改变这个大小,加强得到缩放的效果的记忆。
m_scale[, ] = scale;
m_scale[, ] = scale;
m_scale[, ] = ;
triangles.Transform(m_scale);

最终一张正常的图出现:

          ②为了模拟出直观的3D效果,再来实现把这个三角形进行旋转

          参考上面方法找到旋转矩阵表达式,为了观察直观,我们每隔一小段时间增加2个角度并显示出来。我们增加个Timer(就是可以实现

间隔时间调用方法的工具类,系统一般都会自带,比如unity中update)      

     float a = ;

        /// <summary>
/// 每隔0.02s调用一次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
a++;
double angle = a / * Math.PI;
m_rotation[, ] = Math.Cos(angle);
m_rotation[, ] = Math.Sin(angle);
m_rotation[, ] = ;
m_rotation[, ] = - * Math.Sin(angle);
m_rotation[, ] = Math.Cos(angle);
m_rotation[, ] = ;
//因为矩阵变换规则得知多次变换可以先将变换矩阵相乘 缩放矩阵*旋转矩阵
var newTriangle = m_scale.Mul(m_rotation); triangles.Transform(newTriangle);
this.Invalidate(); //重绘,必须写上
}

得到结果:

       

     上图总是变宽变宰,下面底部线条没有感觉在3D空间中变换。这不是真正意义3D变换, 仅仅是模型到世界。

      ③透视投影变换

      

      假设右边的圈就是我们需要拍摄的摄像机空间的点,还需要进行透视投影变换到屏幕。故,我们还需要世界到摄像机,

要让他从世界到摄像机, 所以我们还需要架设一个摄像机去拍摄他,假设这个模型在z轴为0的位置,摄像机假设到z为负的位置,对于模型而言

在摄像机空间上实际上做了位置平移。所以我们需要一个可以转换为摄像机空间的矩阵(平移矩阵)。

    同上找到平移矩阵,并初始化一个平移矩阵,平移多少位置?还是先假设250(这边可以自行修改数据调节看看效果)。

故在初始化函数中,   

        //View  世界到相机
m_view = new Matrix4x4();
m_view[, ] = ;
m_view[, ] = ;
m_view[, ] = ;
m_view[, ] = * scale;
m_view[, ] = ;

  目前效果上还是没有变换的,虽然平移了摄像机,但是我们没有真正的做投影变换。关于投影矩阵,还是参考https://www.cnblogs.com/u3ddjw/p/10282186.html,

找到投影矩阵。继续在初始化函数中并实现       

         //Projection 相机到屏幕
m_projection = new Matrix4x4();
m_projection[, ] = ;
m_projection[, ] = ;
m_projection[, ] = ;
m_projection[, ] = 1.0f / scale;

  在timer.cs 中的 timer1_Tick 每隔一段时间函数中补充为

/// <summary>
/// 每隔0.02s调用一次
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer1_Tick(object sender, EventArgs e)
{
a+=;
double angle = a / * Math.PI;
m_rotation[, ] = Math.Cos(angle);
m_rotation[, ] = Math.Sin(angle);
m_rotation[, ] = ;
m_rotation[, ] = - * Math.Sin(angle);
m_rotation[, ] = Math.Cos(angle);
m_rotation[, ] = ;
//因为矩阵变换规则得知多次变换可以先将变换矩阵相乘 缩放矩阵*旋转矩阵
var newTriangle = m_scale.Mul(m_rotation);
newTriangle = newTriangle.Mul(m_view);
newTriangle = newTriangle.Mul(m_projection);
triangles.Transform(newTriangle);
this.Invalidate(); //重绘,必须写上
}

这样,最终我们看到了第一张图的效果图,完全的3D空间的效果。现在是具有透视变换的三角形在屏幕上不停的旋转,绕着y轴。  

四.拓展

    ①可以尝试改变各个变换矩阵中的值,比如平移矩阵(世界到相机)的值,来看看效果。

  private void trackBar1_Scroll(object sender, EventArgs e)
{
m_view[, ] = (sender as TrackBar).Value;
} private void trackBar2_Scroll(object sender, EventArgs e)
{
double value = (sender as TrackBar).Value;
m_projection[, ] = 1.0f / value;
}

②不用三角形,自己尝试五角星,四边形之类的变换,效果各种拉风,但是本质还是都是顶点通过矩阵的变换

     ③实际应用,由上述可见Unity中最常见的MVP变换:模型到世界,世界到相机,相机到屏幕 的变换。本质上也分别是缩放+旋转+平移,缩放+旋转+平移,投影 矩阵的变换。

    

2D图形如何运动模拟出3D效果的更多相关文章

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

    原文:在WPF中使用PlaneProjection模拟动态3D效果 虽然在WPF中也集成了3D呈现的功能,在简单的3D应用中,有时候并不需要真实光影的3D场景.毕竟使用3D引擎会消耗很多资源,有时候使 ...

  2. 使用div模拟出frameset效果

    <!doctype html> <html xmlns="http://www.w3.org/1999/xhtml" > <head> < ...

  3. css3 3D效果

    css3 3D变形 transfrom初学 这个礼拜学了css3 3d,感觉到css无穷的魅力,可以通过几个特定的代码符号创建出3D效果的页面. ___ 透视 一个元素需要一个透视点才能激活3D空间, ...

  4. Canvas实现3D效果-可旋转的立方体

    摘要:Canvas画布是一个二维平面,如何展示出3D效果?通过将三维空间中的Z轴抽取出来,将图像的点投影到与Z轴垂直的平面上,在通过旋转等变换效果,我们就能实现3D效果. 一.建立坐标系 1)立方体坐 ...

  5. Shadertoy 教程 Part 5 - 运用SDF绘制出更多的2D图形

    Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been au ...

  6. transition过渡2D、3D效果

    过渡(transition)是CSS3中具有颠覆性的特征之一,我们可以在不使用 Flash 动画或 JavaScript 的情况下,当元素从一种样式变换为另一种样式时为元素添加效果. 帧动画:通过一帧 ...

  7. wpf 模拟3D效果(和手机浏览图片效果相似)(附源码)

    原文 wpf 模拟3D效果(和手机浏览图片效果相似)(附源码) pf的3D是一个很有意思的东西,类似于ps的效果,类似于电影动画的效果,因为动画的效果,(对于3D基础的摄像机,光源,之类不介绍,对于依 ...

  8. 灵感闪现 篇 (一) 2d场景 3d 效果

    中途打断一下 ,框架文档的 更新. 另开一篇主题为 灵感闪现的 板块. 在工作生活中,总有发现新事物或新东西 而让自己突然 灵感闪现的时候,那么这个时候,我必须要抓住,并尽快把 这份灵感实现下来. 之 ...

  9. CSS3新特性2D、3D效果讲解

    希望这篇博客可以对你有所帮助,如果有什么技术上的问题,希望我们可以做进一步的交流,如果你觉得我哪里阐述的不正确或者你有更好的更透彻的理解,也可以联系我,我在这里随时等着你. 对于css/html是每个 ...

随机推荐

  1. uwsgi 服务 invalid request block size: 4161 (max 4096)...skip问题的解决

    问题报错: invalid request block size: 4161 (max 4096)...skip 问题原因:默认的uwsgi分配一个小的buffer(4k)来接收每个请求的头信息,如果 ...

  2. git添加本地的项目到git远程管理仓库

    目标:将本地存在的项目添加到git远程仓库管理 步骤: 1. 需要一个git远程仓库管理地址 例如:https://github.com/xingfupeng/test.git git@github. ...

  3. erlang进程概述

    一.概述 与大多数的进程相反,Erlang中的并发很廉价,派生出一个进程就跟面向对象的语言中分配一个对象的开销差不多. 在启动一个复杂的运算时,启动运算.派生进程以及返回结果后,所有进程神奇的烟消云散 ...

  4. 用calc()绘制手机图案解锁的九宫格样式

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. go语言时间比较

    local, _ := time.LoadLocation("Local") starttime, _ := time.ParseInLocation("2006-01- ...

  6. ES 在聚合结果中进行过滤

    ES查询中,先聚合,在聚合结果中进行过滤 { "size": 0, "aggs": { "terms": { "terms&quo ...

  7. Mina框架(实战详解)

    Apache Mina Server 是一个网络通信应用框架,为开发高性能和高可用性的网络应用程序提供了非常便利的框架. 特点:异步的NIO框架,将UDP当成"面向连接"的协议 一 ...

  8. 如何用plugman编辑和添加cordova插件

    1.安装工具 进入nodejs, 安装工具plugman,管理插件,输入命令npm install -g plugman 等待下载安装 2.使用plugman命令生成插件框架 cmd 进入用于生成插件 ...

  9. Qt中绘制五子棋棋盘

    一个需要做大作业的同学问我相关内容,就顺手写了一个,贴出来. 项目包含头文件 mainwindowh,源文件mainwindow.cpp和主函数main.cpp. 如下: mainwindow.h # ...

  10. CSS的浮动和定位

    一.CSS中的浮动 1.定义和用法 float 属性定义元素在哪个方向浮动.在 CSS 中,任何元素都可以浮动.浮动会使原元素变成一个行级元素,而不论它本身是何种元素.如果浮动非替换元素,则要指定一个 ...