栅格化

摘要:
OpenGL光栅化是将凹多边形或自相交多边形划分为凸多边形的过程。因为OpenGL只接受渲染凸多边形,所以在渲染之前必须对这些非凸多边形进行光栅化。最后,当网格处理完成时,光栅化器将在用户定义的回调函数中调用实际的OpenGL命令来渲染这些光栅化的多边形。光栅使用这些定点坐标来执行网格处理。光栅化器将圆的数量分配给被多个轮廓分割的区域。
OpenGL 栅格化

栅格化是将凹多边形或自相交多边形分割成凸多边形的过程。由于OpenGL只接受渲染凸多边形,那些非凸多边形在渲染之前必须栅格化。

栅格化第1张
左边为凹四边形,中间为有洞多边形,右边为自相交图形

概述

栅格化的基本过程是将非凸多边形的所有顶点发送到栅格器而不是直接发送到OpenGL渲染管线,然后由栅格器对多边形栅格化。最后,当栅格过程结束,栅格器将调用用户定义回调函数中的实际OpenGL命令渲染这些栅格化的多边形。

OpenGL提供一组用于将凹多边形转化为凸多边形的函数:

GLUtessellator* gluNewTess()
void gluDeleteTess(GLUtessellator *tess)

gluNewTess()创建栅格器对象,gluDeleteTess()删除该栅格器对象。如果创建失败,它返回NULL指针。

void gluTessBeginPolygon(GLUtessellator *tess, void *userData)
void gluTessEndPolygon(GLUtessellator *tess)

需要使用栅格器指定的块结构gluTessBeginPolygon()与gluTessEndPolygon()描述多边形的所有定点,而不是使用glBegin()与glEnd()块。你必须在该块结构中描述非凸多边形。

void gluTessBeginContour(GLUtessellator *tess)
void gluTessEndContour(GLUtessellator *tess)

多边形可以具有多个封闭轮廓(封闭环),如含洞的多边形具有2个轮廓:内轮廓与外轮廓。每个轮廓必须在该块结构中组织。gluTessBeginPolygon()与gluTessEndPolygon()块可以进行嵌套。

void gluTessVertex(GLUtessellator *tess, GLdouble cords[3], void *vertexData)

gluTessVertex()指定轮廓定点。栅格器使用这些定点坐标执行栅格过程。所有定点需要近似在一个平面。第二个参数为栅格过程的定点坐标,第三个参数为实际渲染用到的数据。它不仅仅可以是定点坐标,也可以为颜色、法向与纹理坐标。

void gluTessCallback(GLUtessellator *tess, GLUenum type, void (*fn)())

栅格化过程中,栅格器在渲染栅格多边形时将调用一系列回调函数。你必须提供合适的包含用于渲染多边形的实际OpenGL命令的回调函数,如glBegin()、glEnd()、glVertex*()等。

下列代码段为通用实例。

// 创建栅格器
GLUtesselator *tess = gluNewTess();

// 注册回调函数
gluTessCallback(tess, GLU_TESS_BEGIN,   beginCB);
gluTessCallback(tess, GLU_TESS_END,     endCB);
gluTessCallback(tess, GLU_TESS_VERTEX,  vertexCB);
gluTessCallback(tess, GLU_TESS_COMBINE, combineCB);
gluTessCallback(tess, GLU_TESS_ERROR,   errorCB);

// 描述非凸多边形
gluTessBeginPolygon(tess, user_data);
    // 第一个轮廓
    gluTessBeginContour(tess);
        gluTessVertex(tess, coords[0], vertex_data);
        ...
    gluTessEndContour(tess);

    // 第二个轮廓
    gluTessBeginContour(tess);
        gluTessVertex(tess, coords[5], vertex_data);
        ...
    gluTessEndContour(tess);
    ...
gluTessEndPolygon(tess);

// 处理完后删除栅格器
gluDeleteTess(tess);

实例

栅格化第2张

这是3个栅格化实例:左边的为具有四个顶点的简单凹多边形,中间的为含洞多边形,右边的为自相交多边形(星形)。

下载源代码与二进制文件:tessellation.zip

简单凹多边形

栅格化第3张
凹多边形栅格化

<p">该图形的栅格化过程在tessellate1()中描述。OpenGL栅格器在执行栅格过程的时候选择最有效的基础类型:GL_TRIANGLE、GL_TRIANGLE_FAN、GL_TRIANGLE_STRIP与GL_LINE_LOOP。此种情况,栅格器使用GL_TRIANGLE_FAN。

