Android OpenGL ES详解——立方体贴图

news2024/12/23 20:23:12

目录

一、概念

二、如何使用

1、创建立方体贴图

2、生成纹理

3、设置纹理环绕和过滤方式

4、激活和绑定立方体贴图

三、应用举例——天空盒

1、概念

2、加载天空盒

3、显示天空盒

4、优化

四、应用举例——环境映射:反射

五、应用举例——环境映射:折射

六、应用举例——动态环境贴图

七、源码下载


一、概念

我们之前一直使用的是2D纹理,还有更多的纹理类型我们没有探索过,本教程中我们讨论的纹理类型是将多个纹理组合起来映射到一个单一纹理,它就是立方体贴图(Cube Map)

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

从立方体贴图上使用橘黄色向量采样一个纹理值看起来和下图有点像:

方向向量的大小无关紧要。一旦提供了方向,OpenGL就会获取方向向量触碰到立方体表面上的相应的纹理像素(texel),这样就返回了正确的纹理采样值。

方向向量触碰到立方体表面的一点也就是立方体贴图的纹理位置,这意味着只要立方体的中心位于原点上,我们就可以使用立方体的位置向量来对立方体贴图进行采样。然后我们就可以获取所有顶点的纹理坐标,就和立方体上的顶点位置一样。所获得的结果是一个纹理坐标,通过这个纹理坐标就能获取到立方体贴图上正确的纹理。

二、如何使用

1、创建立方体贴图

立方体贴图和其他纹理一样,所以要创建一个立方体贴图,在进行任何纹理操作之前,需要生成一个纹理,激活相应纹理单元然后绑定到合适的纹理目标上。这次要绑定到 GL_TEXTURE_CUBE_MAP纹理类型:

GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);

由于立方体贴图包含6个纹理,立方体的每个面一个纹理,我们必须调用glTexImage2D函数6次,函数的参数和前面教程讲的相似。然而这次我们必须把纹理目标(target)参数设置为立方体贴图特定的面,这是告诉OpenGL我们创建的纹理是对应立方体哪个面的。因此我们便需要为立方体贴图的每个面调用一次 glTexImage2D(方法作用详见:)

由于立方体贴图有6个面,OpenGL就提供了6个不同的纹理目标,来应对立方体贴图的各个面。

纹理目标(Texture target)方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

2、生成纹理

和很多OpenGL其他枚举一样,对应的int值都是连续增加的,所以我们有一个纹理位置的数组或vector,就能以 GL_TEXTURE_CUBE_MAP_POSITIVE_X为起始来对它们进行遍历,每次迭代枚举值加 1,这样循环所有的纹理目标效率较高:

