前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >3D 图形学基础 (下)

3D 图形学基础 (下)

原创
作者头像
serena
修改于 2021-08-03 06:56:06
修改于 2021-08-03 06:56:06
2.7K0
举报
文章被收录于专栏:社区的朋友们社区的朋友们

作者:Lingtonke(柯灵杰)

《 3D 图形学基础 (上)》

6 色彩和纹理

一个纹理实际上就是一个位图。从这个意义上来讲,当纹理一词被用于计算机图形学时,它就有了一个明确的定义。从语义学角度来讲,纹理一词既是指一个物体上颜色的模式,又是指物体表面是粗糙的还是光滑的。

6.1 纹理坐标

纹理实际上是一个二维数组,它的元素是一些颜色值。单个的颜色值被称为纹理元素(texture elements)或纹理像素(texel)。每一个纹理像素在纹理中都有一个唯一的地址。这个地址可以被认为是一个列(column)和行(row)的值,它们分别由U和V来表示。

纹理坐标位于纹理空间中。也就是说,它们和纹理中的(0,0)位置相对应。当我们将一个纹理应用于一个图元时,它的纹理像素地址必须要映射到对象坐标系中。然后再被平移到屏幕坐标系或像素位置上。

6.2 纹理过滤

渲染一个图元时,会将三维图元映射到二维屏幕上。如果图元有纹理,就必须用纹理来产生图元的二维渲染图象上每个像素的颜色。对于图元在二维屏幕上图象的每个像素来说,都必须从纹理中获得一个颜色值。我们把这一过程称为纹理过滤(texture filtering)

进行纹理过滤时,正在使用的纹理通常也正在被进行放大或缩小。换句话说,这个纹理将被映射到一个比它大或小的图元的图象上。纹理的放大会导致许多像素被映射到同一个纹理像素上。那么结果看起来就会使矮矮胖胖的。纹理的缩小会导致一个像素被映射到许多纹理像素上。其结果将会变得模糊或发生变化。要解决这些问题,我们可以将一些纹理像素颜色融合到一个像素颜色上。

每种类型的纹理过滤都有各自的优缺点。例如,线性过滤会产生锯齿状的边缘和矮胖的效果。但是,它对系统的消耗却是最小的。另一方面,mipmap过滤的效果通常是最好的,特别是和各项异性过滤混合使用时。但是它却需要很大的内存消耗。

6.2.1 双线性过滤

线性过滤方法是双线性过滤(bilinear filtering)。和最近点采样一样,双线性过滤首先要计算一个纹理像素的地址,这个地址通常不是整数地址。然后,找到一个地址最接近的整数地址纹理像素。另外,渲染模块还要计算与最近采样的点相邻的四个纹理像素的加权平均(weighted average)。

6.2.2 各项异性过滤

各向异性是对一个三维物体纹理像素的可见的变形,这个物体的表面朝向屏幕平面,并与之有一定的角度。各向异性图元的像素在映射到纹理像素时,它的形状会发生变形。

各向异性纹理过滤可以和线性过滤或mipmap过滤联合使用。

6.2.3 Mipmap

一个mipmap就是一系列的纹理,每一幅纹理都与前一幅是相同的图样,但是分辨率都要比前一幅有所降低。mipmap中的每一幅或者每一级图象的高和宽都比前一级小二分之一。Mipmap并不一定必须是正方形。

高分辨率的mipmap图象用于接近观察者的物体。当物体逐渐远离观察者时,使用低分辨率的图象。Mipmap可以提高场景渲染的质量,但是它的内存消耗却很大。

6.3 纹理环绕

一般来说,分配的U、V纹理坐标值都在0.0到1.0范围内(包括它们)。但是,如果我们分配了超出这个范围的纹理坐标,可能会得到一些特别的纹理效果。

通过设置纹理寻址模式,我们就可以在纹理坐标超出范围时进行控制。

6.3.1 重复

