准备三维开发环境
新建C#窗体应用程序
- 新建项目,版本.NET Framework 4.5.2
- MenuStrip,添加标准项
- SplitContainer
添加OpenTK(单机方式不联网)
- 右键单击“引用”,“添加引用”
- 找到本地 OpenTK.dll和OpenTK.GLControl.dll(3.1.0版本)文件,选中,确定
在窗体里添加三维显示控件
- 工具箱,右键单击空白处,选择项,浏览,找到OpenTK.GLControl.dll并选中
- 将GLControl控件拖入Form窗体
- 控件属性,Anchor,上下左右都选
添加名称空间
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using OpenTK.Platform;
第一个OpenTK程序
初始化OpenGL环境
- 添加函数InitialGL()和SetupViewport()
private void InitialGL()
{
GL.ShadeModel(ShadingModel.Smooth);
GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL.ClearDepth(1.0f);
GL.Enable(EnableCap.DepthTest);
SetupViewport();
}
private void SetupViewport()
{
int w = glControl1.ClientSize.Width;
int h = glControl1.ClientSize.Height;
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Ortho(-1, 1, -1, 1, -1, 1);
GL.Viewport(0, 0, w, h);
}
- 在Form1_Load函数中调用InitialGL()
编写绘制函数并调用
private void Drawtriangle()
{
GL.Begin(PrimitiveType.Triangles);
GL.Color4(Color4.Yellow);
GL.Vertex3(0, 0, 0);
GL.Color4(Color4.Red);
GL.Vertex3(0.9, 0, 0);
GL.Color4(Color4.Green);
GL.Vertex3(0.9, 0.9, 0);
GL.End();
}
private void Render()
{
glControl1.MakeCurrent();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
Drawtriangle();
glControl1.SwapBuffers();
}
- Form1窗体添加Paint事件
- Form1_Paint函数中调用Render
- 添加类成员变量,确保OpenGL已经初始化
bool bOpenGLInitial = false;
bOpenGLInitial = true;
- Form窗体添加Resize事件
- Form1_Resize()中,添加语句
if (bOpenGLInitial)
{
SetupViewport();
Invalidate();
}
OpenGL基础
投影
- 添加like_gluPerspective(double fovy, double aspect, double near, double far)函数
public void like_gluPerspective(double fovy, double aspect, double near, double far)
{
const double DEG2RAD = 3.14159265 / 180.0;
double tangent = Math.Tan(fovy / 2 * DEG2RAD);
double height = near * tangent;
double width = height * aspect;
GL.Frustum(-width, width, -height, height, near, far);
}
- 创建类成员变量fov和perspective_projection,会在后续的扩展功能中使用
float fov = (float)Math.PI / 3.0f;
bool perspective_projection = false;
- 修改SetupViewport()函数,选择投影方式
private void SetupViewport()
{
int w = glControl1.ClientSize.Width;
int h = glControl1.ClientSize.Height;
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
double aspect;
if(perspective_projection)
{
aspect = w / (double)h;
like_gluPerspective(fov, aspect, 0.001, 10);
GL.Viewport(0, 0, w, h);
}
else
{
aspect = (w >= h) ? (1.0 * w / h) : (1.0 * h / w);
if (w <= h)
GL.Ortho(-1, 1, -aspect, aspect, -1, 1);
else
GL.Ortho(-aspect, aspect, -1, 1, -1, 1);
GL.Viewport(0, 0, w, h);
}
}
平移与旋转
- 创建类成员变量transX,transY,angleX,angleY并赋初值为零
double transX = 0;
double transY = 0;
double angleX = 0;
double angleY = 0;
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Translate(transX, transY, 0);
GL.Rotate(angleY, 1, 0, 0);
GL.Rotate(angleX, 0, 1, 0);
}
- 增添类成员变量记录鼠标状态
- glControl1_MouseDown中添加代码
if (e.Button == MouseButtons.Left)
{
bLeftButtonPushed = true;
leftButtonPosition = e.Location;
}
else if (e.Button == MouseButtons.Right)
{
bRightButtonPushed = true;
RightButtonPosition = e.Location;
}
bLeftButtonPushed = false;
bRightButtonPushed = false;
- glControl1_MouseMove中添加代码
if (bLeftButtonPushed)
{
transX += (e.Location.X - leftButtonPosition.X) / 120.0;
transY += -(e.Location.Y - leftButtonPosition.Y) / 120.0;
leftButtonPosition = e.Location;
Invalidate();
}
if (bRightButtonPushed)
{
angleX += (e.Location.X - RightButtonPosition.X) / 10.0;
angleY += -(e.Location.Y - RightButtonPosition.Y) / 10.0;
RightButtonPosition = e.Location;
Invalidate();
}
缩放
float scaling = 1.0f;
- Render()中调用GL.Scale(scaling, scaling, scaling)
private void Render()
{
glControl1.MakeCurrent();
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Projection);
GL.LoadIdentity();
GL.Translate(transX, transY, 0);
GL.Rotate(angleY, 1, 0, 0);
GL.Rotate(angleX, 0, 1, 0);
GL.Scale(scaling, scaling, scaling);
Drawtriangle();
glControl1.SwapBuffers();
}
- 添加函数glControl1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
private void glControl1_MouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Delta > 0)
{
scaling += 0.1f;
}
else if (e.Delta < 0)
{
scaling -= 0.1f;
}
SetupViewport();
Invalidate();
}
glControl1.MouseWheel += new MouseEventHandler(glControl1_MouseWheel);
绘制一个球体
private void DrawSphere()
{
const double radius = 0.5;
const int step = 5;
int xWidth = 360 / step + 1;
int zHeight = 180 / step + 1;
int halfZHeight = (zHeight - 1) / 2;
int v = 0;
double xx, yy, zz;
GL.Begin(PrimitiveType.Points);
GL.Color4(Color4.Yellow);
for (int z = -halfZHeight; z <= halfZHeight; z++)
{
var d = 0;
for (int x = 0; x < xWidth; x++)
{
xx = radius * Math.Cos(x * step * Math.PI / 180)
* Math.Cos(z * step * Math.PI / 180.0);
zz = radius * Math.Sin(x * step * Math.PI / 180)
* Math.Cos(z * step * Math.PI / 180.0);
yy = radius * Math.Sin(z * step * Math.PI / 180);
GL.Vertex3(xx, yy, zz);
}
}
GL.End();
}
准备las点云文件导入环境(laszip)
添加类库(联网)
- 工具,NuGet包管理器,管理解决方案的NuGet程序包
- 搜索laszip
- 安装Unofficial.laszip.net(2.2.0版本)
- 添加名称空间laszip.net
using laszip.net;
读取las文件代码
- Form1窗体中文件菜单的子菜单“打开”,添加子菜单“las点云文件”
- 跳转到las点云文件ToolStripMenuItem_Click(object sender, EventArgs e)函数,函数内添加代码
OpenFileDialog pOpenFileDialog = new OpenFileDialog();
pOpenFileDialog.Title = "打开las点云文件";
pOpenFileDialog.Filter = "las文件(*.las)|*.las";
pOpenFileDialog.CheckFileExists = true;
if (pOpenFileDialog.ShowDialog() == DialogResult.OK)
{
points = ReadLas(pOpenFileDialog.FileName);
Invalidate();
}
List<Vector3d> points;
- 创建ReadLas(string fileName)函数
private List<Vector3d> ReadLas(string fileName)
{
var lazReader = new laszip_dll();
var compressed = true;
lazReader.laszip_open_reader(fileName, ref compressed);
var numberOfPoints = lazReader.header.number_of_point_records;
double minx = lazReader.header.min_x;
double miny = lazReader.header.min_y;
double minz = lazReader.header.min_z;
double maxx = lazReader.header.max_x;
double maxy = lazReader.header.max_y;
double maxz = lazReader.header.max_z;
double centx = (minx + maxx) / 2;
double centy = (miny + maxy) / 2;
double centz = (minz + maxz) / 2;
double scale = Math.Max(Math.Max(maxx - minx, maxy - miny), (maxz - minz));
int classification = 0;
var coordArray = new double[3];
Vector3d point = new Vector3d();
List<Vector3d> points = new List<Vector3d>((int)numberOfPoints);
for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
{
lazReader.laszip_read_point();
lazReader.laszip_get_coordinates(coordArray);
point.X = (coordArray[0] - centx) / scale;
point.Y = (coordArray[1] - centy) / scale;
point.Z = (coordArray[2] - centz) / scale;
points.Add(point);
classification = lazReader.point.classification;
}
lazReader.laszip_close_reader();
return points;
}
绘制点云
private void DrawPointCloud()
{
if (points == null)
return;
GL.Begin(PrimitiveType.Points);
foreach(var v in points)
{
GL.Color3(1.0, 1.0, 1.0);
GL.Vertex3(v.X, v.Y, v.Z);
}
GL.End();
}
- Render()中调用DrawPointCloud()
点云显示进阶
点云着色
List<Vector3d> colors;
private void DrawPointClout()
{
if (points == null)
return;
GL.Begin(PrimitiveType.Points);
for (int i = 0; i < points.Count; i++)
{
Vector3d cr = colors[i];
Vector3d v = points[i];
GL.Color3(cr.X, cr.Y, cr.Z);
GL.Vertex3(v.X, v.Y, v.Z);
}
GL.End();
}
private List<Vector3d> ReadLas(string fileName)
{
var lazReader = new laszip_dll();
var compressed = true;
lazReader.laszip_open_reader(fileName, ref compressed);
var numberOfPoints = lazReader.header.number_of_point_records;
double minx = lazReader.header.min_x;
double miny = lazReader.header.min_y;
double minz = lazReader.header.min_z;
double maxx = lazReader.header.max_x;
double maxy = lazReader.header.max_y;
double maxz = lazReader.header.max_z;
double centx = (minx + maxx) / 2;
double centy = (miny + maxy) / 2;
double centz = (minz + maxz) / 2;
double scale = Math.Max(Math.Max(maxx - minx, maxy - miny), (maxz - minz));
int classification = 0;
var coordArray = new double[3];
Vector3d point = new Vector3d();
Vector3d cr = new Vector3d();
List<Vector3d> points = new List<Vector3d>((int)numberOfPoints);
colors = new List<Vector3d>((int)numberOfPoints);
Vector3d cr1 = new Vector3d(1, 0, 0);
Vector3d cr2 = new Vector3d(0, 1, 1);
for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
{
lazReader.laszip_read_point();
lazReader.laszip_get_coordinates(coordArray);
cr.X = (coordArray[2] - minz) / (maxz - minz) * (cr2.X - cr1.X) + cr1.X;
cr.Y = (coordArray[2] - minz) / (maxz - minz) * (cr2.Y - cr1.Y) + cr1.Y;
cr.Z = (coordArray[2] - minz) / (maxz - minz) * (cr2.Z - cr1.Z) + cr1.Z;
colors.Add(cr);
point.X = (coordArray[0] - centx) / scale;
point.Y = (coordArray[1] - centy) / scale;
point.Z = (coordArray[2] - centz) / scale;
points.Add(point);
classification = lazReader.point.classification;
}
lazReader.laszip_close_reader();
return points;
}
private PointCloudOctree ReadLas(string fileName)
{
var lazReader = new laszip_dll();
var compressed = true;
lazReader.laszip_open_reader(fileName, ref compressed);
var numberOfPoints = lazReader.header.number_of_point_records;
double minx = lazReader.header.min_x;
double miny = lazReader.header.min_y;
double minz = lazReader.header.min_z;
double maxx = lazReader.header.max_x;
double maxy = lazReader.header.max_y;
double maxz = lazReader.header.max_z;
double centx = (minx + maxx) / 2;
double centy = (miny + maxy) / 2;
double centz = (minz + maxz) / 2;
double scale = Math.Max(Math.Max(maxx - minx, maxy - miny), (maxz - minz));
int classification = 0;
var coordArray = new double[3];
Vector3d point = new Vector3d();
Vector3d cr = new Vector3d();
List<Vector3d> points = new List<Vector3d>((int)numberOfPoints);
colors = new List<Vector3d>((int)numberOfPoints);
Vector3d cr_red = new Vector3d(1, 0, 0);
Vector3d cr_orange = new Vector3d(1, 0.647, 0);
Vector3d cr_yellow = new Vector3d(1, 1, 0);
Vector3d cr_green = new Vector3d(0, 1, 0);
Vector3d cr_cyan = new Vector3d(0, 0.498, 1);
Vector3d cr_bule = new Vector3d(0, 0, 1);
Vector3d cr_purple = new Vector3d(0.545, 0, 1);
double z_1_6 = 1 * (maxz - minz) / 6 + minz;
double z_2_6 = 2 * (maxz - minz) / 6 + minz;
double z_3_6 = 3 * (maxz - minz) / 6 + minz;
double z_4_6 = 4 * (maxz - minz) / 6 + minz;
double z_5_6 = 5 * (maxz - minz) / 6 + minz;
for (int pointIndex = 0; pointIndex < numberOfPoints; pointIndex++)
{
lazReader.laszip_read_point();
lazReader.laszip_get_coordinates(coordArray);
cr.X = (coordArray[2] - minz) / (maxz - minz) * (cr2.X - cr1.X) + cr1.X;
cr.Y = (coordArray[2] - minz) / (maxz - minz) * (cr2.Y - cr1.Y) + cr1.Y;
cr.Z = (coordArray[2] - minz) / (maxz - minz) * (cr2.Z - cr1.Z) + cr1.Z;
colors.Add(cr);
point.X = (coordArray[0] - centx) / scale;
point.Y = (coordArray[1] - centy) / scale;
point.Z = (coordArray[2] - centz) / scale;
points.Add(point);
classification = lazReader.point.classification;
}
lazReader.laszip_close_reader();
return points;
}
点云管理(八叉树)
- 定义类成员变量pco并删除points和colors(所有点都应该放在八叉树里)
PointCloudOctree pco;
- las点云文件ToolStripMenuItem_Click(object sender, EventArgs e)函数中,ReadLas(pOpenFileDialog.FileName)返回值为pco
pco = ReadLas(pOpenFileDialog.FileName);
- 更改Render()中的绘图函数(八叉树应该能把自己画出来)
if (pco != null)
{
pco.Render();
}
- 项目添加类PointCloudOctree
- PointCloudOctree.cs中创建类PointCloudNode
- PointCloudOctree.cs中创建结构体ColorPoint
struct ColorPoint
{
public Vector3d point;
public Vector3d color;
}
- PointCloudOctree.cs中添加名称空间
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
using OpenTK.Platform;
- 在PointCloudNode中创建类成员变量data,并定义子树
List<ColorPoint> data;
PointCloudNode[] child;
- PointCloudOctree中创建根结点root
PointCloudNode root;
public PointCloudOctree(List<ColorPoint> data,
Vector3d minv, Vector3d maxv)
{
}
- 在Form.cs中把函数需要的输入参数准备好
- 修改ReadLas函数返回类型为八叉树
- ReadLas中定义ColorPoint类型变量color_point,删除colors和cr
ColorPoint color_point = new ColorPoint();
- 将着色和归一化的变量改为color_point,不再需要添加cr,points改为添加color_point
if (coordArray[2] <= z_1_6)
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_orange.X - cr_red.X) + cr_red.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_orange.Y - cr_red.Y) + cr_red.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_orange.Z - cr_red.Z) + cr_red.Z;
}
else if (coordArray[2] <= z_2_6)
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_yellow.X - cr_orange.X) + cr_orange.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_yellow.Y - cr_orange.Y) + cr_orange.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_yellow.Z - cr_orange.Z) + cr_orange.Z;
}
else if(coordArray[2] <= z_3_6)
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_green.X - cr_yellow.X) + cr_yellow.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_green.Y - cr_yellow.Y) + cr_yellow.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_green.Z - cr_yellow.Z) + cr_yellow.Z;
}
else if (coordArray[2] <= z_4_6)
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_cyan.X - cr_green.X) + cr_green.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_cyan.Y - cr_green.Y) + cr_green.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_cyan.Z - cr_green.Z) + cr_green.Z;
}
else if (coordArray[2] <= z_5_6)
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_bule.X - cr_cyan.X) + cr_cyan.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_bule.Y - cr_cyan.Y) + cr_cyan.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_bule.Z - cr_cyan.Z) + cr_cyan.Z;
}
else
{
color_point.color.X = (coordArray[2] - minz) / (maxz - minz) * (cr_purple.X - cr_bule.X) + cr_bule.X;
color_point.color.Y = (coordArray[2] - minz) / (maxz - minz) * (cr_purple.Y - cr_bule.Y) + cr_bule.Y;
color_point.color.Z = (coordArray[2] - minz) / (maxz - minz) * (cr_purple.Z - cr_bule.Z) + cr_bule.Z;
}
color_point.point.X = (coordArray[0] - centx) / scale;
color_point.point.Y = (coordArray[1] - centy) / scale;
color_point.point.Z = (coordArray[2] - centz) / scale;
points.Add(color_point);
List<ColorPoint> points = new List<ColorPoint>((int)numberOfPoints);
Vector3d minv;
minv.X = (minx - centx) / scale;
minv.Y = (miny - centy) / scale;
minv.Z = (minz - centz) / scale;
Vector3d maxv;
maxv.X = (maxx - centx) / scale;
maxv.Y = (maxy - centy) / scale;
maxv.Z = (maxz - centz) / scale;
- ReadLas中创建八叉树p,并将函数返回值改为p
PointCloudOctree p = new PointCloudOctree(points, minv, maxv);
return p;
- ReadLas中删除point
- 删除DrawPointCloud函数
- PointCloudOctree类中创建属性Count和Render函数
public int Count
{
get { return 0; }
}
public void Render()
{
}
public PointCloudNode(List<ColorPoint> data, Vector3d minv, Vector3d maxv)
{
if (data == null)
return;
if (data.Count == 0)
return;
}
if (data == null)
return;
if (data.Count == 0)
return;
root = new PointCloudNode(data, minv, maxv);
- PointCloudNode函数中定义常量控制节点内点的数量
public const int max_point_num = 10000;
- PointCloudNode函数中,如果节点内点的数量小于阈值,不必再分,否则,再次分割节点
if (data.Count < max_point_num)
this.data = data;
else
{
this.data = null;
child = new PointCloudNode[8];
}
List<ColorPoint>[] childData = new List<ColorPoint>[8];
for (int i = 0; i < 8; i++)
childData[i] = new List<ColorPoint>();
Vector3d[] minva = new Vector3d[8];
Vector3d[] maxva = new Vector3d[8];
Vector3d split = (minv + maxv) / 2;
foreach (var v in data)
{
if (v.point.Z > split.Z)
{
if (v.point.Y > split.Y)
{
if (v.point.X > split.X)
{
childData[0].Add(v);
}
else
{
childData[1].Add(v);
}
}
else
{
if (v.point.X > split.X)
{
childData[2].Add(v);
}
else
{
childData[3].Add(v);
}
}
}
else
{
if (v.point.Y > split.Y)
{
if (v.point.X > split.X)
{
childData[4].Add(v);
}
else
{
childData[5].Add(v);
}
}
else
{
if (v.point.X > split.X)
{
childData[6].Add(v);
}
else
{
childData[7].Add(v);
}
}
}
}
minva[0].X = split.X; minva[0].Y = split.Y; minva[0].Z = split.Z;
maxva[0].X = maxv.X; maxva[0].Y = maxv.Y; maxva[0].Z = maxv.Z;
minva[1].X = split.X; minva[1].Y = split.Y; minva[1].Z = split.Z;
maxva[1].X = split.X; maxva[1].Y = maxv.Y; maxva[1].Z = maxv.Z;
minva[2].X = split.X; minva[2].Y = minv.Y; minva[2].Z = split.Z;
maxva[2].X = maxv.X; maxva[2].Y = split.Y; maxva[2].Z = maxv.Z;
minva[3].X = minv.X; minva[3].Y = minv.Y; minva[3].Z = split.Z;
maxva[3].X = split.X; maxva[3].Y = split.Y; maxva[3].Z = maxv.Z;
minva[4].X = split.X; minva[4].Y = split.Y; minva[4].Z = minv.Z;
maxva[4].X = maxv.X; maxva[4].Y = maxv.Y; maxva[4].Z = split.Z;
minva[5].X = minv.X; minva[5].Y = split.Y; minva[5].Z = minv.Z;
maxva[5].X = split.X; maxva[5].Y = maxv.Y; maxva[5].Z = split.Z;
minva[6].X = split.X; minva[6].Y = minv.Y; minva[6].Z = minv.Z;
maxva[6].X = maxv.X; maxva[6].Y = split.Y; maxva[6].Z = split.Z;
minva[7].X = minv.X; minva[7].Y = minv.Y; minva[7].Z = minv.Z;
maxva[7].X = split.X; maxva[7].Y = split.Y; maxva[7].Z = split.Z;
for (int i = 0; i < 8; i++)
{
if (childData[i].Count <= 0)
continue;
child[i] = new PointCloudNode(childData[i], minva[i], maxva[i]);
}
- PointCloudNode类中,定义Render函数
public void Render()
{
if (data != null)
{
GL.Begin(PrimitiveType.Points);
foreach (var v in data)
{
GL.Color3(v.color.X, v.color.Y, v.color.Z);
GL.Vertex3(v.point.X, v.point.Y, v.point.Z);
}
GL.End();
}
if (child != null)
{
for (int i = 0; i < 8; i++)
{
if (child[i] != null)
{
child[i].Render();
}
}
}
}
- 完善PointCloudOctree类中Render函数
if (root != null)
root.Render();
显示加速
显示列表
int iShowListNum;
if (data.Count < max_point_num)
{
this.data = data;
iShowListNum = GL.GenLists(1);
GL.NewList(iShowListNum, ListMode.Compile);
GL.Begin(PrimitiveType.Points);
for (int i = 0; i < data.Count; i++)
{
ColorPoint v = data[i];
GL.Color3(v.color.X, v.color.Y, v.color.Z);
GL.Vertex3(v.point.X, v.point.Y, v.point.Z);
}
GL.End();
GL.EndList();
}
if (data != null)
{
GL.CallList(iShowListNum);
}
视景体切割
double[,] mFrustum = new double[6, 4];
- 添加函数CalculateFrustum和NormalizePlane
public void CalculateFrustum()
{
Matrix4 projectionMatrix = new Matrix4();
GL.GetFloat(GetPName.ProjectionMatrix, out projectionMatrix);
Matrix4 modelViewMatrix = new Matrix4();
GL.GetFloat(GetPName.ModelviewMatrix, out modelViewMatrix);
float[] _clipMatrix = new float[16];
const int RIGHT = 0, LEFT = 1, BOTTOM = 2, TOP = 3, BACK = 4, FRONT = 5;
_clipMatrix[0] = (modelViewMatrix.M11 * projectionMatrix.M11)
+ (modelViewMatrix.M12 * projectionMatrix.M21) + (modelViewMatrix.M13 * projectionMatrix.M31)
+ (modelViewMatrix.M14 * projectionMatrix.M41);
_clipMatrix[1] = (modelViewMatrix.M11 * projectionMatrix.M12)
+ (modelViewMatrix.M12 * projectionMatrix.M22) + (modelViewMatrix.M13 * projectionMatrix.M32)
+ (modelViewMatrix.M14 * projectionMatrix.M42);
_clipMatrix[2] = (modelViewMatrix.M11 * projectionMatrix.M13)
+ (modelViewMatrix.M12 * projectionMatrix.M23) + (modelViewMatrix.M13 * projectionMatrix.M33)
+ (modelViewMatrix.M14 * projectionMatrix.M43);
_clipMatrix[3] = (modelViewMatrix.M11 * projectionMatrix.M14)
+ (modelViewMatrix.M12 * projectionMatrix.M24) + (modelViewMatrix.M13 * projectionMatrix.M34)
+ (modelViewMatrix.M14 * projectionMatrix.M44);
_clipMatrix[4] = (modelViewMatrix.M21 * projectionMatrix.M11)
+ (modelViewMatrix.M22 * projectionMatrix.M21) + (modelViewMatrix.M23 * projectionMatrix.M31)
+ (modelViewMatrix.M24 * projectionMatrix.M41);
_clipMatrix[5] = (modelViewMatrix.M21 * projectionMatrix.M12)
+ (modelViewMatrix.M22 * projectionMatrix.M22) + (modelViewMatrix.M23 * projectionMatrix.M32)
+ (modelViewMatrix.M24 * projectionMatrix.M42);
_clipMatrix[6] = (modelViewMatrix.M21 * projectionMatrix.M13)
+ (modelViewMatrix.M22 * projectionMatrix.M23) + (modelViewMatrix.M23 * projectionMatrix.M33)
+ (modelViewMatrix.M24 * projectionMatrix.M43);
_clipMatrix[7] = (modelViewMatrix.M21 * projectionMatrix.M14)
+ (modelViewMatrix.M22 * projectionMatrix.M24) + (modelViewMatrix.M23 * projectionMatrix.M34)
+ (modelViewMatrix.M24 * projectionMatrix.M44);
_clipMatrix[8] = (modelViewMatrix.M31 * projectionMatrix.M11)
+ (modelViewMatrix.M32 * projectionMatrix.M21) + (modelViewMatrix.M33 * projectionMatrix.M31)
+ (modelViewMatrix.M34 * projectionMatrix.M41);
_clipMatrix[9] = (modelViewMatrix.M31 * projectionMatrix.M12)
+ (modelViewMatrix.M32 * projectionMatrix.M22) + (modelViewMatrix.M33 * projectionMatrix.M32)
+ (modelViewMatrix.M34 * projectionMatrix.M42);
_clipMatrix[10] = (modelViewMatrix.M31 * projectionMatrix.M13)
+ (modelViewMatrix.M32 * projectionMatrix.M23) + (modelViewMatrix.M33 * projectionMatrix.M33)
+ (modelViewMatrix.M34 * projectionMatrix.M43);
_clipMatrix[11] = (modelViewMatrix.M31 * projectionMatrix.M14)
+ (modelViewMatrix.M32 * projectionMatrix.M24) + (modelViewMatrix.M33 * projectionMatrix.M34)
+ (modelViewMatrix.M34 * projectionMatrix.M44);
_clipMatrix[12] = (modelViewMatrix.M41 * projectionMatrix.M11)
+ (modelViewMatrix.M42 * projectionMatrix.M21) + (modelViewMatrix.M43 * projectionMatrix.M31)
+ (modelViewMatrix.M44 * projectionMatrix.M41);
_clipMatrix[13] = (modelViewMatrix.M41 * projectionMatrix.M12)
+ (modelViewMatrix.M42 * projectionMatrix.M22) + (modelViewMatrix.M43 * projectionMatrix.M32)
+ (modelViewMatrix.M44 * projectionMatrix.M42);
_clipMatrix[14] = (modelViewMatrix.M41 * projectionMatrix.M13)
+ (modelViewMatrix.M42 * projectionMatrix.M23) + (modelViewMatrix.M43 * projectionMatrix.M33)
+ (modelViewMatrix.M44 * projectionMatrix.M43);
_clipMatrix[15] = (modelViewMatrix.M41 * projectionMatrix.M14)
+ (modelViewMatrix.M42 * projectionMatrix.M24) + (modelViewMatrix.M43 * projectionMatrix.M34)
+ (modelViewMatrix.M44 * projectionMatrix.M44);
mFrustum[RIGHT, 0] = _clipMatrix[3] - _clipMatrix[0];
mFrustum[RIGHT, 1] = _clipMatrix[7] - _clipMatrix[4];
mFrustum[RIGHT, 2] = _clipMatrix[11] - _clipMatrix[8];
mFrustum[RIGHT, 3] = _clipMatrix[15] - _clipMatrix[12];
NormalizePlane(mFrustum, RIGHT);
mFrustum[LEFT, 0] = _clipMatrix[3] + _clipMatrix[0];
mFrustum[LEFT, 1] = _clipMatrix[7] + _clipMatrix[4];
mFrustum[LEFT, 2] = _clipMatrix[11] + _clipMatrix[8];
mFrustum[LEFT, 3] = _clipMatrix[15] + _clipMatrix[12];
NormalizePlane(mFrustum, LEFT);
mFrustum[BOTTOM, 0] = _clipMatrix[3] + _clipMatrix[1];
mFrustum[BOTTOM, 1] = _clipMatrix[7] + _clipMatrix[5];
mFrustum[BOTTOM, 2] = _clipMatrix[11] + _clipMatrix[9];
mFrustum[BOTTOM, 3] = _clipMatrix[15] + _clipMatrix[13];
NormalizePlane(mFrustum, BOTTOM);
mFrustum[TOP, 0] = _clipMatrix[3] - _clipMatrix[1];
mFrustum[TOP, 1] = _clipMatrix[7] - _clipMatrix[5];
mFrustum[TOP, 2] = _clipMatrix[11] - _clipMatrix[9];
mFrustum[TOP, 3] = _clipMatrix[15] - _clipMatrix[13];
NormalizePlane(mFrustum, TOP);
mFrustum[BACK, 0] = _clipMatrix[3] - _clipMatrix[2];
mFrustum[BACK, 1] = _clipMatrix[7] - _clipMatrix[6];
mFrustum[BACK, 2] = _clipMatrix[11] - _clipMatrix[10];
mFrustum[BACK, 3] = _clipMatrix[15] - _clipMatrix[14];
NormalizePlane(mFrustum, BACK);
mFrustum[FRONT, 0] = _clipMatrix[3] + _clipMatrix[2];
mFrustum[FRONT, 1] = _clipMatrix[7] + _clipMatrix[6];
mFrustum[FRONT, 2] = _clipMatrix[11] + _clipMatrix[10];
mFrustum[FRONT, 3] = _clipMatrix[15] + _clipMatrix[14];
NormalizePlane(mFrustum, FRONT);
}
private void NormalizePlane(double[,] frustum, int side)
{
double magnitude = Math.Sqrt((frustum[side, 0] * frustum[side, 0]) +
(frustum[side, 1] * frustum[side, 1]) + (frustum[side, 2] * frustum[side, 2]));
frustum[side, 0] /= magnitude;
frustum[side, 1] /= magnitude;
frustum[side, 2] /= magnitude;
frustum[side, 3] /= magnitude;
}
- Form1的Render函数中调用CalculateFrustum
- PointCloudNode中添加类成员变量
Vector3d min_coordinate, max_coordinate;
bool OutlineInFrustum;
if (data.Count < max_point_num)
min_coordinate = minv;
max_coordinate = maxv;
OutlineInFrustum = VoxelWithinFrustum(frustum, min_coordinate.X, min_coordinate.Y, min_coordinate.Z,
max_coordinate.X, max_coordinate.Y, max_coordinate.Z);
if (!OutlineInFrustum)
return;
bool VoxelWithinFrustum(double[,] ftum, double minx, double miny, double minz,
double maxx, double maxy, double maxz)
{
double x1 = minx, y1 = miny, z1 = minz;
double x2 = maxx, y2 = maxy, z2 = maxz;
for (int i = 0; i < 6; i++)
{
if ((ftum[i, 0] * x1 + ftum[i, 1] * y1 + ftum[i, 2] * z1 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x2 + ftum[i, 1] * y1 + ftum[i, 2] * z1 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x1 + ftum[i, 1] * y2 + ftum[i, 2] * z1 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x2 + ftum[i, 1] * y2 + ftum[i, 2] * z1 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x1 + ftum[i, 1] * y1 + ftum[i, 2] * z2 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x2 + ftum[i, 1] * y1 + ftum[i, 2] * z2 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x1 + ftum[i, 1] * y2 + ftum[i, 2] * z2 + ftum[i, 3] <= 0.0F) &&
(ftum[i, 0] * x2 + ftum[i, 1] * y2 + ftum[i, 2] * z2 + ftum[i, 3] <= 0.0F))
{
return false;
}
}
return true;
}
- 修改窗体Render函数,增加mFrustum参数
if (pco != null)
{
pco.Render(mFrustum);
}
- 修改PointCloudOctree的Render函数
public void Render(double[,] frustum)
{
if (root != null)
root.Render(frustum);
}
- 修改PointCloudNode函数,增加double[,] frustum参数
public void Render(double[,] frustum)
child[i].Render(frustum);
鼠标选点
private void glControl1_MouseDoubleClick(object sender, System.Windows.Forms.MouseEventArgs e)
{
Point ptClicked = e.Location;
Vector3d winxyz;
winxyz.X = ptClicked.X;
winxyz.Y = ptClicked.Y;
winxyz.Z = 0.0f;
Vector3d nearPoint = new Vector3d(0, 0, 0);
UnProject(winxyz, ref nearPoint);
winxyz.Z = 1.0f;
Vector3d farPoint = new Vector3d(0, 0, 0);
UnProject(winxyz, ref farPoint);
Vector3d line;
line = farPoint - nearPoint;
Point3DExt close_point = new Point3DExt();
close_point.flag = 10000;
if (pco != null)
{
pco.FindClosestPoint(mFrustum, nearPoint, farPoint, ref close_point);
MessageBox.Show("选中点坐标为:\nX坐标:" + close_point.point.X
+ "\nY坐标:" + close_point.point.Y + "\nZ坐标:" + close_point.point.Z);
Render();
Invalidate();
glControl1.Invalidate();
}
}
- 添加函数UnProject、UnProject和like_gluUnProject
int UnProject(Vector3d win, ref Vector3d obj)
{
Matrix4d modelMatrix;
GL.GetDouble(GetPName.ModelviewMatrix, out modelMatrix);
Matrix4d projMatrix;
GL.GetDouble(GetPName.ProjectionMatrix, out projMatrix);
int[] viewport = new int[4];
GL.GetInteger(GetPName.Viewport, viewport);
return UnProject(win, modelMatrix, projMatrix, viewport, ref obj);
}
int UnProject(Vector3d win, Matrix4d modelMatrix, Matrix4d projMatrix, int[] viewport, ref Vector3d obj)
{
return like_gluUnProject(win.X, win.Y, win.Z, modelMatrix, projMatrix,
viewport, ref obj.X, ref obj.Y, ref obj.Z);
}
int like_gluUnProject(double winx, double winy, double winz,
Matrix4d modelMatrix, Matrix4d projMatrix, int[] viewport,
ref double objx, ref double objy, ref double objz)
{
Matrix4d finalMatrix;
Vector4d _in;
Vector4d _out;
finalMatrix = Matrix4d.Mult(modelMatrix, projMatrix);
finalMatrix.Invert();
_in.X = winx;
_in.Y = viewport[3] - winy;
_in.Z = winz;
_in.W = 1.0f;
_in.X = (_in.X - viewport[0]) / viewport[2];
_in.Y = (_in.Y - viewport[1]) / viewport[3];
_in.X = _in.X * 2 - 1;
_in.Y = _in.Y * 2 - 1;
_in.Z = _in.Z * 2 - 1;
_out = Vector4d.Transform(_in, finalMatrix);
if (_out.W == 0.0)
return (0);
_out.X /= _out.W;
_out.Y /= _out.W;
_out.Z /= _out.W;
objx = _out.X;
objy = _out.Y;
objz = _out.Z;
return (1);
}
struct Point3DExt
{
public Vector3d point;
public double flag;
}
- PointCloudOctree中添加函数FindClosestPoint
public void FindClosestPoint(double[,] frustum, Vector3d near_point, Vector3d far_point,
ref Point3DExt closest_point)
{
if (root != null)
root.FindClosestPoint(frustum, near_point, far_point, ref closest_point);
}
- PointCloudNode中添加函数FindClosestPoint。注意,节点调用的任何函数,都应在孩子节点中递归调用
public void FindClosestPoint(double[,] frustum, Vector3d near_point, Vector3d far_point,
ref Point3DExt closest_point)
{
if (!OutlineInFrustum)
return;
Vector3d line;
line = far_point - near_point;
if (data != null)
{
double distance;
for (int i = 0; i < data.Count; i++)
{
distance = CalculateDistance(data[i].point, far_point, near_point);
if (closest_point.flag > distance)
{
closest_point.point = data[i].point;
closest_point.flag = distance;
}
}
}
if (child != null)
{
for (int i = 0; i < 8; i++)
{
if (child[i] != null)
{
child[i].FindClosestPoint(frustum, near_point, far_point, ref closest_point);
}
}
}
}
private double CalculateSquare(double lenght1, double lenght2, double lenght3)
{
double s;
double half_circumference = (lenght1 + lenght2 + lenght3) / 2;
double ss = half_circumference * (half_circumference - lenght1)
* (half_circumference - lenght2) * (half_circumference - lenght3);
s = Math.Sqrt(ss);
return s;
}
private double CalculateLength(Vector3d point1, Vector3d point2)
{
double x = 100 * (point1.X - point2.X);
double y = 100 * (point1.Y - point2.Y);
double z = 100 * (point1.Z - point2.Z);
double ll = x * x + y * y + z * z;
double l = Math.Sqrt(ll);
return l;
}
private double CalculateDistance(Vector3d now_point, Vector3d far_point, Vector3d near_point)
{
double now_far_distance = CalculateLength(now_point, far_point);
double now_near_distance = CalculateLength(now_point, near_point);
double near_far_distance = CalculateLength(near_point, far_point);
double triangle_square = CalculateSquare(now_far_distance, now_near_distance, near_far_distance);
double distance = (2 * triangle_square / 10000) / (near_far_distance / 100);
return distance;
}
扩展功能
计算两点间距离
- Form窗体中,添加GroupBox,修改Text为“扩展功能”
- GroupBox中,添加CheckBox,修改Text为“计算两点间距离”
- 双击这个CheckBox,跳转到相应函数,添加代码
if (calculate_distance_between_points)
calculate_distance_between_points = false;
else
calculate_distance_between_points = true;
bool calculate_distance_between_points = false;
List<Point3DExt> two_points = new List<Point3DExt>(2);
int num_two_points = -1;
- 修改glControl1_MouseDoubleClick函数
if (pco != null)
{
pco.FindClosestPoint(mFrustum, nearPoint, farPoint, ref close_point);
if (!calculate_distance_between_points)
MessageBox.Show("选中点坐标为:\nX坐标:" + close_point.point.X
+ "\nY坐标:" + close_point.point.Y + "\nZ坐标:" + close_point.point.Z);
else
{
if (num_two_points > 0)
{
num_two_points = 0;
two_points.Clear();
}
else
num_two_points += 1;
two_points.Add(close_point);
if (num_two_points == 1)
{
double dis_points = calculateDistance(two_points[0], two_points[1]);
MessageBox.Show("选取的两点坐标为:\n"
+ coordinate2string(two_points[0].point) + "\n"
+ coordinate2string(two_points[1].point) + "\n"
+ "这两点之间的距离为:\n"
+ Convert.ToString(dis_points));
}
}
glControl1.Invalidate();
}
private string coordinate2string(Vector3d v)
{
string string_of_coordinate = "(" + Convert.ToString(v.X) + "," + Convert.ToString(v.Y) + ","
+ Convert.ToString(v.Z) + ")";
return string_of_coordinate;
}
private double calculateDistance(Point3DExt point1, Point3DExt point2)
{
double x = point1.point.X - point2.point.X;
double y = point1.point.Y - point2.point.Y;
double z = point1.point.Z - point2.point.Z;
double dd = x * x + y * y + z * z;
double d = Math.Sqrt(dd);
return d;
}
截图
- MenuStrip中添加截图按钮
- 双击截图按钮,跳转到截图ToolStripMenuItem_Click,添加代码
int[] vdata = new int[4];
GL.GetInteger(GetPName.Viewport, vdata);
int w = vdata[2];
int h = vdata[3];
if ((w % 4) != 0)
w = (w / 4 + 1) * 4;
byte[] imgBuffer = new byte[w * h * 3];
GL.ReadPixels(0, 0, w, h, OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
PixelType.UnsignedByte, imgBuffer);
FlipHeight(imgBuffer, w, h);
Bitmap bmp = BytesToImg(imgBuffer, w, h);
bmp.Save("D:\\opentk.bmp");
MessageBox.Show("截图成功!");
private void FlipHeight(byte[] data, int w, int h)
{
int wstep = w * 3;
byte[] temp = new byte[wstep];
for (int i = 0; i < h / 2; i++)
{
Array.Copy(data, wstep * i, temp, 0, wstep);
Array.Copy(data, wstep * (h - i - 1), data, wstep * i, wstep);
Array.Copy(temp, 0, data, wstep * (h - i - 1), wstep);
}
}
private Bitmap BytesToImg(byte[] bytes, int w, int h)
{
Bitmap bmp = new Bitmap(w, h);
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, w, h),
ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
IntPtr ptr = bd.Scan0;
int bmpLen = bd.Stride * bd.Height;
Marshal.Copy(bytes, 0, ptr, bmpLen);
bmp.UnlockBits(bd);
return bmp;
}
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
显示包围盒
- GroupBox中添加CheckBox,修改Text为“显示八叉树轮廓”
- 双击CheckBox,跳转到checkBox2_CheckedChanged函数
- Form1添加类成员变量
bool show_octree_outline = false;
- Form1的Render函数添加show_octree_outline参量
private void Form1_Paint(object sender, PaintEventArgs e)
{
Render(show_octree_outline);
}
private void Render(bool ShowOctreeOutline)
if (pco != null)
{
pco.Render(mFrustum, ShowOctreeOutline);
}
public void Render(double[,] frustum, bool ShowOctreeOutline)
{
if (root != null)
root.Render(frustum, ShowOctreeOutline);
}
if (data != null)
{
GL.CallList(iShowListNum);
if (ShowOctreeOutline == true)
{
DrawNodeOutline(min_coordinate, max_coordinate);
}
}
if (child != null)
{
for (int i = 0; i < 8; i++)
{
if (child[i] != null)
{
child[i].Render(frustum, ShowOctreeOutline);
}
}
}
- PointCloudNode中添加DrawNodeOutline函数
private void DrawNodeOutline(Vector3d min_node_coordinate, Vector3d max_node_coordinate)
{
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.End();
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.End();
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.End();
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.End();
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(max_node_coordinate.X, min_node_coordinate.Y, max_node_coordinate.Z);
GL.End();
GL.Begin(PrimitiveType.Lines);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, min_node_coordinate.Z);
GL.Color4(Color4.White);
GL.Vertex3(min_node_coordinate.X, max_node_coordinate.Y, max_node_coordinate.Z);
GL.End();
}
初始化点云色系
- Form1窗体添加GroupBox,修改Text为“选择点云色系”
- 添加三个RadioButton,修改Text为“彩虹”、“暖色调”、“冷色调”
- 添加类成员变量
string point_cloud_color = "rainbow";
- 双击三个RadioButton,分别对point_cloud_color赋值
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{
point_cloud_color = "rainbow";
Invalidate();
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)
{
point_cloud_color = "warm";
Invalidate();
}
private void radioButton3_CheckedChanged(object sender, EventArgs e)
{
point_cloud_color = "cold";
Invalidate();
}
if (point_cloud_color == "rainbow")
{
if (coordArray[2] <= z_1_6)
{
color_point.color.X = (coordArray[2] - minz) / (z_1_6 - minz) * (cr_orange.X - cr_red.X) + cr_red.X;
color_point.color.Y = (coordArray[2] - minz) / (z_1_6 - minz) * (cr_orange.Y - cr_red.Y) + cr_red.Y;
color_point.color.Z = (coordArray[2] - minz) / (z_1_6 - minz) * (cr_orange.Z - cr_red.Z) + cr_red.Z;
}
else if (coordArray[2] <= z_2_6)
{
color_point.color.X = (coordArray[2] - z_1_6) / (z_2_6 - z_1_6) * (cr_yellow.X - cr_orange.X) + cr_orange.X;
color_point.color.Y = (coordArray[2] - z_1_6) / (z_2_6 - z_1_6) * (cr_yellow.Y - cr_orange.Y) + cr_orange.Y;
color_point.color.Z = (coordArray[2] - z_1_6) / (z_2_6 - z_1_6) * (cr_yellow.Z - cr_orange.Z) + cr_orange.Z;
}
else if (coordArray[2] <= z_3_6)
{
color_point.color.X = (coordArray[2] - z_2_6) / (z_3_6 - z_2_6) * (cr_green.X - cr_yellow.X) + cr_yellow.X;
color_point.color.Y = (coordArray[2] - z_2_6) / (z_3_6 - z_2_6) * (cr_green.Y - cr_yellow.Y) + cr_yellow.Y;
color_point.color.Z = (coordArray[2] - z_2_6) / (z_3_6 - z_2_6) * (cr_green.Z - cr_yellow.Z) + cr_yellow.Z;
}
else if (coordArray[2] <= z_4_6)
{
color_point.color.X = (coordArray[2] - z_3_6) / (z_4_6 - z_3_6) * (cr_cyan.X - cr_green.X) + cr_green.X;
color_point.color.Y = (coordArray[2] - z_3_6) / (z_4_6 - z_3_6) * (cr_cyan.Y - cr_green.Y) + cr_green.Y;
color_point.color.Z = (coordArray[2] - z_3_6) / (z_4_6 - z_3_6) * (cr_cyan.Z - cr_green.Z) + cr_green.Z;
}
else if (coordArray[2] <= z_5_6)
{
color_point.color.X = (coordArray[2] - z_4_6) / (z_5_6 - z_4_6) * (cr_bule.X - cr_cyan.X) + cr_cyan.X;
color_point.color.Y = (coordArray[2] - z_4_6) / (z_5_6 - z_4_6) * (cr_bule.Y - cr_cyan.Y) + cr_cyan.Y;
color_point.color.Z = (coordArray[2] - z_4_6) / (z_5_6 - z_4_6) * (cr_bule.Z - cr_cyan.Z) + cr_cyan.Z;
}
else
{
color_point.color.X = (coordArray[2] - z_5_6) / (maxz - z_5_6) * (cr_purple.X - cr_bule.X) + cr_bule.X;
color_point.color.Y = (coordArray[2] - z_5_6) / (maxz - z_5_6) * (cr_purple.Y - cr_bule.Y) + cr_bule.Y;
color_point.color.Z = (coordArray[2] - z_5_6) / (maxz - z_5_6) * (cr_purple.Z - cr_bule.Z) + cr_bule.Z;
}
}
else if (point_cloud_color == "warm")
{
double z_1_2 = 1 * (maxz - minz) / 2 + minz;
if (coordArray[2] <= z_1_2)
{
color_point.color.X = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_orange.X - cr_red.X) + cr_red.X;
color_point.color.Y = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_orange.Y - cr_red.Y) + cr_red.Y;
color_point.color.Z = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_orange.Z - cr_red.Z) + cr_red.Z;
}
else
{
color_point.color.X = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_yellow.X - cr_orange.X) + cr_orange.X;
color_point.color.Y = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_yellow.Y - cr_orange.Y) + cr_orange.Y;
color_point.color.Z = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_yellow.Z - cr_orange.Z) + cr_orange.Z;
}
}
else if (point_cloud_color == "cold")
{
double z_1_2 = 1 * (maxz - minz) / 2 + minz;
if (coordArray[2] <= z_1_2)
{
color_point.color.X = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_cyan.X - cr_green.X) + cr_green.X;
color_point.color.Y = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_cyan.Y - cr_green.Y) + cr_green.Y;
color_point.color.Z = (coordArray[2] - minz) / (z_1_2 - minz) * (cr_cyan.Z - cr_green.Z) + cr_green.Z;
}
else
{
color_point.color.X = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_bule.X - cr_cyan.X) + cr_cyan.X;
color_point.color.Y = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_bule.Y - cr_cyan.Y) + cr_cyan.Y;
color_point.color.Z = (coordArray[2] - z_1_2) / (maxz - z_1_2) * (cr_bule.Z - cr_cyan.Z) + cr_cyan.Z;
}
}
选择绘制图形
string paint_object = "las";
- 打开菜单中添加“三角形”和“球体”两个选项
- 双击,分别对paint_object赋值
private void 三角形ToolStripMenuItem_Click(object sender, EventArgs e)
{
paint_object = "triangle";
}
private void 球体ToolStripMenuItem_Click(object sender, EventArgs e)
{
paint_object = "sphere";
}
if (paint_object == "las")
{
if (pco != null)
{
pco.Render(mFrustum, ShowOctreeOutline);
}
}
else if (paint_object == "sphere")
{
DrawSphere();
}
else if (paint_object == "triangle")
{
DrawTriangle();
}
选择点的大小
int ptSize = 1;
- Form1窗体添加GroupBox,修改Text为“请输入点的大小,1~10之间的整数”
- 添加TextBox,双击转到函数textBox1_TextChanged,添加代码
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textBox1.Text == "")
return;
int a = Int32.Parse(textBox1.Text);
if (a <= 0 || a >= 10)
a = 1;
ptSize = a;
Invalidate();
}
- DrawSphere函数中,GL.Begin语句前,添加代码
GL.PointSize(ptSize);
- Form1的Render函数,添加参数point_size,修改各处Render
private void Form1_Paint(object sender, PaintEventArgs e)
{
Render(show_octree_outline, ptSize);
}
private void Render(bool ShowOctreeOutline, int point_size)
pco.Render(mFrustum, ShowOctreeOutline, point_size);
public void Render(double[,] frustum, bool ShowOctreeOutline, int point_size)
{
if (root != null)
root.Render(frustum, ShowOctreeOutline, point_size);
}
public void Render(double[,] frustum, bool ShowOctreeOutline, int point_size)
{
OutlineInFrustum = VoxelWithinFrustum(frustum, min_coordinate.X, min_coordinate.Y, min_coordinate.Z,
max_coordinate.X, max_coordinate.Y, max_coordinate.Z);
if (!OutlineInFrustum)
return;
if (data != null)
{
GL.PointSize(point_size);
GL.CallList(iShowListNum);
if (ShowOctreeOutline == true)
DrawNodeOutline(min_coordinate, max_coordinate);
}
if (child != null)
{
for (int i = 0; i < 8; i++)
{
if (child[i] != null)
{
child[i].Render(frustum, ShowOctreeOutline, point_size);
}
}
}
}
选择投影方式
- GroupBox中添加两个RadioButton,修改Text为“平行投影”、“透视投影”
- 双击RadioButton,分别对perspective_projection赋值
private void radioButton4_CheckedChanged(object sender, EventArgs e)
{
perspective_projection = false;
Invalidate();
}
private void radioButton5_CheckedChanged(object sender, EventArgs e)
{
perspective_projection = true;
Invalidate();
}
if (perspective_projection)
GL.Translate(transX, transY, -1);
else
GL.Translate(transX, transY, 0);
使用说明
- Form1的MenuStrip添加“使用说明”,双击转到函数
private void 使用说明ToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("登陆后,从打开菜单中选择绘制的图像\n" +
"鼠标左键可以控制物体平移\n" +
"鼠标右键可以控制物体旋转\n" +
"鼠标滚轮可以控制物体缩放\n" +
"双击鼠标可以选点" +
"复选框可以选择绘制包围核(默认不绘制),也可以选择计算两点间距离(默认不计算)");
}
打开新文件与退出
- Form1窗体添加两个Button,分别修改Text为“打开新文件”、“退出”
- 双击两个Button,添加代码
private void button1_Click(object sender, EventArgs e)
{
this.Hide();
Form1 form1 = new Form1();
form1.Show();
}
private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
设置登录界面
- 项目添加窗体,命名为InitialForm
- BackColor设置为白色
- 添加Label,“三维可视化软件”,Font,隶书,一号
- 添加两个Label,“用户名”,“密码”,均设置为楷体,三号
- 添加两个TextBox
- 添加两个Button,“使用须知”、“登录”
- 双击Button,分别添加代码
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("本软件最终解释权归开发者所有,未经授权不得商用!!!");
}
private void button2_Click(object sender, EventArgs e)
{
if (textBox1.Text == "368" && textBox2.Text == "")
{
DialogResult = DialogResult.OK;
Dispose();
Close();
}
else
{
MessageBox.Show("用户名或密码错误,请重新输入");
}
}
- InitialForm中添加代码从而添加背景图片,将图片存放在项目的bin/Debug文件夹
this.BackgroundImage = Image.FromFile("whu1.jpg");
- 将InitialForm的Size设置为图片大小
- 打开Program.cs文件,将
Application.Run(new Form1());
InitialForm f1 = new InitialForm();
Form1 form1 = new Form1();
f1.ShowDialog();
if (f1.DialogResult == DialogResult.OK)
{
Application.Run(form1);
}
美化界面
- Form1,WindowState,Maximize
- 将所有panel的BackColor设置为纯白