OPENGL: 多边形网格化(tessellation)
虽然在OpenGL中可以使用glBegin(GL_POLYGON)来画一个多边形,但是它只能实现简单的凸多边形。对于一些复杂的多边形,比如凹多边形,或者有实心有空心的多边形,OpenGL的glBegin(GL_POLYGON)就不能满足需求了。通常可以采用一种叫做"分格化"的方法来画复杂的多边形。
1. 实现方法
要用分格化的方法画多边形,步骤如下:
a. gluNewTess(); //创建一个新的分格化对象
b. gluTessCallback(); //注册回调函数,完成分格化的一些操作,照着写就行了。
c. gluTessProperty(); //可有可无的,设置一些分格化的属性值
d. gluTessBeginPolygon(); //开始画多边形
draw polygon...//在这里画多边形,一个一个点画就可以,最后一个点会和第一个点自动连接起来
gluTessEdnPolygon(); //结束画多边形
e. gluDeleteTess(); //删除分格化对象
2. 代码
用C#写了个OpenTK 的Tessallation调用类,代码如下:
使用时需要在项目中添加引用OpenTK.Compatibility.dll。
using System; using System.Collections.Generic; using OpenTK; using OpenTK.Graphics; using System.Runtime.InteropServices; namespace Render { public static class Tessellation { //Define the signatures for the callback functions, and declare the callbacks. delegate void BeginCallbackDelegate(BeginMode mode); delegate void EndCallbackDelegate(); delegate void VertexCallbackDelegate(IntPtr v); delegate void ErrorCallbackDelegate(OpenTK.Graphics.GluErrorCode code); unsafe delegate void CombineCallbackDelegate( [MarshalAs(UnmanagedType.LPArray, SizeConst = 3)]double[] coordinates, [MarshalAs(UnmanagedType.LPArray, SizeConst = 4)]double*[] vertexData, [MarshalAs(UnmanagedType.LPArray, SizeConst = 4)]float[] weight, double** dataOut); static bool InvalidPolygon; static IntPtr tess; static unsafe double*[] combineData; static int data_index = 0; unsafe static public bool Triangulate(List<Vector3> vertexes) { tess = Glu.NewTess(); //register tesselation callbacks Glu.TessCallback(tess, TessCallback.TessVertex, new VertexCallbackDelegate(VertexCallback)); Glu.TessCallback(tess, TessCallback.TessBegin, new BeginCallbackDelegate(BeginCallback)); Glu.TessCallback(tess, TessCallback.TessEnd, new EndCallbackDelegate(EndCallback)); Glu.TessCallback(tess, TessCallback.TessError, new ErrorCallbackDelegate(ErrorCallback)); Glu.TessCallback(tess, TessCallback.TessCombine, new CombineCallbackDelegate(CombineCallback)); //plot polygon Glu.TessBeginPolygon(tess, IntPtr.Zero); Glu.TessBeginContour(tess); GL.Normal3(Vector3.Cross(vertexes[1] - vertexes[0], vertexes[2] - vertexes[0])); for (int i= 0; i< vertexes.Count; i++) { double[] position = new double[3] { vertexes[i].X, vertexes[i].Y, vertexes[i].Z }; Glu.TessVertex(tess, position, position); } Glu.TessEndContour(tess); if (InvalidPolygon) { //destroy the tesselation object Glu.DeleteTess(tess); tess = IntPtr.Zero; return false; //error in polygon definition } else { // end polygon Glu.TessEndPolygon(tess); //destroy the tessellation object Glu.DeleteTess(tess); tess = IntPtr.Zero; //The Indices object is now valid return true; } } static void BeginCallback(BeginMode mode) { GL.Begin(mode); } static void EndCallback() { GL.End(); } static void VertexCallback(IntPtr v) { unsafe { GL.Vertex3((double*)v); } } static void ErrorCallback(OpenTK.Graphics.GluErrorCode code) { //some error ocurred, mark this triangulation as invalid InvalidPolygon = true; } unsafe static void CombineCallback(double[] coordinates, double*[] data, float[] weight, double** dataOut) { //This means the polygon is self-intersecting InvalidPolygon = true; } } }