图象在表面上重复出现。忽略纹理坐标的整数部分,并将纹理图的拷贝粘贴在物体表面上,这样才能做到无缝连接。

6.3.2 截取

将大于1.0的数值设置为1.0,将小于0.0的数值设置为0.0,即将超出0.0,1.0范围的数值截取到0.0,1.0范围内,

这样会导致纹理边缘的重复。

6.3.3 镜像重复

图象在物体表面上不断重复,但是每次重复的时候对图象进行镜像或者反转。这样在纹理边缘处比较连贯。

6.3.4 边界截取

在0.0,1.0范围外的参数值用单独定义的边界颜色或纹理边缘进行绘制。适合于绘制物体表面的贴花纸。

6.3.5 边缘截取

总是忽略边界。处于纹理边缘或者靠近纹理边缘的纹理单元都用作纹理计算,但是不包括边界上的纹理单元。

6.4 立方体纹理

立方体纹理是一种特殊的纹理技术,它用6幅二维纹理图像构成一个以原点为中心的纹理立方体。对于每个片段,纹理坐标(s, t, r)被当作方向向量看待,每个纹素(texel)都表示从原点所看到的纹理立方体上的图像。

基本上说cubemap它包含6个2D纹理,这每个2D纹理是一个立方体(cube)的一个面,也就是说它是一个有贴图的立方体。你可能会奇怪这样的立方体有什么用?为什么费事地把6个独立纹理结合为一个单独的纹理,只使用6个各自独立的不行吗?这是因为cubemap有自己特有的属性,可以使用方向向量对它们索引和采样。想象一下,我们有一个1×1×1的单位立方体,有个以原点为起点的方向向量在它的中心。

6.5 点精灵

6.5.1 什么是点精灵

openGL的图形由顶点构成,以后利用顶点进行纹理的映射。点精灵就是,一个顶点被当作一个精灵来处理。特别之处就是,一个顶点也可进行纹理贴出。例如,原来是个顶点构成的一个矩形,现在一个顶点就可以完成了。瞬间我们就可以想想,粒子效果,那些云雾水流火花什么的用了点精灵,就可以瞬间减少3个顶点的计算,glDrawArrays使用GL_POINT就可以了,完全也不需要什么顶点索引了。这是非常诱人的效率。

6.5.2 点精灵的局限

一个顶点缩放都必须是矩形。并且大小的最大最小值是有范围的。既然是一个纹理映射到一个顶点上,那么纹理映射就和原来完全不同,有些复杂。可能会抵消掉一些性能的优势。

6.6 纹理压缩

贴图是在 3D 场景中,增加真实性的一个重要的工具。就像一般的影像一样,贴图的大小愈大,它的图像就愈精细。事实上,贴图往往需要比一般的影像更大。因为,在 3D 场景中,观察者可能会很靠近贴图,使得贴图需要放大很多倍,而造成模糊的现象。所以,一般来说,如果可能的话,贴图愈大就愈好。

不过,贴图是非常占用内存空间的。

常用的图像文件格式有BMP,TGA,JPG,GIF,PNG等;

不过象JPG这种常见图像压缩格式对于多数应用的内存占用和显示总线带宽占用并没有直接的好处,因为还得解压缩成原始像素再传给显卡,而且还有加载时的解碼计算负担。这是因为显卡的纹理解碼硬件不理解JPG格式。所以,在没有显卡硬件支持的情况下,用压缩格式保存纹理没什么意义,特别是对于手持移动设备来说,解碼象JPG这种复杂格式是很浪费电的。

考虑到现代游戏对纹理图片的严重依赖,及相应的对视频总线的巨大压力,硬件实时解压缩获得了广泛的支持,不过这个还没有一种格式获得多个厂家的支持。纹理数据的格式则没有标准,要参考厂商的SDK或文档获得format值。这也就意味着,使用了压缩纹理之后就不能跨平台了。

纹理格式是能被GPU所识别的像素格式,能被快速寻址并采样。