int width,height;
unsigned char* image;  
for(GLuint i = 0; i < textures_faces.size(); i++)
{
    image = SOIL_load_image(textures_faces[i], &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(
        GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
        0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image
    );
}

这儿我们有个vector叫textures_faces,它包含立方体贴图所各个纹理的文件路径,并且以上表所列的顺序排列。它将为每个当前绑定的cubemp的每个面生成一个纹理。

3、设置纹理环绕和过滤方式

由于立方体贴图和其他纹理没什么不同,我们也要定义它的环绕方式和过滤方式:

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

别被 GL_TEXTURE_WRAP_R吓到,它只是简单的设置了纹理的R坐标,R坐标对应于纹理的第三个维度(就像位置的z一样)。我们把放置方式设置为 GL_CLAMP_TO_EDGE ,由于纹理坐标在两个面之间,所以可能并不能触及哪个面(由于硬件限制),因此使用 GL_CLAMP_TO_EDGE 后OpenGL会返回它们的边界的值,尽管我们可能在两个面中间进行的采样。

4、激活和绑定立方体贴图

在绘制物体之前,将使用立方体贴图,而在渲染前我们要激活相应的纹理单元并绑定到立方体贴图上,这和普通的2D纹理没什么区别。

在片段着色器中,我们也必须使用一个不同的采样器——samplerCube,用它来从texture函数中采样,但是这次使用的是一个vec3方向向量,取代vec2。下面是一个片段着色器使用了立方体贴图的例子:

in vec3 textureDir; // 用一个三维方向向量来表示立方体贴图纹理的坐标

uniform samplerCube cubemap;  // 立方体贴图纹理采样器

void main()
{
    color = texture(cubemap, textureDir);
}

看起来不错,但是何必这么做呢?因为恰巧使用立方体贴图可以简单的实现很多有意思的技术。其中之一便是著名的天空盒(Skybox)

三、应用举例——天空盒

1、概念

天空盒(Skybox)是一个包裹整个场景的立方体,它由6个图像构成一个环绕的环境,给玩家一种他所在的场景比实际的要大得多的幻觉。比如有些在视频游戏中使用的天空盒的图像是群山、白云或者满天繁星。比如下面的夜空繁星的图像就来自《上古卷轴》:

你现在可能已经猜到立方体贴图完全满足天空盒的要求:我们有一个立方体,它有6个面,每个面需要一个贴图。上图中使用了几个夜空的图片给予玩家一种置身广袤宇宙的感觉,可实际上,他还是在一个小盒子之中。

网上有很多这样的天空盒的资源。这些天空盒图像通常有下面的样式:

如果你把这6个面折叠到一个立方体中,你机会获得模拟了一个巨大的风景的立方体。有些资源所提供的天空盒比如这个例子6个图是连在一起的,你必须手工它们切割出来,不过大多数情况它们都是6个单独的纹理图像。

2、加载天空盒

由于天空盒实际上就是一个立方体贴图,加载天空盒和之前我们加载立方体贴图的没什么大的不同。为了加载天空盒我们将使用下面的函数,它接收一个包含6个纹理文件路径的vector:

GLuint loadCubemap(vector<const GLchar*> faces)
{
    GLuint textureID;
    glGenTextures(1, &textureID);
    glActiveTexture(GL_TEXTURE0);

    int width,height;
    unsigned char* image;

    glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
    for(GLuint i = 0; i < faces.size(); i++)
    {
        image = SOIL_load_image(faces[i], &width, &height, 0, SOIL_LOAD_RGB);
        glTexImage2D(
            GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
            GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image
        );
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

    return textureID;
}

这个函数没什么特别之处。这就是我们前面已经见过的立方体贴图代码,只不过放进了一个可管理的函数中。

然后,在我们调用这个函数之前,我们将把合适的纹理路径加载到一个vector之中,顺序还是按照立方体贴图枚举的特定顺序:

vector<const GLchar*> faces;
faces.push_back("right.jpg");
faces.push_back("left.jpg");
faces.push_back("top.jpg");
faces.push_back("bottom.jpg");
faces.push_back("back.jpg");
faces.push_back("front.jpg");
GLuint cubemapTexture = loadCubemap(faces);

现在我们已经用cubemapTexture作为id把天空盒加载为立方体贴图。

3、显示天空盒

因为天空盒绘制在了一个立方体上,我们还需要另一个VAO、VBO以及一组全新的顶点,和任何其他物体一样。

立方体贴图用于给3D立方体帖上纹理,可以用立方体的位置作为纹理坐标进行采样。当一个立方体的中心位于原点(0,0,0)的时候,它的每一个位置向量也就是以原点为起点的方向向量。这个方向向量就是我们要得到的立方体某个位置的相应纹理值。出于这个理由,我们只需要提供位置向量,而无需纹理坐标。为了渲染天空盒,我们需要一组新着色器,它们不会太复杂。因为我们只有一个顶点属性,顶点着色器非常简单:

#version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;

uniform mat4 projection;
uniform mat4 view;

void main()
{
    gl_Position =   projection * view * vec4(position, 1.0);  
    TexCoords = position;
}

注意,顶点着色器有意思的地方在于我们把输入的位置向量作为输出给片段着色器的纹理坐标。片段着色器就会把它们作为输入去采样samplerCube:

#version 330 core
in vec3 TexCoords;
out vec4 color;

uniform samplerCube skybox;

void main()
{
    color = texture(skybox, TexCoords);
}

片段着色器比较明了,我们把顶点属性中的位置向量作为纹理的方向向量,使用它们从立方体贴图采样纹理值。渲染天空盒现在很简单,我们有了一个立方体贴图纹理,我们简单绑定立方体贴图纹理,天空盒就自动地用天空盒的立方体贴图填充了。为了绘制天空盒,我们将把它作为场景中第一个绘制的物体并且关闭深度写入。这样天空盒才能成为所有其他物体的背景来绘制出来。


glDepthMask(GL_FALSE);
skyboxShader.Use();
// ... Set view and projection matrix
glBindVertexArray(skyboxVAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthMask(GL_TRUE);
// ... Draw rest of the scene

如果你运行程序就会陷入困境,我们希望天空盒以玩家为中心,这样无论玩家移动了多远,天空盒都不会变近,这样就产生一种四周的环境真的非常大的印象。当前的视图矩阵对所有天空盒的位置进行了转转缩放和平移变换,所以玩家移动,立方体贴图也会跟着移动!我们打算移除视图矩阵的平移部分,这样移动就影响不到天空盒的位置向量了。在基础光照教程里我们提到过我们可以只用4X4矩阵的3×3部分去除平移。我们可以简单地将矩阵转为33矩阵再转回来,就能达到目标

glm::mat4 view = glm::mat4(glm::mat3(camera.GetViewMatrix()));

这会移除所有平移,但保留所有旋转,因此用户仍然能够向四面八方看。由于有了天空盒,场景即可变得巨大了。如果你添加些物体然后自由在其中游荡一会儿你会发现场景的真实度有了极大提升。最后的效果看起来像这样:

尝试用不同的天空盒实验,看看它们对场景有多大影响。

4、优化

现在我们在渲染场景中的其他物体之前渲染了天空盒。这么做没错,但是不怎么高效。如果我们先渲染了天空盒,那么我们就是在为每一个屏幕上的像素运行片段着色器,即使天空盒只有部分在显示着;fragment可以使用前置深度测试(early depth testing)简单地被丢弃,这样就节省了我们宝贵的带宽。

所以最后渲染天空盒就能够给我们带来轻微的性能提升。采用这种方式,深度缓冲被全部物体的深度值完全填充,所以我们只需要渲染通过前置深度测试的那部分天空的片段就行了,而且能显著减少片段着色器的调用。问题是天空盒是个1×1×1的立方体,极有可能会渲染失败,因为极有可能通不过深度测试。简单地不用深度测试渲染它也不是解决方案,这是因为天空盒会在之后覆盖所有的场景中其他物体。我们需要耍个花招让深度缓冲相信天空盒的深度缓冲有着最大深度值1.0,如此只要有个物体存在深度测试就会失败,看似物体就在它前面了。

在坐标系教程中我们说过,透视除法(perspective division)是在顶点着色器运行之后执行的,把gl_Position的xyz坐标除以w元素。我们从深度测试教程了解到除法结果的z元素等于顶点的深度值。利用这个信息,我们可以把输出位置的z元素设置为它的w元素,这样就会导致z元素等于1.0了,因为,当透视除法应用后,它的z元素转换为w/w = 1.0:

void main()
{
    vec4 pos = projection * view * vec4(position, 1.0);
    gl_Position = pos.xyww;
    TexCoords = position;
}

最终,标准化设备坐标就总会有个与1.0相等的z值了,1.0就是深度值的最大值。只有在没有任何物体可见的情况下天空盒才会被渲染(只有通过深度测试才渲染,否则假如有任何物体存在,就不会被渲染,只去渲染物体)。

我们必须改变一下深度方程,把它设置为GL_LEQUAL,原来默认的是GL_LESS。深度缓冲会为天空盒用1.0这个值填充深度缓冲,所以我们需要保证天空盒是使用小于等于深度缓冲来通过深度测试的,而不是小于。

四、应用举例——环境映射:反射

我们现在有了一个把整个环境映射到为一个单独纹理的对象,我们利用这个信息能做的不仅是天空盒。使用带有场景环境的立方体贴图,我们还可以让物体有一个反射或折射属性。像这样使用了环境立方体贴图的技术叫做环境贴图技术,其中最重要的两个是反射(reflection)折射(refraction)

反射

凡是是一个物体(或物体的某部分)反射(Reflect)他周围的环境的属性,比如物体的颜色多少有些等于它周围的环境,这要基于观察者的角度。例如一个镜子是一个反射物体:它会基于观察者的角度泛着它周围的环境。

反射的基本思路不难。下图展示了我们如何计算反射向量,然后使用这个向量去从一个立方体贴图中采样:

我们基于观察方向向量I和物体的法线向量N计算出反射向量R。我们可以使用GLSL的内建函数reflect来计算这个反射向量。最后向量R作为一个方向向量对立方体贴图进行索引/采样,返回一个环境的颜色值。最后的效果看起来就像物体反射了天空盒。

因为我们在场景中已经设置了一个天空盒,创建反射就不难了。我们改变一下箱子使用的那个片段着色器,给箱子一个反射属性:

#version 330 core
in vec3 Normal;
in vec3 Position;
out vec4 color;

uniform vec3 cameraPos;
uniform samplerCube skybox;

void main()
{
    vec3 I = normalize(Position - cameraPos);
    vec3 R = reflect(I, normalize(Normal));
    color = texture(skybox, R);
}

我们先来计算观察/摄像机方向向量I,然后使用它来计算反射向量R,接着我们用R从天空盒立方体贴图采样。要注意的是,我们有了片段的插值Normal和Position变量,所以我们需要修正顶点着色器适应它。

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;

out vec3 Normal;
out vec3 Position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);
    Normal = mat3(transpose(inverse(model))) * normal;
    Position = vec3(model * vec4(position, 1.0f));
}

我们用了法线向量,所以我们打算使用一个法线矩阵(normal matrix)变换它们。Position输出的向量是一个世界空间位置向量。顶点着色器输出的Position用来在片段着色器计算观察方向向量。

因为我们使用法线,你还得更新顶点数据,更新属性指针。还要确保设置cameraPos的uniform。

然后在渲染箱子前我们还得绑定立方体贴图纹理:

glBindVertexArray(cubeVAO);
glBindTexture(GL_TEXTURE_CUBE_MAP, skyboxTexture);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);

