图形学笔记(八)着色2 —— 纹理映射、重心坐标、双线性插值、Mipmap、三线性插值、各向异性过滤、纹理的应用(环境贴图、法线贴图等)

图形学笔记(七)着色 —— Blinn-Phone 反射模型、着色频率、渲染管线、GPU
图形学笔记(九)几何 ——几何表示方法(CSG、距离函数、水平集 、点云、网格(obj格式))、贝塞尔曲线(面)

文章目录

1 纹理映射定义

纹理映射(Texture Mapping),又称纹理贴图,是将纹理空间中的纹理像素映射到屏幕空间中的像素的过程。简单来说,就是把一幅图像贴到三维物体的表面上来增强真实感。

在这里插入图片描述

任意三角形的顶点都能找到顶点在纹理上哪个位置。
在这里插入图片描述

2 纹理的坐标系 —— UV坐标

在这里插入图片描述

U和V的范围都在0到1内。三角形的三个顶点,每个顶点都对应一个UV。

纹理能被多次使用。如果纹理重复上下左右可以无缝衔接,则称这个纹理是tiled textures。(有一种算法叫Wang tiling)

3 三角形的插值:重心坐标(Barycentric Coordinates)

3.1 插值的用处

如果对三角形的三个顶点都有特定的值,那么通过重心坐标的方法可以得到三角形内任何一点的这个值,并且这些值从一个顶点到另一个顶点是平滑过渡的。

对什么进行插值?
纹理坐标,颜色,法线向量…

3.2 重心坐标系

  1. 重心坐标是定义在三角形上的。
    在这里插入图片描述

  2. 在重心坐标系中,三角形平面的任何一个点(x,y)都可以表示成三角形三个顶点的线性组合(系数分别是 α β γ \alpha \beta \gamma αβγ,且满足这三个系数相加和为1)。
    在这里插入图片描述

  3. 如果这个点在三角形,则需要 α β γ \alpha \beta \gamma αβγ都是非负的

eg:
A点的重心坐标 ( α , β , γ ) = ( 1 , 0 , 0 ) (\alpha,\beta,\gamma)=(1,0,0) (α,β,γ)=(1,0,0) ( x , y ) = α A + β B + γ C = A (x,y)=\alpha A+\beta B+\gamma C=A (x,y)=αA+βB+γC=A

3.3 获得三角形任意一点的重心坐标

可以通过面积比求重心坐标。 α , β , γ \alpha,\beta,\gamma α,β,γ的计算如下所示,其中A点对应 A A A_A AA(表示A点的面积),B点对应 B B B_B BB,C点对应 C C C_C CC
在这里插入图片描述

然后通过叉积求面积,得到重心坐标的一般的表达式。
在这里插入图片描述

3.4 获得三角形的重心

重心的性质:将重心与三个顶点相连,可以把三角形分割成等面积的三个三角形。下面是重心坐标系和普通坐标系下的三角形重心坐标。
在这里插入图片描述

3.5 使用重心坐标

使用重心坐标系通常有如下步骤:

  1. 先算出重心坐标的位置
  2. 再利用重心坐标做插值

如下图所示,V可以是任何属性,只要根据位置把 α β γ \alpha \beta \gamma αβγ确定下来,就可以确定该位置对应某个属性的插值。
在这里插入图片描述

Tips:投影变换下,重心坐标是不能保持不变的。所以想要插值三维空间的属性,就要找三维的坐标,不能在投影后的三角形做(比如深度)。

4 将重心坐标的插值应用到纹理上

对于每一个光栅化的样本(x,y)的应用流程如下:

  1. 根据三角形顶点的纹理坐标使用重心坐标进行插值,获得该位置的(u,v)。
  2. 在纹理上查询这个(u,v)对应的颜色值
  3. 在该样本设置这个颜色
    在这里插入图片描述

5 纹理的放大 Texture Magnification 相关问题

5.1 纹理太小了

5.1.1 问题引出

如果有4k分辨率,纹理只有515X512怎么办?

5.1.2 纹理太小的问题

对于任何一个点找到纹理上的位置,如果纹理上的这个位置不是整数,采用四舍五入的办法变成整数。这样的话在一定范围内查找的是相同的纹理上的像素(texel 称为纹理元素、纹素 )。
在这里插入图片描述

很多pixel由于纹理太小了可能会映射到同一个texel上去(就像最左边的图,不是很连续,有马赛克格子)。

5.1.3 解决纹理太小问题的目标