在回调函数中,你需要记录实际的OpenGL命令,它们在栅格过程中执行。下列代码由上个器生成并在回调函数中记录。参考源文件中每个回调函数,查看它如何记录。

glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v3);
    glVertex3dv(v0);
    glVertex3dv(v1);
    glVertex3dv(v2);
glEnd();

注意,该多边形的环绕方向为逆时针方向(CCW),表明多边形的面法线朝向平面外面。如果环绕方向为顺时针方向(CW),法向量为相反方向,因此你将看到背面而不是正面。你可以通过使用gluTessNormal()显示定义面法线。

void gluTessNormal(GLUtessellator *tess, GLdouble x, GLdouble y, GLdouble z)

如果你指定法向量为(0,0,1),你将总是看到正面,即使环绕方向为顺时针防线(假定相机朝向默认方向-Z)。默认法向量值为(0,0,0),表示栅格器将从给定顶点与环绕方向计算法向。

含洞多边形

栅格化第4张
含洞多变形的栅格化

第二个示例具有2个逆时针(CCW)的内环与外环,在tessellate2()中指定。栅格器生成如下的OpenGL命令:1个三角形条与1个三角形。

glBegin(GL_TRIANGLE_STRIP);
    glVertex3dv(v1);
    glVertex3dv(v5);
    glVertex3dv(v0);
    glVertex3dv(v4);
    glVertex3dv(v3);
    glVertex3dv(v7);
    glVertex3dv(v2);
    glVertex3dv(v6);
    glVertex3dv(v5);
glEnd();
glBegin(GL_TRIANGLES);
    glVertex3dv(v5);
 glVertex3dv(v1);
    glVertex3dv(v2);
glEnd();
栅格化第5张
不同环绕方式与环绕数

你或许疑惑OpenGL栅格器如何知道中间的区域为洞(非填充)。答案为环绕规则与环绕数量。栅格器给由多个轮廓分割的区域赋值环绕数。这个例子,有2个独立区域:内部区域的环绕书为2,外部区域的环绕书为1。假定默认环绕规则GLU_TESS_WINDING_ODD,奇数标示的区域为填充区域,偶数标示的为非填充区域。

如果内轮廓为顺时针旋转,内部区域的环绕数为0,外区域的环绕数还是1.因此,由于内部区域的环绕数非奇(0),内部区域依旧是洞(非填充)。环绕规则在下一节介绍。

自相交多边形

栅格化第6张
自相交多边形的栅格化

最后一个实例为星形,一个自相交多边形,在tessellator3()中指定。注意,栅格器添加5个额外的顶点,其中2个边线相交,v5、v6、v7、v8、v9。当栅格器算法检测到一个相交时,为了创建新顶点数据,必须提供GLU_TESS_COMBINE_CALLBACK回调函数,因此可以在后面绘制它。

void combineCB(GLdouble newVert[3], GLdouble *neighbourVert[4],
               GLfloat neighborWeight[4], void **outData);

combine回调函数,用于当2条边相交时创建新顶点数据。它拥有4个参数:newVert[3]为GLdouble类型的x、y、z坐标数组。它表示栅格器找寻的新相交顶点位置。第二个参数为四个已存邻接顶点指针数组,它们构成2相交边。第三个参数为四个已存邻接顶点的权重因子。该权重用于计算相交点的颜色、法线、纹理坐标差值。最后一个参数为输出顶点数据指针。输出数据不仅包含顶点坐标,还有颜色、法线与纹理坐标。为了绘制相交顶点,栅格器将输出数据传递到GLU_TESS_VERTEX回调函数中。

下述OpenGL命令由栅格器生成:2个三角形条与一个三角形。

glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v9);
    glVertex3dv(v0);
    glVertex3dv(v5);
    glVertex3dv(v7);
    glVertex3dv(v8);
    glVertex3dv(v2);
glEnd();
glBegin(GL_TRIANGLE_FAN);
    glVertex3dv(v6);
    glVertex3dv(v1);
    glVertex3dv(v7);
    glVertex3dv(v5);
    glVertex3dv(v3);
glEnd();
glBegin(GL_TRIANGLES);
    glVertex3dv(v4);
    glVertex3dv(v8);
    glVertex3dv(v7);
glEnd();
栅格化第7张
环绕数

考虑到该多边形的环绕规则与环绕数。多边形分为6个独立区域,环绕数如图所示。

