LearnOpenGL-入门-你好,三角形
本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正
我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject
LearnOpenGL中文官网:https://learnopengl-cn.github.io/
 
文章目录
- 图形渲染管线
- 基本介绍
- 着色器阶段
 
- 顶点输入
- 着色器代码流程
- 链接顶点属性
- 顶点数组对象VAO
- 绘制三角形
- 元素(索引)缓冲对象EBO
- 小结
- 草稿图
- 重复重要的流程
- 着色器流程
- 绘制流程
 
 
图形渲染管线
基本介绍
-  功能 将3D坐标变为2D坐标 将2D坐标转换为实际的有颜色的像素 
-  图形渲染管线与着色器 图形渲染管线分为多个阶段,多个阶段对应多个自己特定的函数(小程序),在各自特定的函数可并行执行调用显卡的成千上万的核心,这些小程序被称为着色器 
着色器阶段

-  顶点数据 以数组的形式传递3个3D坐标作为图形渲染管线的输入,用来表示一个三角形,这个数组叫做顶点数据(Vertex Data) 
-  顶点着色器 - 它把一个单独的顶点作为输入
- 主要的目的是把3D坐标转为另一种3D坐标
 
-  形状(图元)装配 将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状 
-  几何着色器 - 把图元形式的一系列顶点的集合作为输入
- 它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状
 
-  光栅化 - 把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)
- 在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
 
-  片段着色器 片段着色器的主要目的是计算一个像素的最终颜色 
-  Alpha测试和混合 -  检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃 
-  检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend),可以认为改变片段的颜色 
 
-  
在现代OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。
顶点输入
-  标准化设备坐标 -  在顶点着色器中处理过,它们就应该是标准化设备坐标,x、y和z值在-1.0到1.0的一小段空间 
-  图示  
 
-  
-  顶点数据 float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f };一个在CPU内存的数组 
-  顶点缓冲对象VBO 由于CPU内存的顶点数据需要传入GPU内存中,就需要在GPU内存中存储同样大小的顶点数据,而顶点缓冲对象管理这个在GPU上的内存(有点模糊这个概念) unsigned int VBO; // 1.在GPU上生成一个缓冲,返回ID glGenBuffers(1, &VBO); // 2.绑定缓冲 glBindBuffer(GL_ARRAY_BUFFER, VBO); // 3.CPU内存的顶点数据复制到GPU内存中 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
着色器代码流程
-  创建一个着色器对象 用glCreateShader创建这个着色器 unsigned int vertexShader; vertexShader = glCreateShader(GL_VERTEX_SHADER);
-  着色器源码附加到着色器对象 glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
-  编译着色器 glCompileShader(vertexShader);编译完顶点着色器后,片段着色器同样这样编译 
-  两个着色器对象链接到一个用来渲染的着色器程序 -  创建一个着色器程序对象 unsigned int shaderProgram; shaderProgram = glCreateProgram();
-  编译的着色器附加到着色器程序对象 glAttachShader(shaderProgram, vertexShader); glAttachShader(shaderProgram, fragmentShader);
-  glLinkProgram链接着色器程序对象 glLinkProgram(shaderProgram);
 
-  
-  使用着色器程序 glUseProgram(shaderProgram);
-  着色器对象链接到着色器程序对象以后,删除着色器对象 glDeleteShader(vertexShader); glDeleteShader(fragmentShader);
-  另外 在编译着色器对象和链接时可以看是否成功 int success; char infoLog[512]; glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); if(!success) {glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; }
链接顶点属性
-  我们必须手动指定顶点输入数据的哪一个部分对应顶点着色器的哪一个顶点属性  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 若第一个参数为0,对应顶点着色器的layout(location = 0) in vec3 a_Position; // 若第一个参数为1,对应顶点着色器的layout(location = 1) in vec4 a_Color; glEnableVertexAttribArray(0);// 代表启用顶点着色器location=0的输入设置好OpenGL如何解释顶点数据,但是设置的顶点数据来源于上一次将顶点缓冲对象绑定的那个VBO。 glVertexAttribPointer参数: -  1:要配置的顶点属性 layout(location = 0) 
-  2:顶点属性的大小 
-  3:数据的类型 
-  4:是否希望数据被标准化 GL_TRUE:所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间 
-  5:步长 
-  6:偏移量 
 