通过改进,使得图片看上去像下面右边的两张图一样,虽然有点糊,但是没那么有割裂感。
在这里插入图片描述

5.1.4 方法一 —— 双线性插值 Bilinear interpolation

  1. 找到映射的中心的临近的四个点
    在这里插入图片描述

  2. 找到中心点距离四个点距离左下的那个点的宽和高(s和t)。
    在这里插入图片描述

  3. 定义一个线性插值的操作。
    在这里插入图片描述

  4. 先获得两个辅助点的插值。
    在这里插入图片描述

  5. 最后获得竖直的插值
    在这里插入图片描述

这样的话,这个点就可以获得四个点的颜色信息,实现了平滑过渡。
因为做了两次插值,所以称为双线性插值。

5.1.5 方法二 —— Bicubic 双向三次插值

效果好于Bilinear,取的不是四个点,而是十六个点。做的是三次的插值,而不是线性的插值。

5.2 纹理太大了

5.2.1 纹理太大出现的问题 —— 摩尔纹和锯齿

在这里插入图片描述
可以发现如果纹理过大会出现如右图的情况,远处摩尔纹,近处锯齿

5.2.2 原因分析

1> 从pixel与texel上的对应来看
在这里插入图片描述

屏幕上的像素从近到远,一个像素覆盖纹理上的区域是由少到多的。
但是对于很远的点,一个像素的颜色值以一个中心点来采样是相当不准确的。

解决办法:超采样(消耗太大,需改进)
在这里插入图片描述
2> 从采样来看纹理过大的影响
纹理太大时,一个像素包含很大一块纹理,采样的速度跟不上信号变化的速度。(所以可以通过增加采样频率来解决)

解决方法:避免采样,不采样就不会消耗那么大了,所以有办法能快速获得一片区域的平均值吗?

5.2.3 方法一 —— Mipmap

1 引出
  1. 超采样可以解决纹理过大的问题,但是它消耗过大,如果有一种算法可以立刻获得平均而不用进行太多计算就好了。
  2. 从近到远一个像素对应的采样区域大小是不同的,所以这个算法的范围查询应该可以查出任意不同范围的大小。
    在这里插入图片描述
2 作用

允许进行不同大小的范围查询。(快速的正方形的近似范围查询fast、approx.、square)

3 定义

Mip意思是很多不同的小东西。
对于一张贴图,可以不断将分辨率缩小一半。
在这里插入图片描述
在这里插入图片描述
然后只需要找出每个像素在对应层对应位置的映射就可以了。

Tips:经过Mipmap,只多了三分之一的存储量。

4 Mipmap的流程
  1. 获得自己和邻居的中心位置
    在这里插入图片描述

  2. 将这四个位置在纹理空间上进行映射
    在这里插入图片描述

  3. 根据以下公示进行计算
    在这里插入图片描述

其中,D是层数,只需要知道L就可以计算,代表这个像素应该在第D( l o g 2 L log_2L log2L)层Mipmap去寻找颜色。

  1. 根据D和映射后的UV坐标得到结果
5 问题

如下图是Mipmap可视化,可以发现有渐变,但是渐变不连续。
在这里插入图片描述

6 深度不连续的解决方法 —— 插值

进行插值(比如在1.8层,则可以对第1层和第二层进行插值)

6 几种插值方法

6.1 三线性插值 Trilinear Interpolation

6.1.1 介绍

三线性插值即两次查询,一次插值,找到D和D+1层的结果然后进行插值。
在这里插入图片描述

经过三线性插值后的Mipmap可视化
在这里插入图片描述

6.1.2 三线性插值的的局限性

出现了Overblur(远处模糊的过分了)
在这里插入图片描述

原因:只能对正方形进行近似,插值不是精确而是近似值。

6.2 (部分)解决三线性插值问题:各向异性过滤(Anisotropic Filtering)

6.2.1 介绍

在这里插入图片描述
Anisotropic Filtering相比于Mipmap多了不均匀的水平和竖直的压缩,如下图所示(下图又称Mipmap)
在这里插入图片描述
Tips:

  1. 各向异性的意思是不同方向上的表现各不相同
  2. 倍率不管开多大,存储量最多是3倍

使用各项异性,就可以查询到一个矩形的区域,而不用限制在一个正方形上。

6.2.2 局限性

虽然Anisotropic Filtering解决了矩形查询的问题,但是对于斜着的效果不好,仍然具有局限性。
在这里插入图片描述

6.3 覆盖不规则的形状 —— EWA filtering

