vtk教程第三章 计算机图形学入门

2023-11-12

计算机图形学是数据可视化的基础。实际上,可视化是将数据转换为一组图形原语的过程。然后使用计算机图形学的方法将这些原语转换成图片或动画。本章讨论计算机图形学的基本原理。我们从描述光和物理物体如何相互作用形成我们所看到的开始。接下来,我们将研究如何使用计算机图形技术模拟这些交互作用。硬件问题在这里扮演着重要的角色,因为现代计算机对图形有内置的硬件支持。本章最后以一系列的例子来说明我们的三维计算机图形的面向对象模型。

3.1简介

计算机图形学是利用计算机生成图像的过程。我们称这个过程为渲染。有许多类型的渲染过程,从2D绘制程序到复杂的3D技术。在本章中,我们将重点介绍可视化的基本3D技术。

我们可以把渲染看作是把图形数据转换成图像的过程。在数据可视化中,我们的目标是将数据转换为图形数据或图形原语,然后进行呈现。我们渲染的目标与其说是照片真实感,不如说是信息内容。我们还努力实现交互式图形显示,从而可以直接操作底层数据。本章解释了从图形数据渲染图像的过程。我们从观察我们周围世界中的灯光、摄像机和物体(或演员)相互作用的方式开始。在此基础上,我们解释了如何在计算机上模拟这一过程。

渲染的物理描述

图3 - 1展示了一个简化的视图,当我们观察一个对象时,在这种情况下是一个立方体。光线从光源向各个方向发射。(在这个例子中,我们假设光源是太阳。)其中一些光线恰好照射到立方体上,立方体的表面吸收了一些入射光,并反射了其余的光。其中一些反射光可能朝向我们,进入我们的眼睛。

如果发生这种情况,那么我们就能“看到”这个物体。同样地,太阳光的一部分照射到地面,其中一小部分会反射到我们的眼睛里。正如你可以想象的那样,来自太阳的光线穿过太空击中相对较小的行星上的小物体的几率很低。光线从物体反射到我们眼睛的几率很小。我们能看到的唯一原因是太阳产生了如此巨大的光,它压倒了概率。

虽然这在现实生活中可行,但试图用计算机模拟它可能很困难。幸运的是,还有其他方法来看待这个问题。一种常见而有效的三维计算机图形技术被称为光线追踪或光线投射。光线追踪通过跟踪每条光线的路径来模拟光与物体的相互作用。通常情况下,我们跟随光线从观众的眼睛反向进入世界,以确定光线击中了什么。射线的方向是我们正在看的方向(即,视图方向),包括透视效果(如果需要的话)。当光线与物体相交时,我们可以确定这一点是否被光源照射。这是通过追踪光线从交点到光来完成的。如果光线与光线相交,那么这个点就被照亮了。如果光线在到达光线之前与其他物体相交,那么这个光线就不能照亮这个点。对于多个光源,我们只是对每个光源重复这个过程。所有光源的总贡献,加上任何环境散射光,将决定该点的总光照或阴影。通过追踪光线的反向路径,光线追踪只观察最终进入观察者眼睛的光线。这极大地减少了必须由模拟程序计算的射线数量。

在将光线追踪描述为渲染过程之后,许多图形社区成员不使用它可能会让人感到惊讶。这是因为光线追踪是一种相对较慢的图像生成方法,因为它通常是在软件中实现的。其他图形技术已经开发出来,使用专用的计算机硬件生成图像。为了理解为什么会出现这种情况,简要地研究一下计算机图形学的分类和历史是有指导意义的。

渲染过程可以分为两类:图像顺序和对象顺序。

光线追踪是一个像序过程。它的工作原理是确定每一束光的变化,一次一束。对象顺序处理通过每次呈现一个对象来工作。在上面的例子中,对象顺序技术首先呈现地面,然后是立方体。

从另一个角度来看,考虑画一幅谷仓的画。使用图像顺序算法,你可以从画布的左上角开始,滴下正确颜色的颜料。(每一滴颜料被称为一个图片元素或像素。)然后你会向右移动一点,再滴一滴颜料。你可以继续,直到你到达画布的右边缘,然后你可以向下移动一点,开始下一行。每次你放下一滴颜料,你都要确保它是画布上每个像素的正确颜色。当你完成时,你将有一个谷仓的画。另一种方法是基于更自然的(至少对许多人来说)对象顺序过程。我们通过在场景中绘制不同的对象来工作,独立于对象在场景中的实际位置。我们可以从后到前画,从前到后画,或者以任意顺序画。

例如,我们可以先画天空,然后再加上地面。在这两个物体被画完之后,我们将添加谷仓。在图像排序过程中,我们以非常有序的方式在画布上工作;从左到右,从上到下。使用对象顺序过程,我们倾向于从画布的一个部分跳转到另一个部分,这取决于我们正在绘制的对象。计算机图形学领域开始使用对象顺序过程。早期的许多工作都与硬件显示设备紧密相关,最初是矢量显示。这只是一个示波器,但它鼓励图形数据以一系列线段的形式绘制。随着最初的矢量显示让位于目前普遍存在的光栅显示,将图形数据表示为一系列待绘制对象的概念被保留了下来。由IBM的Bresenham [Bresenham65]开创的大部分早期工作都集中在如何正确地将线段转换为适合直线绘图仪的形式。在取代示波器的光栅显示器上呈现线条的任务上也进行了同样的工作。从那时起,硬件变得更加强大,能够显示比行复杂得多的原语。

直到20世纪80年代初,Turner Whitted [Whitted80]的一篇论文才促使许多人从更物理的角度来看待渲染。最终,光线追踪成为传统对象顺序渲染技术的有力竞争者,部分原因是它可以生成高度逼真的图像。对象顺序渲染一直很受欢迎,因为有大量的图形硬件设计用于快速渲染对象。光线追踪往往不需要任何专门的硬件,因此是一个耗时的过程。

表面与体绘制

本文对这一点的讨论已经默认了,当我们渲染一个物体时,我们正在观察物体的表面及其与光的相互作用。然而,常见的物体,如云、水和雾,是半透明的,或者散射穿过它们的光。这样的对象不能使用仅基于表面相互作用的模型来渲染。相反,我们需要考虑对象内部不断变化的属性,以正确地呈现它们。我们将这两种渲染模型称为表面渲染(即渲染物体的表面)和体渲染(即渲染物体的表面和内部)。

一般来说,当我们使用表面渲染技术渲染一个对象时,我们用表面描述(如点、线、三角形、多边形或2D和3D样条)对对象进行数学建模。物体的内部没有被描述,或者只是从表面表示中隐式地表示出来(即,表面是体积的边界)。虽然技术确实存在,允许我们使表面透明或半透明,但仍然有许多现象不能单独使用表面渲染技术来模拟(例如,散射或光发射)。如果我们试图渲染物体内部的数据,例如来自CT扫描的x射线强度,则尤其如此。

体绘制技术可以让我们看到物体内部的不均匀性。在前面的CT例子中,我们可以通过考虑数据的表面和内部的强度值来真实地再现x射线图像。虽然现在在本文中描述这个过程还为时过早,但您可以想象一下扩展上一节中的光线跟踪示例。

因此,射线不仅与物体表面相互作用,也与物体内部相互作用。在本章中,我们将重点介绍表面渲染技术。虽然不像体绘制那样强大,但表面绘制被广泛使用,因为与体绘制技术相比,它相对更快,并且允许我们为各种数据和对象创建图像。第7章更详细地描述了体绘制。

可视化而非图形

虽然作者会喜欢提供计算机图形学的全面论述,但这样的论述超出了本文的范围。相反,我们区分了可视化(探索、转换和映射数据)和计算机图形学(映射和渲染)。重点将是可视化的原理和实践,而不是3D计算机图形。在本章和第7章中,我们将介绍3D计算机图形的基本概念和工作知识。对于那些对这一领域更感兴趣的人,我们建议你参考本章末尾第77页的“书目注释”中推荐的文本。

关于这个姿势,我们有一个遗憾,那就是某些渲染技术本质上是可视化技术。我们可以在前面的段落中看到这一点,在那里我们使用术语“映射”来描述可视化和计算机图形。可视化和图形之间目前没有,将来也可能永远不会有明确的区别。例如,许多研究人员认为体绘制完全属于可视化领域,因为它解决了可视化数据最重要的形式之一。我们的区分主要是为了我们自己的方便,并为我们提供了完成这篇文章的机会。我们建议一个认真学习可视化的学生用计算机图形学和体绘制的更深入的书籍来补充这里提供的材料。

在接下来的几页中,我们将更详细地描述呈现过程。我们从描述几种颜色模型开始。接下来,我们将研究渲染过程的主要组件。这里有光源,如太阳,我们希望渲染的物体,如立方体或球体(我们将这些物体称为actor),还有一个面向世界的摄像机。这些术语来自电影行业,大多数人都很熟悉。actor表示图形数据或对象,灯光照亮actor,摄像机通过将actor投射到视图平面来构造一幅图像。我们将灯光、摄像机和演员的组合称为场景,并将渲染过程称为渲染场景。

3.2颜色

人类可见的电磁波谱包含大约400到700纳米的波长。进入我们眼睛的光由这些波长的不同强度组成,如图3 - 2所示。这个强度图定义了光的颜色,因此不同的强度图会产生不同的颜色。不幸的是,我们可能没有注意到这些差异,因为人眼会丢弃大部分信息。人眼中有三种颜色感受器,叫做视锥细胞。每种类型都响应400到700纳米波长范围的子集,如图3 - 3所示。我们看到的任何颜色都被我们的眼睛编码成这三种重叠的反应。这大大减少了实际进入我们眼睛的信息量。因此,人眼无法识别任何颜色之间的差异,当将其强度曲线应用于人眼的反应曲线时,会产生相同的三重反应。这也意味着我们可以在计算机中使用简化的形式存储和表示颜色,而人眼无法识别其中的差异。我们用来描述颜色的两个简化组件系统是RGB和HSV颜色系统。RGB系统基于红色、绿色和蓝色的强度来表示颜色。这可以看作是一个三维空间,轴是红色、绿色和蓝色。一些常见的颜色及其RGB组成如图3 - 4所示。HSV系统根据颜色的色相、饱和度和值来表示颜色。值分量也称为亮度或强度分量,表示颜色中有多少光。0.0值总是会给你黑色,1.0值会给你一些明亮的东西。色相代表颜色的主导波长,通常用图3 - 5所示的圆圈表示。这个圆的圆周上的每个位置都代表不同的色调和

图3-3人类视网膜中三种视锥细胞对光的相对吸收[Dartnall83]。

