✺ch5——纹理贴图

目录

    • 加载纹理图像文件
    • 纹理坐标
    • 在着色器中使用纹理:采样器变量和纹理单元
    • 纹理贴图:示例程序
    • 多级渐远纹理贴图
    • 各向异性过滤
    • 环绕和平铺
    • 透视变形
    • 材质——更多OpenGL细节
    • 补充说明

纹理贴图是在栅格化的模型表面上覆盖图像的技术。 它是为渲染场景添加真实感的最基本和最重要的方法之一。

纹理贴图非常重要,因此硬件也为它提供了支持,使得它具备实现实时的照片级真实感的超高性能。纹理单元是专为纹理设计的硬件组件,现代显卡通常带有数个纹理单元。

加载纹理图像文件

为了在 OpenGL/GLSL 中有效地完成纹理贴图,需要协调好以下几个不同的数据集和机制:

  • 用于保存纹理图像的纹理对象(在本章中我们仅考虑 2D 图像);
  • 特殊的统一采样器变量,以便顶点着色器访问纹理;
  • 用于保存纹理坐标的缓冲区;
  • 用于将纹理坐标传递给管线的顶点属性;
  • 显卡上的纹理单元。

图像通常存储在图像文件中,例如.jpg、.png、.gif 或.tiff 文件。为了使纹理图像用于 OpenGL 管线中的着色器, 我们需要从图像中提取颜色并将它们放入 OpenGL 纹理对象(用于保存纹理图像的内置 OpenGL 结构)中。

许多 C++ 库可用于读取和处理图像文件,常见的选择包括 Cimg 、 BoostGIL 和 Magick++。我们选择使用专为 OpenGL 设计的 SOIL2 库。

通常,将纹理加载到 OpenGL 应用程序的步骤是:
(a)使用 SOIL2 实例化 OpenGL 纹理对象并从图像文件中读入数据;
(b)调用 glBindTexture() 以使新创建的纹理对象处于激活状态;
(c)使用 glTexParameter() 函数调整纹理设置。
最终得到的结果就是现在可用的 OpenGL 纹理对象的整型 ID。

GLuint loadTexture(const char* texImagePath) {
	GLuint textureID;
	textureID = SOIL_load_OGL_texture(texImagePath, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_INVERT_Y);
	if (textureID == 0)
		cout << "Could not find texture file:" << texImagePath << endl;
	return textureID;
}

纹理坐标

现在我们已经有了将纹理图像加载到 OpenGL 中的方法, 需要指定希望如何将纹理应用于对象的渲染表面。我们通过为模型中的每个顶点指定纹理坐标来完成此操作。

纹理坐标是对纹理图像(通常是 2D 图像)中的像素的引用。纹理图像中的像素被称为纹元(texel),以便将它们与在屏幕上呈现的像素区分开。纹理坐标用于将 3D 模型上的点映射到纹理中的位置。除了将它定位在 3D 空间中的坐标(x,y,z)之外,模型表面上的每个点还具有纹理坐标(s,t), 用来指定纹理图像中的哪个纹元为它提供颜色。 因此,我们将设置两个缓冲区,一个用于顶点坐标(每个条目中有 3 个分量,即 x、 y 和 z),另一个用于相应的纹理坐标(每个条目中有两个分量,即 s 和 t)。这样,每次顶点着色器的调用会接收到一个顶点的数据,包括其空间坐标和相应的纹理坐标。

2D 纹理图像被设定为矩形,左下角的位置坐标为(0,0),右上角的位置坐标为(1,1)。理想情况下,纹理坐标应该在[0, 1]区间内取值。

纹理坐标(由 s 和 t 描述)将图像的部分(纹元)映射到模型正面的栅格化像素上。顶点之间的所有中间像素都已使用图像中间插值的纹元进行绘制。这正是因为纹理坐标在顶点属性中被发送到片段着色器,从而也像顶点本身一样被插值。

