使用 C# 与 OpenGL (SharpGL) 加载点云模型

2019-12-02 (Mon.)

效果图

Intro

这一次计算机图形学的作业要求是使用提供的 1.dat (数据点文件)和 2.txt (片面文件)内的数据加载出 3D 模型。在实现的过程中为了探索 OpenGL 的各种细节也增增补补了许多东西。

作业要求及数据源

程序使用C# .Net Framework 4.5,OpenGL 使用库SharpGL链接。这个库使用起来很顺手,获取了 OpenGL 实例之后并命名为 gl 就可以获得和在 C++ 中几乎一样的变成体验。

之前本来打算用 DirectX3d 12 做,奈何实在是太复杂,尝试了许久,用 100 行代码也就清空了一下画布。感受到 d3d 的强大的同时,也被狠狠地劝退了,回到了相对熟悉一些的 OpenGL。

Usage

数据文件 1.dat 和 2.txt 应当和程序放在同一个目录下。

使用 Render All 选项可以渲染所有内容, Render Dots Only 只渲染云点和坐标轴,Render Surfaces With Light 渲染光照和 3D 模型的混合。也可以直接在上面勾选需要渲染的内容。

效果图

勾选 Enable Auto Rotation 可以让模型自动绕 y 轴旋转。

取消勾选 Clear Before Repaint 可以在每一次渲染前不清空深度缓存,保留上一次的渲染数据。

Reset 选项将会清空画布并将视角重置为一个比较合适的初始值,重新调整窗口大小也会重新加载画布清空缓存。

Code

核心部分的绘制代码并不难,大概在以下几个函数中。其中有一些和交互相关的代码,逻辑也比较好理解。

private void Initialize() {
	gl = glCanvas.OpenGL;
	LookAtMatrix = new double[9]{200, 200, 200, -30, 20, 0, 0, 12, 0};
	LightPosition = new float[4]{7.5f, 20f, 20f, 1f};
	chkAmbient.Checked = false;
	chkAxis.Checked = false;
	chkDiffuse.Checked = false;
	chkDots.Checked = false;
	chkSpecular.Checked = false;
	chkSurface.Checked = false;
	chkRotate.Checked = false;
	chkRepaint.Checked = true;
	gl.Disable(OpenGL.GL_LIGHTING);
	//抗锯齿和混合
	gl.Enable(OpenGL.GL_BLEND);
	gl.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA);
	gl.Enable(OpenGL.GL_LINE_SMOOTH);
	gl.Enable(OpenGL.GL_POLYGON_SMOOTH);
}

//绘制坐标轴
private void RenderAxis() {

	Log("Rendering axis... (Arrow indicates the positive direction of each axis)");
	gl.Disable(OpenGL.GL_LIGHTING);
	gl.Color(1f, 1f, 1f);
	gl.Begin(OpenGL.GL_LINES);
	{
		gl.Vertex(-100f, 0f, 0f);
		gl.Vertex(100f, 0f, 0f);
		gl.Vertex(20f, 0f, 0f);
		gl.Vertex(19f, 0.5f, 0f);
		gl.Vertex(19f, -0.5f, 0f);
		gl.Vertex(20f, 0f, 0f);
	}
	gl.End();
	gl.Begin(OpenGL.GL_LINES);
	{
		gl.Vertex(0f, -100f, 0f);
		gl.Vertex(0f, 100f, 0f);
		gl.Vertex(0f, 20f, 0f);
		gl.Vertex(0f, 19f, 0.5f);
		gl.Vertex(0f, 19f, -0.5f);
		gl.Vertex(0f, 20f, 0f);
	}
	gl.End();
	gl.Begin(OpenGL.GL_LINES);
	{
		gl.Vertex(0f, 0f, -100f);
		gl.Vertex(0f, 0f, 100f);
		gl.Vertex(0f, 0f, 20f);
		gl.Vertex(0.5f, 0f, 19f);
		gl.Vertex(-0.5f, 0f, 19f);
		gl.Vertex(0f, 0f, 20f);
	}
	gl.End();
}