可以使用角度指定。当我们指定色相时,我们使用从0到1的范围,其中0对应于色相圆上的0度,1对应于360度。饱和度表示颜色中混合了多少色相。例如,我们可以将值设置为1,这将为我们提供明亮的颜色,并将色调设置为0.66,为我们提供蓝色的主要波长。现在,如果我们将饱和度设置为1,颜色将是明亮的原色蓝色。如果我们将饱和度设置为0.5,颜色将是天蓝色,一种混合了更多白色的蓝色。如果我们将饱和度设置为零,这表明在颜色中没有比任何其他波长更多的主导波长(色相)。结果,最终的颜色将是白色(不管色调值)。图3 - 4列出了一些常见颜色的HSV值。

图3-4 RGB和HSV空间常用颜色

图3-5顶部为色调的圆形表示。底部的另外两张图像是通过HSV颜色空间的切片。第一个切片的值为1.0,另一个切片的值为0.5。

3.3灯

控制渲染过程的主要因素之一是光线与场景中演员的相互作用。如果没有灯光,得到的图像将是黑色的,而且信息量不大。在很大程度上,是发出的光与场景中演员的表面(在某些情况下是内部)之间的相互作用决定了我们所看到的东西。一旦光线与场景中的演员相互作用,我们的相机就有东西可看了。在计算机图形学中使用的许多不同类型的光源中,我们将讨论最简单的,无限远的点光源。与我们在家里和工作场所使用的灯相比,这是一个简化的模型。我们习惯使用的光源通常来自空间中的某个区域(白炽灯中的灯丝,或荧光灯中的发光气体)。点源照明模型假设光从空间中的一个点向各个方向发射。对于一个无限的光源,我们假设它的位置与被照亮的地方有无限远的距离。这很重要,因为这意味着来自这样一个源的入射射线将彼此平行。局部光源的发射,如房间里的灯,是不平行的。图3 - 6说明了有限体积的局部光源与

图3-6有限体积的局部光源与无限体积的点光源

一个无限的点光源。我们无限光源发出的光的强度在传播过程中也保持不变,而不是实际的1/距离2

关系物理光服从。

正如你所看到的,这是一个很大的简化,这将允许我们使用不那么复杂的照明方程。

3.4表面性质

当光线穿过空间时,其中一些会与我们的作用体相交。当这种情况发生时,光线与演员的表面相互作用产生一种颜色。造成这种颜色的部分原因实际上不是由于直接的光线,而是由于周围的光线被其他物体反射或散射。环境照明模型解释了这一点,它是对现实世界中发生的复杂光散射的简单近似。它将光源的强度曲线应用于物体的颜色,也表示为强度曲线。结果就是我们在看那个物体时所看到的光的颜色。有了这样一个模型,重要的是要认识到,照射在蓝球上的白光与照射在白球上的蓝光是没有区别的。环境照明方程为

其中Ra是由于环境照明产生的强度曲线,Lc是环境光的强度曲线,Oa是物体的颜色曲线。为了使方程简单,我们假设所有的方向向量都是标准化的(即大小为1)。结果颜色的两个组成部分取决于直接照明。漫射照明,也被称为兰伯反射,考虑到光在物体上的入射角。图3 - 7显示了一个圆柱体的图像,当你从它的中心横向移动时,它变得更暗。圆柱体的颜色是恒定的;照射在圆柱体表面的光量会发生变化。在中心,入射光几乎垂直于圆柱体的表面,它在每个表面积上接收到更多的光线。当我们向侧面移动时,亮度下降,直到最后入射光与圆柱体的侧面平行,因此强度为零。

图3-7 Flat和Gouraud遮阳。不同的着色方法可以极大地改善用多边形表示的对象的外观。在顶部,平面阴影使用一个固定的表面法线横跨每个多边形。在底部,Gouraud着色从多边形顶点插值法线,以提供更平滑的外观。

漫射照明的贡献如公式3-2所示,如图3- 8所示。

其中Rd是由于漫射照明产生的强度曲线,Lc是光的强度曲线,Oc是物体的颜色曲线。注意,漫射光是入射光矢量和物体表面法线之间相对角度的函数。因此,漫射照明是独立于观众的位置。

高光照明代表光源在闪亮物体上的直接反射。图3 - 10显示了一个漫射光球,具有不同的镜面反射。高光强度(在顶部和底部行之间变化)控制高光照明的强度。高光功率表示物体有多闪亮,更具体地说,它表示当反射角度偏离完美反射时,高光反射减少的速度有多快。数值越高表明下降越快,因此表面越亮。根据图3 - 9,镜面照明的方程为

其中为相机的投影方向,为镜面反射方向。

我们分别给出了不同照明模型的方程。我们可以同时应用所有的照明模型或组合。公式3-4将环境光、漫射光和镜面光结合到一个方程中。

结果是物体表面某一点上的颜色。常量、和控制物体的环境光、漫射光和高光的相对数量。常数,

,并指定每种照明所使用的颜色。这六个常数

图3-10镜面系数的影响。镜面系数控制表观

物体的“光泽”。顶部一行的镜面强度值为0.5;下面一行是1.0。

高光功率沿水平方向变化。从左到右依次为5、10、20和40 (SpecularSpheres)。cxx)。

具有高光功率的部分是表面材料的性质。(其他属性,如透明度将在后面的章节中介绍。)这些属性值的不同组合可以模拟钝塑料和抛光金属。该方程假设有一个无限的点光源,如第39页的“光”所述。然而,等式可以很容易地修改,以纳入其他类型的定向照明。

3.5摄像头

我们有发射光线的光源和具有表面性质的物体。在actor表面的每个点上,这种交互作用都会产生一些复合颜色(例如,来自光、物体表面、镜面和环境效果的组合颜色)。我们现在渲染场景所需要的是一个相机。有许多重要因素决定了3D场景如何投射到平面上以形成2D图像(参见图3 - 11)。这些是相机的位置、方向和焦点,相机投影的方法,以及相机剪辑平面的位置。

相机的位置和焦点定义了相机的位置和它指向的地方。从相机位置到焦点所定义的矢量称为投影方向。摄像机图像平面位于焦点处,通常垂直于投影矢量。相机的方向是由位置和焦点加上相机视角向上矢量控制的。这些都完全定义了相机视图。投影方法控制如何将角色映射到图像平面。

正字法投影是一种并行映射过程。在正投影(或平行投影)中,所有进入相机的光线都平行于投影矢量。透视投影

当所有光线都经过一个共同的点(即,视点或投影中心)时发生。要应用透视投影,必须指定透视角度或相机视角。

前、后裁剪平面与投影向量相交,且通常垂直于投影向量。裁剪平面用于消除太靠近相机或太远的数据。因此,只有裁剪平面内的参与者或参与者的部分(潜在地)可见。剪切平面通常垂直于投影方向。它们的位置可以通过相机的剪辑范围来设置。平面的位置从相机的位置沿投影方向测量。前剪平面为最小范围值,后剪平面为最大范围值。稍后在第7章中,当我们讨论立体渲染时,我们将看到不垂直于投影方向的裁剪平面的例子。

这些相机参数一起定义了一个矩形金字塔,其顶端在相机的位置,并沿投影方向延伸。所述金字塔顶部用前剪平面截断,底部用后剪平面截断。由此产生的视图截锥定义了相机可见的3D空间区域。虽然可以通过直接设置上面提到的属性来操作相机,但有一些常见的操作可以使工作更容易。

具体操作如图3 - 12和图3 - 13所示。改变相机的方位角将旋转其位置围绕其视角向上矢量,以焦点为中心。这可以看作是将相机向左或向右移动,同时始终保持到焦点的距离不变。改变相机的仰角旋转它的位置围绕它的投影方向的叉乘,并以焦点为中心向上看。这对应于上下移动相机。要滚动摄像机,我们旋转视图向上矢量关于视图平面法线。滚动有时被称为扭转。

接下来的两个动作保持相机的位置不变,而是修改焦点。改变偏航旋转焦点关于视图上的中心在相机的位置。这就像一个方位角,除了焦点移动而不是位置。俯仰的变化使焦点大约在投影方向的叉积和视点上方居中

相机的位置。进出移动相机的位置沿投影方向,或更接近或更远的焦点。此操作指定为其当前距离与新距离的比值。大于1的值将移动进来,而小于1的值将移动出去。最后,缩放改变相机的视角,这样或多或少的场景落在视图截锥内。一旦我们设置好摄像机,我们就可以生成2D图像。

一些穿过3D空间的光线会穿过相机上的镜头。然后这些射线照射在一个平面上产生图像。这有效地将我们的3D场景投影到2D图像中。相机的位置和其他属性决定了哪些光线被捕捉和投射。

更具体地说,只有光线相交于相机的位置,并在其视锥内,才会影响最终的2D图像。我们的渲染概述到此结束。光从光源到达行动者,在那里被反射和散射。其中一些光线被相机捕捉到,并产生了2D图像。现在我们来看看这个过程的一些细节。

3.6坐标系

计算机图形学中常用的坐标系有四种,其中的点有两种不同的表示方式(图3 - 14)。虽然这看起来有些过分,但每一个都是有目的的。我们使用的四个坐标系是:模型、世界、视图和显示。

模型坐标系是定义模型的坐标系,通常是局部笛卡尔坐标系。如果我们的一个参与者代表一个足球,它将基于一个自然的足球几何坐标系统(例如,一个圆柱形系统)。这个模型有一个固有的坐标系统,由生成它的人的决定决定。他们可能使用英寸或米作为他们的单位,并且足球可以用任意的轴作为它的主轴来建模。

世界坐标系是角色所处的3D空间。参与者的职责之一是将模型坐标转换为世界坐标。每个模型都有自己的坐标系,但世界坐标系只有一个。每个参与者必须缩放、旋转并将其模型转换为世界坐标系统。(建模者可能还需要将其自然坐标系转换为局部笛卡尔坐标系。这是因为参与者通常假定模型坐标系是局部笛卡尔坐标系。)世界坐标系统也是相机和灯光的位置和方向被指定的系统。视图坐标系统表示摄像机可见的内容。

它由一对x和y值组成,范围在(-1,1)和一个z深度坐标之间。x, y值指定图像平面中的位置,而z坐标表示距离摄像机的距离或范围。摄像机的属性由一个4 × 4的转换矩阵表示(稍后将进行描述),该矩阵用于将世界坐标转换为视图坐标。

这里介绍了相机的透视效果。显示坐标系统使用与视图坐标系统相同的基础,但不是使用负1到1作为范围,坐标是图像平面上实际的x, y像素位置。诸如显示上的窗口大小之类的因素决定了视图坐标范围(-1,1)如何映射到像素位置。这也是视口生效的地方。