下面我们渲染四棱锥,只是这次用砖的图像添加纹理。我们需要指定:
(a)引用纹理图像的整型 ID;
(b)模型顶点的纹理坐标;
(c)用于保存纹理坐标的缓冲区;
(d)顶点属性,以便顶点着色器接收并通过管线转发纹理坐标;
(e)显卡上用于保存纹理对象的纹理单元;
(f)用于访问 GLSL 中纹理单元的统一采样器变量。

在着色器中使用纹理:采样器变量和纹理单元

为了最大限度地提高性能,我们希望在硬件中执行纹理处理。这意味着片段着色器需要一种访问我们在 C++/OpenGL 应用程序中创建的纹理对象的方法。它的实现机制是通过一个叫作统一采样器变量的特殊 GLSL 工具。这是一个变量,用于指示显卡上的纹理单元,从加载的纹理对象中提取或“采样”纹元。

layout(binding = 0) uniform sampler2D samp;

我们声明的变量叫作 samp。声明的 layout(binding=0)部分指定此采样器与第 0 个纹理单元关联。

纹理单元(和相关的采样器)可以对我们希望的任何纹理对象进行采样,也可以在运行时更改。 display()函数需要指定纹理单元要为当前帧采样的纹理对象。因此,每次绘制对象时,都需要激活纹理单元并将其绑定到特定的纹理对象,例如:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, brickTexture);

可用纹理单元的数量取决于显卡提供的数量。根据 OpenGL API 文档, OpenGL 4.5 要求每个着色器阶段至少有 16 个单元,所有阶段总共至少有 80 个单元。在这个例子中,我们通过在 glActiveTexture() 调用中指定 GL_TEXTURE0,使得第 0 个纹理单元处于激活状态。

要实际执行纹理处理,我们需要修改片段着色器输出颜色的方式。以前,我们的片段着色器要么输出一个固定的颜色常量,要么从顶点属性获取颜色,而这次我们需要使用从顶点着色器(通过光栅着色器)接收的插值纹理坐标来对纹理对象进行采样。调用 texture()函数如下:

in vec2 tc;// 输入插值过的纹理坐标
...
color = texture(samp, tc);

纹理贴图:示例程序

...
GLuint brickTexture;

void setupVertices(void) {
	float pyramidPositions[54] =
	{ -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f,    //front
		1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f,    //right
		1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f,  //back
		-1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 0.0f,  //left
		-1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, //LF
		1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f  //RR
	};
	float textureCoordinates[36] =
	{ 0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
		0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
		0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
		0.0f, 0.0f, 1.0f, 0.0f, 0.5f, 1.0f,
		0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f,
		1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f
	};
	glGenVertexArrays(1, vao);
	glBindVertexArray(vao[0]);
	glGenBuffers(numVBOs, vbo);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(pyramidPositions), pyramidPositions, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glBufferData(GL_ARRAY_BUFFER, sizeof(textureCoordinates), textureCoordinates, GL_STATIC_DRAW);
}

void init(GLFWwindow* window) {
	...
	brickTexture = Utils::loadTexture("brick1.jpg");
}

void display(GLFWwindow* window, double currentTime) {
	glClear(GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(renderingProgram);

	mvLoc = glGetUniformLocation(renderingProgram, "mv_matrix");
	projLoc = glGetUniformLocation(renderingProgram, "proj_matrix");

	vMat = glm::translate(glm::mat4(1.0f), glm::vec3(-cameraX, -cameraY, -cameraZ));

	mMat = glm::translate(glm::mat4(1.0f), glm::vec3(pyrLocX, pyrLocY, pyrLocZ));

	mMat = glm::rotate(mMat, -0.45f, glm::vec3(1.0f, 0.0f, 0.0f));
	mMat = glm::rotate(mMat,  0.61f, glm::vec3(0.0f, 1.0f, 0.0f));
	mMat = glm::rotate(mMat,  0.00f, glm::vec3(0.0f, 0.0f, 1.0f));

	mvMat = vMat * mMat;

	glUniformMatrix4fv(mvLoc, 1, GL_FALSE, glm::value_ptr(mvMat));
	glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(pMat));

	glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);

	glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(1);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, brickTexture);

	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	glDrawArrays(GL_TRIANGLES, 0, 18);
}