编译运行你的代码,你等得到一个镜子一样的箱子。箱子完美地反射了周围的天空盒:

看起来挺惊艳,但是现实中大多数模型都不是完全反射的。我们可以引进反射贴图(reflection map)来使模型有另一层细节。和diffuse漫反射、specular镜面反射贴图一样,我们可以从反射贴图上采样来决定fragment的反射率。使用反射贴图我们还可以决定模型的哪个部分有反射能力,以及强度是多少。

五、应用举例——环境映射:折射

环境映射的另一个形式叫做折射(Refraction),它和反射差不多。折射是光线通过特定材质对光线方向的改变。我们通常看到像水一样的表面,光线并不是直接通过的,而是让光线弯曲了一点。它看起来像你把半只手伸进水里的效果。

折射遵守斯涅尔定律,使用环境贴图看起来就像这样:

我们有个观察向量I,一个法线向量N,这次折射向量是R。就像你所看到的那样,观察向量的方向有轻微弯曲。弯曲的向量R随后用来从立方体贴图上采样。

折射可以通过GLSL的内建函数refract来实现,除此之外还需要一个法线向量,一个观察方向和一个两种材质之间的折射指数。

折射指数决定了一个材质上光线扭曲的数量,每个材质都有自己的折射指数。下表是常见的折射指数:

材质折射指数
空气1.00
1.33
1.309
玻璃1.52
宝石2.42

我们使用这些折射指数来计算光线通过两个材质的比率。在我们的例子中,光线/视线从空气进入玻璃(如果我们假设箱子是玻璃做的)所以比率是1.00/1.52 = 0.658。

我们已经绑定了立方体贴图,提供了定点数据,设置了摄像机位置的uniform。现在只需要改变片段着色器:

void main()
{
    float ratio = 1.00 / 1.52;
    vec3 I = normalize(Position - cameraPos);
    vec3 R = refract(I, normalize(Normal), ratio);
    color = texture(skybox, R);
}

通过改变折射指数你可以创建出完全不同的视觉效果。编译运行应用,结果也不是太有趣,因为我们只是用了一个普通箱子,这不能显示出折射的效果,看起来像个放大镜。使用同一个着色器,纳米服模型却可以展示出我们期待的效果:玻璃制物体。

你可以向想象一下,如果将光线、反射、折射和顶点的移动合理的结合起来就能创造出漂亮的水的图像。一定要注意,出于物理精确的考虑当光线离开物体的时候还要再次进行折射;现在我们简单的使用了单边(一次)折射,大多数目的都可以得到满足。

六、应用举例——动态环境贴图

现在,我们已经使用了静态图像组合的天空盒,看起来不错,但是没有考虑到物体可能移动的实际场景。我们到现在还没注意到这点,是因为我们目前还只使用了一个物体。如果我们有个镜子一样的物体,它周围有多个物体,只有天空盒在镜子中可见,和场景中只有这一个物体一样。