你可能想渲染两个不同的场景,但是把它们显示在同一个窗口中。这可以通过将窗口划分为矩形视口来实现。然后,可以告诉每个渲染器应该使用窗口的哪个部分进行渲染。viewport在x轴和y轴上的范围都是(0,1)。与视图坐标系类似,显示坐标系中的z值也表示进入窗口的深度。这个z值的含义将在第57页的“Z-Buffer”一节中进一步描述。

3.7坐标变换

当我们用计算机图形学创建图像时,我们将三维定义的对象投射到二维图像平面上。正如我们前面看到的,这个投影自然包括透视视图到显示变换(视口,窗口大小和位置)世界坐标-灯光位置-摄像机位置-演员位置图3-14建模,世界,视图和显示坐标系统。显示坐标视图坐标摄像机变换模型A的坐标系模型B的坐标系行动者A的变换行动者B的变换x轴y轴1 -1 -1模型坐标48计算机图形学基本知识。为了包括投影效果,比如消失点,我们使用一种特殊的坐标系,称为齐次坐标。在3D中表示点的常用方法是三元笛卡尔向量(x, y, z)。齐次坐标由四元向量(xh, yh, zh, wh)表示。笛卡尔坐标与齐次坐标的转换为:

使用齐次坐标,我们可以将wh设为零来表示无穷大点。这个功能被相机用于透视转换。变换是通过使用变换矩阵来应用的。变换矩阵在计算机图形学中被广泛使用,因为它们允许我们通过重复的矩阵乘法来执行对象的平移、缩放和旋转。并不是所有这些操作都可以使用矩阵来执行。

例如,假设我们想要创建一个变换矩阵,用向量(tx, ty, tz)在笛卡尔空间中平移一个点(x, y, z)。我们只需要构造平移矩阵

然后再乘以齐次坐标(xh, y h, z h, wh)为了实现这个例子,我们通过设置从笛卡尔坐标(x, y, z)构造齐次坐标

屈服:屈服然后,为了确定平移点,我们将当前位置预先乘以变换矩阵以得到平移坐标。代入公式3-6,得到结果

通过公式3-5转换回笛卡尔坐标,我们得到了期望的解

同样的过程用于缩放或旋转对象。要缩放一个对象,我们使用变换矩阵

参数sx sy和sz在哪

是沿x, y, z轴的比例因子。类似地,我们可以使用矩阵绕x轴旋转一个物体

绕y轴

绕着z轴

另一个有用的旋转矩阵用于将一个坐标轴转换为另一个坐标轴。为了推导变换矩阵,我们假设单位轴构成了围绕轴的角度(这些被称为方向余弦)。类似地,单位轴构成角度,单位轴构成角度

. 由此得到的旋转矩阵是通过将方向余弦沿变换矩阵的行放置形成的,如下所示

围绕坐标原点旋转。围绕对象的中心(或用户指定的点)旋转通常更方便。假设我们称这个点为物体的中心。为了旋转,我们必须首先将对象从原点平移到原点,应用旋转,然后将对象平移回原点。变换矩阵可以通过矩阵乘法进行组合,以实现平移、旋转和缩放的组合。一个变换矩阵可以同时表示所有类型的变换。这个矩阵是重复矩阵乘法的结果。警告一句:乘法的顺序很重要。例如,将平移矩阵乘以旋转矩阵与将旋转矩阵乘以平移矩阵得到的结果是不同的。

3.8 Actor几何

我们已经看到了照明属性如何控制一个actor的外观,以及如何结合变换矩阵使用相机将一个actor投影到图像平面。剩下要定义的是参与者的几何形状,以及我们如何在世界坐标系统中定位它。计算机图形学研究中的一个主要课题是建模或表示物理对象的几何形状。应用了各种数学技术,包括各种形式的点、线、多边形、曲线和样条的组合,甚至是隐式数学函数。这个话题超出了本文的范围。这里重要的一点是,有一个底层的几何模型,它指定了对象的形状以及它在模型坐标系中的位置。在数据可视化中,建模扮演着不同的角色。

可视化算法不是直接创建几何图形来表示对象,而是计算这些形式。通常几何图形是抽象的(如等高线),与现实世界的几何图形几乎没有关系。当我们在第6章和第9章描述可视化算法时,我们将看到这些模型是如何计算的。

用于数据可视化的几何表示往往是简单的,即使计算表示并非如此。这些形式通常是基本的,如点、线和多边形,或可视化数据,如体积数据。我们使用简单的表单,因为我们渴望高性能和交互式系统。因此,我们利用了计算机硬件(将在第51页的“图形硬件”中介绍)或特殊的渲染技术,如体绘制(参见第218页的“体绘制”)。每个Actor都有一个变换矩阵来控制它在世界空间中的位置和缩放。

actor的几何形状由模型坐标中的模型定义。

我们使用沿坐标轴的方向、位置和比例因子来指定参与者的位置。此外,我们可以定义参与者旋转的原点。这个特性很有用,因为我们可以围绕它的中心或其他有意义的点旋转参与者。参与者的方向由存储在方向向量(,,)中的旋转决定。这个向量定义了一系列旋转变换矩阵。正如我们在

前面关于变换矩阵的部分,变换的应用顺序不是任意的。我们选择了一个固定的顺序,这是基于我们认为对用户来说是自然的。变换的顺序是绕y轴旋转,然后绕x轴旋转,最后绕z轴旋转。这个顺序是任意的,是基于标准的相机操作。这些操作(按顺序)是相机方位角,然后是仰角,然后是滚动(图3 - 15)。所有这些旋转都围绕着actor的原点进行。通常这被设置为其包围框的中心,但也可以设置为任何方便的点。有许多不同的方法来改变一个演员的方向。RotateX()、RotateY()和RotateZ()是围绕各自轴旋转的常用方法。许多系统还包括一种绕用户定义的轴旋转的方法。在可视化工具包中,RotateXYZ()方法用于围绕穿过原点的任意矢量旋转。

3.9图形硬件

前面我们提到,图形硬件的进步对渲染的执行方式产生了很大的影响。现在我们已经介绍了渲染场景的基本原理,我们来看看一些硬件问题。首先,我们讨论取代矢量显示器作为主要输出设备的光栅设备。然后,我们看看我们的程序如何与图形硬件通信。

我们还研究了计算机图形学、隐藏线/面去除和z-缓冲中使用的不同坐标系。

光栅设备

计算机图形学的成果在当今世界无处不在

这是25像素宽,10像素高。每个像素存储一位信息,无论它是黑的还是白的。这就是黑白激光打印机的工作原理,对于纸张上的每个点,它要么打印一个黑点,要么保持纸张的颜色。由于硬件的限制,激光打印机和计算机显示器等光栅设备实际上不能像图3 - 16中那样绘制精确的正方形像素。相反,它们往往有轻微的模糊和重叠。栅格设备的另一个硬件限制是分辨率。这就是为什么一台300dpi(每英寸点数)激光打印机比一台9针点阵打印机输出更详细的结果。300dpi激光打印机的分辨率为每英寸300像素,而点阵打印机的分辨率约为每英寸50 dpi。彩色计算机显示器的分辨率通常为每英寸80个像素,这使得屏幕的像素阵列在宽度和高度上大约为1000个像素。结果是超过100万个像素,每个像素都有一个值,表明它应该是什么颜色。

由于彩色显示器中的硬件使用RGB系统,因此使用RGB系统来描述像素中的颜色是有意义的。不幸的是,拥有超过100万个像素,每个像素都有红色、绿色和蓝色组件,会占用大量内存。这是区分市场上各种图形硬件的部分原因。一些公司每像素使用24位存储空间,其他公司使用8位存储空间,一些先进的系统每像素使用超过100位存储空间。

通常情况下,每个像素的比特数越多,颜色就越准确。解决图形硬件中颜色限制的一种方法是使用一种称为抖动的技术。例如,假设您想使用一些不同深浅的灰色,但您的图形硬件只支持黑色和白色。抖动让你通过使用黑色和白色像素的混合来近似灰度。在图3 - 17中,使用黑白混合像素绘制了7个灰色正方形。从远处看,这七个正方形看起来是不同深浅的灰色,但近看,很明显它们只是黑白像素的不同混合。同样的方法也适用于其他颜色。例如,如果您的图形硬件支持主蓝色、主绿色和白色,但不支持柔和的海绿色,则可以通过抖动硬件支持的绿色、蓝色和白色来近似此颜色。

硬件接口

现在我们已经介绍了显示硬件的基础知识,好消息是您很少需要担心它们。大多数图形编程是使用比单个像素更高级别的原语完成的。图3 - 18显示了一个可视化程序的典型布局。在层次结构的底部是我们已经讨论过的显示硬件;您的程序可能不会直接与它交互。硬件之上的前三层是您可能需要关注的层。

许多程序利用应用程序库作为系统图形功能的高级接口。本书附带的可视化工具包就是一个很好的例子。它允许您仅使用几个命令就显示复杂的对象或图形。还可以与许多不同的图形库进行接口,因为不同的库可能在不同的硬件平台上得到支持。

图形库和图形硬件层都执行类似的功能。它们负责从应用程序库或程序中获取高级命令并执行它们。这通过提供更复杂的原语来简化编程。我们可以绘制多边形、三角形和线等基本元素,而不是一次绘制一个像素,而不用担心哪些像素被设置为哪些颜色的细节。图3 - 19展示了一些所有主流图形库都支持的高级原语。

这个功能被分为两个不同的层,因为不同的机器可能有非常不同的图形硬件。如果您编写一个绘制红色多边形的程序,图形库或图形硬件必须能够执行该命令。在高端系统上,这可能在图形硬件中完成,在其他系统上则由软件中的图形库完成。因此,相同的命令可以用于各种各样的机器,而不用担心底层的图形硬件。图3 - 19中原语的基本构建块是一个点(或顶点)。顶点有位置、法线和颜色,每一个都是一个三元素向量。位置指定顶点所在的位置,法线指定顶点面对的方向,以及它的颜色

指定顶点的红色、绿色和蓝色组件。多边形由一系列点或顶点连接而成,如图3 - 20所示。你可能想知道为什么每个顶点都有法线,而不是整个多边形只有一个法线。一个平面多边形只能面向一个方向,而不管其顶点的法线是什么。原因是有时多边形被用作其他东西的近似值,比如曲线。圆柱体的自上而下视图如图3 - 21所示。正如你所看到的,它不是一个真正的圆柱体,而是一个用灰色画出的圆柱体的多边形近似。每个顶点由两个多边形共享,顶点的正确法线与多边形的法线不相同。类似的逻辑解释了为什么每个顶点都有一种颜色,而不是整个多边形只有一种颜色。