顶点着色器:

#version 430

layout (location=0) in vec3 pos;
layout (location=1) in vec2 texCoord;
out vec2 tc;

uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
layout (binding=0) uniform sampler2D samp;

void main(void) {
	gl_Position = proj_matrix * mv_matrix * vec4(pos,1.0);
	tc = texCoord;
} 

片段着色器:

#version 430

in vec2 tc;
out vec4 color;

layout(binding = 0) uniform sampler2D samp;

void main(void) {
	color = texture(samp, tc);
}

多级渐远纹理贴图




多级渐远纹理贴图通过一种巧妙的机制来工作, 它在纹理图像中存储相同图像的连续的一系列较低分辨率的副本,所用的纹理图像比原始图像大 1/3,其中图像的 RGB 值分别存储在纹理图像空间的 3 个 1/4 区域中来实现的。剩余的 1/4 区域中迭代地将图像分辨率设置为原来的 1/4,直到剩余区域太小而不包含任何有用的图像数据。示例图像和生成的多级渐远纹理的可视化如下图所示。

这种将几个图像填充到一个小空间中的方法(只比存储原始图像所需的空间大一点)是 mipmapping 得名的原因。 mip 代表拉丁语 multumin parvo,意思是“在很小的空间里有很多东西”。

实际给对象添加纹理时,可以通过多种方法对多级渐远纹理进行采样。在 OpenGL 中,可以通过将 GL_TEXTURE_MIN_FILTER 参数设置为所需的缩小方法来选择多级渐远纹理的采样方法,可以选取以下方法之一:

  • GL_NEAREST_MIPMAP_NEAREST:选择具有与纹元区域最相似的分辨率的多级渐远纹理。然后,它获得所需纹理坐标的最近纹元。
  • GL_LINEAR_MIPMAP_NEAREST:选择具有与纹元区域最相似的分辨率的多级渐远纹理。然后,它取最接近纹理坐标的 4 个纹元的插值。这被称为“线性过滤”。
  • GL_NEAREST_MIPMAP_LINEAR:选择具有与纹元区域最相似的分辨率的 2 个多级渐远纹理。然后,它从每个多级渐远纹理获取纹理坐标的最近纹元并对其进行插值。这被称为“双线性过滤”。
  • GL_LINEAR_MIPMAP_LINEAR: 选择具有与纹元区域最相似的分辨率的 2 个多级渐远纹理。然后,它取各自最接近纹理坐标的 4 个纹元,并计算插值。这被称为“三线性过滤”。

三线性过滤通常是比较好的选择,因为较低的混合级别通常会产生伪影。

OpenGL 提供了丰富的多级渐远纹理支持,其中一些机制可用于构建你自己的多级渐远纹理级别,另一些机制可以让 OpenGL 为你构建它们。在大多数情况下, OpenGL 自动构建的多级渐远纹理已足够。这是通过将以下代码行添加进紧跟 getTextureObject()函数的 Utils::loadTexture()函数来实现的:

glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);

上述代码用于通知 OpenGL 生成多级渐远纹理。 glBindTexture()调用激活砖纹理,然后glTexParameteri()函数调用启用前面列出的缩小方法之一,此处为 GL_LINEAR_ MIPMAP_LINEAR,即三线性过滤。

构建多级渐远纹理后,可以在 display()函数中或其他位置再次调用 glTexParameteri()来更改过滤选项(尽管很少有需要这样做的情况),甚至通过选择 GL_NEAREST 或 GL_LINEAR 来禁用多级渐远纹理。

各向异性过滤