使用帧缓冲可以为提到的物体的所有6个不同角度创建一个场景的纹理,把它们每次渲染迭代储存为一个立方体贴图。之后我们可以使用这个(动态生成的)立方体贴图来创建真实的反射和折射表面,这样就能包含所有其他物体了。这种方法叫做动态环境映射(Dynamic Environment Mapping),因为我们动态地创建了一个物体的以其四周为参考的立方体贴图,并把它用作环境贴图。

它看起效果很好,但是有一个劣势:使用环境贴图我们必须为每个物体渲染场景6次,这需要非常大的开销。现代应用尝试尽量使用天空盒子,凡可能预编译立方体贴图就创建少量动态环境贴图。动态环境映射是个非常棒的技术,要想在不降低执行效率的情况下实现它就需要很多巧妙的技巧。

七、源码下载

Android OpenGL 立方体贴图应用举例-天空盒演示demo源码下载:

https://download.csdn.net/download/github_27263697/89991149

推荐文章

https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/06%20Cubemaps/#_4

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

聊天服务器(9)一对一聊天功能

目录 一对一聊天离线消息服务器异常处理 一对一聊天 先新添一个消息码 在业务层增加该业务 没有绑定事件处理器的话消息会派发不出去 聊天其实是服务器做一个中转 现在同时登录两个账号 收到了聊天信息 再回复一下 离线消息 声明中提供接口和方法 张三对离线的李…

T6识别好莱坞明星

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 导入基础的包 from tensorflow import keras from tensorflow.keras import layers,models import os, PIL, pathlib import matplotlib.pyplot as pl…

Odoo :一款免费开源的日化行业ERP管理系统

文 / 开源智造Odoo亚太金牌服务 概述 构建以 IPD 体系作为核心的产品创新研发管控体系&#xff0c;增进企业跨部门业务协同的效率&#xff0c;支撑研发管控、智慧供应链、智能制造以及全渠道营销等行业的场景化&#xff0c;构筑行业的研产供销财一体化管理平台。 行业的最新…

48.第二阶段x86游戏实战2-鼠标点击call

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

Vue 学习随笔系列十五 -- 数组遍历方法