对于不规则的形状可以拆成几个圆形去覆盖这个形状。每次查询一个圆形,然后多次查询来覆盖不规则的形状。
在这里插入图片描述

7 纹理的应用

7.1 纹理的理解

在现代的GPU中, texture = memory + range query

7.2 环境光照(贴图) Environment Map

如下茶壶会被环境光照亮,即它可以反射出任何方向来的光。如下图就反射出了窗户和门。
在这里插入图片描述假设,环境光只记录方向,深度没有实际意义。

可以把环境光记录到球上,并且展开。
在这里插入图片描述
在这里插入图片描述

展开后出现了扭曲问题(向中间扭曲)。

解决办法 —— Cube Map
即不把光照信息存在球的表面上,而是立方体的表面。
在这里插入图片描述因为立方体各个面是均匀的,所以不会出现扭曲。
在这里插入图片描述

存在的问题:相对于球来说,需要多记录一个方向。

本质:记录不同方向的光照信息。

7.3 纹理可以影响着色

纹理可以定义任何不同位置任意不同的属性,比如定义任何一个点的相对高度。

如下图就是一种凹凸贴图(定义法线的不同),这样就可以不用定义复杂的几何形体。
在这里插入图片描述

7.3.1 Bump Mapping 凹凸贴图

Normal Mapping法线贴图不等于Bump Mapping凹凸贴图,法线贴图存储的是法线,凹凸贴图存储的是高度差。

1> 凹凸贴图的作用

凹凸贴图可以定义复杂的纹理,但是不改变任何几何信息(三角形数量不变)。

  • 把任何一个像素的法线做扰动
  • 纹理定义任何一个点相对高度的移动
  • 然后可以通过高度的变化改变法线
    在这里插入图片描述
