LearnOpenGL学习笔记——法线贴图

LearnOpenGL——法线贴图、视差贴图学习笔记 我们将观察方向变化到切线空间中,所以P向量的x和y分量会与表面切线和副切线对齐,由于切线和副切线向量与表面纹理坐标的方向相同,我们可以用P的x和y元素作为纹理坐标的偏移量。目前,如果我们让平面竖直面对我们,此时效果正常,因为法线贴图中的法线方向指向z正方向并且平面的法线也指向z轴正方向。将法线向量的x、y、z元素储存到纹理中,代替颜色的r、g、b元素,因为法线向量的范围在[-1,1],所以我们要先将其映射到[0,1],变换为RGB颜色元素。法线向量从z轴方向也有向其他方向的偏差,颜色也就发生了轻微的变化。 阅读详情

法线贴图

我们的场景中已经充满了多边形物体,其中每个都可能由成百上千平坦的三角形组成。我们以向三角形上附加纹理的方式来增加额外细节,提升真实感,隐藏多边形几何体是由无数三角形组成的事实。纹理确有助益,然而当你近看它们时,这个事实便隐藏不住了。现实中的物体表面并非是平坦的,而是表现出无数(凹凸不平的)细节。

例如,砖块的表面。砖块的表面非常粗糙,显然不是完全平坦的:它包含着接缝处水泥凹痕,以及非常多的细小的空洞。如果我们在一个有光的场景中看这样一个砖块的表面,问题就出来了。下图中我们可以看到砖块纹理应用到了平坦的表面,并被一个点光源照亮。
在这里插入图片描述
光照并没有呈现出任何裂痕和孔洞,完全忽略了砖块之间凹进去的线条;表面看起来完全就是平的。我们可以使用specular贴图根据深度或其他细节阻止部分表面被照的更亮,以此部分地解决问题,但这并不是一个好方案。我们需要的是某种可以告知光照系统给所有有关物体表面类似深度这样的细节的方式。如果我们以光的视角来看这个问题:是什么使表面被视为完全平坦的表面来照亮?答案会是表面的法线向量。以光照算法的视角考虑的话,只有一件事决定物体的形状,这就是垂直于它的法线向量。砖块表面只有一个法线向量,表面完全根据这个法线向量被以一致的方式照亮。如果每个fragment都是用自己的不同的法线会怎样?这样我们就可以根据表面细微的细节对法线向量进行改变;这样就会获得一种表面看起来要复杂得多的幻觉
在这里插入图片描述
每个fragment使用了自己的法线,我们就可以让光照相信一个表面由很多微小的(垂直于法线向量的)平面所组成,物体表面的细节将会得到极大提升。这种每个fragment使用各自的法线,替代一个面上所有fragment使用同一个法线的技术叫做法线贴图(normal mapping)或凹凸贴图(bump mapping)。应用到砖墙上,效果像这样:
在这里插入图片描述
你可以看到细节获得了极大提升,开销却不大。因为我们只需要改变每个fragment的法线向量,并不需要改变所有光照公式。现在我们是为每个fragment传递一个法线,不再使用插值表面法线。这样光照使表面拥有了自己的细节。

法线贴图
为使法线贴图工作,我们需要为每个fragment提供一个法线。像diffuse贴图和specular贴图一样,我们可以使用一个2D纹理来储存法线数据。2D纹理不仅可以储存颜色和光照数据,还可以储存法线向量。这样我们可以从2D纹理中采样得到特定纹理的法线向量。由于法线向量是个几何工具,而纹理通常只用于储存颜色信息,用纹理储存法线向量不是非常直接。如果你想一想,就会知道纹理中的颜色向量用r、g、b元素代表一个3D向量。类似的我们也可以将法线向量的x、y、z元素储存到纹理中,代替颜色的r、g、b元素。法线向量的范围在-1到1之间,所以我们先要将其映射到0到1的范围:

vec3 rgb_normal = normal * 0.5 + 0.5; // 从 [-1,1] 转换至 [0,1]