在Beers,Agrawala和Chaddha于1996发表的一篇影响深远的论文基于已压缩纹理的渲染1中,他们列举四项纹理压缩的特点,使其不同于其他图像压缩技术。

解压速度:由于最好能直接从已压缩的纹理直接渲染,为了尽可能地不影响性能,解压缩要尽可能快。

随机访问:由于几乎不可能预测纹素被访问的顺序,任何纹理压缩算法必须允许对其中纹素的随机访问。所以几乎所有的纹理压缩算法都以块为单位压缩和存储纹素,当某一纹素被访问时,只有同一块中若干纹素被读取和解压缩。这项需求也排除了很多压缩率较高的图像压缩方式,例如JPEG和行程长度编码。

压缩率和图像质量:由于人眼的不精确性,相比于其他应用领域,图像渲染更适宜使用有损数据压缩。

编码速度:纹理压缩对压缩速度要求不高,因为绝大多数情况下,纹理只需要进行一次压缩。

由于其数据访问模式是事先知道的,纹理压缩常作为整个绘图管线的一部分,在绘制时对动态地已压缩数据进行解压缩。而反过来绘制管线也可以通过纹理压缩技术来降低对于带宽和存储的需求。在纹理贴图中,已压缩纹理和没有经过压缩的纹理使用起来基本没有区别,都可以被用来存储颜色数据或其他数据,例如凹凸贴图或法线贴图,也都可以和Mipmapping或各向异性过滤等共同使用。

6.6.1 主要GPU厂商及支持的纹理压缩算法

7 粒子系统

7.1 效果预览

粒子系统表示三维计算机图形学中模拟一些特定的模糊现象的技术,而这些现象用其它传统的渲染技术难以实现的真实感的效果。经常使用粒子系统模拟的现象有火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等。

7.2 主要参数

什么是粒子系统?粒子系统是由总体具有相同的表现规律,个体却随机表现出不同的特征的大量显示元素构成的集合。

这个定义有几个要素:

1、 群体性:粒子系统是由“大量显示元素”构成的。因此,用粒子系统来描述一群蜜蜂是正确的,但描述一只蜜蜂没有意义。

2、 统一性:粒子系统的每个元素具有相同的表现规律。比如组成火堆的每一个火苗,都是红色,发亮,向上跳动,并且会在上升途中逐渐变小以至消失。

3、 随机性:粒子系统的每个元素又随机表现出不同特征。比如蜂群中的每一只蜜蜂,它的飞行路线可能会弯弯曲曲,就象布郎运动一般无规则可寻,但整个蜂群,却是看起来直线向一个方向运动(这就是上一点所说的统一性)。

8 数学基础

这一部分是很简单的,对于不同数学知识背景的读者来说都容易阅读。对于想了解更多更全的这方面信息的读者,请查看有关线性代数和高等数学的书籍。

8.1 向量

几何学中,我们用有向线段表示向量。向量的两个属性是他的长度和他的顶点所指的方向。因此,可以用向量来模拟既有大小又有方向的物理模型。例如,以后我们要实现的粒子系统。我们用向量来模拟粒子的速度和加速度。在3D计算机图形学中我们用向量不仅仅模拟方向。例如我们常常想知道光线的照射方向,以及在3D世界中的摄象机。向量为在3维空间中表示方向的提供了方便。

8.2 变换

3D空间中最常用的变换主要是,平移,旋转,缩放

8.2.1 变换的顺序

变换作用的顺序会影响变换最终的结果。

通过使用矩阵相乘把3个变换矩阵合成一个矩阵。注意我们必须按实际应用的顺序来进行矩阵相乘。

8.2.2 模型视图的二元性

视图和模型变换对场景外部的最终效果来看是一样的,将两者区分开纯粹是为了程序员方便。将对象向后移动和将参考坐标系向前移动在视觉上没有区别。

8.2.3 透视投影