当您将自己限制在上面描述的基本类型时,还有一些许多图形系统支持的附加属性。边缘颜色和边缘可见性可用于突出显示组成参与者的多边形原语。另一种方法是通过调整从表面到线框或点的表示。这将分别用它们的边界边或点替换多边形等曲面。虽然从物理角度来看,这可能没有多大意义,但它可以在一些插图中有所帮助。在渲染CAD模型时使用边缘可见性可以帮助显示组成模型的不同部分。

栅格化

在本文中,我们已经描述了如何使用渲染原语表示图形数据,以及如何使用光栅显示设备表示图像。问题仍然存在,我们如何将图形原语转换为光栅图像?这是我们在

这一节。虽然关于这个主题的详细论述超出了本文的范围,但我们将尽最大努力提供一个高层次的概述。将几何表示转换为光栅图像的过程称为光栅化或扫描转换。

在下面的描述中,我们假设图形原语是三角形多边形。这并不像你想象的那样有限制,因为任何一般的多边形都可以镶嵌成一组三角形。此外,其他表面表示形式,如样条,通常由图形系统镶嵌成三角形或多边形。(这里描述的方法实际上适用于凸多边形。)

现在的大多数硬件都是基于对象顺序的栅格化技术。正如我们在本章前面看到的,这意味着按顺序处理我们的actor。由于我们的参与者是由多边形原语表示的,所以我们一次处理一个多边形。因此,尽管我们描述了一个多边形的处理,但请记住,许多多边形和可能的许多参与者被处理。

第一步是使用适当的变换矩阵变换多边形。我们还使用平行投影或正交投影将多边形投影到图像平面上。的一部分

图3-22凸多边形栅格化。像素在图像平面的水平跨度(或扫描线)中进行处理。在点pi处的数据值di沿边缘插值,然后使用增量数据值沿扫描线插值。典型的数据值是颜色的RGB组件。

这个过程包括裁剪多边形。我们不仅使用前面和后面的剪辑平面来剪辑过近或过远的多边形,而且我们还必须剪辑跨越图像平面边界的多边形。裁剪跨越视图截锥边界的多边形意味着我们必须生成新的多边形边界。

随着多边形剪辑和投影到图像平面,我们可以开始扫描线处理(图3 - 22)。第一步确定与投影多边形相交的初始扫描线。这是通过对顶点的y值排序来找到的。然后,我们发现两条边在左右两边连接顶点。利用边的斜率和数据值,我们计算增量数据值。这些数据通常是R、G和B颜色组件。其他数据值包括透明度值和z深度值。(如果我们使用z-buffer, z值是必要的,将在下一节中描述。)多边形内的像素行(即从左右边缘开始)称为span。数据值从跨度两侧的边缘插值,以计算内部像素值。这个过程一span一span地继续,直到整个多边形被填充。注意,当遇到新的顶点时,有必要重新计算增量数据值。多边形的底纹(即跨多边形的颜色插值)取决于参与者的插值属性。

有三种可能:平底纹、高洛德底纹或Phong底纹。图3 - 7说明了平面插值和Gouraud插值的区别。平面底纹通过将光照方程应用于多边形的一个法线(通常是表面法线)来计算多边形的颜色。Gouraud shading使用顶点的法线和标准光照方程计算多边形在所有顶点上的颜色。然后通过应用扫描线插值过程填充多边形的内部和边缘。Phong底纹是三种底纹中最逼真的。它通过插值顶点法线来计算多边形上每个位置的法线。然后在照明方程中使用这些来确定结果的像素col图3-22光栅化一个凸多边形。像素在图像平面的水平跨度(或扫描线)中进行处理。在点pi处的数据值di沿边缘插值,然后使用增量数据值沿扫描线插值。典型的数据值是颜色的RGB组件。V2 V1 V0 p0 pn pi di di1 - Dd = + i 3.10综合起来57倍。平面和高劳阴影都是常用的方法。Phong着色的复杂性阻碍了它在硬件上的广泛支持。在我们之前对渲染过程的描述中,我们跟随光线从我们的眼睛穿过图像平面中的像素到达演员,然后回到光源。光线追踪的一个很好的副作用是,观看的光线会击中它们遇到的第一个actor,而忽略隐藏在它后面的任何actor。当使用上面描述的多边形方法渲染角色时,我们没有这样的方法来计算哪些多边形是隐藏的,哪些是不隐藏的。我们通常不能指望多边形的顺序是正确的。相反,我们可以使用许多隐藏面方法来渲染多边形。

Z-Buffer

在我们之前对渲染过程的描述中,我们跟随光线从我们的眼睛穿过图像平面中的像素到达演员,然后返回光源。光线追踪的一个很好的副作用是,观看的光线会击中它们遇到的第一个actor,而忽略隐藏在它后面的任何actor。当使用上面描述的多边形方法渲染角色时,我们没有这样的方法来计算哪些多边形是隐藏的,哪些是不隐藏的。我们通常不能指望多边形的顺序是正确的。相反,我们可以使用许多隐藏面方法来渲染多边形。

一种方法是将我们所有的多边形从后往前排序(沿着相机的视图向量),然后按此顺序渲染它们。这被称为画家的算法或画家的排序,它有一个主要的缺点,如图3 - 23所示。不管我们画这三个三角形的顺序如何,我们都不能得到想要的结果,因为每个三角形既在另一个三角形的前面,又在另一个三角形的后面。有一些算法可以对多边形进行排序和分割,以处理这种情况[Carlson85]。这需要更多的初始处理来执行排序和拆分。

如果图像之间的几何原语发生变化或相机视图发生变化,则必须在每次渲染之前执行此处理。另一种隐面算法,z-buffering,解决了这个问题,并且不需要排序。z缓冲利用了视图坐标系中的z值(即沿投影方向的深度值)。在绘制新像素之前,将其z值与该像素位置的当前z值进行比较。如果新像素将在当前像素的前面,那么它将被绘制,该像素位置的z值将被更新。否则,当前像素保持不变,新像素被忽略。z -缓冲由于其简单和健壮性在硬件中得到了广泛的实现。z-buffer的缺点是它需要大量的内存(称为z-buffer)来存储每个像素的z值。大多数系统使用深度为24或32位的z缓冲区。对于一个1000 * 1000的显示器来说,仅仅是z缓冲区就相当于3到4兆字节。z-缓冲的另一个问题是它的精度取决于它的深度。24位z缓冲器的精度为观看截锥高度的16,777,216分之一。如果物体靠得很近,这种分辨率通常是不够的。如果你确实遇到了z缓冲精度的情况,请确保前面和后面的剪辑平面尽可能接近可见的几何图形。

3.10把一切放在一起

本节提供图形对象的概述以及如何在VTK中使用它们。

  • 图形对象模型

我们已经讨论了许多在场景渲染中起作用的对象。现在是时候将它们组合到一个用于图形和可视化的综合对象模型中了。

在可视化工具包中,我们使用七个基本对象来渲染一个场景。在幕后还有更多的物体,但这七个是我们使用最频繁的。对象如下所示,如图3 - 24所示。

1. vtkRenderWindow -在显示设备上管理一个窗口;一个或多个渲染器绘制到vtkRenderWindow的实例中。

2. vtkRenderer -协调涉及灯光、摄像机和演员的渲染过程。

3.vtkLight -照亮场景的光源。

4. vtkCamera -定义场景的视图位置、焦点和其他视图属性。

5. vtkActor——表示在场景中呈现的对象,包括它的属性和在世界坐标系统中的位置。(注意:vtkActor是vtkProp的子类。vtkProp是一种更通用的actor形式,它包括注释和2D绘图类。更多信息请参见第74页的“程序集和其他类型的vtkProp”。

6.vtkProperty -定义参与者的外观属性,包括颜色、透明度和光照属性,如镜面和漫反射。还有代表性的属性,如线框和固体表面。vtkRenderer实例图3-24图形对象(Model. renderer)示意图cxx)。一个vtkCamera为每个渲染器定义视图一个或多个vtkLight照亮场景vtkMapper定义actor几何形状vtkProperty定义actor表面属性

7.vtkMapper -参与者的几何表示。多个参与者可以引用同一个映射器。

类vtkRenderWindow将渲染过程联系在一起。它负责管理显示设备上的一个窗口。对于运行Windows的pc,这将是一个Microsoft显示窗口,对于Linux和UNIX系统,这将是一个X窗口,而在Mac (OSX)上,这将是一个Quartz窗口。在VTK中,vtkRenderWindow的实例是独立于设备的。这意味着您不需要关心底层图形硬件或软件正在使用,软件自动适应您的计算机作为创建vtkRenderWindow的实例。(更多信息请参见第60页的“实现设备独立性”。)

除了窗口管理,vtkRenderWindow对象还用于管理渲染器和存储显示窗口的特定图形特征,如大小、位置、窗口标题、窗口深度和双缓冲标志。窗口的深度表示每个像素分配了多少位。双缓冲是一种将一个窗口在逻辑上划分为两个缓冲区的技术。在任何给定的时间,当前都有一个缓冲区对用户可见。同时,第二个缓冲区可用于在动画中绘制下一张图像。一旦渲染完成,两个缓冲区就可以交换,这样新图像就可见了。这种常见的技术允许在用户看不到原语实际呈现的情况下显示动画。高端图形系统在硬件上执行双重缓冲。典型的系统会有一个深度为72位的呈现窗口。前24位用于存储前端缓冲区的红色、绿色和蓝色(RGB)像素组件。接下来的24位存储反向缓冲区的RGB值。最后24位用作z缓冲区。

类vtkRenderer负责协调它的灯光、摄像机和actor来生成图像。每个实例都维护一个特定场景中演员、灯光和活动摄像机的列表。必须至少定义一个actor,但如果没有定义灯光和摄像机,它们将由渲染器自动创建。在这种情况下,演员在图像的中心,默认的相机视图是向下的z轴。类vtkRenderer的实例还提供了指定背景和环境照明颜色的方法。还可以使用方法来转换世界、视图和显示坐标系统。

渲染器的一个重要方面是,它必须与它要绘制的vtkRenderWindow类的实例相关联,并且它所绘制的渲染窗口中的区域必须由一个矩形视口定义。视口由x和y图像坐标轴上的归一化坐标(0,1)定义。默认情况下,渲染器绘制到渲染窗口的全部范围(视点坐标(0,0,1,1))。可以指定一个更小的视口。以及让多个渲染器绘制到同一个渲染窗口。

类vtkLight的实例照亮了场景。各种实例变量定向和定位光可用。它也可以打开/关闭灯,以及设置他们的颜色。通常情况下,至少有一盏灯是“亮”的,以照亮现场。如果没有定义并打开灯光,渲染器会自动构造一个灯光。VTK中的灯可以是定位的,也可以是无限的。位置灯有一个相关的锥角和衰减因素。无限的光投射出彼此平行的光线。