将法线向量变换为像这样的RGB颜色元素,我们就能把根据表面的形状的fragment的法线保存在2D纹理中。教程开头展示的那个砖块的例子的法线贴图如下所示:
在这里插入图片描述
这会是一种偏蓝色调的纹理(你在网上找到的几乎所有法线贴图都是这样的)。这是因为所有法线的指向都偏向z轴(0, 0, 1)这是一种偏蓝的颜色。法线向量从z轴方向也向其他方向轻微偏移,颜色也就发生了轻微变化,这样看起来便有了一种深度。例如,你可以看到在每个砖块的顶部,颜色倾向于偏绿,这是因为砖块的顶部的法线偏向于指向正y轴方向(0, 1, 0),这样它就是绿色的了。在一个简单的朝向正z轴的平面上,我们可以用这个diffuse纹理和这个法线贴图来渲染前面部分的图片。要注意的是这个链接里的法线贴图和上面展示的那个不一样。原因是OpenGL读取的纹理的y(或V)坐标和纹理通常被创建的方式相反。链接里的法线贴图的y(或绿色)元素是相反的(你可以看到绿色现在在下边);如果你没考虑这个,光照就不正确了(译注:如果你现在不再使用SOIL了,那就不要用链接里的那个法线贴图,这个问题是SOIL载入纹理上下颠倒所致,它也会把法线在y方向上颠倒)。加载纹理,把它们绑定到合适的纹理单元,然后使用下面的改变了的像素着色器来渲染一个平面:

	uniform sampler2D normalMap;  
	void main()
	{           
    	// 从法线贴图范围[0,1]获取法线
   		normal = texture(normalMap, fs_in.TexCoords).rgb;
    	// 将法线向量转换为范围[-1,1]
    	normal = normalize(normal * 2.0 - 1.0);   
    	[...]
   	 	// 像往常那样处理光照
   	 }

这里我们将被采样的法线颜色从0到1重新映射回-1到1,便能将RGB颜色重新处理成法线,然后使用采样出的法线向量应用于光照的计算。在例子中我们使用的是Blinn-Phong着色器。

通过慢慢随着时间慢慢移动光源,你就能明白法线贴图是什么意思了。运行这个例子你就能得到本教程开始的那个效果:
在这里插入图片描述
然而有个问题限制了刚才讲的那种法线贴图的使用。我们使用的那个法线贴图里面的所有法线向量都是指向正z方向的。上面的例子能用,是因为那个平面的表面法线也是指向正z方向的。可是,如果我们在表面法线指向正y方向的平面上使用同一个法线贴图会发生什么?
在这里插入图片描述
光照看起来完全不对!发生这种情况是平面的表面法线现在指向了y,而采样得到的法线仍然指向的是z。结果就是光照仍然认为表面法线和之前朝向正z方向时一样;这样光照就不对了。下面的图片展示了这个表面上采样的法线的近似情况:
在这里插入图片描述
你可以看到所有法线都指向z方向,它们本该朝着表面法线指向y方向的。一个可行方案是为每个表面制作一个单独的法线贴图。如果是一个立方体的话我们就需要6个法线贴图,但是如果模型上有无数的朝向不同方向的表面,这就不可行了(译注:实际上对于复杂模型可以把朝向各个方向的法线储存在同一张贴图上,你可能看到过不只是蓝色的法线贴图,不过用那样的法线贴图有个问题是你必须记住模型的起始朝向,如果模型运动了还要记录模型的变换,这是非常不方便的;此外就像作者所说的,如果把一个diffuse纹理应用在同一个物体的不同表面上,就像立方体那样的,就需要做6个法线贴图,这也不可取)。

另一个稍微有点难的解决方案是,在一个不同的坐标空间中进行光照,这个坐标空间里,法线贴图向量总是指向这个坐标空间的正z方向;所有的光照向量都相对与这个正z方向进行变换。这样我们就能始终使用同样的法线贴图,不管朝向问题。这个坐标空间叫做切线空间(tangent space)。

切线空间

法线贴图中的法线向量定义在切线空间中,在切线空间中,法线永远指着正z方向。切线空间是位于三角形表面之上的空间:法线相对于单个三角形的本地参考框架。它就像法线贴图向量的本地空间;它们都被定义为指向正z方向,无论最终变换到什么方向。使用一个特定的矩阵我们就能将本地/切线空间中的法线向量转成世界或视图空间下,使它们转向到最终的贴图表面的方向。

我们可以说,上个部分那个朝向正y的法线贴图错误的贴到了表面上。法线贴图被定义在切线空间中,所以一种解决问题的方式是计算出一种矩阵,把法线从切线空间变换到一个不同的空间,这样它们就能和表面法线方向对齐了:法线向量都会指向正y方向。切线空间的一大好处是我们可以为任何类型的表面计算出一个这样的矩阵,由此我们可以把切线空间的z方向和表面的法线方向对齐。

