OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)
http://my.oschina.net/sweetdark/blog/183721
参数方程表现形式
在中学的时候,我们都学习过直线的参数方程:y = kx + b;其中k表示斜率,b表示截距(即与y轴的交点坐标)。类似地,我们也可以用一个参数方程来表示一条曲线。1962年,法国工程师贝塞尔发明了贝塞尔曲线方程。关于贝塞尔曲线的详细介绍可以参考(维基贝塞尔)。这里只介绍OpenGL实现贝塞尔的函数。
OpenGl定义一条曲线时,也把它定义为一个曲线方程。我们把这条曲线的参数成为u,它的值域就是曲线的定义域。曲面则需要u和v两个参数来描述。注意,u和v参数只表示了描述曲线的参数方程的范围,它们并没有反映实际的坐标值。其坐标可以表示为:
x = f(u); y = g(u); z = h(u);
如下图:
控制点
贝塞尔曲线的形状由控制点来控制。贝塞尔曲线的控制点个数为曲线的阶。根据控制点的个数,贝塞尔曲线又分为二次贝塞尔曲线,三次贝塞尔曲线,高阶贝塞尔曲线。
线性曲线
线性贝塞尔曲线演示动画,t in [0,1]
二次方曲线
为建构二次贝塞尔曲线,可以中介点Q0和Q1作为由0至1的t:
由P0至P1的连续点Q0,描述一条线性贝塞尔曲线。
由P1至P2的连续点Q1,描述一条线性贝塞尔曲线。
由Q0至Q1的连续点B(t),描述一条二次贝塞尔曲线。
二次贝塞尔曲线的结构
二次贝塞尔曲线演示动画,t in [0,1]
三次方曲线
为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:
三次贝塞尔曲线的结构
三次贝塞尔曲线演示动画,t in [0,1]
连续性
两段曲线是否相连接,代表这两段曲线是否连续的。曲线的连续性分为4种,无连续,点连续,正切连续,曲率连续。下图分别表示了这几种情况:
其中曲率连续的曲线过渡的更平滑。我们可以通过参数来设置曲线的连续性。
求值器
OpenGL提供了一些函数来绘制贝塞尔曲线和曲面。我们只需要提供控制点和u,v作为参数,然后调用求值函数来绘制曲线。
2D曲线的例子:
//控制点 GLint numOfPoints = 4; static GLfloat controlPoints[4][3] = {{-4.0f, 0.0f, 0.0f},
{-6.0f, 4.0f, 0.0f},
{6.0f, -4.0f, 0.0f},
{4.0f, 0.0f, 0.0f}}; void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glColor3f(1.0f, 0.0f, 1.0f);
}
//画控制点
void DrawPoints()
{
glPointSize(2.5f);
glBegin(GL_POINTS); for (int i = 0; i < numOfPoints; ++i)
{
glVertex3fv(controlPoints[i]);
}
glEnd();
} void ChangeSize(GLsizei w, GLsizei h)
{
if (h == 0)
{
h = 1;
} glViewport(0, 0, w, h);
//使用正交投影
glMatrixMode(GL_PROJECTION);
glLoadIdentity(); gluOrtho2D(-10.0f, 10.0f, -10.0f, 10.0f); glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
} void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT);
//设置贝塞尔曲线,这个函数其实只需要调用一次,可以放在SetupRC中设置
glMap1f(GL_MAP1_VERTEX_3, //生成的数据类型
0.0f, //u值的下界
100.0f, //u值的上界
3, //顶点在数据中的间隔,x,y,z所以间隔是3
numOfPoints, //u方向上的阶,即控制点的个数
&controlPoints[0][0] //指向控制点数据的指针 );
//必须在绘制顶点之前开启
glEnable(GL_MAP1_VERTEX_3);
//使用画线的方式来连接点
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= 100; i++)
{
glEvalCoord1f((GLfloat)i);
}
glEnd(); DrawPoints(); glutSwapBuffers(); }
在RenderScene函数中调用glMap1f来为曲线创建映射。第一个参数为GL_MAP1_VERTEX3,设置求值器产生顶点为三元组(x,y,z).还可以设置为产生纹理坐标和颜色信息。参考glMap1.后面的两个参数设定了u的取值范围[0,100],第四个参数指定了顶点在数组中的间隔,由于顶点是由3个浮点数组成,所以间隔是3.第五个参数指定了控制点的个数,最后一个参数是控制点数组。然后我们需要启用求值器,调用如下:
glEnable(GL_MAP1_VERTEX3);
glEvalCoord1f函数,接受一个参数为曲线的参数值。调用这个函数会通过求值函数求出顶点坐标值,然后内部调用了glVertex。这里使用连线的方式来连接这些顶点:
glBegin(GL_LINE_STRIP);
for(i = 0; I <= 100; i++)
{
glEvalCoord1f((GLfloat)i);
}
glEnd();
计算曲线
OpenGl还提供了更简单的方式来完成上面的任务。我们可以通过glMapGrid函数来设置一个网格,来告诉OpenGL在u的值域的范围内创建一个包含各个点的空间对称的网格。然后,我们调用glEvalMesh,使用指定的图元(GL_LINE或GL_POINTS)来链接各个点。
我们用下面的两个函数调用
glMapGrid1f(100, 0.0f, 100.0f); glEvalMesh1(GL_LINE, 0, 100);
可以替换下面的代码
glBegin(GL_LINE_STRIP);
for (int i = 0; i <= 100; i++)
{
glEvalCoord1f((GLfloat)i);
}
glEnd();
使用这种方式更为紧凑。
3D表面
创建一个贝塞尔曲面与创建一个贝塞尔曲线类似。除了给出u的定义域之外,还要给出v的定义域。下面的例子是创建一个贝塞尔曲面。与之前不同的是,我们沿着v的定义域定义了3组控制点。为了保持曲面的简单,这几组控制点只是z值不同。用这种方式画的曲面,看起来像是曲线沿z轴的扩展。
//控制点 GLint nNumPoints = 3; GLfloat ctrlPoints[3][3][3]= {{{ -4.0f, 0.0f, 4.0f},
{ -2.0f, 4.0f, 4.0f},
{ 4.0f, 0.0f, 4.0f }}, {{ -4.0f, 0.0f, 0.0f},
{ -2.0f, 4.0f, 0.0f},
{ 4.0f, 0.0f, 0.0f }}, {{ -4.0f, 0.0f, -4.0f},
{ -2.0f, 4.0f, -4.0f},
{ 4.0f, 0.0f, -4.0f }}}; //画控制点 void DrawPoints(void)
{ int i,j; glColor3f(1.0f, 0.0f, 0.0f); //把点放大一点,看得更清楚 glPointSize(5.0f); glBegin(GL_POINTS);
for(i = 0; i < nNumPoints; i++)
for(j = 0; j < 3; j++)
glVertex3fv(ctrlPoints[i][j]);
glEnd();
}
void RenderScene(void)
{
// Clear the window with current clearing color
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 保存模型视图矩阵
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
//旋转一定的角度方便观察
glRotatef(45.0f, 0.0f, 1.0f, 0.0f);
glRotatef(60.0f, 1.0f, 0.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); //设置映射方式,只需要设置一次可以在SetupRC中调用。
glMap2f(GL_MAP2_VERTEX_3, //生成的数据类型
0.0f, // u的下界
10.0f, //u的上界
3, //数据中点的间隔
3, //u方向上的阶
0.0f, //v的下界
10.0f, //v的上界
9, // 控制点之间的间隔
3, // v方向上的阶
&ctrlPoints[0][0][0]); //控制点数组
//启用求值器
glEnable(GL_MAP2_VERTEX_3);
//从0到10映射一个包含10个点的网格
glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);
// 计算网格
glEvalMesh2(GL_LINE,0,10,0,10);
//画控制点
DrawPoints();
glPopMatrix(); glutSwapBuffers();
}
在这里我们用glMap2f替换了之前的glMap1f, 这个函数指定了u和v两个域上的点。除了指定u的上界和下界之外,还要指定v的上界和下界。v定义域内点的距离是9,因为这里使用了3维数组,包含了3个u值,每个u值又包含了3个点,3x3=9。然后指定v方向上的阶,即每个u分支上v方向有多少个点。最后一个参数是指向控制点的指针。
然后我们设置求值器.
//启用求值器
glEnable(GL_MAP2_VERTEX_3);
//从0到10映射一个包含10个点的网格
glMapGrid2f(10,0.0f,10.0f,10,0.0f,10.0f);
计算网格网格表面,用线的方式表示。
// 计算网格
glEvalMesh2(GL_LINE,0,10,0,10);
光照和法线
求值器还可以帮我们生成表面的法线,只需简单的修改一些代码:
把glEvalMesh2(GL_LINE, 0, 10, 0, 10);替换为glEvalMesh2(GL_FILL, 0, 10, 0, 10);然后在初始化时 SetupRC中调用glEnable(GL_AUTO_NORMAL);就可以得到一个收到光照的曲面了。
OpenGL超级宝典笔记——贝塞尔曲线和曲面(转)的更多相关文章
- OpenGL超级宝典笔记----框架搭建
自从工作后,总是或多或少的会接触到客户端3d图形渲染,正好自己对于3d图形的渲染也很感兴趣,所以最近打算从学习OpenGL的图形API出发,进而了解3d图形的渲染技术.到网上查了一些资料,OpenGL ...
- OpenGL超级宝典笔记----渲染管线
在OpenGL中任何事物都在3D空间中,但是屏幕和窗口是一个2D像素阵列,所以OpenGL的大部分工作都是关于如何把3D坐标转变为适应你屏幕的2D像素.3D坐标转为2D坐标的处理过程是由OpenGL的 ...
- 【转】OpenGL超级宝典笔记——纹理映射Mipmap
原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人. 目录[-] Mipmapping Mipmap过滤 构建Mip层 ...
- 【转载】OpenGL超级宝典笔记——GLSL语言基础
变量 GLSL的变量命名方式与C语言类似.变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量.当然还有一些 ...
- OpenGL超级宝典笔记——画三角形(转)
http://my.oschina.net/sweetdark/blog/161002 学习了画线的知识,我们可以使用GL_LINE_LOOP来画闭合的多边形.但是使用这种方式画出来的只有线框,多边形 ...
- OpenGL超级宝典笔记——遮挡查询 [转]
目录[-] 遮挡查询之前 包围体 遮挡查询 在一个场景中,如果有有些物体被其他物体遮住了不可见.那么我们就不需要绘制它.在复杂的场景中,这可以减少大量的顶点和像素的处理,大幅度的提高帧率.遮挡查询就是 ...
- OpenGL超级宝典笔记——深度纹理和阴影 【转】
目录[-] 光源视角 新型的纹理 深度纹理的大小 首先绘制阴影 然后是光照 投影阴影贴图 阴影比较 之前我们介绍过简单的把物体压平到投影平面来制造阴影.但这种阴影方式有其局限性(如投影平面须是平面). ...
- win8+VS2012搭建OpenGL超级宝典的环境
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/booirror/article/details/36957799 自从公司搬到腾讯附近,每天上班都迟 ...
- OpenGL超级宝典visual studio 2013开发环境配置,GLTools
做三维重建需要用到OpenGL,开始看<OpenGL超级宝典>,新手第一步配置环境就折腾了一天,记录下环境的配置过程. <超级宝典>中的例子使用了GLEW,freeglut以及 ...
随机推荐
- 百度全站 https FAQ:技术宅告诉你如何搜索更安全
百度从 14 年开始对外开放了 https 的访问,并于 3 月初正式对全网用户进行了 https 跳转. 你也许会问,切换就切换呗,和我有啥关系?我平常用百度还不是照常顺顺当当的,没感觉到什么切换. ...
- RecyclerView 使用指南
最近看了很多 RecyclerView 的使用文章,一直晕乎乎的,完全不知道套路是啥.很多人都是直接上代码,但是却没有详细说明代码的使用,于是打算自己写写,理理思路.顺便帮助那些正在学习 Androi ...
- zuul超时及重试配置
配置实例 ##timeout config hystrix: command: default: execution: timeout: enabled: true isolation: thread ...
- [转]关于Linux安装mysql默认配置文件位置
本文转自:https://blog.csdn.net/smile___you/article/details/54409073 在linux下面安装mysql如果在/etc下面没有存在my.cnf配置 ...
- WCF发布多个服务
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WcfServi ...
- C# 导出Excel表格
function exportExcel() { //查询条件 var startTime = F.ui.startTime.getText() == "" ? null : F. ...
- SQL - 循环
declare @itemnumber int --定义需要循环的次数 declare @tagint int --定义标志字段,用于结束循环 select @itemnumber = count(d ...
- Java ArrayList 数组之间相互转换
做研发的朋友都知道,在项目开发中经常会碰到list与数组类型之间的相互转换,本文通过一个简单的例子给大家讲解具有转换过程. package test.test1; import java.util.A ...
- 【Java基础】8、java中的native方法
native是与C++联合开发的时候用的!java自己开发不用的! 使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用.这些函 ...
- Android插件化
http://www.androidblog.cn/index.php/Index/detail/id/16# Android Hotfix 新方案——Amigo 源码解读 https://www.d ...