多级渐远纹理贴图有时看起来比非多级渐远纹理贴图更模糊, 尤其是当被贴图对象以严重倾斜的视角渲染时。使用多级渐远纹理贴图在减少伪影的同时也损失了图像细节。

这种细节的丢失是因为当物体倾斜时,其图元看起来在一个轴(即沿宽或高)上的尺寸比在另一个轴上更小。当 OpenGL 为图元贴图时,它选择适合两个轴中尺寸较小的轴的多级渐远纹理(以避免“闪烁”伪影)。表面远离观察者倾斜,因此每个渲染图元将使用适合其更小尺寸(即高度)的多级渐远纹理,对其宽度来说,这个分辨率似乎太小了。

一种恢复一些丢失细节的方法是使用各向异性过滤(Anisotropic Filtering,AF)。标准的多级渐远纹理贴图以各种正方形分辨率(如 256 像素×256 像素、 128 像素×128 像素等)对纹理图像进行采样,而各向异性过滤却以多种矩形分辨率对纹理进行采样(如 256 像素×128 像素、 64 像素×128 像素等)。这使得从各种角度观看的纹理都保留尽可能多的细节成为可能。

各向异性过滤比标准多级渐远纹理贴图的计算代价更高,并且不是 OpenGL 的必需部分。但是,大多数显卡都支持各向异性过滤(称为 OpenGL 扩展),而 OpenGL 也确实提供了一种查询显卡是否支持各向异性过滤的方法,以及一种访问各向异性过滤的方法,只需在生成多级渐远纹理贴图后立即添加代码:

...
// 如果使用多级渐远纹理贴图
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
// 如果还使用各向异性过滤
if (glewIsSupported("GL_EXT_texture_filter_anisotropic")) {
	GLfloat anisoSetting = 0.0f;
	glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisoSetting);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoSetting);
}

对 glewIsSupported()进行调用可以测试显卡是否支持各向异性过滤。如果支持,我们将其设置为支持的最
大采样程度,这个最大值通过 glGetFloatv()获取。使用glTexParameterf()可以将其应用于激活纹理对象,结果如下图所示。请注意,丢失的大部分细节已经恢复,同时我们仍然消除了闪烁的伪影。

环绕和平铺

到目前为止,我们假设纹理坐标都落在[0, 1]区间。但是, OpenGL 实际上支持任何取值范围的纹理坐标。有几个选项可以用来指定当纹理坐标超出[0, 1]区间时会发生什么,可以使用glTexParameteri()设置。这些选项如下:

  • GL_REPEAT:重复,即忽略纹理坐标的整数部分,生成重复或“平铺”图案。这是默认行为。
  • GL_MIRRORED_REPEAT:镜像重复,即忽略纹理坐标的整数部分,但是当整数部分为奇数时反转坐标,因此重复的图案在原图案和其镜像图案之间交替。
  • GL_CLAMP_TO_EDGE: 夹紧到边缘, 即将小于 0 的坐标和大于 1 的坐标分别设置为 0 和 1。
  • GL_CLAMP_TO_BORDER: 夹紧到边框, 即将[0, 1]以外的纹元设置成指定的边框颜色。

例如,考虑一个使用图 5.2 中纹理图像的四棱锥,其纹理坐标区间已达[0, 5],而不是通常
的[0, 1]。默认行为(即 GL_REPEAT)会导致纹理在表面上重复(有时称为“平铺”),如图 5.17
所示。

为了使平铺块的外观在原图案和其镜像之间交替,我们可以指定以下内容:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

可以按如下方式来将小于 0 或大于 1 的值设定为指定颜色:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float redColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, redColor);

下图中分别(从左到右)显示了重复、镜像重复、夹紧到边缘和夹紧到边框的效果,其中四棱锥的纹理坐标取值范围为−2~+3:

透视变形