这种矩阵叫做TBN矩阵这三个字母分别代表tangent、bitangent和normal向量。这是建构这个矩阵所需的向量。要建构这样一个把切线空间转变为不同空间的变异矩阵,我们需要三个相互垂直的向量,它们沿一个表面的法线贴图对齐于:上、右、前;这和我们在摄像机教程中做的类似。
在这里插入图片描述
计算出切线和副切线并不像法线向量那么容易。从图中可以看到法线贴图的切线和副切线与纹理坐标的两个方向对齐。我们就是用到这个特性计算每个表面的切线和副切线的。需要用到一些数学才能得到它们;请看下图:
在这里插入图片描述
注意上图中边E2与纹理坐标的差ΔU2、ΔV2构成一个三角形。ΔU2与切线向量T方向相同,而ΔV2与副切线向量B方向相同。这也就是说,所以我们可以将三角形的边E1与E2写成切线向量\T和副切线向量B的线性组合:

E1=ΔU1T+ΔV1B
E2=ΔU2T+ΔV2B
我们也可以写成这样:

(E1x,E1y,E1z)=ΔU1(Tx,Ty,Tz)+ΔV1(Bx,By,Bz)
(E2x,E2y,E2z)=ΔU2(Tx,Ty,Tz)+ΔV2(Bx,By,Bz)
E是两个向量位置的差,ΔU和ΔV是纹理坐标的差。然后我们得到两个未知数(切线T和副切线B)和两个等式。你可能想起你的代数课了,这是让我们去解T和B。

上面的方程允许我们把它们写成另一种格式:矩阵乘法

[E1xE2xE1yE2yE1zE2z]=[ΔU1ΔU2ΔV1ΔV2][TxBxTyByTzBz]
尝试会意一下矩阵乘法,它们确实是同一种等式。把等式写成矩阵形式的好处是,解T和B会因此变得很容易。两边都乘以ΔUΔV的逆矩阵等于:

[ΔU1ΔU2ΔV1ΔV2]−1[E1xE2xE1yE2yE1zE2z]=[TxBxTyByTzBz]
这样我们就可以解出T和B了。这需要我们计算出delta纹理坐标矩阵的逆矩阵。我不打算讲解计算逆矩阵的细节,但大致是把它变化为,1除以矩阵的行列式,再乘以它的伴随矩阵(Adjugate Matrix)。

[TxBxTyByTzBz]=1ΔU1ΔV2−ΔU2ΔV1[ΔV2−ΔU2−ΔV1ΔU1][E1xE2xE1yE2yE1zE2z]
有了最后这个等式,我们就可以用公式、三角形的两条边以及纹理坐标计算出切线向量T和副切线B。

如果你对这些数学内容不理解也不用担心。当你知道我们可以用一个三角形的顶点和纹理坐标(因为纹理坐标和切线向量在同一空间中)计算出切线和副切线你就已经部分地达到目的了(译注:上面的推导已经很清楚了,如果你不明白可以参考任意线性代数教材,就像作者所说的记住求得切线空间的公式也行,不过不管怎样都得理解切线空间的含义)。
计算切线和负切线
这个教程的demo场景中有一个简单的2D平面,它朝向正z方向。这次我们会使用切线空间来实现法线贴图,所以我们可以使平面朝向任意方向,法线贴图仍然能够工作。使用前面讨论的数学方法,我们来手工计算出表面的切线和副切线向量。

假设平面使用下面的向量建立起来(1、2、3和1、3、4,它们是两个三角形):

// positions
glm::vec3 pos1(-1.0,  1.0, 0.0);
glm::vec3 pos2(-1.0, -1.0, 0.0);
glm::vec3 pos3(1.0, -1.0, 0.0);
glm::vec3 pos4(1.0, 1.0, 0.0);
// texture coordinates
glm::vec2 uv1(0.0, 1.0);
glm::vec2 uv2(0.0, 0.0);
glm::vec2 uv3(1.0, 0.0);
glm::vec2 uv4(1.0, 1.0);
// normal vector
glm::vec3 nm(0.0, 0.0, 1.0);

我们先计算第一个三角形的边和deltaUV坐标:

glm::vec3 edge1 = pos2 - pos1;
glm::vec3 edge2 = pos3 - pos1;
glm::vec2 deltaUV1 = uv2 - uv1;
glm::vec2 deltaUV2 = uv3 - uv1;

有了计算切线和副切线的必备数据,我们就可以开始写出来自于前面部分中的下列等式:

GLfloat f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);

tangent1.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
tangent1.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
tangent1.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);
tangent1 = glm::normalize(tangent1);

