之前使用C#编写了一个立方体的渲染窗口。效果如下:
粗糙地实现了一个立方体基本漫反射和全局光。让立方体随时间旋转。
本文主要讲一下摄像机部分的代码,和一个编写过程中的小问题。
摄像机类
摄像机属性
private double posX,posY,posZ;//摄像机位置
private double xAngle, yAngle, zAngle;//摄像机旋转角度
private Vector4 xAxle, yAxle, zAxle;//摄像机坐标空间的下x、y、z轴在世界空间中的表示
private double fov;//视野角度
private double aspect;//宽比高
private double near;//近裁距离
private double far;//远裁距离
摄像机类的属性主要包括摄像机位置、摄像机旋转角度、摄像机坐标空间的下x、y、z轴在世界空间中的表示、视野角度、近裁距离和远裁距离。
摄像机类构造器
没啥说的,构造器。
public Camera() { }
public Camera(double x,double y,double z)
{
this.fov = 90;
this.aspect = 1;
this.near = 2;
this.far = 20000;
this.posX = x;
this.posY = y;
this.posZ = z;
this.xAngle = 0;
this.yAngle = 0;
this.zAngle = 0;
this.xAxle = new Vector4(1, 0, 0, 0);
this.yAxle = new Vector4(0, 1, 0, 0);
this.zAxle = new Vector4(0, 0, 1, 0);
}
摄像机类GetWorldtoView方法
调用此方法将返回一个世界空间到摄像机空间的变换矩阵。
public Matrix4x4 GetWorldtoView()
{
Matrix4x4 view = new Matrix4x4();
view[4, 1] = posX;
view[4, 2] = posY;
view[4, 3] = posZ;
view[4, 4] = 1;
view[1, 1] = xAxle.x;
view[2, 1] = xAxle.y;
view[3, 1] = xAxle.z;
view[1, 2] = yAxle.x;
view[2, 2] = yAxle.y;
view[3, 2] = yAxle.z;
view[1, 3] = zAxle.x;
view[2, 3] = zAxle.y;
view[3, 3] = zAxle.z;
return view;
}
摄像机类的GetClip方法
调用此方法返回一个摄像机空间到透视裁减空间的变换矩阵。
public Matrix4x4 GetClip()
{
Matrix4x4 m = new Matrix4x4();
m[1, 1] = Math.Cos(this.fov / 360.0 * Math.PI) / this.aspect;
m[2, 2] = Math.Cos(this.fov / 360.0 * Math.PI);
m[3, 3] = -(this.far + this.near) / (this.far - this.near);
m[4, 3] = -(2 * this.near * this.far) / (this.far - this.near);
m[3, 4] = -1;
return m;
}
裁减空间变换矩阵:
图片来自网络
矩阵将xyz轴分别进行了缩放,使物体呈现近大远小的效果,并且满足-w≤x≤w、-w≤y≤w、-w≤z≤w的顶点才能不被剔除或裁减。
问题
在编写过程中遇到个问题,立方体在旋转过程中不断的闪烁。查阅网络发现是没有开启双缓冲。只要在窗体类中的load方法中增加一下语句即可解决:
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();