写在前面
C# Tips是博主开启的第一个项目,以C#为示例语言,旨在普及编程技巧、经典算法以及计算机视觉、图形学知识。
CSharpGL
计算机图形学是一门将三维空间下的图形映射到计算机栅格屏幕的学科,广泛应用于游戏、虚拟现实等需要三维图形可视化的领域。OpenGL和DirectX是业内公认的标准化计算机图形学库,相较而言,OpenGL的跨平台性更好。而CSharpGL则是国内的一位大佬针对.NET平台对OpenGL的移植,除了实现OpenGL的可编程管线外,还封装了类似于GLWindow的WinGLCanvas图形显示控件。和一些包含OpenGL的三维库,如osg、vtk类似,CSharpGL基于面向对象思想提供了丰富的视图控制方法。
三维模型可视化
模型的重写
实际上,随意打开一个CSharpGL的模型示例,就可以清晰地看到如何编写一个自定义的能被CSharpGL成功渲染的三维模型。首先新建一个继承PickableNode(用于选择三维模型的元素)和IRenderable(用于渲染三维模型)的Model3DNode类,基本框架和示例类似,只需要修改静态方法Create用于创建自定义的三维模型。
通常情况下,我们需要从三维模型文件(常用的有TXT、ASC、PLY等)中读取我们所需的三维数据,所以为Create添加一个输入参数path(三维模型路径):
public static Model3DNode Create(string path)
{
}
为了解析不同类型的三维模型,我们还需要创建一个继承IBufferSource(用于传递数据)的Model3D类,基本框架也和示例类似,首先添加几个存储数据的属性:
/// <summary>
/// 点集
/// </summary>
private List<vec3> points;
/// <summary>
/// 颜色
/// </summary>
private List<vec3> colors;
/// <summary>
/// 面集
/// </summary>
private List<Face> faceList;
再添加一个用于解析三维模型文件并初始化以上三个属性的方法LoadModel:
public void LoadModel(string modelPath)
{
points = new List<vec3>();
colors = new List<vec3>();
faceList = new List<Face>();
StreamReader sr = new StreamReader(modelPath);
//从文件中解析三维数据
//添加到points、colors、faceList 中
sr.Close();
}
修改GetVertexAttribute和GetDrawCommand方法以支持渲染点云和三角网格。
public IEnumerable<VertexBuffer> GetVertexAttribute(string bufferName)
{
if (bufferName == strPosition)
{
if (this.positionBuffer == null)
{
this.positionBuffer = points.ToArray().GenVertexBuffer(VBOConfig.Vec3, BufferUsage.StaticDraw);
}
yield return this.positionBuffer;
}
else if (bufferName == strColor)
{
if (this.colorBuffer == null)
{
this.colorBuffer = colors.ToArray().GenVertexBuffer(VBOConfig.Vec3, BufferUsage.StaticDraw);
}
yield return this.colorBuffer;
}
else
{
throw new ArgumentException();
}
}
public IEnumerable<IDrawCommand> GetDrawCommand()
{
if (this.drawCmd == null)
{
if (faceList != null)
{
Face[] faces = faceList.ToArray();
IndexBuffer buffer = faces.GenIndexBuffer(IndexBufferElementType.UShort, BufferUsage.StaticDraw);
this.drawCmd = new DrawElementsCmd(buffer, DrawMode.Triangles);
}
else
{
DrawArraysCmd buffer = new DrawArraysCmd(DrawMode.Points,points.Count);
this.drawCmd = buffer;
}
}
}
重新回到最开始的Create方法,利用Model3D提供的方法补全其代码:
public static Model3DNode Create(string path)
{
IBufferSource model; vec3 size;
Model3D m = new Model3D();
size = m.GetModelSize();
m.LoadModel(path);
model = m;
string position = Model3D.strPosition;
string color = Model3D.strColor;
var vs = new VertexShader(vertexCode);
var fs = new FragmentShader(fragmentCode);
var provider = new ShaderArray(vs, fs);
var map = new AttributeMap();
map.Add(inPosition, position);
map.Add(inColor, color);
var builder = new RenderMethodBuilder(provider, map);
var node = new Model3DNode(model, position, builder);
node.Initialize();
node.ModelSize = size;
return node;
}
可视化流程
得到三维模型后,我们还要将其放到WinGLCanvas控件中进行显示,可视化流程如下:
- 创建相机和实例化三维模型;
- 初始化Scene;
- 添加OpenGLDraw事件。
创建相机
CSharpGL提供了两类相机:透视(Perspective)和正交(Ortho)相机。下面是创建一个透视相机的例子:
var position = new vec3(5, 3, 4);
var center = new vec3(0, 0, 0);
var up = new vec3(0, 1, 0);
var camera = new CSharpGL.Camera(position, center, up, CameraType.Perspective, this.winGLCanvas1.Width, this.winGLCanvas1.Height);
初始化Scene
CSharpGL提供的Scene类用于管理三维场景中的模型和相机。利用上面创建的透视相机可以初始化一个Scene:
CSharpGL.Scene scene = new CSharpGL.Scene(camera);
Scene采用树状结构管理三维模型结点,Model3DNode 的一个实例就可以添加到Scene的结点树中。
Model3DNode node = Model3DNode.Create(模型路径);
scene.RootNode = node;
var list = new ActionList();
var transformAction = new TransformAction(scene);
list.Add(transformAction);
var renderAction = new RenderAction(scene);
list.Add(renderAction);
ActionList actionList = list;
添加OpenGLDraw事件
private void winGLCanvas1_OpenGLDraw(object sender, PaintEventArgs e)
{
var canvas = sender as WinGLCanvas;
canvas.MakeCurrent();
ActionList list = this.actionList;
if (list != null)
{
GL.Instance.ClearColor(0, 0, 0, 255);
GL.Instance.Clear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
list.Act(new ActionParams(Viewport.GetCurrent()));
}
}
其中的GL.Instance.ClearColor(0, 0, 0, 255)可以改变WinGLCanvas的默认颜色。