bitangent1.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);
bitangent1.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);
bitangent1.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);
bitangent1 = glm::normalize(bitangent1);  

[...] // 对平面的第二个三角形采用类似步骤计算切线和副切线

我们预先计算出等式的分数部分f,然后把它和每个向量的元素进行相应矩阵乘法。如果你把代码和最终的等式对比你会发现,这就是直接套用。最后我们还要进行标准化,来确保切线/副切线向量最后是单位向量。

因为一个三角形永远是平坦的形状,我们只需为每个三角形计算一个切线/副切线,它们对于每个三角形上的顶点都是一样的。要注意的是大多数实现通常三角形和三角形之间都会共享顶点。这种情况下开发者通常将每个顶点的法线和切线/副切线等顶点属性平均化,以获得更加柔和的效果。我们的平面的三角形之间分享了一些顶点,但是因为两个三角形相互并行,因此并不需要将结果平均化,但无论何时只要你遇到这种情况记住它就是件好事。

最后的切线和副切线向量的值应该是(1, 0, 0)和(0, 1, 0),它们和法线(0, 0, 1)组成相互垂直的TBN矩阵。在平面上显示出来TBN应该是这样的:

在这里插入图片描述
每个顶点定义了切线和副切线向量,我们就可以开始实现正确的法线贴图了。

切线空间法线贴图
为让法线贴图工作,我们先得在着色器中创建一个TBN矩阵。我们先将前面计算出来的切线和副切线向量传给顶点着色器,作为它的属性:

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 texCoords;
layout (location = 3) in vec3 tangent;
layout (location = 4) in vec3 bitangent;

在顶点着色器的main函数中我们创建TBN矩阵:

void main()
{
   [...]
   vec3 T = normalize(vec3(model * vec4(tangent,   0.0)));
   vec3 B = normalize(vec3(model * vec4(bitangent, 0.0)));
   vec3 N = normalize(vec3(model * vec4(normal,    0.0)));
   mat3 TBN = mat3(T, B, N)
}

我们先将所有TBN向量变换到我们所操作的坐标系中,现在是世界空间,我们可以乘以model矩阵。然后我们创建实际的TBN矩阵,直接把相应的向量应用到mat3构造器就行。注意,如果我们希望更精确的话就不要将TBN向量乘以model矩阵,而是使用法线矩阵,因为我们只关心向量的方向,不关心平移和缩放。

从技术上讲,顶点着色器中无需副切线。所有的这三个TBN向量都是相互垂直的所以我们可以在顶点着色器中用T和N向量的叉乘,自己计算出副切线:vec3 B = cross(T, N);

现在我们有了TBN矩阵,如果来使用它呢?通常来说有两种方式使用它,我们会把这两种方式都说明一下:

  • 我们直接使用TBN矩阵,这个矩阵可以把切线坐标空间的向量转换到世界坐标空间。因此我们把它传给片段着色器中,把通过采样得到的法线坐标左乘上TBN矩阵,转换到世界坐标空间中,这样所有法线和其他光照变量就在同一个坐标系中了。
  • 我们也可以使用TBN矩阵的逆矩阵,这个矩阵可以把世界坐标空间的向量转换到切线坐标空间。因此我们使用这个矩阵左乘其他光照变量,把他们转换到切线空间,这样法线和其他光照变量再一次在一个坐标系中了。

我们来看看第一种情况。我们从法线贴图采样得来的法线向量,是在切线空间表示的,尽管其他光照向量都是在世界空间表示的。把TBN传给像素着色器,我们就能将采样得来的切线空间的法线乘以这个TBN矩阵,将法线向量变换到和其他光照向量一样的参考空间中。这种方式随后所有光照计算都可以简单的理解。

把TBN矩阵发给像素着色器很简单:

out VS_OUT {
    vec3 FragPos;
    vec2 TexCoords;
    mat3 TBN;
} vs_out;  

void main()
{
    [...]
    vs_out.TBN = mat3(T, B, N);
}

在像素着色器中我们用mat3作为输入变量:

in VS_OUT {
    vec3 FragPos;
    vec2 TexCoords;
    mat3 TBN;
} fs_in;

有了TBN矩阵我们现在就可以更新法线贴图代码,引入切线到世界空间变换:

normal = texture(normalMap, fs_in.TexCoords).rgb;
normal = normalize(normal * 2.0 - 1.0);   
normal = normalize(fs_in.TBN * normal);