-  
-  由此绘制的代码 // 省略创建缓冲 // 0. CPU内存的顶点数据复制到GPU内存中 glBindBuffer(GL_ARRAY_BUFFER, VBO1); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 1. 设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 2. 当我们渲染一个物体时要使用着色器程序 glUseProgram(shaderProgram); // 3. 绘制物体 someOpenGLFunctionThatDrawsOurTriangle();若有第二个不同的物体(不同的顶点数据)需要渲染 又要写一遍这个代码 // 0. CPU内存的顶点数据复制到GPU内存中 glBindBuffer(GL_ARRAY_BUFFER, VBO2); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 1. 设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 2. 当我们渲染一个物体时要使用着色器程序 glUseProgram(shaderProgram); // 3. 绘制物体 someOpenGLFunctionThatDrawsOurTriangle();
-  缺点 由上可看出,有多少个物体,就得重复写绑定的顶点缓冲区、顶点属性指针,属实麻烦,则应该使用顶点数组对象VAO 
顶点数组对象VAO
-  使用这个有什么用 原话: -  当配置顶点属性指针时,你只需要将那些调用执行一次,之后再绘制物体的时候只需要绑定相应的VAO就行了。 
-  这使在不同顶点数据和属性配置之间切换变得非常简单,只需要绑定不同的VAO就行了。刚刚设置的所有状态都将存储在VAO中 
 我认为: - 顶点数组对象VAO与顶点缓冲对象VBO一对多,一个VAO的顶点属性指针可以来源于多个不同的顶点缓冲对象,在初始化时VAO设置好顶点属性指针后,绘制的时候绑定对应的VAO就行,不需要写绑定顶点缓冲与设置顶点属性指针的代码了,可以在绘制时无关初始化设置状态的代码。
 