摄像机是由类vtkCamera构造的。重要参数包括相机位置、焦点、前后剪辑平面位置、向上视场矢量。摄像机也有特殊的方法来简化操作,如本章前面所述。这些包括仰角、方位角、缩放和滚动。类似于vtkLight,如果没有定义,渲染器将自动创建一个vtkCamera实例。

类vtkActor的实例表示场景中的对象。特别是,vtkActor组合了对象属性(颜色、阴影类型等)、几何定义和世界坐标系统中的方向。这是通过维护引用vtkProperty、vtkMapper和vtkTransform实例的实例变量来实现的。通常不需要显式地创建属性或转换,因为这些是使用vtkActor的方法自动创建和操作的。您确实需要创建一个vtkMapper实例(或它的一个子类)。映射器将数据可视化管道绑定到图形设备上。(我们将在下一章中详细介绍管道。)

在VTK中,actor实际上是vtkProp(任意道具)和vtkProp3D(可以在3D空间中转换的道具)的子类。(“prop”这个词来源于舞台,在舞台上,props是场景中的物体。)还有其他具有特殊行为的props和actor子类(更多信息请参阅第74页的“程序集和vtkProp的其他类型”)。一个例子是vtkFollower。该类的实例总是面向活动摄像机。这在设计标识或文本时非常有用,这些标识或文本必须能从场景中的任何摄像机位置读取。

类vtkProperty的实例会影响参与者的呈现外观。当创建角色时,属性实例将自动使用它们创建。也可以直接创建属性对象,然后将属性对象与一个或多个参与者关联起来。通过这种方式,参与者可以共享公共属性。

最后,vtkMapper(及其子类)定义对象几何形状,还可选地定义顶点颜色。此外,vtkMapper引用了一个颜色表(即vtkLookupTable),用于给几何图形上色。(我们在第163页的“颜色映射”中讨论了数据到颜色的映射。)我们将在195页的“Mapper Design”中更详细地研究映射过程。现在假设vtkMapper是一个表示几何图形和其他类型可视化数据的对象。

还有另一个重要的对象vtkRenderWindowInteractor,它为渲染窗口中的渲染器捕获事件(比如鼠标点击和鼠标移动)。vtkRenderWindowInteractor捕获这些事件,然后触发某些操作,如相机移动,平移和旋转,演员选择,进入/退出立体模式,等等。该类的实例使用SetRenderWindow()方法与呈现窗口关联。

实现设备独立性

使用VTK构建的应用程序的一个理想属性是它们与设备无关。这意味着在一个具有特定软件/硬件配置的操作系统上运行的计算机代码在另一个不同的操作系统和软件/硬件配置上运行时不变。这样做的好处是程序员不需要花费精力在不同的计算机系统之间移植应用程序。此外,现有的应用程序不需要重写以利用硬件或软件技术的新发展。相反,VTK通过继承和一种称为对象工厂的技术的组合来透明地处理这个问题。

图3 - 25 (a)说明了如何使用继承来实现设备独立性。某些类(如vtkActor)被分为两部分:一个与设备无关的超类和一个与设备相关的子类。这里的技巧是,用户通过调用与设备无关的超类中的特殊构造函数New()来创建与设备相关的子类。例如,我们会使用(在c++中)

vtkActor *anActor = vtkActor::New()

创建一个设备依赖的vtkActor实例。用户看不到设备相关的代码,但实际上,一个actor是指向vtkActor的一个设备相关子类的指针。图3 - 25 (b)是构造函数New()的代码片段,它使用了VTK的对象工厂机制。反过来,vtkGraphicsFactory(用于实例化图形化类)在被请求实例化一个参与者时产生适当的具体子类,如图3 - 25 (c)所示。

使用New()方法实现的对象工厂允许我们创建独立于设备的代码,这些代码可以从一台计算机移动到另一台计算机,并适应不断变化的技术。例如,如果有一个新的图形库可用,我们只需要创建一个新的依赖于设备的子类,然后修改图形工厂,根据环境变量或其他系统信息实例化适当的子类。这个扩展将被本地化,并且只做一次,所有基于这些对象工厂的应用程序都将自动移植而无需更改。

例子

本节介绍一些使用VTK图形对象实现的简单应用程序。重点是基本的:如何创建渲染器,灯光,摄像机和演员。后面的章节将这些基本原则结合在一起,创建数据可视化的应用程序。

图3-25通过(a)继承和对象工厂实现设备独立性(b)和(c).

vtkOpenGLActor vtkXGLActor vtkActor vtkStarbaseActor设备独立的超类设备依赖的子类(a)设备类继承。(注:在VTK 4.2中不再支持Starbase和XGL图形库)(b)代码片段来自

vtkActor::New() vtkActor *vtkActor::New() {

vtkObject* ret = vtkGraphicsFactory::CreateInstance("vtkActor");返回(vtkActor *)受潮湿腐烂;

}

(c) vtkGraphicsFactory::CreateInstance(vtkclassname)的代码片段。