对于GLU_TESS_WINDING_ODD规则,由于环绕数为偶数,中间区域不会被填充。我们需要一个不同的环绕规则,因此我们能够填充奇数与偶数区域。此种情况,环绕规则为GLU_TESS_WINDING_NONEZERO,它准许填充非零区域。(GLU_TESS_WINDING_POSITIVE也可以实现)。

栅格器提供gluTessProperty()以改变环绕规则以及其他属性,如GLU_TESS_BOUNDING_ONLY与GLU_TESS_TOLERANCE。关于环绕规则的更详尽信息参考下一节。

void gluTessProperty(GLUtesselator *tess, GLenum property, GLdouble *value)

// 示例
gluTessProperty(tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE);
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);

环绕规则与环绕数

想象一下多个轮廓线,它们相互重叠或嵌套,将平面分成多个区域。环绕规则决定哪些区域为内部区域哪些为外部区域。因此,内部区域为填充区域,外部区域为非填充区域。

栅格化第8张
环绕数示例

对于每一个由多个轮廓线分割的封闭区域,OpenGL栅格器都予以赋值。从区域中引一条任意方向的射线,从0开始,每遇到一个逆时针轮廓线(从右到左)就加1,每遇到一个顺时针轮廓线(从左到右)就减1。统计完所有相交区域之后,环绕数代表该区域。

若环绕规则为GLU_TESS_WINDING_ODD,奇数区域为内部区域且被填充,偶数区域为外部区域非填充。因此,区域1与-1被填充,区域0为洞。

可能的环绕规则为:

GLU_TESS_WINDING_ODD:填充奇数(默认设置)

GLU_TESS_WINDING_NONZERO:填充非零数

GLU_TESS_WINDING_POSITIVE:填充正数

GLU_TESS_WINDING_NEGATIVE:填充负

GLU_TESS_WINDING_ABS_GEO_TWO:填充绝对值大于等于2的数

下图显示不同环绕规则的不同内部区域(阴影)。如果设置GLU_TESS_WINDING_ABS_GEQ_TWO,所有都不绘制(所有区域都是外部区域)。

栅格化第9张

英文原文:http://www.songho.ca/opengl/gl_tessellation.html

免责声明:文章转载自《栅格化》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇让requestAnimationFrame实现定时调用功能关于《货币金融学》若干问题的思考《三》下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JQuery DataTable的配置项及事件

  当我们需要在加载或者显示表格的时候,改变jquery datatable 的数据,或增加一些html标签处理。 可以通过Jquery DataTable的回调函数处理。 实例代码: if (oTable != null) { oTable.fnDestroy(); }; var detail="../Pages/detail/detail.asp...

PhantomJS 基础及示例 (转)

概述 PhantomJS is a headless WebKit scriptable with a JavaScript API. It has fast and native support for various web standards: DOM handling, CSS selector, JSON, Canvas, and SVG.(ht...

[译] 如何使用 WebGL 技术进行风力地图可视化

翻译:@四季留歌 部分翻译。原文:https://blog.mapbox.com/how-i-built-a-wind-map-with-webgl-b63022b5537f 目录 如果使用 CPU 进行风向可视化: 慢 OpenGL 基础 获取风力数据 使用 GPU 移动粒子 绘制粒子 绘制粒子轨迹 插值以获取风力值 使用 GPU 上的伪随机...

Jquery Ztree异步加载树

更多JS实战记录,请前往:https://www.yuque.com/smallwhy/yyvuqy 1. 下载jquery的JS文件/ztree的CSS文件和JS文件 https://jquery.com/download/ https://gitee.com/zTree/zTree_v3/tree/master/ 2. 目录结构 3. async...

【js学习】js连接RabbitMQ达到实时消息推送

js连接RabbitMQ达到实时消息推送 最近在自己捯饬一个网站,有一个功能是需要后端处理完数据把数据发布到MQ中,前端再从MQ中接收数据。但是前端连接MQ又成了一个问题,在网上搜了下资料,点进去一篇IBM DW后发现竟然是超哥写的,真是巧哈~因为超哥写的很好所以很多我就直接摘抄过来了,他应该不会介意的(逃。 参考 基于 RabbitMQ 的实时消息推送...

OpenGL的glClearColor和glClear改变背景颜色

OpenGL的glClearColor和glClear改变背景颜色 结合以下两个函数voidglClearColor(GLclampfred,             GLclampfgreen,           GLclampfblue,           GLclampfalpha); 和 voidglClear(GLbitfieldmask);...