考虑一个由两个三角形组成的矩形,纹理贴图是棋盘图样,面向相机。当矩形围绕 x 轴旋转时,矩形的顶部会倾斜并远离相机,而矩形的下半部分则更靠近相机。因此,我们希望顶部的方块变小,底部的方块变大。但是,纹理坐标的线性插值将导致所有正方形的高度相等。沿着构成矩形的两个三角形接缝处的对角线加剧失真。产生的失真如下图所示。

幸运的是,存在用于校正透视失真的算法,并且默认情况下, OpenGL 在栅格化期间会应用透视校正算法。

可以通过在包含纹理坐标的顶点属性的声明中添加关键字“noperspective”来禁用 OpenGL 的透视校正,虽然这样做并不常见。顶点着色器和片段着色器中都需要这样添加关键字。例如,顶点着色器中的顶点属性将声明如下:

noperspective out vec2 texCoord;

片段着色器中的相应属性声明为:

noperspective in vec2 texCoord;

实际上,上图中的扭曲的棋盘图样就使用了这种语法来生成图的。

材质——更多OpenGL细节

我们使用的 SOIL2 纹理图像加载库具有相对简单和直观的优点。但是,在学习 OpenGL 时,使用 SOIL2 会产生一项我们不想要的后果,即用户会接触不到一些有用的重要OpenGL 细节。在本节中,我们将描述程序员在没有纹理加载库(如 SOIL2)的情况下加载和使用纹理时需要了解的一些细节。

可以使用 C++和 OpenGL 函数直接将纹理图像文件数据加载到 OpenGL 中。这虽然有点儿复杂,但并不少见。一般步骤如下:
(1)使用 C++ 工具读取图像文件数据;
(2)生成 OpenGL 纹理对象;
(3)将图像文件数据复制到纹理对象中。

我们不会详细描述第一步——有太多方法了。 opengl-tutorials 网站中很好地描述了一种方法,可以使用 C++函数 fopen()和 fread()将数据从.bmp 图像文件读入 unsigned char 类型的数组。

步骤(2)和步骤(3)更通用,主要涉及 OpenGL 调用。在步骤(2)中,我们使用 OpenGL 的glGenTextures()命令创建一个或多个纹理对象。例如,生成单个 OpenGL 纹理对象(使用整型引用 ID)可以按如下方式完成:

GLuint textureID;// 如果需要创建多于一个纹理对象,则使用 GLuint 类型的数组
glGenTextures(1, &textureID);

在步骤(3)中,我们将步骤(1)中的图像文件数据关联到步骤(2)中创建的纹理对象。这是使用 OpenGL 的 glTexImage2D()命令完成的。下面的示例将图像文件数据从步骤(1)中描述的 unsigned char 类型的数组(此处表示为 data)加载到步骤(2)创建的纹理对象中:

glBindTexture(GL_TEXTURE_2D, textureID)
glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);

此时,本章前面介绍的用于设置多级渐远纹理贴图等的各种 glTexParameteri()调用也可以应用于纹理对象。

补充说明

研究人员开发了纹理单元的许多用途,不仅仅用于场景中的纹理模型。在后面的章节中,我们将看到如何使用纹理单元来改变物体反射光线,使其看起来凹凸不平。我们还可以使用纹理单元来存储“高度图”以生成地形,以及存储“阴影贴图”以有效地为场景添加阴影。

着色器还可以向纹理写入数据,允许着色器修改纹理图像,甚至将一个纹理的一部分复制到另一个纹理的某个部分。

多级渐远纹理贴图和各向异性过滤不是减少纹理中的叠影、伪影的唯一工具。例如,全屏抗锯齿( Full-Scene Anti-Aliasing, FSAA)和其他超采样方法也可以改善 3D 场景中纹理的外观。它们虽然不是 OpenGL 核心的一部分,但通过 OpenGL 的扩展机制在许多显卡上得到了支持。

还有一种用于配置和管理纹理和采样器的替代机制。 OpenGL 3.3 引入了采样器对象(有时称为“采样器状态”,不要与采样器变量混淆),可用于保存一组独立于实际纹理对象的纹理设置。将采样器对象附加到纹理单元,可以方便、 有效地更改纹理设置。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/254454.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