if(!strcmp("OpenGL",rl) || !strcmp("Win32OpenGL",rl) || !strcmp("CarbonOpenGL",rl) || !strcmp("CocoaOpenGL",rl)) {

if(strcmp(vtkclassname, "vtkActor") == 0){

返回vtkOpenGLActor::New();计算机图形初级渲染圆锥。下面的c++代码使用本节中介绍的大部分对象来创建一个锥体的图像。vtkConeSource生成一个圆锥的多边形表示,vtkPolyDataMapper将几何图形(与参与者一起)映射到底层图形库。(此示例的源代码可以在Cone中找到。cxx。源代码还包含额外的文档。)

#include "vtkConeSource.h"

#include "vtkPolyDataMapper.h"

#include "vtkRenderWindow.h"

#include "vtkCamera.h"

#include "vtkActor.h"

#include "vtkRenderer.h" int main( int argc, char *argv[] )

{

vtkConeSource *cone = vtkConeSource::New();

cone->SetHeight( 3.0 );

cone->SetRadius( 1.0 );

cone->SetResolution( 10 );

vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();

coneMapper->SetInputConnection( cone->GetOutputPort() );

vtkActor *coneActor = vtkActor::New();

coneActor->SetMapper( coneMapper );

vtkRenderer *ren1= vtkRenderer::New();

ren1->AddActor( coneActor );

ren1->SetBackground( 0.1, 0.2, 0.4 );

vtkRenderWindow *renWin = vtkRenderWindow::New();

renWin->AddRenderer( ren1 );

renWin->SetSize( 300, 300 );

int i;

for (i = 0; i < 360; ++i) {

// render the image

renWin->Render();

// rotate the active camera by one degree

ren1->GetActiveCamera()->Azimuth( 1 );

}

cone->Delete();

coneMapper->Delete();

coneActor->Delete();

ren1->Delete();

renWin->Delete();

return 0;

}

图3-26程序生成多边形模型的源对象示例。

这九张图仅仅代表了VTK的部分能力。从左上依次为:球体、圆锥、圆柱体、立方体、平面、文本、随机点云、圆盘(带孔或不带孔)和线源。其他多边形源对象是可用的;检查vtkPolyDataAlgorithm的子类。

一些关于这个例子的评论。包含文件vtk__。h包含编译此示例所需的VTK中对象的类定义。在本例中,我们使用构造函数New()创建对象,使用方法Delete()销毁对象。在VTK中,必须使用New()和Delete()来确保设备独立性并正确管理引用计数。(详情请参阅VTK用户指南)在本例中,实际上没有必要使用Delete(),因为对象会在程序终止时自动删除。但一般来说,每次调用New()都应该使用Delete()。(以后的示例不会在main()程序的作用域中显示Delete()方法以节省空间,也不会显示所需的#include语句。)

在这个例子中,表示锥体(一组多边形)的数据是通过将一系列对象链接到一个管道(这是下一章的主题)来创建的。首先,使用vtkConeSource创建圆锥的多边形表示,并作为SetInput()方法指定的数据映射器的输入。SetMapper()方法将映射器的数据与coneActor关联起来。下一行将coneActor添加到呈现器的actor列表中。锥体以360度的循环呈现。由于上面的示例中没有定义相机或灯,为了方便用户,VTK自动生成一个默认的灯和相机。通过GetActiveCamera()方法访问摄像机,并应用一个度方角,如图所示。每次对任何对象进行更改时,都会调用Render()方法来生成相应的图像。一旦循环完成,所有分配的对象都被销毁,程序退出。

VTK中有许多不同类型的源对象,类似于vtkConeSource,如图3 - 26所示。在下一章中,我们将学习更多关于源和其他类型的过滤器。

事件和观察者。像VTK这样的可视化工具包经常用于交互式应用程序,或者可能需要在操作期间提供状态。此外,与其他包(如GUI工具包)的集成是一项常见任务。支持这些特性需要一种机制,图3-26程序生成多边形模型的源对象示例。这九张图仅仅代表了VTK的部分能力。从左上依次为:球体、圆锥、圆柱体、立方体、平面、文本、随机点云、圆盘(带孔或不带孔)和线源。其他多边形源对象是可用的;

检查vtkPolyDataAlgorithm的子类。64计算机图形入门插入用户功能到软件。在VTK中,命令/观察者设计模式[Gamma95]用于此目的。在VTK中实现的这种设计模式的基础是事件的概念。事件表示软件中发生了重要操作。例如,如果用户在呈现窗口中按下鼠标左键,VTK将调用LeftButtonPressEvent。观察者是记录他们对一个或多个特定事件的兴趣的对象。当这些事件之一被调用时,观察者会收到通知,并可以在那时执行任何有效的操作;也就是说,执行与观察者关联的命令。命令/观察者设计模式的好处是在概念和实现上都很简单,但为用户提供了强大的功能。但是,它确实需要软件实现在操作时调用事件。

在下一个示例中,观察者在呈现程序开始呈现过程时观察它调用的StartEvent。观察者依次执行其相关的命令,该命令只是打印出相机的当前位置。

#include "vtkCommand.h"

// Callback for the interaction

class vtkMyCallback : public vtkCommand

{

public:

static vtkMyCallback *New()

{ return new vtkMyCallback; }

virtual void Execute(vtkObject *caller, unsigned long, void*)

{

vtkRenderer *ren =

reinterpret_cast(caller);

cout << ren->GetActiveCamera()->GetPosition()[0] << " "

<< ren->GetActiveCamera()->GetPosition()[1] << " "

<< ren->GetActiveCamera()->GetPosition()[2] << "\n";

}

};

int main( int argc, char *argv[] )

{

vtkConeSource *cone = vtkConeSource::New();

cone->SetHeight( 3.0 );

cone->SetRadius( 1.0 );

cone->SetResolution( 10 );

vtkPolyDataMapper *coneMapper = vtkPolyDataMapper::New();

coneMapper->SetInputConnection( cone->GetOutputPort() );

vtkActor *coneActor = vtkActor::New();

coneActor->SetMapper( coneMapper );

vtkRenderer *ren1= vtkRenderer::New();

ren1->AddActor( coneActor );

ren1->SetBackground( 0.1, 0.2, 0.4 );

vtkRenderWindow *renWin = vtkRenderWindow::New();

renWin->AddRenderer( ren1 );

renWin->SetSize( 300, 300 );

vtkMyCallback *mo1 = vtkMyCallback::New();

ren1->AddObserver(vtkCommand::StartEvent,mo1);

mo1->Delete();

int i;

for (i = 0; i < 360; ++i)

{

// render the image

renWin->Render();

// rotate the active camera by one degree

ren1->GetActiveCamera()->Azimuth( 1 );

}

cone->Delete();

coneMapper->Delete();

coneActor->Delete();

ren1->Delete();

renWin->Delete();

return 0;

}

观察者是通过派生类vtkCommand创建的。Execute()方法需要由vtkCommand的任何具体子类实现(即,该方法是纯虚拟的)。生成的子类vtkMyCommand使用AddObserver()方法实例化并注册到呈现器实例ren1。在本例中,StartEvent是观察到的事件。这个简单的例子并没有展示命令/观察者设计模式的真正威力。在本章的后面(第68页的“解释代码”),我们将看到如何使用这个功能将一个简单的GUI集成到VTK中。第7章将介绍三维交互小部件(“3D小部件和用户交互”在252页)。创建多个渲染器。下一个示例稍微复杂一些,它使用了共享单个呈现窗口的多个呈现器。我们使用视口来定义渲染器应该在渲染窗口中绘制的位置。(此c++代码可以在Cone3中找到。cxx)。

vtkRenderer *ren1= vtkRenderer::New();

ren1->AddActor( coneActor );

ren1->SetBackground( 0.1, 0.2, 0.4 );

ren1->SetViewport(0.0, 0.0, 0.5, 1.0);

vtkRenderer *ren2= vtkRenderer::New();

ren2->AddActor( coneActor );

ren2->SetBackground( 0.2, 0.3, 0.5 );

ren2->SetViewport(0.5, 0.0, 1.0, 1.0);

图3-27 Cone3.cxx的4帧输出

vtkRenderWindow *renWin = vtkRenderWindow::New();

renWin->AddRenderer( ren1 );

renWin->AddRenderer( ren2 );

renWin->SetSize( 600, 300 );

ren1->GetActiveCamera()->Azimuth(90);

int i;

for (i = 0; i < 360; ++i)

{

// render the image

renWin->Render();

// rotate the active camera by one degree

ren1->GetActiveCamera()->Azimuth( 1 );

ren2->GetActiveCamera()->Azimuth( 1 );

}

如您所见,大部分代码与前面的示例相同。第一个区别是我们创建了两个渲染器而不是一个。我们将相同的actor分配给两个呈现器,但是将每个呈现器的背景设置为不同的颜色。我们设置了两个渲染器的视口,使一个在渲染窗口的左半部分,另一个在右边。渲染窗口的大小被指定为600 * 300像素,这将导致每个渲染器绘制到一个300 * 300像素的视口。

多个呈现器的一个很好的应用是显示同一个世界的不同视图,如本例所示。这里我们用90度的方位角调整第一个渲染器的相机。然后我们开始一个循环,使两个摄像机围绕锥体旋转。图3 - 27显示了该动画中的四帧。

属性和转换。前面的例子没有显式地创建属性或转换对象,也没有应用影响这些对象的actor方法。相反,我们接受了违约

实例变量值。这个过程是典型的VTK应用程序。大多数实例变量都已预设为可生成可接受的结果,但总有一些方法可用于覆盖默认值。

这个例子创建了两个不同颜色和镜面属性的锥的图像。此外,我们将其中一个对象转换为与另一个对象相邻。本例的c++源代码可以在Cone4中找到。cxx。

我们通过修改由参与者自动创建的属性对象来设置参与者的coneActor属性。这与参与者coneActor2不同,后者直接创建属性,然后将其分配给参与者。通过应用SetPosition()方法将ConeActor2从其默认位置移动。此方法影响作为参与者的实例变量的转换矩阵。如图3 - 28所示。图3-28修改属性和变换矩阵(Cone4. properties)cxx)。68计算机图形学入门介绍vtkRenderWindowInteractor。前面的例子不是交互式的。也就是说,如果不修改和重新编译c++代码,就不可能直接与数据交互。一种常见的交互类型是改变相机位置,以便我们可以从不同的有利位置查看我们的场景。

在可视化工具包中,我们提供了一套方便的对象来做到这一点:vtkRenderWindowInteractor, vtkInteractorStyle及其派生类。类vtkRenderWindowInteractor的实例捕获呈现窗口中特定于窗口系统的鼠标和键盘事件,然后将这些事件转换为VTK事件。例如,X11或Windows应用程序中的鼠标运动(发生在渲染窗口中)将由vtkRenderWindowInteractor转换为VTK的MouseMoveEvent。任何参加本次活动的观察员都将收到通知(见第63页的“活动和观察员”)。通常,vtkInteractorStyle的实例与vtkRenderWindowInteractor结合使用,以定义与特定事件相关的行为。例如,我们可以通过使用不同的鼠标按钮和运动组合来执行相机移动、平移和旋转。下面的代码片段展示了如何实例化和使用这些对象。这个示例与第一个示例相同,只是添加了interactor和interactor样式。完整的c++示例代码在Cone5中。cxx。vtkRenderWindowInteract

在使用New()方法创建交互器之后,我们必须告诉它使用SetRenderWindow()方法捕获哪个呈现窗口中的事件。为了使用交互器,我们必须使用initialize()和start()方法初始化和启动事件循环,这与窗口系统的事件循环一起工作以开始捕获事件。一些更有用的事件包括“w”键,它在线框中绘制所有参与者;“s”键,以曲面形式绘制参与者;“3”键,用于在支持3D立体声的系统中切换3D立体声;“r”键,重置相机视图;以及退出应用程序的“e”键。此外,鼠标按钮围绕相机的焦点旋转、平移和移动。两个高级功能是“u”键,它执行用户定义的函数;还有“p”键,它选择鼠标指针下的参与者。解释代码。

在前面的例子中,我们看到了如何与vtkRenderWindowInteractor一起创建一个交互器样式的对象,使我们能够通过在渲染窗口中移动鼠标来操作相机。尽管这为大量应用程序提供了灵活性和交互性,但在本文中,我们还希望在一些示例中修改其他参数。这些参数的范围从角色属性(如颜色)到输入文件的名称。当然,我们总是可以编写或修改c++代码来实现这一点,但在许多情况下,从进行更改到看到结果之间的转换时间太长了。提高系统整体交互性的一种方法是使用解释接口。解释型系统允许我们修改对象并直接看到结果,而不需要重新编译和重新链接源代码。解释型语言还提供了许多工具,例如GUI(图形用户界面)工具,这些工具可以简化应用程序的创建。

可视化工具包在其编译过程中内置了自动生成到解释语言Tcl、Python和Java的语言绑定的能力[Ousterhout94]。这个所谓的包装过程会自动在c++ VTK库和解释器之间创建一个层,如图3 - 29所示。对于系统中的大多数对象和方法,c++方法和Tcl函数之间是一对一的映射。为了演示这一点,下面的示例重复前面的c++示例,只是它是用Tcl脚本实现的。(该脚本可以在Cone5中找到。tcl)。

示例首先加载一些定义各种VTK类的共享库。接下来,从vtkConeSource和vtkPolyDataMapper创建标准可视化管道。呈现类的创建方法与c++示例完全相同。一个主要的添加是一个观察者,观察呈现窗口中的UserEvent(默认为“keypress-u”)。观察者触发对Tcl脚本的调用,以引发一个名为. vtkinteract的Tk交互器GUI小部件。这个GUI允许直接输入Tcl语句,如图3 - 30所示,由脚本前面执行的Tcl命令包require vtkinteraction定义。(注意:Tk是一个流行的用于解释语言的GUI工具包,是作为Tcl的一部分发布的。)

正如我们从这个例子中看到的,Tcl例子的代码行数比等效的c++代码要少。而且,c++的许多复杂性在使用解释语言时都被隐藏起来了。使用这个用户界面GUI,我们可以创建、修改和删除对象,并修改它们的实例变量。一旦应用Render()方法或呈现窗口中的鼠标事件导致呈现发生,就会立即显示所产生的更改。我们鼓励您使用Tcl(或其他解释器之一)快速创建图形和可视化示例。当您需要更高性能的应用程序时,最好使用c++。

  • 变换矩阵

转换矩阵在整个可视化工具包中都有使用。actor (vtkprop3d的子类,参见第74页的“程序集和其他类型的vtkProp”)使用它们来定位和定位自己。各种过滤器,包括vtkGlyph3D和vtkTransformFilter,使用转换矩阵来实现它们自己的功能。作为用户,您可能从来没有直接使用过转换矩阵,但是理解它们对于成功使用许多VTK类是很重要的。

应用变换矩阵最重要的方面是理解应用变换的顺序。如果您将一系列复杂的转换分解为平移、缩放和旋转的简单组合,并仔细跟踪应用程序的顺序,那么您将在掌握它们的使用方面走得很远。转换矩阵的一个很好的演示示例是检查vtkActor如何使用其内部矩阵。

vtkActor有一个内部实例变量Transform,它将许多方法委托给它,或者使用矩阵来实现它的方法。例如,RotateX(), RotateY(),图3-30使用Tcl和Tk构建解释型应用程序(Cone5. Tk)tcl)。71和RotateZ()方法都委托给Transform。SetOrientation()方法使用Transform来定向参与者。

vtkActor类以我们认为对大多数用户来说比较自然的顺序应用转换。为了方便起见,我们创建了抽象变换矩阵的实例变量。原点指定旋转和缩放的中心点。位置指定对象的最终转换。方向定义了关于x、y和z轴的旋转。Scale定义x、y和z轴的比例因子。在内部,参与者使用这些实例变量来创建以下转换序列(参见公式3-6、公式3-9、公式3-13)

这个项表示在x, y, z方向上的平移。回想一下,我们用变换矩阵乘以位置向量。这意味着从右向左读取转换。也就是说,由公式3-14可知:将actor翻译到它的原点。

1.缩放和旋转将发生在这一点。在应用缩放和旋转之后,初始平移将被反方向的平移抵消。

2. 缩放几何。

3.围绕y轴,然后是x轴,然后是z轴旋转actor。

4. 撤消第1步的转换,并将参与者移动到其最终位置。变换的顺序很重要。在VTK中,旋转的顺序在大多数情况下是自然的。我们建议您花一些时间使用该软件,了解这些转换如何与您自己的数据一起工作。

转换中最令人困惑的方面可能是旋转及其对Orientation实例变量的影响。通常方向不是由用户直接设置的,大多数用户更喜欢使用RotateX()、rotateey()和RotateZ()方法指定旋转。这些方法按照用户指定的顺序围绕x、y和z轴执行旋转。在旋转变换的右侧应用新的旋转。如果您需要围绕单个轴旋转actor,则actor将完全按照您预期的方式旋转,并且所得到的方向向量将与预期的一样。例如,操作RotateY(20)将生成(0,20,0)的方向,而操作RotateZ(20)将生成(0,0,20)的方向。然而,RotateY(20)后面跟着RotateZ(20)将不会生成(0,20,20),而是生成(6.71771,18.8817,18.8817)的方向!这是因为公式3-14中的旋转部分是根据旋转顺序z,然后x,然后y构建的。

为了验证这一点,RotateZ(20)后面跟随RotateY(20)确实产生了(0,20,20)的方向。添加第三个旋转可能会更令人困惑。一个好的经验法则是只使用SetOrientation()方法将方向重置为(0,0,0)或只设置其中一个旋转。当需要多个角度时,RotateX()、rotateey()和RotateZ()方法优先于SetOrientation()方法。记住,这些旋转是以相反的顺序进行的。旋转方法的使用如图3 - 31所示。我们使用渲染窗口的EraseOff()方法关闭帧之间的擦除,这样我们就可以看到旋转的效果。请注意,在第四幅图中,奶牛仍然围绕自己的y轴旋转,即使x轴在y轴旋转之前旋转。

图3-31奶牛绕轴旋转在这个模型中,x轴从左到右;y轴从下到上;z轴从图像中显现出来。在所有四幅图像中,摄像机的位置是相同的(旋转。tcl)。

我们已经看到,VTK通过使用比变换矩阵更自然的实例变量来隐藏矩阵变换的一些复杂性。但是,有时actor执行的预定义转换顺序是不够的。vtkActor有一个实例变量UserMatrix,它包含一个4 x 4的变换矩阵。这个矩阵应用于由参与者组成的变换之前。当你对4 × 4变换矩阵越来越熟悉时,你可能想要建立自己的矩阵。对象vtkTransform创建并操作这些矩阵。与参与者不同,vtkTransform的实例没有用于位置、比例、原点等的实例变量。你可以直接控制矩阵的组成。下面的语句创建了一个与actor创建的相同的4 x 4矩阵:

vtkTransform *myTrans = vtkTransform::New ();

myTrans->Translate (position[0],position[1],position[2]);

myTrans->Translate (origin[0],origin[1],origin[2]);

myTrans->RotateZ (orientation[2]);

myTrans->RotateX (orientation[0]);

myTrans->RotateZ (orientation[1];

myTrans->Scale (scale[0],scale[1],scale[2]);

myTrans->Translate (-origin[0],-origin[1],-origin[2]);

将此变换操作序列与公式3-14中的变换进行比较。

最后一个示例展示了用vtkTransform构建的转换与用vtkActor构建的转换的比较。在本例中,我们将变换我们的奶牛,使其围绕世界坐标原点(0,0,0)旋转。她看起来像是在绕着原点走。我们通过两种方式实现这一点:一种是使用vtkTransform和参与者的UserMatrix,另一种是使用参与者的实例变量。

首先,我们将牛沿着z轴移动5英尺,然后围绕原点旋转。我们总是以应用程序的相反顺序指定转换:

这些操作产生转换序列:

现在我们用牛的实例变量做同样的事情:

当actor构建它的转换时,它将是:

取消最右边平移矩阵中的负号,并结合位置平移和原点平移,生成我们使用vtktransform构建的等效变换。图3 - 32显示了奶牛按照指定的变换顺序旋转。您的偏好取决于您的品味以及您对矩阵变换的适应程度。随着您变得越来越熟练(并且您的需求越来越大),您可能更喜欢始终构建您的转换。VTK为您提供了选择。

有一个最终的和强大的操作,影响一个演员的方向。您可以围绕位于actor原点的任意向量旋转一个actor。这是通过参与者(和转换)的RotateWXYZ()方法完成的。该操作的第一个参数指定了围绕接下来三个参数指定的向量旋转的度数。图3 - 33显示了如何围绕一个穿过奶牛鼻子的矢量旋转奶牛。首先,原点在(0,0,0)处。这显然不是我们想要的。第二幅图显示的是我们将奶牛的旋转原点改为鼻尖时的旋转。

程序集和其他类型的vtkProp

通常需要将参与者收集到依赖于转换的组的层次结构中。例如,机器人手臂可以由连接在关节处的刚性连杆表示,如肩关节、上臂、肘关节、下臂、手腕关节和手。在这样的配置中,当肩关节旋转时,预期的行为是整个手臂旋转,因为链接连接在一起。这是一个在VTK中被称为程序集的例子。vtkAssembly只是众多图3-32中的一个。tcl)。图3-33母牛绕着一个穿过她鼻子的矢量旋转。(a)原点为(0,0,0)。(b)原点为(6.1,1.3,.02)。(walkCow。tcl)。3.10在VTK中有75个类似演员的职业。如图3 - 34所示,这些类被安排在一个vtkProps的层次结构中。(在舞台和电影术语中,道具是指在舞台上出现或使用的东西。)

通过实例化一个vtkAssembly,然后向其添加部件,在VTK中形成程序集。部件是vtkProp3D的任何实例—包括其他程序集。这意味着程序集可以形成层次结构(只要它们不包含自引用循环)。程序集遵循前一节中描述的转换连接规则(请参阅第70页的“转换矩阵”)。下面是一个示例,说明如何创建简单的程序集层次结构(从程序集。tcl)。

图3-34 vtkProp的层次结构。可以在3D空间中转换的道具是vtkProp3D的一个子类。使用vtkImageActor可以有效地绘制图像。

覆盖文本和图形使用vtkActor2D。vtkProps的分层组被聚集到一个vtkPropAssembly中。卷渲染使用vtkVolume。可转换道具的集合创建了一个vtkAssembly。细节级渲染使用vtkLODProp3D和vtkLODActor。vtkFollower允许面向指定的摄像机,并用于广告牌。

注意,在本例中,使用AddPart()方法将各种参与者添加到程序集。程序集的顶级元素是层次结构中添加到呈现器的唯一道具(使用AddActor())。还要注意,coneActor出现了两次:一次是作为程序集的一部分,一次是作为使用AddActor()添加到呈现器的单独actor。正如您可能想象的那样,这意味着程序集的呈现需要连接转换矩阵,以确保每个vtkProp3D的正确定位。此外,层次程序集在选择过程中需要特殊处理(即图形化地选择道具),因为vtkProp可以在不同的程序集层次结构中出现多次。采摘问题在308页的“采摘”中有更详细的讨论。

如图3 - 34所示,还有其他类型的vtkProp。其中大部分将在本书中找到的许多例子中进行非正式描述。特别地,当我们描述体积渲染时,大量的内容被给予了vtkVolume(参见218页的“体积渲染”)。

3.11章节小结

渲染是用计算机生成图像的过程。计算机图形学是一门包含渲染技术的研究领域,是数据可视化的基础。三维渲染技术模拟灯光和摄像机与物体或演员的交互作用,以生成图像。

一个场景由灯光、摄像机和演员组成。对象顺序渲染技术通过按顺序渲染场景中的角色来生成图像。图像顺序技术每次渲染一个像素的图像。基于多边形的图形硬件基于对象顺序技术。光线追踪或光线投射是一种图像顺序技术。

照明模型需要特定的颜色。我们看到了RGB(红-绿-蓝)和HSV(色调-饱和度-值)颜色模型。HSV模型比RGB模型更自然图3-34 vtkProp的层次结构可以在3D空间中转换的道具是vtkProp3D的一个子类。

使用vtkImageActor可以有效地绘制图像。覆盖文本和图形使用vtkActor2D。vtkProps的分层组被聚集到一个vtkPropAssembly中。卷渲染使用vtkVolume。可转换道具的集合创建了一个vtkAssembly。细节级渲染使用vtkLODProp3D和vtkLODActor。vtkFollower允许面向指定的摄像机,并用于广告牌。vtkProp vtkPropAssembly vtkProp3D vtkActor2D vtkImageActor vtkAssembly vtkActor vtkVolume vtkLODProp3D vtkFollower vtkLODActor 3.12 77模型适用于大多数用户。

照明模型还包括环境照明、漫射照明和高光照明的效果。在计算机图形学中有四个重要的坐标系。模型系统是定义几何图形的三维坐标系。这个世界系统就是笛卡尔系统。所有建模的数据最终都转换为世界系统。视图坐标系统表示摄像机可见的内容。这是一个从(-1,1)缩放的2D系统。

显示坐标系使用计算机显示器上的实际像素位置。齐次坐标系是一个四维坐标系,我们可以在其中包含透视变换的影响。变换矩阵是在齐次坐标上运算的矩阵。变换矩阵可以表示角色的平移、缩放和旋转的效果。

这些矩阵可以相乘得到组合变换。图形编程通常使用高级图形库和专门的硬件系统来实现。这些专用系统提供了更好的性能和更容易实现的图形应用程序。在这些系统中实现的常见技术包括抖动和z缓冲。抖动是一种通过混合可用颜色的组合来模拟颜色的技术。Z-buffering是一种执行隐线和隐面去除的技术。可视化工具包使用基于灯光、摄像机、演员和渲染器的图形模型。渲染器绘制到渲染窗口中。角色属性由属性对象表示,其几何形状由映射器对象表示。这些不同类的实例化放在一起就形成了一个场景。与场景中对象的交互由vtkRenderWindowInteractor和vtkInteractorStyle类实现。它们使用命令/观察者设计模式来触发和响应事件。用户可以观察特定事件并编写可以执行任意任务的回调,从而为特定应用程序轻松扩展工具包。

3.12书目注释

本章为读者提供了足够的信息来理解计算机图形学中使用的基本问题和术语。有许多优秀的教科书更详细地介绍了计算机图形学,推荐给想要更全面了解计算机图形学的读者。计算机图形学的圣经是[FoleyVanDam90]。对于那些希望不那么吓人的书[BurgerGillies89]和[Watt93]也是有用的参考。您也可能希望阅读ACM SIGGRAPH会议记录。这些论文包括计算机图形学中一些最重要工作的论文和参考文献。[Carlson85]为那些希望更多地了解人类视觉系统的人提供了一个很好的介绍。

3.13参考资料

[布列森汉姆65]李志强。数字绘图仪的计算机控制算法IBM系统学报,4(1):25-30,1965年1月。[BurgerGillies89] P. Burger和D. Gillies。交互式计算图形功能、过程和设备级方法。Addison-Wesley, Reading, MA, 1989。44´78计算机图形学入门

[Carlson85] n·r·卡尔森。行为生理学(3d版)。阿林和培根公司,牛顿,马萨诸塞州,1985年。[Dartnall83]李国强,李国强。《人类视色素:七个人眼睛的显微分光光度测定结果》英国皇家学会学报,伦敦,1983年。

[刘志刚,李志刚,李志刚,等。计算机图形学原理与实践(2d版)。Addison-Wesley, Reading, MA, 1990。

[陈晓明,李志刚。基于先验树结构的可见表面生成计算机图形学学报,2014,34(3):377 - 382。[中国科学院科学院学报,2001]。可重用面向对象软件的设计模式。addison - wesley 1995。isbn0 - 201 - 63361 - 2。

[刘国强]刘国强。Tcl和Tk工具包。Addison-Wesley出版公司,Reading, MA, 1994。瓦特。三维计算机图形学(2d版)。Addison-Wesley, Reading, MA, 1993。

[Whitted80]李国强。阴影显示的改进照明模型通信学报,23(6):343-349,1980。

3.14运动

3.1估算一束光线从太阳发射到地球,照射到一平方米见方的野餐毯上的概率。你可以假设太阳是一个点光源,它向各个方向均匀地发光。太阳到地球的距离大约是1.5亿公里。a)当太阳直射头顶时,概率有多大?b)太阳相对于野餐毯表面法线倾斜45度的概率是多少?c)你做了什么假设或近似?