因为最后的normal现在在世界空间中了,就不用改变其他像素着色器的代码了,因为光照代码就是假设法线向量在世界空间中。

我们同样看看第二种情况。我们用TBN矩阵的逆矩阵将所有相关的世界空间向量转变到采样所得法线向量的空间:切线空间。TBN的建构还是一样,但我们在将其发送给像素着色器之前先要求逆矩阵:

vs_out.TBN = transpose(mat3(T, B, N));

注意,这里我们使用transpose函数,而不是inverse函数。正交矩阵(每个轴既是单位向量同时相互垂直)的一大属性是一个正交矩阵的置换矩阵与它的逆矩阵相等。这个属性很重要因为逆矩阵的求得比求置换开销大;结果却是一样的。
在像素着色器中我们不用对法线向量变换,但我们要把其他相关向量转换到切线空间,它们是lightDir和viewDir。这样每个向量还是在同一个空间(切线空间)中了。

void main()
{           
    vec3 normal = texture(normalMap, fs_in.TexCoords).rgb;
    normal = normalize(normal * 2.0 - 1.0);   

    vec3 lightDir = fs_in.TBN * normalize(lightPos - fs_in.FragPos);
    vec3 viewDir  = fs_in.TBN * normalize(viewPos - fs_in.FragPos);    
    [...]
}

第二种方法看似要做的更多,它还需要在像素着色器中进行更多的乘法操作,所以为何还用第二种方法呢?

将向量从世界空间转换到切线空间有个额外好处,我们可以把所有相关向量在顶点着色器中转换到切线空间,不用在像素着色器中做这件事。这是可行的,因为lightPos和viewPos不是每个fragment运行都要改变,对于fs_in.FragPos,我们也可以在顶点着色器计算它的切线空间位置。基本上,不需要把任何向量在像素着色器中进行变换,而第一种方法中就是必须的,因为采样出来的法线向量对于每个像素着色器都不一样。

所以现在不是把TBN矩阵的逆矩阵发送给像素着色器,而是将切线空间的光源位置,观察位置以及顶点位置发送给像素着色器。这样我们就不用在像素着色器里进行矩阵乘法了。这是一个极佳的优化,因为顶点着色器通常比像素着色器运行的少。这也是为什么这种方法是一种更好的实现方式的原因。

out VS_OUT {
    vec3 FragPos;
    vec2 TexCoords;
    vec3 TangentLightPos;
    vec3 TangentViewPos;
    vec3 TangentFragPos;
} vs_out;

uniform vec3 lightPos;
uniform vec3 viewPos;

[...]

void main()
{    
    [...]
    mat3 TBN = transpose(mat3(T, B, N));
    vs_out.TangentLightPos = TBN * lightPos;
    vs_out.TangentViewPos  = TBN * viewPos;
    vs_out.TangentFragPos  = TBN * vec3(model * vec4(position, 0.0));
}

在像素着色器中我们使用这些新的输入变量来计算切线空间的光照。因为法线向量已经在切线空间中了,光照就有意义了。

将法线贴图应用到切线空间上,我们会得到混合教程一开始那个例子相似的结果,但这次我们可以将平面朝向各个方向,光照一直都会是正确的:

glm::mat4 model;
model = glm::rotate(model, (GLfloat)glfwGetTime() * -10, glm::normalize(glm::vec3(1.0, 0.0, 1.0)));
glUniformMatrix4fv(modelLoc 1, GL_FALSE, glm::value_ptr(model));
RenderQuad();

在这里插入图片描述
最后一件事

关于法线贴图还有最后一个技巧要讨论,它可以在不必花费太多性能开销的情况下稍稍提升画质表现。

当在更大的网格上计算切线向量的时候,它们往往有很大数量的共享顶点,当法向贴图应用到这些表面时将切线向量平均化通常能获得更好更平滑的结果。这样做有个问题,就是TBN向量可能会不能互相垂直,这意味着TBN矩阵不再是正交矩阵了。法线贴图可能会稍稍偏移,但这仍然可以改进。

使用叫做格拉姆-施密特正交化过程(Gram-Schmidt process)的数学技巧,我们可以对TBN向量进行重正交化,这样每个向量就又会重新垂直了。在顶点着色器中我们这样做:

vec3 T = normalize(vec3(model * vec4(tangent, 0.0)));
vec3 N = normalize(vec3(model * vec4(normal, 0.0)));
// re-orthogonalize T with respect to N
T = normalize(T - dot(T, N) * N);
// then retrieve perpendicular vector B with the cross product of T and N
vec3 B = cross(T, N);