宏基因组学Metagenome-磷循环Pcycle功能基因分析-从分析过程到代码及结果演示-超详细保姆级流程

大背景介绍 生信分析,凡事先看论文,有了论文就有了参考,后续分析就有底了,直接上硬菜开干: PCycDB: a comprehensive and accurate database for fast analysis of phosphorus cycling genes - PubMed 数据库及部分分析代码github库: GitHub - ZengJiaxiong/Phospho…

docker 与 ffmpeg

创建容器 docker run -it -v /mnt/f/ffmpeg:/mnt/f/ffmpeg --name ffmpeg 49a981f2b85f /bin/bash 在 Linux 上编译 FFmpeg&#xff1a; 安装依赖库&#xff1a; sudo apt-get update sudo apt-get install build-essential yasm cmake libtool libc6 libc6-dev unzip wget下…

SpringCloud-高级篇(八)

&#xff08;1&#xff09;TCC模式 前面学了XA和AT模式&#xff0c;这两种模式最终都能实现一致性&#xff0c;和隔离性&#xff0c;XA是强一致&#xff0c;AT是最终一致&#xff0c;隔离性呢XA是在第一阶段不提交&#xff0c;基于事务本身的特性来完成隔离&#xff0c;AT则是…

Python都有什么特性,为什么适合每个人学习?详细解读来了

Python编程语言简介 Python&#xff0c;一种高层次的、解释型的编程语言&#xff0c;以其跨平台性、易读性和灵活性在全球编程界占据了重要的地位。自1991年首次发布以来&#xff0c;Python已经成为了无数程序员和企业的首选语言&#xff0c;尤其是在快速开发和原型设计方面。 …

Ansible:模块1

Ansible&#xff1a; 远程操作主机功能 自动化运维&#xff08;playbook 剧本 yaml&#xff09; 是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是一军突起。 Ansible能批量配置&#xff0c;部署&#xff0c;管理上千台主机。类似于xshell的一…

MFC 程序执行流程

目录 MFC 程序启动 MFC 入口函数 程序执行流程总结 在Win32课程中WinMain由程序员自己实现&#xff0c;那么流程是程序员安排&#xff0c;但到了MFC中&#xff0c;由于MFC库实现WinMain&#xff0c;也就意味着MFC负责安排程序的流程。 MFC 程序启动 程序的启动&#xff0c;…

红枣期货个股(红枣期货个股:投资方向分析)

红枣期货个股介绍 红枣是我国传统的绿色健康食品&#xff0c;具有营养丰富、味道独特的特点&#xff0c;深受消费者喜爱。红枣产业链较长&#xff0c;包括种植、采摘、加工、销售等环节&#xff0c;其中期货是红枣产业不可或缺的一环。红枣期货个股作为期货交易市场上的重要投…

mybatis中oracle的sql没走索引导致特别慢(未加jdbcType的)

如果直接跑sql是能走索引很快&#xff0c;在mybatis中不能&#xff0c;可能就是jdbcType的原因。 比如&#xff0c;我有一个属性A&#xff0c;在表里面是VARCHAR2类型&#xff0c;但是在mybatis中的sql是#{a}&#xff0c;缺少jdbcTypeJdbcType.VARCHAR&#xff0c;就会导致myba…

risc-v system instruction

ECALL ecall 指令以前叫做 scall&#xff0c;用于执行环境的变更&#xff0c;它会根据当前所处模式触发不同的执行环境切换异常, 用来执行需要更高权限才能执行的功能;简单来说&#xff0c;ecall 指令将权限提升到内核模式并将程序跳转到指定的地址。操作系统内核和应用程序其实…

AD采集卡设计方案:630-基于PCIe的高速模拟AD采集卡