3.2从练习3.1的结果出发,确定一束来自太阳的光线击中野餐毯然后进入观众眼睛的概率有什么困难?3.3青色可以用HSV和RGB两种颜色空间表示,如图3 - 4所示。这两种表示青色不产生相同的波长强度图。它们有什么不同?

3.4 vtkSphereSource类生成一个球体的多边形模型。以本章末尾的例子为起点,创建一个显示白色球体的程序。设置环境和扩散强度为0.5。然后在这个程序中添加一个for循环,调整球体的环境色和漫反射色,以便随着循环的进行,漫反射色从红色变为蓝色,环境色从蓝色变为绿色。您也可以尝试调整其他照明参数,如高光颜色,环境,漫反射和高光强度。

3.5使用练习3.4中描述的vtkSphereSource,创建一个程序来显示带有(1,1,1)光源的球体。然后通过添加forloop来扩展这个程序,该程序将调整活动摄像机的剪辑范围,以便可以看到球体内部的增加部分。通过增加剪辑范围的第一个值,您将调整前剪辑平面的位置。一旦前面的裁剪平面开始与球体相交,你应该能够看到它的内部。vtkSphereSource的默认半径是0.5,因此请确保以小于1.0的增量调整剪切范围。

3.6修改第62页“Render a Cone”中给出的程序,用户可以输入一个同质坐标的世界坐标,程序会打印出结果显示坐标。请参考vtkRenderer的参考页面,以获得一些有用的方法。a)在显示坐标中是否有任何世界坐标是未定义的?b)当世界坐标在相机后面时会发生什么?