-  
-  代码 // ..:: 初始化代码(只运行一次 (除非你的物体频繁改变)) :: .. unsigned int VAO; glGenVertexArrays(1, &VAO); // 1. 绑定VAO glBindVertexArray(VAO); // 2. 把顶点数组复制到缓冲中供OpenGL使用 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 3. 设置顶点属性指针 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);[...]// ..:: 绘制代码(渲染循环中) :: .. // 4. 绘制物体 glUseProgram(shaderProgram); glBindVertexArray(VAO); someOpenGLFunctionThatDrawsOurTriangle();若有第二个物体要绘制,跟上一样,在初始化部分绑定相应顶点缓冲对象设置顶点属性后,在渲染绘制代码只要切换VAO就行 // 4. 绘制物体 glUseProgram(shaderProgram); glBindVertexArray(VAO); someOpenGLFunctionThatDrawsOurTriangle();glUseProgram(shaderProgram2); glBindVertexArray(VAO2); someOpenGLFunctionThatDrawsOurTriangle();
-  图示  如图:VAO与VBO一一对应,但实际上VAO的顶点属性指针可以来源于多个不同的顶点缓冲VBO,一般是一一对应 
绘制三角形
-  代码 glsl version 330 core layout (location = 0) in vec3 aPos; void main() {gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" }#version 330 core out vec4 FragColor; void main() {FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); }cpp // 0.顶点数据 float vertices[] = {-0.5f, -0.5f, 0.0f, // left 0.5f, -0.5f, 0.0f, // right 0.0f, 0.5f, 0.0f // top }; unsigned int VBO, VAO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). // 1. 绑定顶点数组对象 glBindVertexArray(VAO); // 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中,供OpenGL使用 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 3. 设定顶点属性指针,来解释顶点缓冲中的顶点属性布局 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); while (!glfwWindowShouldClose(window)) {.....// 4.使用着色器程序对象glUseProgram(shaderProgram);// 5.绑定顶点数组对象,并绘制glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 3);// 这里..... }
-  效果 

元素(索引)缓冲对象EBO
-  简介 绘制矩形,有重复的顶点,正确使用索引顺序绘制图形可以重复利用顶点从而减少顶点数据。 
-  使用 和VBO同样的生成使用方法,生成EBO缓冲区返回ID、绑定ID、设置索引数据、绑定在VAO上 绘制不同: 用glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);代替glDrawArrays(GL_TRIANGLES, 0, 3); 
-  图示  由图可见,VAO索引缓冲区的指针只有一个,且在最后 
-  代码 glsl不变 version 330 core layout (location = 0) in vec3 aPos; void main() {gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" }#version 330 core out vec4 FragColor; void main() {FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f); }cpp // 0.1顶点数据 float vertices[] = {0.5f, 0.5f, 0.0f, // top right0.5f, -0.5f, 0.0f, // bottom right-0.5f, -0.5f, 0.0f, // bottom left-0.5f, 0.5f, 0.0f // top left }; // 0.2索引数据 unsigned int indices[] = { // note that we start from 0!0, 1, 3, // first Triangle1, 2, 3 // second Triangle };unsigned int VBO, VAO, EBO; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). // 1. 绑定顶点数组对象 glBindVertexArray(VAO); // 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中,供OpenGL使用 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 3. 复制我们的CPU的索引数组到GPU索引缓冲中,供OpenGL使用 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 4. 设定顶点属性指针,来解释顶点缓冲中的顶点属性布局 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); while (!glfwWindowShouldClose(window)) {.....// 5.使用着色器程序对象glUseProgram(shaderProgram);// 6.绑定顶点数组对象,并绘制glBindVertexArray(VAO); //glDrawArrays(GL_TRIANGLES, 0, 3);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);// 这里不同// 由于只有一个顶点数组对象,不需要解绑..... }
-  效果  
小结
草稿图

重复重要的流程
着色器流程
-  顶点着色器 -  创建顶点着色器对象 
-  附加源码给顶点着色器对象 
-  编译顶点着色器对象 可以打印是否编译成功 
 
-  
-  片段着色器同上 
-  着色器程序 -  创建着色器程序对象 
-  附加着色器对象给着色器程序对象 
-  链接着色器程序对象 可以检查是否成功 
-  删除着色器对象 
 
-  
const char* vShaderCode = vertexCode.c_str();
const char* fShaderCode = fragmentCode.c_str();
unsigned int vertex, fragment;
// 1.1创建顶点着色器对象
vertex = glCreateShader(GL_VERTEX_SHADER);
// 1.2附加顶点着色器源码给顶点着色器对象
glShaderSource(vertex, 1, &vShaderCode, NULL);
// 1.3编译顶点着色器对象
glCompileShader(vertex);
// 1.4检测是否编译成功
checkCompileErrors(vertex, "VERTEX");
// 2.1创建片段着色器对象
fragment = glCreateShader(GL_FRAGMENT_SHADER);
// 2.2附加片段着色器源码给片段着色器对象
glShaderSource(fragment, 1, &fShaderCode, NULL);
// 2.3编译片段着色器对象
glCompileShader(fragment);
// 2.4检测是否编译成功
checkCompileErrors(fragment, "FRAGMENT");// 3.1创建着色器程序对象
ID = glCreateProgram();
// 3.2附加着色器对象给着色器程序对象
glAttachShader(ID, vertex);
glAttachShader(ID, fragment);
// 3.3链接着色器程序对象
glLinkProgram(ID);
checkCompileErrors(ID, "PROGRAM");// 可以检查是否成功
// 4.删除着色器对象
glDeleteShader(vertex);
glDeleteShader(fragment);void checkCompileErrors(unsigned int shader, std::string type)
{int success;char infoLog[1024];if (type != "PROGRAM"){glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}else{glGetProgramiv(shader, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(shader, 1024, NULL, infoLog);std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "\n -- --------------------------------------------------- -- " << std::endl;}}
}
绘制流程
- 顶点数组对象 - 创建顶点数组对象
- 绑定顶点数组对象
 
- 顶点缓冲对象 - 创建顶点缓冲对象
- 绑定顶点缓冲对象
- 将顶点数据从CPU拷贝到GPU的顶点缓冲对象中
- 设置顶点数组里的顶点属性指针,解释此顶点缓冲区的布局
 
- 索引缓冲对象 - 创建索引缓冲对象
- 绑定索引缓冲对象,当前绑定顶点数组对象的索引缓冲对象指针会指向当前索引缓冲对象(自己的语言)
- 将索引数据从CPU拷贝到GPU的索引缓冲对象中
 
- 绘制代码 - 使用着色器程序对象
- 绑定顶点数组对象
- 绘制元素
- 解绑顶点数组对象
 
// ..:: 初始化代码 :: ..
// 1. 绑定顶点数组对象
glBindVertexArray(VAO);
// 2. 把我们的CPU的顶点数据复制到GPU顶点缓冲中,供OpenGL使用
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// 3. 复制我们的CPU的索引数组到GPU索引缓冲中,供OpenGL使用
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
// 4. 设定顶点属性指针,来解释顶点缓冲中的顶点属性布局
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);[...]// ..:: 绘制代码(渲染循环中) :: ..
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
相关文章:
 