透视投影属于中心投影。透视投影图简称为透视图或透视,它是从某个投射中心将物体投射到单一投影面上所得到的图形。透视图与人们观看物体时所产生的视觉效果非常接近,所以它能更加生动形象地表现建筑外貌及内部装饰。在已有实景实物的情况下,通过拍照或摄像即能得到透视图;对于尚在设计、规划中的建筑物则作图(手工或计算机)的方法才能画出透视图。透视图以渲染、配景,使之成为形象逼真的效果图。

透视投影符合人们心理习惯,即离视点近的物体大,离视点远的物体小,远到极点即为消失,成为灭点。它的视景体类似于一个顶部和底部都被切除掉的棱椎,也就是棱台。这个投影通常用于动画、视觉仿真以及其它许多具有真实性反映的方面。

8.2.4 正交投影

投影线垂直于投影面的投影属于正交投影 ,属于平行投影的一种。

8.3 粒子的轨迹

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
C++随记(三)---动态分配内存问题(2)
本文讲述了动态分配内存和数组的一些注意事项,包括指针、数组名、数组尺寸等。作者通过举例说明了指针和数组名之间的区别,以及使用sizeof运算符获取数组尺寸的便捷性。同时,文章也提到了使用new运算符动态分配内存时的注意事项,包括指针、数组名、数组尺寸等。
TeeyoHuang
2017/12/28
8830
C++ 指针常量与常量指针
本文介绍了C++指针常量和常量指针的概念、区别以及用法。指针常量声明为const,但常量指针声明为int const *。指针常量不能修改指向的数据,但常量指针可以。此外,常量指针必须初始化,而指针常量可以在声明时初始化。总之,两者在用法上存在一定的区别,需要根据具体情况选择合适的声明方式。"
chaibubble
2018/01/02
1.8K0
C++ 指针常量与常量指针
C语言中函数参数传递的三种方式
(1)传值,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值。 (2)传址,就是传变量的地址赋给函数里形式参数的指针,使指针指向真实的变量的地址,因为对指针所指地址的内容的改变能反映到函数外,也就是能改变函数外的变量的值。 (3)传引用,实际是通过指针来实现的,能达到使用的效果如传址,可是使用方式如传值。 说几点建议:如果传值的话,会生成新的对象,花费时间和空间,而在退出函数的时候,又会销毁该对象,花费时间和空间。 因而如果int,char等固有类型,而是你自己定义的类或结构等,都建议传指针或引用,因为他们不会创建新的对象。
全栈程序员站长
2022/07/02
4.8K0
C语言中函数参数传递的三种方式
C++之类和对象的使用(三)
对象数组 如果构造函数只有一个参数,在定义数组时可以直接在等号后面的花括号内提供。Student stud[3]={90,92,01};//合法 如果构造函数有多个参数,则不能用在定义时直接所提供所有实参的方法。 但可以如下定义: //构造函数有三个参数:学号,年龄,成绩 Student stud[3]{ Student(1000,19,22); Student(1001,19,22); Student(1002,19,22); };   对象指针 指向对象的指针 class Box {public:
互联网金融打杂
2018/04/03
9130
C++之类和对象的使用(三)
[基础]《C Primer》笔记(上篇)
要把一个较小的常量作为long类型对待时,可以在值的末尾加上L后缀(小写的l不容易和数字1区分)。在支持long long的系统中,可以在值的末尾加上LL区分long long类型。
TOMOCAT
2020/06/09
2.2K0
[基础]《C Primer》笔记(上篇)
C/C++ const
const是C语言的关键字,经C++扩充,功能变得强大,用法复杂。const用于定义一个常变量(只读变量)。当const与指针、引用、函数等结合起来使用时,情况会变得更加复杂。下面将从七个方面总结const的用法。
恋喵大鲤鱼
2018/08/03
9430
C:数组与指针,指针与const
我们都知道一个指针是代表的一个地址,指针,顾名思义,指向一块区域。那么数组呢?数组并不是代表一堆变量,数组其实也是一种指针,指向一个地址,一般是指向数组的首地址,也就是 a[0]的地址。 a==&a[0] a 是一个指针,指向数组 a 的首地址。 下面四种函数原型都是等价的,第一个参数均为一个地址(指针)。 若定义 int a[10];
字节星球Henry
2021/08/09
8530
const括号前括号后
将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数
ljw695
2024/10/18
1340
const括号前括号后
C++中指针和引用的区别
指针和引用主要有以下区别: 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。 引用初始化后不能被改变,指针可以改变所指的对象。 不存在指向空值的引用,但是存在指向空值的指针。 注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能引发错误。所以使用时一定要小心谨慎。 从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改
猿人谷
2018/01/17
5.2K0
与C++类和对象的宿命(下)
• 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。
Undoom
2024/10/09
2420
与C++类和对象的宿命(下)
c++之指针
在32位操作系统下,无论指针数据类型是什么数据类型,都是占4个字节,而在64位操作系统中占8个字节。
西西嘛呦
2020/08/26
5670
EasyC++28,const和指针
我们知道const关键字修饰的是不可变量,将它和指针一起使用,会有很多微妙的地方。
TechFlow-承志
2022/08/26
1650
C++ 万字长文第一篇---拿下字节面试
有些情况下,基类生成的对象是不合理的,比如动物可以派生出狮子、孔雀等,这些派生类显然存在着较大的差异。那么可以让基类定义一个函数,并不给出具体的操作内容,让派生类在继承的时候在给出具体的操作,这样的函数被称为纯虚函数。含有纯虚函数的类成为抽象类,抽象类不能声明对象,只能用于其他类的继承。 纯虚函数的定义方法为:
syy
2020/08/13
1.6K0
C++ 万字长文第一篇---拿下字节面试
c++ primer2 变量和基本类型。
这四种初始化方式c++11都是支持的。c++11中用花括号来初始化变量得到了全面应用。
和蔼的zhxing
2018/09/04
5490
C++从入门到精通——const与取地址重载
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。
鲜于言悠
2024/04/21
2250
C++从入门到精通——const与取地址重载
指针讲解:*&p和&*p 及const的用法总结
一讲到指针,不少同学就会觉得云里雾里。首先要明白,指针和地址是一个概念;然后明白指针和指针变量的区别。
怪兽
2022/10/04
6220
指针讲解:*&p和&*p 及const的用法总结
【C++】类和对象练习——日期类的实现
我们看这个计算出来的日期确实是没问题的,但是d1+100,这里是+而不是+=,所以d1是不是不应该变啊,我们只是把d1+100得到的日期赋给了d2,但是现在d1也变了。 所以我们这样写其实实现的是啥,是不是+=啊。
YIN_尹
2024/01/23
3650
【C++】类和对象练习——日期类的实现
C++基础 杂记(一)
函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但会破坏此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。
xxpcb
2020/08/04
4040
C/C++中const的作用总结
1)#define定义的常量没有类型,所给出的是一个立即数;const定义的常量有类型名字,存放在静态存储区。
_咯噔_
2022/03/08
1.1K0
C++基础知识
static 定义的静态局部变量分配在数据段上,普通的局部变量分配在栈上,会因为函数栈帧的释放而被释放掉。
代码的路
2022/08/23
1.5K0
C++基础知识
相关推荐
C++随记(三)---动态分配内存问题(2)
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 6 色彩和纹理
    • 6.1 纹理坐标
    • 6.2 纹理过滤
    • 6.3 纹理环绕
    • 6.4 立方体纹理
    • 6.5 点精灵
    • 6.6 纹理压缩
    • 6.6.1 主要GPU厂商及支持的纹理压缩算法
  • 7 粒子系统
    • 7.1 效果预览
    • 7.2 主要参数
  • 8 数学基础
  • 8.1 向量
    • 8.2 变换
    • 8.3 粒子的轨迹
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档