2> 如何扰动法线(in flatland)
  • 假设原本的表面是平面(原本 n ( p ) = ( 0 , 1 ) n(p)=(0,1) n(p)=(0,1)
  • p点处的导数 d p = c ∗ [ h ( p + 1 ) − h ( p ) ] dp = c * [h(p+1) - h(p)] dp=c[h(p+1)h(p)] (c是一个常数,用来定义凹凸贴图的影响程度)
  • 扰动后的法线是 n ( p ) = ( − d p , 1 ) . n o r m a l i z e d ( ) n(p) = (-dp, 1).normalized() n(p)=(dp,1).normalized()(就是把切线旋转90°)
    在这里插入图片描述
3> 如何扰动法线(in 3D)
  • 假设原本的表面是平面(原本n(p)=(0,0,1))
  • p点处的导数
    dp/du = c1 * [h(u+1) - h(u)]
    dp/dv = c2 * [h(v+1) - h(v)]
  • 扰动后的法线是n(p) = (-dp/du,-dp/dv, 1).normalized() (就是把切线旋转90°)
  • 注意以上都是处于局部坐标系内
4> TBN矩阵(参考

TBN矩阵用于将存储在纹理空间的法向量转换到模型空间中。(但是实际使用时,由于光线条数远远小于法线数目,为了减少计算量,要将光线从模型空间转换到纹理空间,然后计算反射光线。)

法线纹理的映射方式和漫反射纹理相似。麻烦之处在于如何将法线从各三角形局部空间(切线空间tangent space,亦称图像空间image space)变换到模型空间(着色计算所采用的空间)。

如何定义切线空间?

众所周知,定义一个空间需要三个向量。现在向上的向量已经有了(就是法线)。
在这里插入图片描述然后需要切线 T ⃗ \vec T T ,这个 T ⃗ \vec T T 需要和曲面平行,理论上满足这个条件的切线很多。
在这里插入图片描述
但是为了方便起见,也为了保持连续性,避免衔接出现瑕疵,标准的做法是将切线方向和纹理空间对齐:

在这里插入图片描述
那么这组基还差一个向量(副切线 B ⃗ \vec B B ),为了好算,选一条垂直于刚才算出的两个轴的切线。
在这里插入图片描述
最后来计算TBN矩阵,记三角形的两条边为deltaPos1和deltaPos2,deltaUV1和deltaUV2是对应的UV坐标下的差值;则问题可用如下方程表示:

deltaPos1 = deltaUV1.x * T + deltaUV1.y * B
deltaPos2 = deltaUV2.x * T + deltaUV2.y * B

求解T和B就得到了切线和副切线!

已知T、B、N向量之后,即可得TBN矩阵,完成从切线空间到模型空间的变换:
在这里插入图片描述
有了TBN矩阵,我们就能把(从法线纹理中获取的)法线变换到模型空间。

可我们需要的却是从切线空间到模型空间的变换,法线则保持不变。所有计算均在切线空间中进行,不会对其他计算产生影响。

只需对上述矩阵求逆即可得逆变换。这个矩阵(正交阵,即各向量相互正交的矩阵,参见下文”延伸阅读”小节)的逆矩阵恰好也就是其转置矩阵,计算十分简单:
在这里插入图片描述

7.3.2 位移贴图

得到结果要比凹凸贴图结果更好一些,但是要求三角形比较细致。(要求三角形顶点间的间隔高于纹理定义的频率)

  • 与凹凸贴图使用相同的纹理
  • 实际上移动了顶点的位置
    在这里插入图片描述

7.4 三维纹理

没有真实生成纹理的图,而是定义了三维空间中的噪声函数,通过三维噪声函数来计算空间中每一点噪声的值,来动态得到纹理。
3D Procedural Noise + Solid Modeling
在这里插入图片描述

7.5 存储计算好的着色信息

计算好了之后得到一张纹理的图,后面再把着色的结果乘以环境光遮蔽得到最终结果。(把计算拿到之前去做,也说明了纹理不止可以存储颜色)
在这里插入图片描述

7.6 三维纹理和体渲染(Volume Rendering)

在这里插入图片描述

  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
湖 南 大 学 信息科学与工程学院 实 验 报 告 "实验名称 "香农编码 " "课程名称 "信息论与编码 " " " " 1、实验目的 (1)进一步熟悉Shannon编码算法; (2)掌握C语言程序设计和调试过程中数值的进制转换、数值与字符串之间的转换等技 术。 2、实验要求 (1)输入:信源符号个数q、信源的概率分布p; (2)输出:每个信源符号对应的Shannon编码的码字。 3、Shannon编码算法 1:procedure SHANNON(q,{}) 2: 降序排列{} 3: for i=1 q do 4: F() 5: 6:将累加概率F()(十进制小数)变换成二进制小数。 7:取小数点后个二进制数字作为第i个消息的码字。 8:end for 9:end procedure ---------------------------------------------------------------------------- -------------------------------------- 调试过程 1、fatal error C1083: Cannot open include file: 'unistd.h': No such file or directory fatal error C1083: Cannot open include file: 'values.h': No such file or directory 原因:unistd.h和values.h是Unix操作系统下所使用的头文件 纠错:删去即可 2、error C2144: syntax error : missing ')' before type 'int' error C2064: term does not evaluate to a function 原因:l_i(int *)calloc(n,sizeof(int)); l_i后缺少赋值符号使之不能通过编译 纠错:添加上赋值符号 error C2018: unknown character '0xa1' 原因:有不能被识别的符号 纠错:在错误处将不能识别的符号改为符合C语言规范的符号 error C2021: expected exponent value, not ' ' 原因:if(fabs(sum-1.0)>DELTA); 这一行中DELTA宏定义不正确 纠错:# define DELTA 0.000001 error C2143: syntax error : missing ';' before '}' 原因:少写了";"号 纠错:在对应位置添加上";"号 参考代码 # include<stdio.h> # include<math.h> # include<stdlib.h> # include<string.h> # define DELTA 0.000001/*精度*/ void sort(float*,int);/*排序*/ int main(void) { register int i,j; int n; /*符号个数*/ int temp;/*中间变量*/ float *p_i; /*符号的概率*/ float *P_i; /*累加概率*/ int *l_i; /*码长*/ char * *C; /*码集合*/ /*用sum来检验数据,用p来缓存了中间数据*/ float sum,p; /*输入符号数*/ fscanf(stdin,"%d",&n); /*分配内存地址 */ p_i=(float *)calloc(n,sizeof(float)); P_i=(float *)calloc(n,sizeof(float)); l_i=(int *)calloc(n,sizeof(int)); /* 存储信道传输的概率*/ for(i=0;i<n;i++) fscanf(stdin,"%f",&p_i[i]); /*确认输入的数据*/ sum=0.0; for(i=0;i<n;i++) sum+=p_i[i]; if(fabs(sum-(1.0))>DELTA) fprintf(stderr,"Invalid input data \n"); fprintf(stdout,"Starting…\n\n"); /*以降序排列概率*/ sort (p_i,n); /*计算每个符号的码长*/ for(i=0;i<n;i++) { p=(float)(-(log(p_i[i])))/log(2.0); l_i[i]=(int)ceil(p); } /*为码字分配内存地址*/ C=(char **)calloc(n,sizeof(char *)); for(i=
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值