mat3 TBN = mat3(T, B, N)

https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/#_3

【OpenGL】LearnOpenGL学习笔记25 - 法线贴图 NormalMap LearnOpenGL学习笔记 - 法线贴图 NormalMap 阅读详情

相关推荐

learnOpenGL 5.3 法线贴图

要理解和应用法线贴图,首先须明白切线空间,这里贴一个知乎的文章,里面对切线空间的讲解非常细致切线空间(Tangent Space)完全解析 简单来说,切线空间就是把某个三角面片放到正对屏幕的位置,也就是其法线与屏幕空间z轴平行。其中最重要的一个公式 该公式描述的数学意义是,表示一个点在uv空间与三维空间的映射关系,其中TB作为基矢,以uv空间中的u和v的增长作为控制参数。假设三角形中存在一点P,则向量AP=u(p)*T+v(p)*B,只要知道P点的uv坐标值,即可得到P点的三维坐标值,反之亦然。 通过这个

qq_43533956的博客 309

技术美术知识学习_06:关于法线贴图详解

在切线空间中,X 、Y 、Z三个坐标的向量取值范围为(-1,1),而位图上R G B三个值的取值范围(0,1),需要进行转换,因为模型的法线方向大部分是相同的,即Z轴方向为(0,0,1),映射到颜色空间是(0.5,0.5,1),所以你会看到法线贴图为什么是蓝紫色。法线的坐标刚好与平面垂直,它的坐标是(0,0,1),如果把它转换成一张图片里的颜色那么(0,0,1)就相当于RGB里的B,也就是Blue,因此使用蓝色来表示一个物体的表面法线方向。(切线空间),顾名思义,切线空间法线是基于每个面的切线方向。

qq_17775727的博客 5044

OpenGL 法线贴图Normal Mapping

OpenGL法线贴图Normal Mapping法线贴图Normal Mapping简介法线贴图切线空间手工计算切线和副切线切线空间法线贴图复杂物体最后一件事 法线贴图Normal Mapping简介 我们的场景中已经充满了多边形物体,其中每个都可能由成百上千平坦的三角形组成。我们以向三角形上附加纹理的方式来增加额外细节,提升真实感,隐藏多边形几何体是由无数三角形组成的事实。纹理确有助益,然而当你近看它们时,这个事实便隐藏不住了。现实中的物体表面并非是平坦的,而是表现出无数(凹凸不平的)细节。 例如,砖块的

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1275

材质库总结

石头: Note:上面那个材质的效果比较简单,不过可以借这个机会总结一下。目前在游戏里做凹凸感的方法大概有四种。第一种是bumpmap,第二种是normal,第三种是parallaxmap,第四种是reafmap。对应的中文叫法分别是凹凸贴图,法线贴图,视察贴图和浮雕贴图。第二种是第一种的进化(本质上还是不同的)。第四种是第二种的进化。浮雕贴图考虑了相互遮挡,而视察贴图是在normal的基础

魁梧的抠脚大汉 6052

【脑电分析系列】第7篇:频域分析基石 — 功率谱密度(PSD)的计算与常见方法对比

本文探讨了脑电图(EEG)频域分析的核心方法——功率谱密度(PSD)。通过对比基于快速傅里叶变换的周期图法和更稳健的Welch's方法,指出后者通过分段、加窗和平均处理,能有效降低估计方差,更适合分析非稳态的脑电信号。文章详细解析了关键参数设置对频率分辨率和统计稳定性的影响,并提供了Python计算实现。PSD分析将复杂的脑电信号转换为直观的频域特征,为研究认知功能和神经疾病提供重要依据。

qq_32516809的博客 1804

SAP 配额修改 + 修改记录写入实现

采购配额修改并写入修改记录 cdhdr 和 cdpos

GAVIN 1098

OpenGL学习---高级光照---法线贴图

增强纹理细节。例如,砖块的表面。砖块的表面非常粗糙,显然不是完全平坦的:它包含着接缝处水泥凹痕,以及非常多的细小的空洞。 如果只用普通的纹理贴图,只有一件事决定物体的形状,这就是垂直于它的法线向量。砖块表面只有一个法线向量,表面完全根据这个法线向量被以一致的方式照亮。

m0_56399931的博客 2204

使用PlotNeuralNet绘制深度学习网络图的基本操作

使用PlotNeuralNet绘制深度学习网络图的基本操作

weixin_46877907的博客 2110

LearnOpenGL学习笔记—高级光照 04:法线贴图