数组遍历方法 文章目录 数组遍历方法1. for 循环2. forEach (不会修改数组本身)3. map (不修改数组本身)4. some(不修改数组本身&#xff09;5. every(不修改数组本身&#xff09;6. filter(不修改数组本身)7. find(不修改数组本身)8. findIndex拓展 9. reduce(累加)拓展 1. fo…

FreeRTOS的列表与列表项

目录 1.为什么要学列表&#xff1f; 2.什么是列表和列表项&#xff1f; 2.1 列表 2.2列表项 2.3&#xff0c;迷你列表项 3.列表与列表项的初始化 3.1 列表初始化 3.2列表项初始化 4.列表项的“增删查”&#xff08;插入、删除、遍历&#xff09; 4.1列表项的插入 4.1.1…

数字IC后端教程之Innovus hold violation几大典型问题

今天小编给大家分享下数字IC后端实现Physical Implementation过程中经常遇到的几个hold violation问题。每个问题都是小编自己在公司实际项目中遇到的。 数字后端实现静态时序分析STA Timing Signoff之min period violation Q1: 在Innouvs postCTS时序优化的log中我们经常会看…

VS2022编译32位OpenCV

使用环境 Visual Studio 2022 OpenCV: 4.7.0 cmake: 3.30.2一、使用CMake工具生成vs2022的openCV工程解决方案 打开cmake&#xff0c;选择opencv的源代码目录&#xff0c;创建一个文件夹&#xff0c;作为VS工程文件的生成目录 点击configure构建项目&#xff0c;弹出构建设置…

企业生产环境-麒麟V10(ARM架构)操作系统部署Zookeeper单节点集群版

前言&#xff1a;ZooKeeper是一个分布式协调服务&#xff0c;它为分布式应用提供一致性服务&#xff0c;是Apache Hadoop的子项目。它被设计为易于编程&#xff0c;同时具有高性能和高可靠性。ZooKeeper提供了一个简单的接口和一些基本的文件系统操作&#xff0c;使得开发者能够…

vue3 中直接使用 JSX ( lang=“tsx“ 的用法)

1. 安装依赖 npm i vitejs/plugin-vue-jsx2. 添加配置 vite.config.ts 中 import vueJsx from vitejs/plugin-vue-jsxplugins 中添加 vueJsx()3. 页面使用 <!-- 注意 lang 的值为 tsx --> <script setup lang"tsx"> const isDark ref(false)// 此处…

深度学习服务器租赁AutoDL

1. 根据需要选择租用的显卡 算力市场 1.1 显卡选择 1.2 环境配置 2. 服务器使用 2.1 上传文件 2.2 调试环境 2.3 跑代码 python train.py && /usr/bin/shutdown # && /usr/bin/shutdown表示代码成功运行结束后&#xff0c;自动关机3. 省钱绝招 省钱绝招 …

IDEA部署AI代写插件

前言 Hello大家好&#xff0c;当下是AI盛行的时代&#xff0c;好多好多东西在AI大模型的趋势下都变得非常的简单。 比如之前想画一幅风景画得先去采风&#xff0c;然后写实什么的&#xff0c;现在你只需描述出你想要的效果AI就能够根据你的描述在几分钟之内画出一幅你想要的风景…

【大数据技术基础 | 实验十】Hive实验:部署Hive

文章目录 一、实验目的二、实验要求三、实验原理四、实验环境五、实验内容和步骤&#xff08;一&#xff09;安装部署&#xff08;二&#xff09;配置HDFS&#xff08;三&#xff09;启动Hive 六、实验结果&#xff08;一&#xff09;启动结果&#xff08;二&#xff09;Hive基…

Flume1.9.0自定义Sink组件将数据发送至Mysql

需求 1、将Flume采集到的日志数据也同步保存到MySQL中一份&#xff0c;但是Flume目前不支持直接向MySQL中写数据&#xff0c;所以需要用到自定义Sink&#xff0c;自定义一个MysqlSink。 2、日志数据默认在Linux本地的/data/log/user.log日志文件中&#xff0c;使用Flume采集到…

Onlyoffice配置一 JWT認證

案例 使用官網給c# MVC的例子&#xff0c;主要在版本7.2之後&#xff0c;默認加入JWT認證&#xff0c;docker版本尚且可以在创建的时候使用默认的指令避开&#xff0c;但是在exe版本&#xff0c;即使配置为false&#xff0c;重启之后也会默认开启。 简单说一下如何配置 配置J…

ZeroSSL HTTPS SSL证书ACMESSL申请3个月证书

目录 一、引言 二、准备工作 三、申请 SSL 证书 四、证书选型 五、ssl重要性 一、引言 目前免费 Lets Encrypt、ZeroSSL、BuyPass、Google Public CA SSL 证书&#xff0c;一般免费3-6个月。从申请难易程度分析&#xff0c;zerossl申请相对快速和简单&#xff0c;亲测速度非…

MySql 日期周处理方式

MySql 日期周处理方式 最近在做数仓相关工作&#xff0c;最近遇到 几个问题&#xff0c; 1、计算指定日期是一年中的第几周&#xff0c;周一为周的第一天 2、计算周的开始时间&#xff0c;结束时间 3、计算周对应的年 比如 2023-01-01 WEEKOFYEAR(2023-01-01) 是2022年的52周&…

STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56

STM32 BootLoader 刷新项目 (十) Flash擦除-命令0x56 1. STM32F407 BootLoader 中的 Flash 擦除功能详解 在嵌入式系统中&#xff0c;BootLoader 的设计是非常关键的部分&#xff0c;它负责引导主程序的启动、升级以及安全管理。而在 STM32F407 等 MCU 上实现 BootLoader&…

【Homework】【5】Learning resources for DQ Robotics in MATLAB

Lesson 5 代码-TwoDofPlanarRobot.m 表示一个 2 自由度平面机器人。该类包含构造函数、计算正向运动学模型的函数、计算平移雅可比矩阵的函数&#xff0c;以及在二维空间中绘制机器人的函数。 classdef TwoDofPlanarRobot%TwoDofPlanarRobot - 表示一个 2 自由度平面机器人类…

Uniapp 引入 Android aar 包 和 Android 离线打包

需求&#xff1a; 原生安卓 apk 要求嵌入到 uniapp 中&#xff0c;并通过 uniapp 前端调起 app 的相关组件。 下面手把手教你&#xff0c;从 apk 到 aar&#xff0c;以及打包冲突到如何运行&#xff0c;期间我所遇到的问题都会 一 一 进行说明&#xff0c;相关版本以我文章内为…