栅格化

OpenGL 栅格化

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


左边为凹四边形,中间为有洞多边形,右边为自相交图形

概述

栅格化的基本过程是将非凸多边形的所有顶点发送到栅格器而不是直接发送到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);

实例

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

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

简单凹多边形

该图形的栅格化过程在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),表示栅格器将从给定顶点与环绕方向计算法向。

含洞多边形

第二个示例具有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();

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

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

自相交多边形

最后一个实例为星形,一个自相交多边形,在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();

考虑到该多边形的环绕规则与环绕数。多边形分为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);

环绕规则与环绕数

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

对于每一个由多个轮廓线分割的封闭区域,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,所有都不绘制(所有区域都是外部区域)。

栅格化,布布扣,bubuko.com

时间: 2024-08-25 10:35:21

栅格化的相关文章

任务八:响应式网格(栅格化)布局

任务目的 使用 HTML 与 CSS 实现类似 BootStrap 的响应式 12 栏网格布局,根据屏幕宽度,元素占的栏数不同. 任务描述 需要实现如 效果图 所示,调整浏览器宽度查看响应式效果,效果图中的红色的文字是说明,不需要写在 HTML 中. 任务注意事项 网格布局的作用在于更有效地控制元素在网页中所占比例的大小.比如,博客中有一个留言板模块,在比较大的屏幕上,我们希望它占了右边 25% 的宽度,在手机等比较小的屏幕上,我们希望它占 100% 的宽度,出现在博客文章下方.网格布局是一种实

移动端rem布局和百分比栅格化布局

移动端的rem: 使用方法: 设置html的font-size,根据浏览器分辨率缩放 设置根元素font-size为100px这样好用的值,不要设为10px这样的: 然后获取浏览器的分辨率,也就是视口宽度,p(比例尺)= 视口宽度/效果图宽度 根元素font-size=100px*p 然后来个resize去跟随浏览器大小变化 1(function (win){ 7 var doc = win.document, 8 html = doc.documentElement, 9 option = h

任务八——响应式网格(栅格化)布局之问题总结

题目请看:任务八:响应式网格(栅格化)布局 一:要求是每个元素之间的间距均为20px,首先间距的实现有两种方法:    方法1. <div class="container"> <div class="col-sm-4 col-xs-6">4-6</div> . . . . . . </div> container设置box-sizing:border-box:width:100%:padding:10px:每个div

响应式网格(栅格化)布局总结

任务目的 使用 HTML 与 CSS 实现类似 BootStrap 的响应式 12 栏网格布局,根据屏幕宽度,元素占的栏数不同. 任务描述 需要实现如 效果图 所示,调整浏览器宽度查看响应式效果,效果图中的红色的文字是说明,不需要写在 HTML 中. 任务注意事项 网格布局的作用在于更有效地控制元素在网页中所占比例的大小.比如,博客中有一个留言板模块,在比较大的屏幕上,我们希望它占了右边 25% 的宽度,在手机等比较小的屏幕上,我们希望它占 100% 的宽度,出现在博客文章下方.网格布局是一种实

为什么网页设计要使用栅格化

研究网页栅格系统前,来看一组数据: 网站 首页页面宽度 px Yahoo! 950 淘宝 950 MySpace 960 新浪 950 网易 960 Live Search 958 搜狐 950 优酷 960 AOL 960 上面列举的都是Alexa全球排名前100的站点,它们的首页宽度为950px/960px. 除了微软的Live Search, 这些站点有个共同特点:页面结构较复杂,都可以认为是门户型网站. 再来看看Google, YouTube, Facebook, Flickr!, eB

超好用的网页栅格化工具: GridGuide

平面设计中使用栅格化设计是相当重要的,特别是网页和VI设计方面,在设计前都需要来好栅格,但是选择合适栅格和计算无疑是浪费了设计师不少的时间,然而当遇上今天的神器「GridGuide」在线工具,以后再也不怕Web网格布局问题了! 网站名称:GridGuide工具地址:http://grid.guide/ (已加入至设计导航 -> 设计工具类目下) 使用教程 打网站后,用户可以自由设置最大宽度.列数以及留白边界即可生成栅格方案了. 这里说明下留白边界(Outer Gutter Ratio)是以倍率为

Bootstrap中的栅格化布局

bootstarp的栅格化布局使得我们布局简单,我们只需要利用.container/.container-fluid,.row即可实现,其下是一个例子: <div class="container-fluid"> <h3 class="text-primary text-center">jQuery Playground</h3> <div class="row"> <div class=&q

(作业)百度前端技术学院 任务八:响应式网格(栅格化)布局

百度前端技术学院 任务八:响应式网格(栅格化)布局 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>IFE Task1-8</title> <style> body{ margin:0; } .container{ box-sizing: border-box; padding: 10px;

rem 移动端栅格化 适配

1.rem rem (font size of the root element)是指相对于根元素<html>字体大小的单位. <html> <head> <style type="text/css"> html{ font-size: 20px; } div{ width: 100rem; height: 60rem; } </style> </head> <body> <!--div宽度为:1