CSharpGL(4)设计和使用Camera

CSharpGL(4)设计和使用Camera

+BIT祝威+悄悄在此留下版了个权的信息说:

主要内容

描述在OpenGL中Camera的概念和用处。

设计一个Camera以及操控Camera的SatelliteRotator。

以PyramidElement为例演示如何使用Camera和SatelliteRotator。

+BIT祝威+悄悄在此留下版了个权的信息说:

下载

您可以在(https://github.com/bitzhuwei/CSharpGL)找到最新的源码。欢迎感兴趣的同学fork之。

+BIT祝威+悄悄在此留下版了个权的信息说:

Camera

在OpenGL中的Camera概念可以方便我们设定projection矩阵和view矩阵。

定义

  1     /// <summary>
  2     /// 摄像机。
  3     /// </summary>
  4     public class Camera :
  5         ICamera,
  6         IPerspectiveViewCamera, IOrthoViewCamera,
  7         IViewCamera, IPerspectiveCamera, IOrthoCamera
  8     {
  9         /// <summary>
 10         /// 默认目标为vec3(0, 0, 0)
 11         /// </summary>
 12         public static readonly vec3 defaultTarget = new vec3(0, 0, 0);
 13 
 14         /// <summary>
 15         /// 默认位置为vec3(0, 0, 1)
 16         /// </summary>
 17         public static readonly vec3 defaultPosition = new vec3(0, 0, 1);
 18 
 19         /// <summary>
 20         /// 默认上方为vec3(0, 1, 0)
 21         /// </summary>
 22         public static readonly vec3 defaultUpVector = new vec3(0, 1, 0);
 23 
 24         internal Camera() { }
 25 
 26         /// <summary>
 27         /// 摄像机。
 28         /// </summary>
 29         /// <param name="cameraType">类型</param>
 30         /// <param name="width">OpenGL窗口的宽度</param>
 31         /// <param name="height">OpenGL窗口的高度</param>
 32         public Camera(CameraType cameraType, double width, double height)
 33         {
 34             this.lastHeight = width;
 35             this.lastHeight = height;
 36 
 37             IPerspectiveCamera perspectiveCamera = this;
 38             perspectiveCamera.FieldOfView = 60.0f;
 39             perspectiveCamera.AspectRatio = width / height;
 40             perspectiveCamera.Near = 0.01;
 41             perspectiveCamera.Far = 10000;
 42 
 43             const int factor = 100;
 44             IOrthoCamera orthoCamera = this;
 45             orthoCamera.Left = -width / 2 / factor;
 46             orthoCamera.Right = width / 2 / factor;
 47             orthoCamera.Bottom = -height / 2 / factor;
 48             orthoCamera.Top = height / 2 / factor;
 49             orthoCamera.Near = -10000;
 50             orthoCamera.Far = 10000;
 51 
 52             this.Target = defaultTarget;
 53             this.Position = defaultPosition;
 54             this.UpVector = defaultUpVector;
 55 
 56             this.CameraType = cameraType;
 57         }
 58 
 59         public void Resize(double width, double height)
 60         {
 61             double aspectRatio = width / height;
 62 
 63             IPerspectiveCamera perspectiveCamera = this;
 64             perspectiveCamera.AspectRatio = aspectRatio;
 65 
 66             IOrthoCamera orthoCamera = this;
 67 
 68             double lastAspectRatio = this.lastWidth / this.lastHeight;
 69             if (aspectRatio > lastAspectRatio)
 70             {
 71                 double top = orthoCamera.Top;
 72                 double newRight = top * aspectRatio;
 73                 orthoCamera.Left = -newRight;
 74                 orthoCamera.Right = newRight;
 75             }
 76             else if (aspectRatio < lastAspectRatio)
 77             {
 78                 double right = orthoCamera.Right;
 79                 double newTop = right / aspectRatio;
 80                 orthoCamera.Bottom = -newTop;
 81                 orthoCamera.Top = newTop;
 82             }
 83 
 84             //const int factor = 100;
 85             //if (width / 2 / factor != orthoCamera.Right)
 86             //{
 87             //    orthoCamera.Left = -width / 2 / factor;
 88             //    orthoCamera.Right = width / 2 / factor;
 89             //}
 90             //if (height / 2 / factor != orthoCamera.Top)
 91             //{
 92             //    orthoCamera.Bottom = -height / 2 / factor;
 93             //    orthoCamera.Top = height / 2 / factor;
 94             //}
 95         }
 96 
 97         double lastWidth;
 98         double lastHeight;
 99 
100         /// <summary>
101         /// Gets or sets the target.
102         /// </summary>
103         /// <value>
104         /// The target.
105         /// </value>
106         [Description("The target of the camera (the point it's looking at)"), Category("Camera")]
107         public vec3 Target { get; set; }
108 
109         /// <summary>
110         /// Gets or sets up vector.
111         /// </summary>
112         /// <value>
113         /// Up vector.
114         /// </value>
115         [Description("The up direction, relative to camera. (Controls tilt)."), Category("Camera")]
116         public vec3 UpVector { get; set; }
117 
118         /// <summary>
119         /// The camera position.
120         /// </summary>
121         private vec3 position = new vec3(0, 0, 0);
122 
123         /// <summary>
124         /// Every time a camera is used to project, the projection matrix calculated
125         /// and stored here.
126         /// </summary>
127         private mat4 projectionMatrix = mat4.identity();
128 
129         ///// <summary>
130         ///// The screen aspect ratio.
131         ///// </summary>
132         //private double aspectRatio = 1.0f;
133 
134         /// <summary>
135         /// Gets or sets the position.
136         /// </summary>
137         /// <value>
138         /// The position.
139         /// </value>
140         [Description("The position of the camera"), Category("Camera")]
141         public vec3 Position
142         {
143             get { return position; }
144             set { position = value; }
145         }
146 
147         /// <summary>
148         /// camera's perspective type.
149         /// </summary>
150         public CameraType CameraType { get; set; }
151 
152         #region IPerspectiveCamera 成员
153 
154         double IPerspectiveCamera.FieldOfView { get; set; }
155 
156         double IPerspectiveCamera.AspectRatio { get; set; }
157 
158         double IPerspectiveCamera.Near { get; set; }
159 
160         double IPerspectiveCamera.Far { get; set; }
161 
162         #endregion
163 
164         #region IOrthoCamera 成员
165 
166         double IOrthoCamera.Left { get; set; }
167 
168         double IOrthoCamera.Right { get; set; }
169 
170         double IOrthoCamera.Bottom { get; set; }
171 
172         double IOrthoCamera.Top { get; set; }
173 
174         double IOrthoCamera.Near { get; set; }
175 
176         double IOrthoCamera.Far { get; set; }
177 
178         #endregion
179     }
Camera
+BIT祝威+悄悄在此留下版了个权的信息说:

通过Camera获取矩阵

根据上面的Camera的定义,很容易给出其对应的projection矩阵和view矩阵。

 1     public static class Camera2Matrix
 2     {
 3 
 4         /// <summary>
 5         /// 根据摄像机的类型获取其投影矩阵
 6         /// </summary>
 7         /// <param name="camera"></param>
 8         /// <returns></returns>
 9         public static mat4 GetProjectionMat4(this ICamera camera)
10         {
11             mat4 result;
12 
13             switch (camera.CameraType)
14             {
15                 case CameraType.Perspecitive:
16                     result = ((IPerspectiveCamera)camera).GetProjectionMat4();
17                     break;
18                 case CameraType.Ortho:
19                     result = ((IOrthoCamera)camera).GetProjectionMat4();
20                     break;
21                 default:
22                     throw new NotImplementedException();
23             }
24 
25             return result;
26         }
27 
28         /// <summary>
29         /// Extension method for <see cref="IPerspectiveCamera"/> to get projection matrix.
30         /// </summary>
31         /// <param name="camera"></param>
32         /// <returns></returns>
33         public static mat4 GetProjectionMat4(this IPerspectiveCamera camera)
34         {
35             mat4 perspective = glm.perspective(
36                 (float)(camera.FieldOfView * Math.PI / 180.0f),
37                 (float)camera.AspectRatio, (float)camera.Near, (float)camera.Far);
38             return perspective;
39         }
40 
41         /// <summary>
42         /// Extension method for <see cref="IOrthoCamera"/> to get projection matrix.
43         /// </summary>
44         /// <param name="camera"></param>
45         /// <returns></returns>
46         public static mat4 GetProjectionMat4(this IOrthoCamera camera)
47         {
48             mat4 ortho = glm.ortho(
49                 (float)camera.Left, (float)camera.Right,
50                 (float)camera.Bottom, (float)camera.Top,
51                 (float)camera.Near, (float)camera.Far);
52             return ortho;
53         }
54 
55         /// <summary>
56         /// Extension method for <see cref="IViewCamera"/> to get view matrix.
57         /// </summary>
58         /// <param name="camera"></param>
59         /// <returns></returns>
60         public static mat4 GetViewMat4(this IViewCamera camera)
61         {
62             mat4 lookAt = glm.lookAt(camera.Position, camera.Target, camera.UpVector);
63             return lookAt;
64         }
65     }

 

+BIT祝威+悄悄在此留下版了个权的信息说:

缩放Camera

 1     public static class MouseWheelHelper
 2     {
 3         /// <summary>
 4         /// 对摄像机执行一次缩放操作
 5         /// </summary>
 6         /// <param name="camera"></param>
 7         /// <param name="delta"></param>
 8         public static void MouseWheel(this ICamera camera, int delta)
 9         {
10             //if (camera.CameraType == CameraTypes.Perspecitive)
11             {
12                 var target2Position = camera.Position - camera.Target;
13                 if (target2Position.Magnitude() < 0.01)
14                 {
15                     target2Position.Normalize();
16                     target2Position.x *= 0.01f;
17                     target2Position.y *= 0.01f;
18                     target2Position.z *= 0.01f;
19                 }
20                 var scaledTarget2Position = target2Position * (1 - delta * 0.001f);
21                 camera.Position = camera.Target + scaledTarget2Position;
22                 double lengthDiff = scaledTarget2Position.Magnitude() - target2Position.Magnitude();
23                 // Increase ortho camera's Near/Far property in case the camera's position changes too much.
24                 IPerspectiveCamera perspectiveCamera = camera;
25                 perspectiveCamera.Far += lengthDiff;
26                 //perspectiveCamera.Near += lengthDiff;
27                 IOrthoCamera orthoCamera = camera;
28                 orthoCamera.Far += lengthDiff;
29                 orthoCamera.Near += lengthDiff;
30             }
31             //else if (camera.CameraType == CameraTypes.Ortho)
32             {
33                 IOrthoCamera orthoCamera = camera;
34                 double distanceX = orthoCamera.Right - orthoCamera.Left;
35                 double distanceY = orthoCamera.Top - orthoCamera.Bottom;
36                 double centerX = (orthoCamera.Left + orthoCamera.Right) / 2;
37                 double centerY = (orthoCamera.Bottom + orthoCamera.Top) / 2;
38                 orthoCamera.Left = centerX - distanceX * (1 - delta * 0.001) / 2;
39                 orthoCamera.Right = centerX + distanceX * (1 - delta * 0.001) / 2;
40                 orthoCamera.Bottom = centerY - distanceY * (1 - delta * 0.001) / 2;
41                 orthoCamera.Top = centerX + distanceY * (1 - delta * 0.001) / 2;
42             }
43         }
44 
45     }
+BIT祝威+悄悄在此留下版了个权的信息说:

Rotator

有了Camera,我们就想通过改变Camera的位置、朝向来实现在场景中进行移动的功能。SatelliteRotator可以根据鼠标操作使Camera围绕其Target在给定的球面上移动,就像卫星围绕恒星运动一样。

  1     /// <summary>
  2     /// Rotates a camera on a sphere, whose center is camera's Target.
  3     /// <para>Just like a satellite moves around a fixed star.</para>
  4     /// </summary>
  5     public class SatelliteRotator : ICameraRotator
  6     {
  7         private Point downPosition = new Point();
  8         private Size bound = new Size();
  9         public bool mouseDownFlag = false;
 10         private float horizontalRotationFactor = 4;
 11         private float verticalRotationFactor = 4;
 12         private vec3 up;
 13         private vec3 back;
 14         private vec3 right;
 15 
 16         /// <summary>
 17         /// Rotates a camera on a sphere, whose center is camera's Target.
 18         /// <para>Just like a satellite moves around a fixed star.</para>
 19         /// </summary>
 20         /// <param name="camera"></param>
 21         public SatelliteRotator(ICamera camera = null)
 22         {
 23             this.Camera = camera;
 24         }
 25 
 26 
 27         public override string ToString()
 28         {
 29             return string.Format("back:{0}|{3:0.00},up:{1}|{4:0.00},right:{2}|{5:0.00}",
 30                 back, up, right, back.Magnitude(), up.Magnitude(), right.Magnitude());
 31             //return base.ToString();
 32         }
 33 
 34 
 35         private ICamera originalCamera;
 36 
 37         public ICamera Camera { get; set; }
 38 
 39         public void MouseUp(int x, int y)
 40         {
 41             this.mouseDownFlag = false;
 42         }
 43 
 44         public void MouseMove(int x, int y)
 45         {
 46             if (this.mouseDownFlag)
 47             {
 48                 IViewCamera camera = this.Camera;
 49                 if (camera == null) { return; }
 50 
 51                 vec3 back = this.back;
 52                 vec3 right = this.right;
 53                 vec3 up = this.up;
 54                 Size bound = this.bound;
 55                 Point downPosition = this.downPosition;
 56                 {
 57                     float deltaX = -horizontalRotationFactor * (x - downPosition.X) / bound.Width;
 58                     float cos = (float)Math.Cos(deltaX);
 59                     float sin = (float)Math.Sin(deltaX);
 60                     vec3 newBack = new vec3(
 61                         back.x * cos + right.x * sin,
 62                         back.y * cos + right.y * sin,
 63                         back.z * cos + right.z * sin);
 64                     back = newBack;
 65                     right = up.cross(back);
 66                     back.Normalize();
 67                     right.Normalize();
 68                 }
 69                 {
 70                     float deltaY = verticalRotationFactor * (y - downPosition.Y) / bound.Height;
 71                     float cos = (float)Math.Cos(deltaY);
 72                     float sin = (float)Math.Sin(deltaY);
 73                     vec3 newBack = new vec3(
 74                         back.x * cos + up.x * sin,
 75                         back.y * cos + up.y * sin,
 76                         back.z * cos + up.z * sin);
 77                     back = newBack;
 78                     up = back.cross(right);
 79                     back.Normalize();
 80                     up.Normalize();
 81                 }
 82 
 83                 camera.Position = camera.Target +
 84                     back * (float)((camera.Position - camera.Target).Magnitude());
 85                 camera.UpVector = up;
 86                 this.back = back;
 87                 this.right = right;
 88                 this.up = up;
 89                 this.downPosition.X = x;
 90                 this.downPosition.Y = y;
 91             }
 92         }
 93 
 94         public void SetBounds(int width, int height)
 95         {
 96             this.bound.Width = width;
 97             this.bound.Height = height;
 98         }
 99 
100         public void MouseDown(int x, int y)
101         {
102             this.downPosition.X = x;
103             this.downPosition.Y = y;
104             this.mouseDownFlag = true;
105             PrepareCamera();
106         }
107 
108         private void PrepareCamera()
109         {
110             var camera = this.Camera;
111             if (camera != null)
112             {
113                 vec3 back = camera.Position - camera.Target;
114                 vec3 right = Camera.UpVector.cross(back);
115                 vec3 up = back.cross(right);
116                 back.Normalize();
117                 right.Normalize();
118                 up.Normalize();
119 
120                 this.back = back;
121                 this.right = right;
122                 this.up = up;
123 
124                 if (this.originalCamera == null)
125                 { this.originalCamera = new Camera(); }
126                 this.originalCamera.Position = camera.Position;
127                 this.originalCamera.UpVector = camera.UpVector;
128             }
129         }
130 
131         public void Reset()
132         {
133             IViewCamera camera = this.Camera;
134             if (camera == null) { return; }
135             IViewCamera originalCamera = this.originalCamera;
136             if (originalCamera == null) { return; }
137 
138             camera.Position = originalCamera.Position;
139             camera.UpVector = originalCamera.UpVector;
140         }
141     }
SatelliteRotator
+BIT祝威+悄悄在此留下版了个权的信息说:

总结

除了本文的SatelliteRotator,还有一种称为轨迹球(AraBall)的移动Camera的方式。这里暂时就不实现了。

 

posted @ 2015-09-10 22:18  BIT祝威  阅读(1505)  评论(0编辑  收藏  举报