基于PCIe的高速模拟AD采集卡 一、产品概述 基于PCIe的一款分布式高速数据采集系统&#xff0c;实现多路AD的数据采集&#xff0c;并通过PCIe传输到存储计算服务器&#xff0c;实现信号的分析、存储。 北京太速科技&#xff0c;产品固化FPGA逻辑&#xff0c;适配2路…

ShardingSphere-JDBC 和 ShardingSphere-Proxy,你选择哪一个

参考文章 总结&#xff1a; 只使用Java&#xff0c;ShardingSphere-JDBC更好有异构语言的话&#xff0c;ShardingSphere-Proxy 更好混用也挺香

Flink系列之:监控Checkpoint

Flink系列之&#xff1a;监控Checkpoint 一、概览二、概览&#xff08;Overview&#xff09;选项卡三、历史记录&#xff08;History&#xff09;选项卡四、历史记录数量配置五、摘要信息&#xff08;Summary&#xff09;选项卡六、配置信息&#xff08;Configuration&#xff…

100GPTS计划-AI动漫AnimeArtisan

地址 https://poe.com/AnimeArtisan https://chat.openai.com/g/g-LM6ObVhfF-anime-artisan 测试 风景类: 阳光、蓝天、白云、大海、海滩、森林、瀑布、山峰、雪山 日常类: 睡觉、跑步、学习、工作、做家务、看书、听音乐、运动、购物、煮饭 人物类: 女孩、男孩、老人、儿童…

『 Linux 』重新理解挂起状态

文章目录 &#x1f984; 前言新建状态 &#x1f40b;挂起状态 &#x1f40b;唤入唤出 &#x1f40b;进程与操作系统间的联系 &#x1f40b; &#x1f984; 前言 『 Linux 』使用fork函数创建进程与进程状态的查看中提到了对挂起状态的一个理解&#xff1b; ​ 挂起状态相比于其…

爬虫练习-获取imooc课程目录

代码&#xff1a; from bs4 import BeautifulSoup import requests headers{ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0, }id371 #课程id htmlrequests.get(https://coding.imooc.com/class/chapter/id.html#Anchor,head…

kubernetes 学习笔记

1. Kubernetes 介绍 1.1 应用部署方式的演变 在部署应用程序的方式上&#xff0c;主要经理了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上。虚拟化部署&#xff1a;可以在一台物理机上运行多个虚拟机&#xff0c;每个虚…

高可用接入层技术演化及集群概述

集群概述 集群的介绍及优势 集群&#xff1a;将多台服务器通过硬件或软件的方式组合起来&#xff0c;完成特定的任务&#xff0c;而这些服务器对外表现为一个整体。集群的优势 高可靠性&#xff1a;利用集群管理软件&#xff0c;当主服务器故障时&#xff0c;备份服务器能够自…

Cesium 加载 Geoserver WMS 图层以及条件查询和切换图层样式

Cesium 加载 Geoserver WMS 图层以及条件查询和切换图层样式 图层样式核心代码完整代码&#xff1a;在线示例 Cesium 加载 Geoserver WMS 图层&#xff0c;在实际项目中常常会遇到&#xff0c;需要对图层进行过滤&#xff0c;这里介绍一下过滤方法。 Cesium Geoserver 图层条件…

AirPodsPro3爆料汇总,2025年发布?

不止是iPhone&#xff0c;苹果的AirPods Pro系列耳机也是非常受用户青睐的一款产品&#xff0c;相信不少果粉都非常期待它的升级换代。 第一代AirPods Pro于2019年10月发布&#xff0c;第二代AirPods Pro于2022年9月发布&#xff0c;按照这个时间线来看的话&#xff0c;第三代A…

《Linux C编程实战》笔记:进程操作之创建进程

进程是一个动态的实体&#xff0c;是程序的一次执行过程。进程是操作系统资源分配的基本单位。 以下是一些概念&#xff0c;我就直接抄书了 进程是操作系统的知识&#xff0c;简单理解的话&#xff0c;你写的代码运行起来算一个进程&#xff1f; 创建进程 每个进程由进程ID号…
最新文章