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