3.7考虑将一个10 × 10像素的正方形栅格化。对比在平坦、Gouraud或Phong阴影情况下需要执行的算术运算数量的近似差异。

3.8当使用z-buffer时,我们也必须在栅格化原语时插入z值(或深度)。从练习3.7开始,在栅格化我们的正方形时,计算z-buffer值的额外负担是什么?

3.9 vtkTransform有一个GetOrientation()方法,它查看由一系列旋转构建的结果转换矩阵,并提供将重新生成矩阵的单个x、y和z旋转。指定一系列不同顺序的旋转,并使用GetOrientation()请求方向。然后应用与vtkActor相同的旋转顺序,并验证得到的4 x 4变换矩阵是相同的。

3.10 vtkTransform在默认情况下,在当前转换的右侧应用新的转换。方法PostMultiply()更改行为,以便将转换应用于左侧。a)使用vtkTransform创建转换,使用各种转换操作符,包括Scale()、RotateXYZ()和Translate()。然后使用PostMultiplyOn()创建相同的矩阵。b)在一系列变换的右侧应用旋转,实际上会使对象围绕自己的坐标系统旋转。利用旋转。TCL脚本来验证这一点。你能解释一下吗?c)在一系列变换的左边应用旋转,有效地使对象围绕世界坐标系旋转。修改旋转。TCL脚本来说明这一点。(提示:您必须使用vtkTransform创建一个显式转换,并使用SetUserMatrix()设置参与者的转换。)

本书为英文翻译而来,供学习vtk.js的人参考。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

vtk教程第三章 计算机图形学入门 的相关文章

随机推荐

  • 接口测试-第03天-Postman用例集、断言、前置脚本、关联、生成测试报告

    更多功能测试以及全套学习路线图均在专栏 戳进去领取 系列文章目录 身为开发必知必会的Linux Linux远程连接 命令的使用 Linux命令大全 唯一以案例详解文 持续更新中 Linux命令大全以及数据库 唯一以案例详解文 已完结 Web
  • SVM算法笔记(2)

    线性可分支持向量机与硬间隔最大化 1 线性可分支持向量机 一般地 训练数据线性可分 存在无穷个分离超平面可将两类数据正确分开 感知机利用误分类最小的策略 求得分离超平面 解有无穷多个 线性可分支持向量机利用间隔最大化求最优分离超平面 解唯一
  • QT从入门到实战x篇_12_资源文件添加(QWindow的手动布局、路径用反斜杠、增加代码移植性)

    接上文 QT从入门到实战x篇 xx QMainWindow状态栏 铆接部件 核心部件 可以有多个的add 只能有一个的set 本篇介绍如何添加资源文件 创建Qt程序如下 1 手动实现上篇中提到的部件 1 在UI文件中可以双击菜单栏对应项目
  • recognition mnist handwriting digits

    recognition mnist handwriting digits download mnist and load data MNIST can be downloaded in this website http yann lecu
  • Python 实现 MQTT 演示版

    MQTT 消息 MQTT 是一种机器对机器 M2M 物联网 连接协议 MQTT 代表消息队列遥测传输 它是一种基于发布订阅的消息传递协议 它工作在 TCP IP 协议之上 工作原理 首先 订阅者订阅一个或多个主题 然后一个或多个发布者将消息
  • Maven本地仓库jar存在仍然从远程仓库下载且失败的现象

    使用Maven过程中 曾经出现过本地仓库中已经存在某jar包 但是Maven仍然从远程仓库下载jar包的现象 解决方案 可以通过删除包目录中的 remote repositories文件解决问题 Maven使用 remote reposit
  • windows server 2008 R2 修改Adminstrator用户名及密码

    转自 https jingyan baidu com article fcb5aff75d883bedaa4a7128 html 开通服务器后 为了安全起见 应及时修改默认账户名和密码 修改方法如下 一 Windows Server 200
  • Web前端复习——JavaScript复习(DOM)

    1 DOM概述 DHTML 动态网页技术的统称 DHML HTML CSS JS HTML XHTML DHTML XML HTML 超文本标记语言 专门编写网页内容的语言 XHTML 严格的HTML语言标准 DHTML 动态网页技术的统称
  • 动态修改布局

    在代码中设置控件大小的方法 private Button mbtn mbtn Button findViewById R id btn test LayoutParams lp lp mbtn getLayoutParams lp widt
  • nginx配置多个前端项目

    最近一台服务器要配置多个前端项目 当然前后端分离就需要nginx来配置了 单个项目还好说 如下 修改nginx的nginx conf配置文件 user nobody worker processes 1 error log logs err
  • (十四)用StatsModels模块建立线性回归模型

    使用StatsModels中的api子模块 主要使用的函数有 注意大小写 OLS 普通最小二乘法 GLS 广义最小二乘法 WLS 加权最小二乘法 GLM 广义线性模型 mixed 混合效应模型 以OLS为例 函数格式为OLS endog e
  • Docker查看本地所有的镜像命令(docker images)

    查看本地所有的镜像 查看本地所有的镜像 docker images 非root用户使用 查看本地所有的镜像 sudo docker images 查看所用镜像的id 查看所用镜像的id docker images q
  • 第17届开源中国开源世界高峰论坛文集出版

    第17届开源中国开源世界高峰论坛文集将于近日出版刊出 附文集部分内容 1 文集目录 2 陆主席主旨报告 众所周知 1970年是UNIX元年 这一年也是开源在全球实质上的诞生之日 开源在全球流行至今已有52年了 自从1991年我国引进UNIX
  • 【C语言】归并排序

    一 算法描述 用递归算法把一个数组拆成两份 直到拆成一份一份的 在递归算法里添加一个函数 能把两个有序数组合并成为一个有序数组 直到完成递归 二 归并排序代码 三 测试代码 include
  • 微信小游戏_China_Fighting——player类(hero、button)

    目录 微信小游戏 China Fighting 前言 微信小游戏 China Fighting 基础支撑类 sprite animation pool 微信小游戏 China Fighting npc类 enemy mask sars 微信
  • Java图书管理系统 -- 基于Socket实现客户端服务端拆分

    图书管理系统小Demo又又又升级了 本图书管理系统已经经历了三个阶段 通过操作数组来实现图书的增删改查方法 用控制台获取用户输入来实现人机交互 通过集合容器存储对象 使用序列化在管理系统开启关闭时 加载 存储数据到本地 使用TCP协议实现客
  • Vue3生命周期了解

    Vue3生命周期 在 setup 中 可以通过 onX 的方式注册 生命周期钩子 1 beforeCreate gt 使用 setup 2 created gt 使用 setup 3 beforeMount gt onBeforeMount
  • Sentinel服务流控、熔断和降级

    谷粒商城 分布式基础篇 环境准备 谷粒商城 分布式基础 业务编写 谷粒商城 分布式高级篇 业务编写 持续更新 谷粒商城 分布式高级篇 ElasticSearch 谷粒商城 分布式高级篇 分布式锁与缓存 项目托管于gitee 一 Sentin
  • hrnetv2训练自己的数据集(cityscapes格式)

    简单记录利用hrnetv2训练自有数据时 遇到的问题 1 下载github代码 配置环境 2 假设已有原始8位图像和8位label图像 3 修改root lib datasets cityscapes py中的部分 self label m
  • vtk教程第三章 计算机图形学入门

    计算机图形学是数据可视化的基础 实际上 可视化是将数据转换为一组图形原语的过程 然后使用计算机图形学的方法将这些原语转换成图片或动画 本章讨论计算机图形学的基本原理 我们从描述光和物理物体如何相互作用形成我们所看到的开始 接下来 我们将研究