//绘制点云
private void RenderDots() {
	Log("Rendering dots...");
	gl.Color(1f, 1f, 1f);
	gl.PointSize(2.0f);
	gl.Begin(OpenGL.GL_POINTS);
	{
		foreach (Cordinate c in cordinates) {
			gl.Vertex(c.x, c.y, c.z);
		}
	}
	gl.End();
}
private void LightConfiguration() {
	Log("Configuring light and materials...");
	// 光照配置和材质配置
	float[] AmbientLight = new float[4]{0.5f, 1f, 1f, 1f};
	float[] DiffuseLight = new float[4]{0.5f, 1f, 1f, 1f};
	float[] SpecularLight = new float[4]{1f, 1f, 1f, 1f};

	float[] modelMaterial = new float[4]{0.3f, 0.3f, 0.35f, 1f};
	float[] modelSpecularSet = new float[4]{0.5f, 1.0f, 1.0f, 1.0f};

	if (chkAmbient.Checked || chkDiffuse.Checked || chkSpecular.Checked) {
		// 绘制光源
		gl.PointSize(5f);
		gl.Begin(OpenGL.GL_POINTS);
		{
			gl.Vertex(LightPosition[0], LightPosition[1], LightPosition[2]);
		}
		gl.End();
		gl.PointSize(2);
	}

	gl.Enable(OpenGL.GL_LIGHTING);
	gl.Enable(OpenGL.GL_LIGHT0);
	gl.Enable(OpenGL.GL_LIGHT1);
	gl.Enable(OpenGL.GL_LIGHT2);
	if (chkAmbient.Checked) {
		gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, AmbientLight);
	} else {
		gl.Disable(OpenGL.GL_LIGHT0);
	}
	if (chkDiffuse.Checked) {
		gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, DiffuseLight);
	} else {
		gl.Disable(OpenGL.GL_LIGHT1);
	}
	if (chkSpecular.Checked) {
		gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_SPECULAR, SpecularLight);
		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SPECULAR, modelSpecularSet);
		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, 64f);
	} else {

		gl.Material(OpenGL.GL_FRONT, OpenGL.GL_SHININESS, 0f);
		gl.Disable(OpenGL.GL_LIGHT2);
	}
	// gl.Material(OpenGL.GL_FRONT, OpenGL.GL_DIFFUSE, modelMaterial);
	// gl.Material(OpenGL.GL_FRONT, OpenGL.GL_AMBIENT, modelMaterial);
	gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, LightPosition);
	gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, LightPosition);
	gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_POSITION, LightPosition);
}

//绘制边
private void RenderSurfaces() {
	gl.Enable(OpenGL.GL_DEPTH_TEST);
	Log("Rendering surfaces...");
	foreach (Surface s in surfaces) {
		gl.Begin(OpenGL.GL_POLYGON);
		{
			gl.Vertex(cordinates[s.p1 - 1].x, cordinates[s.p1 - 1].y, cordinates[s.p1 - 1].z);
			gl.Vertex(cordinates[s.p2 - 1].x, cordinates[s.p2 - 1].y, cordinates[s.p2 - 1].z);
			gl.Vertex(cordinates[s.p3 - 1].x, cordinates[s.p3 - 1].y, cordinates[s.p3 - 1].z);
			if (s.p4 != 0) {
				gl.Vertex(cordinates[s.p4 - 1].x, cordinates[s.p4 - 1].y, cordinates[s.p4 - 1].z);
			}
		}
		gl.End();
	}
}

//清除所有信息并设置背景色
private void ClearAll() {
	gl.ClearColor(0.3f, 0.4f, 0.5f, 1f);
	gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
}

private void RENDER() {
	if (changeDetecter) {
		sw.Restart();
	}
	if (!dataLoaded) {
		Log("Loading surface and dots data...");
		LoadData();
		Log("Data loaded successfully.");
		dataLoaded = true;
	}

	//改变视角
	gl.MatrixMode(OpenGL.GL_PROJECTION);
	gl.LoadIdentity();
	gl.Perspective(30f, (double)glCanvas.Width / (double)glCanvas.Height, 3, 20000);
	gl.LookAt(LookAtMatrix[0], LookAtMatrix[1], LookAtMatrix[2],
		  LookAtMatrix[3], LookAtMatrix[4], LookAtMatrix[5],
		  LookAtMatrix[6], LookAtMatrix[7], LookAtMatrix[8]);
	gl.MatrixMode(OpenGL.GL_MODELVIEW);
	if (chkRepaint.Checked) {
		ClearAll();
	}

	if (chkAxis.Checked) {
		RenderAxis();
	}

	if (chkDots.Checked) {
		RenderDots();
	}
	LightConfiguration();
	if (chkSurface.Checked) {
		RenderSurfaces();
	}
	Log("Done. Time used: " + sw.ElapsedMilliseconds + " ms");
	sw.Stop();
}

完整的代码、可执行文件和 1.dat,2.txt 数据文件可以在下面下载到:

GitHub:https://github.com/BillChen2K/LearningRepo/tree/master/Course/ComputerGraphics/Work2

© 2019 - 2024Juntong Chen ||
Dark Mode
|| RSS