LearnOpenGL学习笔记—高级光照 04:法线贴图1 引入2 法线贴图2.1 代码3 问题4 切线空间4.1 手工计算切线和副切线4.2 切线空间法线贴图4.2.1 TBN矩阵使用一4.2.2 TBN矩阵使用二5 复杂物体6 代码 本节对应官网学习内容:法线贴图 结合英文原站,中文站,以及个人实践进行描述。 1 引入 我们场景中已经充满了多边形物体,其中每个都可能由成百上千平坦的三角形组成。 我们以向三角形上附加纹理的方式来增加额外细节,提升真实感,隐藏多边形几何体是由无数三角形组成的事实。 纹理确有

清清!的博客 1861

牛耕式全覆盖算法-matlab代码

牛耕式路径全覆盖算法,也称为牛耕算法或者蚂蚁算法,是一种用于解决路径规划问题的启发式算法。这个算法的灵感来自于牛在耕田时的行为。在这个算法中,假设有一块田地需要耕作,牛从田地的某个角落开始行走,走过的路径会被标记。牛会优先选择尚未经过的路径,当所有的路径都走过后,算法停止。在路径规划问题中,田地可以看作是一个由节点和边构成的图。每个节点代表一个位置,每条边代表两个节点之间的连接。路径全覆盖问题是找出一组路径,使得每个节点都被至少一条路径经过。牛耕式路径全覆盖算法的基本思想是以启发式的方式逐步构建路径。

m0_69611489的博客 1689

2026华为OD机试(C语言)真题【E卷+A卷+B卷+C卷+D卷】目录+在线OJ

华为OD机试真题题库【E卷+A卷+B卷+C卷+D卷】目录,C语言实现。

MISAYAONE的博客 11万+

LearnOpenGL学习笔记—PBR:理论

LearnOpenGL学习笔记—PBR1 理论1.1 引入1.2 微表面模型1.3 能量守恒1.4 反射方程1.4.1 辐射度量学1.4.2 辐射通量(Radiant Flux)1.4.3 立体角(Solid Angle)1.4.4 辐射强度(Radiant Intensity)1.4.5 入射辐照度(Irradiance)1.4.6 出射辐射度(Radiance)1.4.6 概念:双向反射分布函数(Bidirectional ReflectanceDistribution Function,BRDF)1.

清清!的博客 2880

Arduino与震动马达模块握手

震动马达模块采用采用高品质手机震动马达,适用于震动感互动产品的制作,可穿戴智能设备震动提醒,适合作为非可听指示器的小型振动马达。当输入为高电平时电动机会震动,就像你的静音模式的手机一样。

vor234的博客 5847

LearnOpenGL——高级光照(五)法线贴图

法线贴图技术通过在纹理中存储法线向量信息,为平坦表面添加微观细节。传统纹理无法表现真实物体表面的凹凸特征,而法线贴图通过为每个片段提供独立法线,使光照系统能够模拟复杂表面结构。文章详细介绍了法线贴图的实现原理,包括法线向量在纹理中的存储方式(从[-1,1]映射到[0,1]范围),以及切线空间的概念。为了解决不同表面朝向问题,提出了使用TBN矩阵(由切线、副切线和法线向量构成)将法线从切线空间转换到世界空间的方法。通过数学推导展示了如何计算切线和副切线向量,最终实现真实感更强的表面细节渲染效果。

QQ805009795的博客 632

基于STM32的智能快递柜设计(全部资料)

本文设计了一种基于STM32F407ZGT6的智能快递柜系统,通过模块化硬件设计和软件算法优化,实现了24小时无人值守、温湿度智能调控、远程监控等功能。系统集成OLED显示、GSM通信、舵机控制、超声波检测等模块,存取响应时间≤3秒,识别准确率达99.2%,较传统模式效率提升60%。测试表明系统运行稳定,用户满意度达92%,具有成本低、安全性高、可扩展性强等特点,为智慧物流末端配送提供了创新解决方案。未来可引入AI算法、太阳能供电等功能进一步优化系统性能。

3254968421的博客 586

电磁场仿真:电磁场基础理论_(13).微波技术基础

微波是指频率在300 MHz到300 GHz之间的电磁波。这一频率范围对应的波长为1米到1毫米。

2401_87715305的博客 1318

【实践LearnOpenGL项目】高级光照篇

