使用 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