计算机图形学 | 实验九:纹理贴图和天空盒
计算机图形学 | 实验九:纹理贴图和天空盒
- 计算机图形学 | 实验九:纹理贴图和天空盒
- 实验概述
- 顶点数据
- 立方体顶点数据
- 天空盒顶点数组
- 纹理载入
- 创建纹理
- 纹理读取
- 纹理绑定
- 使用纹理
- 立方体着色器
- 顶点着色器
- 片元着色器
- 天空盒着色器
- 顶点着色器
- 片元着色器
- 立方体贴图和平面纹理对比
- 着色器中的区别
- 定义和设置上的区别
- 纹理定义
- 资源载入
- 天空盒位置的控制
- 总结
华中科技大学《计算机图形学》课程
MOOC地址:计算机图形学(HUST)
计算机图形学 | 实验九:纹理贴图和天空盒
实验概述
这次实验我们主要学习如何绘制带有平面纹理的立方体,以及运用立方体贴图实现的天空盒。
实验要求:
- 平面纹理(之前实验的基础上,在立方体贴上纹理)
- 立方体贴图(天空盒,背景的天空盒采用立方体贴图)
实验最终的实现效果:
顶点数据
立方体顶点数据
这是立方体顶点数组,可以看到前三个float量是我们熟悉的顶点坐标位置,后面两个float量是顶点所对应的纹理UV坐标,通过这个UV坐标,我们可以控制将纹理上的哪一部分贴在三角形片元上。
const float vertices[] = { //立方体数组-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f
};
天空盒顶点数组
紧接着我们看到天空盒的顶点数组。我们可以发现,天空盒的顶点数组是没有之前的纹理坐标的。
float skybox_vertices[] = { //天空盒顶点数组-1.0f, 1.0f, -1.0f,-1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f,1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f,-1.0f, -1.0f, 1.0f,-1.0f, -1.0f, -1.0f,-1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f,-1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f,1.0f, -1.0f, -1.0f,1.0f, -1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f, -1.0f,1.0f, -1.0f, -1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f,-1.0f, 1.0f, -1.0f,1.0f, 1.0f, -1.0f,1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, -1.0f,-1.0f, -1.0f, -1.0f,-1.0f, -1.0f, 1.0f,1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f,-1.0f, -1.0f, 1.0f,1.0f, -1.0f, 1.0f
};
因为我们使用的是立方体贴图,天空盒的顶点坐标即可对应它纹理坐标。我们可以直接将顶点坐标作为立方体贴图的纹理坐标。
纹理载入
我们用平面纹理包裹住之前实验实现的立方体,用立方体贴图包裹住天空盒,即可得到我们最后需要的结果。
为了实现纹理贴图,我们需要进行几步操作:首先进行纹理定义和设置,然后进行纹理资源载入,生成多级纹理,在使用前进行纹理绑定,以及最后在着色器中进行采样。
创建纹理
首先我们定义一个纹理,绑定在GL_TEXTURE_2D上,然后就开始设置它的纹理属性,最后四行中,前两行是用来设置纹理的环绕方式。后两行则是设置纹理的过滤方式。设置这些纹理的属性有很多参数,下面我们就来介绍一下这里的参数有哪些,分别都是什么作用。
GLuint texture1; glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
上面的代码中,glTexParameteri函数是负责设置纹理的属性。
其中第四、第五行代码:
- GL_TEXTURE_WRAP_S表示在s轴上纹理的环绕方式;
- GL_TEXTURE_WRAP_T表示在T轴上纹理的环绕方式,这里s和t等价于平面纹理图片的x轴和y轴;
- GL_REPEAT是表示纹理重复出现,它也是在不设置的情况下默认环绕方式。GL_MIRRORED_REPEAT也是重复图片,但是他表示以镜像方式重复出现;
- GL_CLAMP_TO_EDGE表示纹理坐标会被约束在0-1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。这种环绕方式通常会在我们设置纹理坐标超过0-1的范围时被使用到。
其中第六第七行代码:
- GL_TEXTURE_MIN_FILTER是设置纹理在缩小时的过滤方式
- GL_TEXTURE_MAG_FILTER是设置纹理在放大时的过滤方式。
过滤方式我们主要使用的有两种,一种是GL_NEAREST即线性过滤,这种过滤方法会产生颗粒状的图案,但是也能更清晰的看到组成纹理的像素。GL_LINEAR即临近过滤,它能够产生更平滑的图案,但是也有更真实的输出。
纹理读取
接下来我们学习如何进行资源载入,即把图片读入内存,最终绑定到着色器。我们使用#include <stb/stb_image.h>中的_stbi_load,根据路径读取纹理图片,并读取纹理的宽高和通道数,将纹理数据存入data数组中,最后判断是否读取成功,若失败,则报错,若成功,则进行下一步操作。部分代码如下:
//加载纹理
int width, height, nrchannels;//纹理长宽,通道数
stbi_set_flip_vertically_on_load(true);
unsigned char *data = stbi_load("res/texture/CG_Sprite.jpg", &width, &height, &nrchannels, 0);
if (data)
{//生成纹理glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);
}
elsestd::cout << "Failed to load texture" << std::endl;
在读取纹理之后,我们还要将data中的纹理数据存入GL_TEXTURE_2D,存入的时候我们需要同时输入纹理的宽高,接着我们使用glGenerateMipmap(GL_TEXTURE_2D)生成多级渐远纹理。
纹理绑定
由于可能出现多个纹理,所以我们每次使用纹理绘制之前,最好进行一次绑定操作。glActiveTexture(GL_TEXTURE0)为选择GL_TEXTUERE0纹理单元,将要要使用的纹理绑定到GL_TEXTURE0纹理单元中,计算机有很多纹理单元GL_TEXTURE1 GL_TEXTURE0 GL_TEXTURE2 GL_TEXTURE3 GL_TEXTURE4,使用时每个纹理最好对一个纹理单元。
接着我们使用glBindTexture(GL_TEXTURE_2D, texture1);去绑定纹理,绑定后即可进行绘制。
glActiveTexture(GL_TEXTURE0); //绑定纹理
glBindTexture(GL_TEXTURE_2D, texture1);...glDrawArrays(GL_TRIANGLES, 0, 36);//绘制
使用纹理
后面介绍如何在着色器中使用纹理,需要注意的是,立方体和天空盒使用纹理的方式是不一样的,我们先看看着色器中如何使用平面纹理。
立方体着色器
顶点着色器
立方体的顶点着色器负责的纹理方面的功能不多,只是将顶点对应的纹理坐标传入片元着色器。
#version 330 core
layout (location = 0)
in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
...
void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0); TexCoord=vec2(aTexCoord.x,aTexCoord.y);
}
片元着色器
片元着色器则是根据传入的sampler2D类型的纹理和纹理对应坐标,使用texture进行采样,获取当前片元的颜色值。
在这里插入代码片
#version 330 core
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D texture1;
void main()
{FragColor = texture(texture1,TexCoord);
}
天空盒着色器
顶点着色器
接着我们看到天空盒的着色器具体跟立方体的着色器到底有哪些不同。
天空盒的顶点着色器将顶点坐标直接作为纹理坐标传入片元着色器,可以发现平面纹理的纹理坐标是vec2类型,而立方体的纹理坐标则是vec3类型,并且pvm矩阵也发生了变化,这个我们后面再提,还有我们可以看到的是我们把z变量替换成了w,这是为了使天空盒永远在所有物体之后,作为背景,使其深度值最大,不遮挡任何物体。
#version 330 core
layout (location = 0) in vec3 aPos;out vec3 TexCoords;uniform mat4 projection;
uniform mat4 view;void main()
{TexCoords = aPos;vec4 pos = projection * view * vec4(aPos, 1.0);//这是因为天空盒会在之后覆盖所有的场景中其他物体。我们需要耍个花招让深度缓冲相信天空盒的深度缓冲有着最大深度值1.0,如此只要有个物体存在深度测试就会失败,看似物体就在它前面了//透视除法(perspective division)是在顶点着色器运行之后执行的,把gl_Position的xyz坐标除以w元素。我们从深度测试教程了解到除法结果的z元素等于顶点的深度值。利用这个信息,我们可以把输出位置的z元素设置为它的w元素,这样就会导致z元素等于1.0了,因为,当透视除法应用后,它的z元素转换为w/w = 1.0:gl_Position = pos.xyww;
}
片元着色器
天空盒的片元着色器也与立方体的片元着色器有些不同,它传入的是samplerCube类型的立方体贴图,且纹理坐标也是vec3类型,但是最终还是通过texture进行采样。
#version 330 core
out vec4 FragColor;in vec3 TexCoords;uniform samplerCube skybox;void main()
{ FragColor = texture(skybox, TexCoords);
}
立方体贴图和平面纹理对比
着色器中的区别
我们刚才这是对立方体和天空盒着色器的区别进行了分析,发现天空盒着色器传入的纹理是samplerCube类型,那我们怎样向着色器传入samplerCube类型的纹理呢?它与立方体纹理的区别又在那里呢?
立方体贴图的六个面,每个面都对应一个纹理,且有其相应的顺序。有人可能会觉得为什么不能贴六个纹理而非要用立方体贴图呢?那是因为立方体贴图有其独特的属性,我们有时候可以直接使用方向向量对立方体贴图进行索引和采样。
我们可以设想一下,有一束光线从立方体的中心向任意一个方向射出,我们知道了射出光线的方向向量,即可在在立方体贴图上找到对应的纹理坐标,获取到相应的颜色值,通过这种方法我们可以实现光的折射和反射,通过出射光和入射光的方向找到对应的颜色值。
定义和设置上的区别
我们之前讲了立方体贴图和平面纹理在着色器中的区别,那他们在别的方面还有什么区别吗?因为立方体贴图包括六个相关的平面纹理,所以他们在纹理的定义和设置和资源载入上还是有一些区别的。
纹理定义
首先设置纹理属性值,首先是定义纹理id时,我们将GLuint换成了unsigned int,这两种方式其实是完全等价的,所以使用的时候我们可以根据自己的需要来,接着就是绑定纹理,因为是立方体纹理,所以之前的绑定在GL_TEXTURE_2D变成了绑定在GL_TEXTURE_CUBE_MAP,其他属性的设置基本相同,但是需要注意的是,由于以及是立体纹理而不仅仅是平面纹理了,所以他的环绕方式也多出了一个R轴的环绕方式,对应的是现实中的z轴。
unsigned int load_cubemap(std::vector<std::string> faces)
{unsigned int textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrchannels;for (unsigned int i = 0; i < faces.size(); i++){stbi_set_flip_vertically_on_load(false);unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrchannels, 0);if (data){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else{std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);return textureID;
}
资源载入
资源载入时我们也需要一次性载入6张纹理,一般我们使用for循环载入,循环六次,同时每次载入纹理存储的地址也有所不同,不同的纹理存储在不同的地址,不过这些地址都是相邻的,所以每次只需要加i即可。
unsigned int load_cubemap(std::vector<std::string> faces)
{unsigned int textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrchannels;for (unsigned int i = 0; i < faces.size(); i++){stbi_set_flip_vertically_on_load(false);unsigned char *data = stbi_load(faces[i].c_str(), &width, &height, &nrchannels, 0);if (data){glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else{std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);return textureID;
}
天空盒位置的控制
那么我们该如何摆放天空盒呢?首先天空盒是一个包裹着相机的大盒子,它不需要进行世界坐标系的模型变换,其次相机不管如何移动,应该始终在天空盒的中心点,那么我们如何取消天空盒着色器中view矩阵的位移而保存相机视角变换之类的操作呢?
相机view矩阵,主要包含了位移和旋转操作,如图4.3.1所示,第一行公式为位移矩阵如何发挥其功能,而位移部分则是在Tx,Ty,Tz中体现, Tx,Ty,Tz为位移量,我们只需要把view矩阵从4x4先转换为3x3矩阵,去掉位移部分,再转换回4x4矩阵,则可完全去掉位移量,并保持旋转效果。
体现在程序中则是如下操作,先转换成mat3矩阵,再转换成mat4矩阵。
//去除相机位移
view = glm::mat4(glm::mat3(glm::lookAt(camera_position, camera_position + camera_front, camera_up)));
总结
以上为本次实验的要点解析,下面是实验结果演示。
运行之后我们移动视角可以发现,不管我们怎样移动相机的位置,天空盒相对于摄像机的位置始终保持不变。
相关文章:

计算机图形学 | 实验九:纹理贴图和天空盒
计算机图形学 | 实验九:纹理贴图和天空盒 计算机图形学 | 实验九:纹理贴图和天空盒实验概述顶点数据立方体顶点数据天空盒顶点数组 纹理载入创建纹理纹理读取纹理绑定 使用纹理立方体着色器顶点着色器片元着色器 天空盒着色器顶点着色器片元着色器 立方体…...

Unity A* Pathfinding Project
先下载免费版 https://arongranberg.com/astar/download# 教程首页 https://arongranberg.com/astar/docs/getstarted.html 创建一个plane 当地面 创建一个gameobject 添加组件 PathFinder 长这样 调整每个格子大小的 创建两个layer 一个是阻挡物的 一个是地面的 这里填入阻…...

SpringBoot ( 一 ) 搭建项目环境
1.搭建环境 1.1.创建项目向导 使用idea中的向导创建SpringBoot项目 1.1.1.建立新的项目 位置 : 菜单 > File > New > Project… 1.1.2.选择向导 默认的向导URL 是 https://start.spring.io 建议使用 https://start.aliyun.com 1.1.3.配置项目信息 Group : 组织…...

idea中关联Git
注意:未安装和配置Git软件,请先跳转到 Git宝典_没办法,我就是这么菜的博客-CSDN博客 idea关联git 关联git.exe 选择你的Version Control 下的Git 选择你的Git安装目录bin下的git.exe,点击ok 点击Test,显示版本号…...

Java面试知识点(全)-分布式微服务-zookeeper面试知识点
Java面试知识点(全) 导航: https://nanxiang.blog.csdn.net/article/details/130640392 注:随时更新 ZooKeeper是什么? ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现&…...

(IDEA)springCloud项目导入本地jar包方法和项目打包时找不到引入本地jar包的问题解决方案
idea导入本地jar包 方法一:点击左上角File–>Project Structure–>Modules。打开Modules界面点击下方号,选择第一项,找到想要导入的本地jar包。此方法可以使项目使用导入的jar包程序不报错,但是在打包项目时,会出现找不到程…...
非线性系统的线性化与泰勒级数
线性系统与非线性系统的区别 我们在读论文的时候经常会遇到这两个系统,线性系统与非线性系统,这两者之间有什么区别呢? 线性指量与量之间按比例、成直线的关系,在空间和时间上代表规则和光滑的运动;非线性则指不按比…...

HOOPS全新文档系统上线!三维模型文件转换更便捷!
HOOPS 2023 U1版本已经正式发布,伴随新版本上线的还有全新的文档系统,新的文档系统亮点包括: 改进了样式和布局,使导航更加简单快捷;修订了导航结构,提高了产品相关信息的清晰度;SDK API参考章…...

第三篇:强化学习发展历史
你好,我是zhenguo(郭震) 这是强化学习第三篇,我们回顾一下它的发展历史:强化学习发展历史 强化学习作为一门研究领域,经历了多年的发展和演进。以下是强化学习的主要发展历史里程碑: 1950年代-1…...
如何学会战略性思维?
上帝在创造一个成功人生时加入了什么配方?一部分努力、一点天赋、必备的人脉、再加入不可或缺的good luck......这都是成功人生的重要因素。 弗雷德佩拉德认为,不管人们在商业领域选择追求什么,要想成功,还需要增加一个名为“战略…...

设备采购信息管理系统
系列文章 任务14 设备采购信息管理系统 文章目录 系列文章一、实践目的与要求1、目的2、要求 二、课题任务三、总体设计1.存储结构及数据类型定义2.程序结构3.所实现的功能函数4、程序流程图 四、小组成员及分工五、 测试界面展示添加采购信息按编号查找采购信息按设备编号查找…...

Puppeteer入门实践
环境 1、安装nodejs 官网:https://nodejs.org/zh-cn 下载安装好nodejs只后 验证:node -v 出现版本号表示安装成功,否则需要配置环境变量 2、创建node项目并初始化 随便新建一个文件夹 进入文件夹搜索cmd回车 执行npm init -y 安装依赖 …...

生成VLC 及其模块的全面帮助
使用vlc.exe -H命令生成VLC帮助文件vlc-help.txt -h, --help, --no-help 打印 VLC 帮助 (可以和 --advanced 和 --help-verbose 一起使用) (默认禁用) -H, --full-help, --no-full-help VLC 及…...

什么是客户自助服务门户及其搭建方法
随着信息技术的快速发展,越来越多的企业开始转向以客户为中心的服务模式,而客户自助服务门户(Customer Self-Service Portal)则成为了重要的服务方式。它可以让客户在不需要人工干预的情况下,自行解决问题,…...

【5.19】三、白盒测试方法—逻辑覆盖法
目录 3.1 逻辑覆盖法 3.1.1 语句覆盖 3.1.2 判定覆盖 3.1.3 条件覆盖 3.1.4 判定—条件覆盖 3.1.5 条件组合覆盖 3.1.6 实例:三角形逻辑覆盖问题 白盒测试又称为透明盒测试、结构测试,它基于程序内部结构进行测试,而不是测试应用程序…...

Linux安装MySQL后无法通过IP地址访问处理方法
本文主要总结Linux安装Mysql后,其他主机访问不了MySQL数据库的原因和解决方法 环境说明: MySQL 5.7.30CentOS Linux release 7.6.1810 (Core) 创建完Mysql数据库后可以查看mysql 日志获取root 用户登录密码 [rootlocalhost mysql-5.7.30]# cat /var/l…...

使用Python实现Rest API指南
在今天的数字化世界中,数据的获取、交换和使用已经成为几乎所有行业的核心部分。无论您正在为一个大型公司设计复杂的软件系统,还是只是为了个人项目尝试获得一些公开的数据,理解和利 用API——尤其是RESTful API——都是一项至关重要的技术。…...

【2023 · CANN训练营第一季】进阶班 应用开发深入讲解→模型推理
1 模型离线推理 各步要解析如下: Host&Device内存管理与数据传输: Host&Device上的内存申请与释放,内存间的相互拷贝;模型加载:将离线的om文件加载到Device上;在样例的资源初始化模块中进行。模型输入输出准备∶根据禹线om的输入输出,在Device…...
单词分析问题
题目描述 小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组 成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出…...

3个月出国|材料科学老师自费赴韩国访学
K老师指定韩国为访学的目标国家,希望专业匹配,尽快出国。最终我们获得了韩国庆北大学的邀请函,其学校名气、专业匹配度及导师影响力都符合K老师的要求。本案例从开始委托我们申请到最终出国,仅仅用时3个月。 K老师背景:…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...