本文详细介绍了OpenGL中三种图形渲染技术的实现方法:1. 阴影贴图技术:通过创建深度纹理和帧缓冲对象,从光源视角渲染场景,解决阴影伪影问题时采用PCF渐进过滤和动态阴影偏移。2. 点光源阴影技术:使用立方体贴图记录六个方向的深度信息,通过几何着色器实现单次多面渲染,并采用20次PCF采样实现柔和阴影效果。3. 法线贴图技术:利用TBN矩阵转换空间坐标,通过切线空间中的法线纹理增强表面细节表现。文章包含完整的代码实现和效果对比图,为图形渲染开发提供了实用参考。

qq_37378423的博客 1963

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

LearnOpenGL学习笔记—高级光照 05:视差贴图

LearnOpenGL学习笔记—高级光照 05:视差贴图1 引入1.1 偏移方法一1.2 偏移方法二2 视差贴图3 陡峭视差映射4 视差遮蔽映射5 代码 本节对应官网学习内容:视差贴图 结合英文原站,中文站,以及个人实践进行描述(这一节英文原站描述有一些不合理之处,进行了更正)。 1 引入 视差贴图(Parallax Mapping)是一种类似于法线贴图的技术,但是基于不同的原理。 就像法线贴图一样,它是一种可以显着提高纹理表面的细节并赋予其深度感的技术。 虽然也是一种错觉,但视差贴图在传达深度感方面要好得

清清!的博客 2402

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

LearnOpenGL笔记——五、高级光照:“法线贴图”和”视差贴图“

五、高级光照:“法线贴图”和“视差贴图” 5.4 法线贴图 以光照算法的视角考虑的话,只有一件事决定物体的形状,这就是垂直于它的法线向量 砖块表面只有一个法线向量,表面完全根据这个法线向量被以一致的方式照亮 如果每个fragment都是用自己的不同的法线会怎样?这样我们就可以根据表面细微的细节对法线向量进行改变;这样就会获得一种表面看起来要复杂得多的幻觉: 这种每个fragment使用各自的法线,替代一个面上所有fragment使用同一个法线的技术叫做法线贴图(normal mapping)或凹凸贴图(

weixin_44478658的博客 805

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

LearnOpenGL学习笔记——视差贴图

视差贴图

VIPCCJ的博客 614

LearnOpenGL 学习笔记 高级光照

phong 模型中的高光项在视线方向和反射方向角大于 90 度时,反射项变为 0这导致了奇怪的效果因此现在计算一个半程向量,他是光线的方向向量和观察向量加到一起,并将结果正规化(Normalize)当视线正好与(现在不需要的)反射向量对齐时,半程向量就会与法线完美契合。所以当观察者视线越接近于原本反射光线的方向时,镜面高光就会越强。现在,不论观察者向哪个方向看,半程向量与表面法线之间的夹角都不会超过90度(除非光源在表面以下)。它产生的效果会与冯氏光照有些许不同,但是大部分情况下看起来会更自然一点,特别是低

hijackedbycsdn的博客 626

【LearnOpenGL 学习笔记】高级光照

LearnOpenGL 学习笔记 高级光照

weixin_42489848的博客 390

LearnOpengl中求TBN矩阵的trick

解析:在此片段中,将旁边的片元视为同一块三角形面片,即可理解为Q1=E1,Q2=E2,将关系映射好,然后就可以套进那个矩阵进行运算了。得去看一下原文中各个参数的含义,得清楚。

qq_42987967的博客 596

LearnOpenGL-笔记-其十一

又到了介绍法线贴图的地方,我感觉我已经写了很多遍了...法线贴图用最简单的话来介绍的话,就是通过修改贴图对应物体表面的法线来修改光照效果,从而在不修改物体实际几何形状的前提下实现不同于物体几何形状的视觉效果。因此对于法线贴图来说,最重要的内容就是去修改法线贴图对于物体表面的法线。这是我们在物体的片元着色器实现的内容,就是根据法线贴图的内容更换法线。

lastXuanGod的博客 1126

一本很好的书LearnOpenGL

一本很好的书LearnOpenGL 这本书好像不怎么出名,但读起来非常易懂,知识全面 https://learnopengl.com/Advanced-Lighting/Normal-Mapping 基于物理的渲染 – 理论篇<译自LearnOpenGL> posted on 2018-08-...

c1sdn19的专栏 187

【OpenGL】LearnOpenGL学习笔记26 - 视差贴图 Parallax Map

LearnOpenGL学习笔记 - 视差贴图 Parallax Map

Duo1J的随笔 1333
上一篇: LearnOpenGL学习笔记——阴影
下一篇: Docker指南
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值