LearnOpenGL-入门-你好,三角形
本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正 我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject LearnOpenGL中文官网:https://learnopengl-cn.github.io/ 文章目录图形渲染管线基本介绍着色器…...
SOEM 源码解析 ecx_init_redundant
/* Initialise lib in redundant NIC mode* 在冗余网卡模式下初始化lib库* param[in] context context struct* 上下文结构体* param[in] redport pointer to redport, redundant port data* 指向冗余端口的指针ÿ…...
 
网页唤起 APP中Activity的实现原理
疑问的开端大家有没有想过一个问题:在浏览器里打开某个网页,网页上有一个按钮点击可以唤起App。这样的效果是怎么实现的呢?浏览器是一个app;为什么一个app可以调起其他app的页面?说到跨app的页面调用,大家是…...
 
【操作系统】概述
基本特征 1. 并发 并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。 并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。 操作系统通过引入进程和线程,使得程序能够并发运行 2. 共享 共享…...
 
Flume三种组件的选择对比
文章目录1.source2.channel3.sink1.source Source: 数据源:通过source组件可以指定让Flume读取哪里的数据,然后将数据传递给后面的 channel Flume内置支持读取很多种数据源,基于文件、基于目录、基于TCP\UDP端口、基于HTTP、Kafka的 等等、当然了&#x…...
 
响应性基础API
一.什么是proxy和懒代理?什么是proxy?proxy对象是用于定义基本操作的自定义行为(如:属性查找,赋值,枚举,函数调用等等)。什么是懒代理?懒代理:在初始化的时候不会进行全部代理,而是…...
剑指 Offer 25. 合并两个排序的链表
剑指 Offer 25. 合并两个排序的链表 难度:easy\color{Green}{easy}easy 题目描述 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。 示例1: 输入:1->2->4, 1->3->4 输出:1…...
 
顿悟日记(一)
目录2023年1月顿悟日记:2023年2月24日顿悟日记:2023年2月25日顿悟日记:2023年2月26日顿悟日记:顿悟的经历是如此的奇妙,且让人亢奋的事情。 2023年1月顿悟日记: 1.我是面向对象还是面向过程? …...
 
前端卷算法系列(二)
前端卷算法系列(二) 回文数 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。 回文数是指正序(从左向右)和倒序(从右向左)读都是一样…...
 
网络应用之HTTP响应报文
HTTP响应报文学习目标能够知道HTTP响应报文的结构1. HTTP响应报文分析HTTP 响应报文效果图:响应报文说明:--- 响应行/状态行 --- HTTP/1.1 200 OK # HTTP协议版本 状态码 状态描述 --- 响应头 --- Server: Tengine # 服务器名称 Content-Type: text/html; charsetUTF-8 # 内容类…...
常见的CSS技巧
1.禁止长按图片弹出菜单 img {-webkit-touch-callout: none; // 主要用于禁止长按菜单。主针对webkit内核的浏览器; } /*或者 user-select , 是css3的新属性,用于设置用户是否能够选中文本*/ .img {-webkit-user-select: none;-khtml-user-select: none…...
 
算法进阶-动态规划
经典例题 大家肯定想用递归做 思路大概就是这样 递归到最后一行就是对应的D(i,j) 然后往上推 但是这样会超时,因为存在大量的重复计算 比如调用第一行MasSum(7)需要调用MaxSum(3)和MaxSum(8) 但是调用第二行MaxSum(3)还要调用3行的MaxSum(8)和3行的MaxSum(1) 第二行…...
python的读写操作
一、使用open函数,可以打开一个已经存在的文件,或着创建一个新文件 语法如下: open(name, mode, encoding) name: 要打开的目标文件的字符串(可以包含文件所在的具体路径) mode: 打开文件模式:只读(r)、写入(w)、追加(a)等 e…...
 
Mybatis中添加、查询、修改、删除
在Mybatis中添加数据的操作 编写相对应的SQL语句,并完成相关数据的对应关系 编写测试用例 需要提交事务 sqlSession commit() 这里需要注意的是mybatis是默认的是手动提交事务,如果不写的话会进行回滚,添加操作就不会被执行 或者在 如果…...
 
C++---线性dp---传纸条(每日一道算法2023.2.26)
注意事项: 本题dp思路与 “线性dp–方格取数” 一致,下方思路仅证明为什么使用方格取数的思路是正确的。 题目: 小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。 一次素质拓展活动中,班上同学安排坐成…...
浅谈 C/C++ 的输入输出
更好的阅读体验\huge{\color{red}{更好的阅读体验}}更好的阅读体验 文章目录0. 叠甲,过1. 谈谈输入输出缓冲区1.1 基本概念输入输出流标准输入输出流文件输入输出流1.2 输入输出缓冲区什么是输入输出缓冲区?为什么要设置输入输出缓冲区?C/C 的…...
 
【计算机三级网络技术】 第二篇 中小型系统总体规划与设计
文章目录一、基于网络的信息系统基本结构二、划分网络系统组建工程阶段三、网络需求调研与系统设计原则四、网络用户调查与网络工程需求分析1.网络用户调查2.网络节点的地理位置分布3.应用概要分析4.网络需求详细分析五、网络总体设计基本方法1.网络工程建设总体目标与设计原则…...
 
Boosting Crowd Counting via Multifaceted Attention之人群密度估计实践
这周闲来无事,看到一篇前不久刚发表的文章,是做密集人群密度估计的,这块我之前虽然也做过,但是主要是基于检测的方式实现的,这里提出来的方法还是比较有意思的,就拿来实践一下。论文在这里,感兴…...
 
python之面向对象编程
1、面向对象介绍: 世界万物,皆可分类 世界万物,皆为对象 只要是对象,就肯定属于某种类 只要是对象,就肯定有属性 2、 面向对象的几个特性: class类: 一个类即对一类拥有相同属性的对象的…...
常见前端基础面试题(HTML,CSS,JS)(七)
同源策略 浏览器有一个重要的安全策略,称之为同源策略 其中,协议、端口号、域名必须一致,,称之为同源,两个源不同,称之为跨源或跨域 同源策略是指,若页面的源和页面运行过程中加载的源不一致…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
 
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
 
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
 
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
 
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
 
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
 
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
 
android13 app的触摸问题定位分析流程
一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
 
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
