Surface Normal Vector in OpenCascade
Surface Normal Vector in OpenCascade
摘要Abstract:表面上某一点的法向量(Normal Vector)指的是在该点处与表面垂直的方向。对于平面,其上各点的法向是一样的,统一为这个平面的法向。对于曲面,各点具有不同的法向量。几何对象的法向量定义了它在空间中的方向,法向量是在进行光照处理时的重要参数。所以在显示造型算法离散曲面后的网格时,设置正确的法向量对场景的光照、光线追踪效果有直接影响。本文结合OpenCascade中代码,对其法向量的计算方法进行分析,稍加修改即可用到实际的程序中。
关键字Key Words:OpenCascade, Normal Vector, Mesh Normal, OpenSceneGraph,
一、引言 Introduction
表面上某一点的法向量(Normal Vector)指的是在该点处与表面垂直的方向。对于平面,其上各点的法向是一样的,统一为这个平面的法向。对于曲面,因为它在计算机图形中是由许多片小平面的多边形逼近来表示的,所以每个顶点的法向量都不一样。因此,曲面上每个点的法向量计算就可以根据不同的应用有不同的算法,则最后的显示效果也是不同的。几何对象的法向量定义了它在空间中的方向,法向量是在进行光照处理时的重要参数。因为法向量决定了该如何计算光照,决定了该点能够吸收多少光照。
OpenGL有很大的灵活性,它只提供赋予当前顶点法向量的函数,并不在内部具体计算其法向量,这个值由编程者自己根据需要设置。尽管法向量并不需要指定为单位向量,但是如果所有表面法向量都使用单位法向量可减少计算量。使用下列命令可自动将所有非单位法向量单位化:glEnable(GL_NORMALIZE),该命令也会对那些经过缩放或错切等几何变换的表面向量进行规范化。另一可用选项是指定一个法向量列表,与顶点数组混合使用。
在很多应用程序中网格上的各顶点都需要一个表面法向量,它的用途很广泛:
l 计算光照;
l 背面剔除;
l 模拟粒子系统在表面的“弹跳”效果;
l 对只需要正面而加速碰撞检测;
通常我们在绘制几何体时都会指定法向量。当得到一个模型本身没有法向量时,则有必要通过现有的数据生成。通常表面法向量可能保存于三角形级或顶点级,其中的一个技巧就是平均相邻三角形的表面法向量,并将结果规范化。一般可以这样假设三角形的顶点按逆时针排列,通过叉乘就可以得到外表面的法向量了。当然有些有情况下,顶点的顺序是未知且比较混乱的,这样就比较麻烦了。这个笔者也没有仔细深入研究,推荐读者看一下《计算非固定结构序列的多边形的顶点法线》这篇论文。通过平均三角形法向量求得顶点法向量是一种经验性的方法,不具有通用性,虽然很多情况下可以正确地工作,但有些情况下还是无法正常使用的。(以上内容摘自《OpenSceneGraph三维渲染引擎编程指南》)
二、计算法向量 Finding Surface Normal Vectors
OpenGL并不能自动计算几何对象的法向量,而只能由用户显式指定。法向量的计算是一个纯粹的几何和数学问题,这里只简略地区分了几种情况。
2.1 计算平面的法向量
首先,讲述平面法向量的计算方法。在平面内,有两条相交的线段,假设其中一条为矢量W,另一条为矢量V,且平面法向量为N。如图2.1所示,则平面法向量就等于两个矢量的叉积(遵循右手法则),即N=W x V。
![]()
Figure 2.1 Normal Vector of Plane
比如计算一个三角形的法向就可以用它的三个顶点来计算,如图2.2所示:
![]()
Figure 2.2 Finding the normal vector of a triangle
2.2 计算解析曲面的法向量
解析曲面是由数学方程描述的平滑的、可微曲面。在OpenCascade中曲面是由Geom_Surface来用参数u, v来表示的,相当于曲面的数学方程,是曲面的精确表示。通过计算曲面上一点u,v对应的一次微分即可得到曲面在该点处的切线,如下图所示:
![]()
Figure 2.3 Tangents on a surface
关于参数u,v表示的Bezier曲面的微分计算方法如下所示:
![]()
若需要计算参数对应点处的法向量,还需要对这两个切向量进行叉乘即可,计算方法如下所示:
![]()
![]()
Figure 2.4 Normal on a surface
2.3 计算多边形的法向量
在OpenGL中,这种情况占了大多数。求平均多边形的法向量,利用不在同一直线上的多边形三个顶点v1, v2, v3,则两个矢量的叉积((v2 - v1)x(v3 - v1))垂直于多边形,即为该多边形的法向量,计算后需要经过规范化处理。
对于求多边形网格上各顶点上的法向量,由于每个顶点同时位于几个不同的多边形边界上,则需要求出周围几个多边形的法向量,然后做加权平均。一般来说,可以使用每个多边形的面积做为加权的权值。
如下图所示,曲面顶点P的法向就等于其相邻的四个平面的法向平均值:
![]()
Figure 2. 曲面顶点的平均法向计算
注:当计算多边形网格表示的曲面时,最好是使用平均法向的方法来计算。当曲面是用参数方程来表示时,就可以用求微分和叉乘的方法来直接计算法向量,不再需要使用平均法向了。
三、程序实现 Demo Code
3.1 OpenCascade中曲面法向量的计算 Compute normal in OpenCascade
在OpenCascade中将形状数据保存为STL格式时就涉及到了将形状三角剖分及其法向量的计算实现。其计算方法如上所述,也是分成三种方式:
l 参数方程表示的曲面;
l 平面;
l 网格;
将其代码列出如下:
//function computes normals for surface
static void Normal(const TopoDS_Face& aFace,
Poly_Connect& pc,
TColgp_Array1OfDir& Nor)
{
const Handle(Poly_Triangulation)& T = pc.Triangulation();
BRepAdaptor_Surface S;
Standard_Boolean hasUV = T->HasUVNodes();
Standard_Integer i;
TopLoc_Location l;
Handle(Geom_Surface) GS = BRep_Tool::Surface(aFace, l); if (hasUV && !GS.IsNull()) {
Standard_Boolean OK = Standard_True;
gp_Vec D1U,D1V;
gp_Vec D2U,D2V,D2UV;
gp_Pnt P;
Standard_Real U, V;
CSLib_DerivativeStatus Status;
CSLib_NormalStatus NStat;
S.Initialize(aFace, Standard_False);
const TColgp_Array1OfPnt2d& UVNodes = T->UVNodes();
if (!S.GetType() == GeomAbs_Plane) {
for (i = UVNodes.Lower(); i <= UVNodes.Upper(); i++) {
U = UVNodes(i).X();
V = UVNodes(i).Y();
S.D1(U,V,P,D1U,D1V);
CSLib::Normal(D1U,D1V,Precision::Angular(),Status,Nor(i));
if (Status != CSLib_Done) {
S.D2(U,V,P,D1U,D1V,D2U,D2V,D2UV);
CSLib::Normal(D1U,D1V,D2U,D2V,D2UV,Precision::Angular(),OK,NStat,Nor(i));
}
if (aFace.Orientation() == TopAbs_REVERSED) (Nor(i)).Reverse();
}
}
else {
gp_Dir NPlane;
U = UVNodes(UVNodes.Lower()).X();
V = UVNodes(UVNodes.Lower()).Y();
S.D1(U,V,P,D1U,D1V);
CSLib::Normal(D1U,D1V,Precision::Angular(),Status,NPlane);
if (Status != CSLib_Done) {
S.D2(U,V,P,D1U,D1V,D2U,D2V,D2UV);
CSLib::Normal(D1U,D1V,D2U,D2V,D2UV,Precision::Angular(),OK,NStat,NPlane);
}
if (aFace.Orientation() == TopAbs_REVERSED) NPlane.Reverse();
Nor.Init(NPlane); }
}
else {
const TColgp_Array1OfPnt& Nodes = T->Nodes();
Standard_Integer n[];
const Poly_Array1OfTriangle& triangles = T->Triangles(); for (i = Nodes.Lower(); i <= Nodes.Upper(); i++) {
gp_XYZ eqPlan(, , );
for (pc.Initialize(i); pc.More(); pc.Next()) {
triangles(pc.Value()).Get(n[], n[], n[]);
gp_XYZ v1(Nodes(n[]).Coord()-Nodes(n[]).Coord());
gp_XYZ v2(Nodes(n[]).Coord()-Nodes(n[]).Coord());
eqPlan += (v1^v2).Normalized();
}
Nor(i) = gp_Dir(eqPlan);
if (aFace.Orientation() == TopAbs_REVERSED) (Nor(i)).Reverse();
}
} }
如果是参数方程表示的曲面,若不是平面,则根据切线的叉乘来计算各顶点处的法向量;若是平面,则只计算一个顶点处理的法向量,减少计算量。 若是离散后的网格面,则根据三角形的法向量的计算方法来计算每个顶点处的法向量。
3.2 OpenSceneGraph中网格曲面的法向量计算 Compute normal in OpenSceneGraph
生成顶点法向量(osgUtil::SmoothingVisitor)类继承自osg::NodeVisitor类,采用Visitor模式,遍历场景中的几何体,生成顶点法向量。osgUtil::SmoothingVisitor的使用很方便。对算法实现感兴趣的读者可以结合源程序来理解研究。
四、结论 Conclusion
OpenCascascade中有曲面的参数表示,所以对这类曲面可以得用参数方程计算出曲面上的顶点的准确法向量。对于没有参数表示的网格曲面,可以用平均法向量的方法来计算出一个法向量。
OpenSceneGraph中也有快速计算网格曲面法向量的类osgUtil::SmoothingVisitor。
五、参考资料 References
1. Kelly Dempski, Focus on Curves and Surfaces, Premier Press, 2003
2. 王锐,钱学雷,OpenSceneGraph三维渲染引擎设计与实践,清华大学出版社
3. 肖鹏,刘更代,徐明亮,OpenSceneGraph三维渲染引擎编程指南,清华大学出版社
PDF Version: Surface Normal Vector
Surface Normal Vector in OpenCascade的更多相关文章
- Surface Normal Averaging
Surface Normal Averaging eryar@163.com 摘要Abstract:正确设置网格面上点的法向,对几何体在光照等情况下显示得更真实,这样就可以减少顶点数量,提高渲染速度. ...
- Normal Vector Using WorldInverseTranspose
shader里面经常看到normal向量是用WorldInverseTranspose矩阵做变换的,有时候也可以用WorldMatrix变换. 原理: If your object is only e ...
- unity, 让主角头顶朝向等于地面法线(character align to surface normal)
计算过程如下: 1,通过由主角中心raycast一条竖直射线获得主角所在处地面法线,用作主角的newUp. 注:一定要从主角中心raycast,而不要从player.transform.positio ...
- OpenCASCADE Face Normals
OpenCASCADE Face Normals eryar@163.com Abstract. 要显示一个逼真的三维模型,其顶点坐标.顶点法向.纹理坐标这三个信息必不可少.本文主要介绍如何在Open ...
- WikiBooks/Cg Programming
https://en.wikibooks.org/wiki/Cg_Programming Basics Minimal Shader(about shaders, materials, and gam ...
- Generating Complex Procedural Terrains Using GPU
前言:感慨于居然不用tesselation也可以产生这么复杂的地形,当然致命的那个关于不能有洞的缺陷还是没有办法,但是这个赶脚生成的已经足够好了,再加上其它模型估 计效果还是比较震撼的.总之好文共分享 ...
- BumpMapping [转]
http://fabiensanglard.net/bumpMapping/index.php Fabien Sanglard's Website Home About FAQ Email Rss T ...
- Surface Shader
Surface Shader: (1)必须放在SubShdader块,不能放在Pass内部: (2)#pragma sufrace surfaceFunction lightModel [option ...
- Vector Math for 3D Computer Graphics (Bradley Kjell 著)
https://chortle.ccsu.edu/VectorLessons/index.html Chapter0 Points and Lines (已看) Chapter1 Vectors, P ...
随机推荐
- 版本控制--github相关
安装 Git 后,你应该做一些只需做一次的事情:系统设置——这样的设置在每台电脑上只需做一次: $ git config --global user.name "Your Name" ...
- vi命令模式下快速注释代码的方法
进入http://www.vim.org/scripts/script.php?script_id=1528 点击这个链接下载comments.vim这个插件 然后把它放入到./vim/plugin下 ...
- premierepro破解
1.安装硬解 360云盘 破解教程:mac sky/破解安装说明.rtf 破解软件:mac sky/Smart Adobe CS6 Blocker v1.1.app 安装文件:mac sky/Prem ...
- 【转】COM技术内幕(笔记)
COM技术内幕(笔记) COM--到底是什么?--COM标准的要点介绍,它被设计用来解决什么问题?基本元素的定义--COM术语以及这些术语的含义.使用和处理COM对象--如何创建.使用和销毁COM对象 ...
- MSVCRTD.lib(mfc.obj) : error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainC (转)
一.问题描述 我所使用的编程环境:VS2010 出现的问题如下: MSVCRTD.lib(mfc.obj) : error LNK2019: 无法解析的外部符号_WinMain@16,该符号在函数 _ ...
- 形参是ofstream
今天写了一段代码报错 void GetEigenvalue(pcl::PointCloud<pcl::PointXYZ>::ConstPtr cloud, vector<int> ...
- ABP入门系列(4)——领域层定义仓储并实现
一.先来介绍下仓储 仓储(Repository): 仓储用来操作数据库进行数据存取.仓储接口在领域层定义,而仓储的实现类应该写在基础设施层. 在ABP中,仓储类要实现IRepository接口,接口定 ...
- Replication的犄角旮旯(六)-- 一个DDL引发的血案(上)(如何近似估算DDL操作进度)
<Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...
- mongoDB研究笔记:分片集群的工作机制
上面的(http://www.cnblogs.com/guoyuanwei/p/3565088.html)介绍了部署了一个默认的分片集群,对mongoDB的分片集群有了大概的认识,到目前为止我们还没有 ...
- 小谈 - web模仿手机打电话与正则表达式
昨天遇到了一个很棘手的问题,就是手机端调用web端的页面,如果用编辑器插入的内容页面中有电话的的数据就要变一下格式,让手机端可以实现拨号的功能. 研究了半天就是没一点头绪,但是偶尔看到数据中每一个电话 ...