OpenGL学习

news2024/12/22 0:09:18

1.1,状态机-上下文-对象

GPU渲染流程 

OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。

状态机:变量(描述该如何操作)的大集合

OpenGL的状态通常被称为OpenGL上下文(Context)

对象(Object):类似一个结构体 

在OpenGL中一个对象是指一些选项的集合,它代表OpenGL状态的一个子集

 

 1.2,创建一个窗口

 1.2.1 GLFW-GLAD

GLFW解决操作系统层面的不同

GLAD使代码可以可以用于不同的OpenGL驱动

 注意头文件顺序,glad的include中包含了glfw所需的gl.h文件

测试代码:

#include <iostream>

#include <glad/glad.h>
#include <GLFW/glfw3.h>

//用户调整窗口大小之后的回调
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

//实现输入控制
void processInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//检查用户是否按下了返回键(Esc)
	{
		glfwSetWindowShouldClose(window, true);
	}
}
int main()
{
	glfwInit();	//初始化GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号(Major)
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号(Minor)
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式(Core-profile)

	GLFWwindow* window = glfwCreateWindow(800, 600, "learnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window!\n" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);//创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
	
	//初始化glad
	//glad:加载所有OpenGL的函数指针
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	//告诉OpenGL渲染窗口的大小
	glViewport(0, 0, 800, 600);

	//设置窗口大小变化之后的回调
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	//渲染循环
	//在窗口结束之前,不断绘制图像,并且能够接收用户的输入
	while (!glfwWindowShouldClose(window))//检查gldw是否被要求退出
	{
		/*
		双缓冲(Double Buffer)
		应用程序使用单缓冲绘图时可能会存在图像闪烁的问题。 这是因为生成的图像不是一下子被绘制出来的,
		而是按照从左到右,由上而下逐像素地绘制而成的。最终图像不是在瞬间显示给用户,而是通过一步一步
		生成的,这会导致渲染的结果很不真实。为了规避这些问题,我们应用双缓冲渲染窗口应用程序。前缓冲
		保存着最终输出的图像,它会在屏幕上显示;而所有的的渲染指令都会在后缓冲上绘制。当所有的渲染指
		令执行完毕后,我们交换(Swap)前缓冲和后缓冲,这样图像就立即呈显出来,之前提到的不真实感就消除了。
		前缓冲区:屏幕上显示的图片
		后缓冲区:正在渲染的图片
		*/
		/*
		当调用glClear函数,清除颜色缓冲之后,整个颜色缓冲都会被填充为glClearColor里所设置的颜色。
		*/
		// 输入
		processInput(window);		//检查是否键盘输入

		//渲染指令
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);//设置状态
		glClear(GL_COLOR_BUFFER_BIT);//使用状态

		// 检查并调用事件,交换缓冲
		glfwSwapBuffers(window);	//交换颜色缓冲(他是一个存储glfw窗口每一个像素颜色值的大缓冲),他在这一迭代中被用来绘制,并且将作为输出显示在屏幕上
		glfwPollEvents();					//检查有没有触发什么事件,并且调用对应的回调
	}

	glfwTerminate();
	return 0;
}

 2.1,渲染管线-VBO-VAO

在学习此节之前,建议将这三个单词先记下来:

  • 顶点数组对象:Vertex Array Object,VAO
  • 顶点缓冲对象:Vertex Buffer Object,VBO
  • 元素缓冲对象:Element Buffer Object,EBO 或 索引缓冲对象 Index Buffer Object,IBO

 图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)

图形渲染管线可以被划分为两个主要部分:

第一部分把你的3D坐标转换为2D坐标,

第二部分是把2D坐标转变为实际的有颜色的像素。

渲染和shader

渲染由一套渲染管线来管理
shader用来设置一些点线面的位置信息,以及映射图片到纹理这些逻辑,从而拿到图像数据,然后如何处理图像,也可以通过shader来处理。

图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做着色器(Shader)。

图形渲染管线的各个阶段的作用:

1,顶点着色器(Vertex Shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。

2,图元装配(Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状;本节例子中是一个三角形。

3,图元装配阶段的输出会传递给几何着色器(Geometry Shader)。几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。

4,几何着色器的输出会被传入光栅化阶段(Rasterization Stage),这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。

OpenGL中的一个片段是OpenGL渲染一个像素所需的所有数据。

5,片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。

6,在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做Alpha测试和混合(Blending)阶段。这个阶段检测片段的对应的深度(和模板(Stencil))值(后面会讲),用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。

 什么是VAO,VBO?

VBO用于存放数据,VAO用于描述数据的属性(如何将数据取出来)

 

我们的顶点缓冲数据会被解析为下面这样子:

  • 位置数据被储存为32位(4字节)浮点值。
  • 每个位置包含3个这样的值。
  • 在这3个值之间没有空隙(或其他值)。这几个值在数组中紧密排列(Tightly Packed)。
  • 数据中第一个值在缓冲开始的位置。

元素缓冲对象(Element Buffer Object,EBO),也叫索引缓冲对象(Index Buffer Object,IBO)。

float vertices[] = {
    // 第一个三角形
    0.5f, 0.5f, 0.0f,   // 右上角
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, 0.5f, 0.0f,  // 左上角
    // 第二个三角形
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, -0.5f, 0.0f, // 左下角
    -0.5f, 0.5f, 0.0f   // 左上角
};

就可以转化成:

float vertices[] = {
    0.5f, 0.5f, 0.0f,   // 右上角
    0.5f, -0.5f, 0.0f,  // 右下角
    -0.5f, -0.5f, 0.0f, // 左下角
    -0.5f, 0.5f, 0.0f   // 左上角
};

unsigned int indices[] = {
    // 注意索引从0开始! 
    // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
    // 这样可以由下标代表顶点组合成矩形

    0, 1, 3, // 第一个三角形
    1, 2, 3  // 第二个三角形
};

着色器程序

着色器程序对象(Shader Program Object)是多个着色器合并之后并最终链接完成的版本。如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。

函数理解:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

  • 第一个参数指定我们要配置的顶点属性。还记得我们在顶点着色器中使用layout(location = 0)定义了position顶点属性的位置值(Location)吗?它可以把顶点属性的位置值设置为0。因为我们希望把数据传递到这一个顶点属性中,所以这里我们传入0
  • 第二个参数指定顶点属性的大小。顶点属性是一个vec3,它由3个值组成,所以大小是3。
  • 每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取则是通过在调用glVertexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。由于在调用glVertexAttribPointer之前绑定的是先前定义的VBO对象,顶点属性0现在会链接到它的顶点数据。

 

代码:画一个矩形

// sample_1_1_HelloWindow.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>

#include <glad/glad.h>
#include <GLFW/glfw3.h>


//顶点着色器
const char* vertexShaderSource ="#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";

//片段着色器
const char * fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0";

//用户调整窗口大小之后的回调
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	glViewport(0, 0, width, height);
}

//实现输入控制
void processInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//检查用户是否按下了返回键(Esc)
	{
		glfwSetWindowShouldClose(window, true);
	}
}
int main()
{
	glfwInit();	//初始化GLFW
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号(Major)
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//次版本号(Minor)
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//核心模式(Core-profile)

	GLFWwindow* window = glfwCreateWindow(800, 600, "learnOpenGL", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window!\n" << std::endl;
		glfwTerminate();
		return -1;
	}
	glfwMakeContextCurrent(window);//创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了
	
	//glad:加载所有OpenGL的函数指针
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	//告诉OpenGL渲染窗口的大小
	glViewport(0, 0, 800, 600);

	//设置窗口大小变化之后的回调
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

	/************************************************************************/
	/*画一个三角形                                                                      */
	//顶点着色器创建
	//顶点着色器附加源码,并且运行时动态编译它的源代码
	unsigned int vertexShader=glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
	glCompileShader(vertexShader);
	int success = -1;
	char infoLog[512];
	glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILEED\n" << infoLog << std::endl;
	}

	//片段着色器
	unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
	glCompileShader(fragmentShader);
	glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
	if (!success)
	{
		glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
		std::cout << "ERROR::SHADER::FRAGMEMT::COMPILATION_FAILEED\n" << infoLog << std::endl;
	}

	//着色器程序,链接着色器
	unsigned int shaderProgram = glCreateProgram();
	glAttachShader(shaderProgram, vertexShader);
	glAttachShader(shaderProgram, fragmentShader);
	glLinkProgram(shaderProgram);
	glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
	if (!success)
	{
		glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
		std::cout << "ERROR::PROGRAM::LINK\n" << infoLog << std::endl;
	}
	//把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);

/*
	float vertices[] = {
		 0.5f,  0.5f, 0.0f,  // top right
		 0.5f, -0.5f, 0.0f,  // bottom right
		-0.5f, -0.5f, 0.0f,  // bottom left
		-0.5f,  0.5f, 0.0f   // top left 
	};*/
	float vertices[] = {
	-0.5f, -0.5f, 0.0f,
	 0.5f, -0.5f, 0.0f,
	 0.0f,  0.5f, 0.0f
	};
	unsigned int indices[] = {  // note that we start from 0!
	0, 1, 3,  // first Triangle
	1, 2, 3   // second Triangle
	};
	//使用glGenBuffers函数和一个缓冲ID生成一个VBO对象
	unsigned int VBO = 0;
	unsigned int VAO = 0;
	unsigned int EBO = 0;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);

	//OpenGL有很多缓冲对象类型,顶点缓冲对象的缓冲类型是GL_ARRAY_BUFFER。
	// OpenGL允许我们同时绑定多个缓冲,只要它们是不同的缓冲类型。我们可以使用
	// glBindBuffer函数把新创建的缓冲绑定到GL_ARRAY_BUFFER目标上
	//任何对GL_ARRAY_BUFFER的操作,都是对VBO的操作
	//把之前定义的顶点数据复制到缓冲的内存
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices), indices, GL_STATIC_DRAW);

	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
	/*画一个三角形 end                                                                     */
	/************************************************************************/
	//渲染循环
	//在窗口结束之前,不断绘制图像,并且能够接收用户的输入
	while (!glfwWindowShouldClose(window))
	{
		// 输入
		processInput(window);	

		//渲染指令
		//激活,在glUseProgram函数调用之后,每个着色器调用和渲染调用都会使用这个程序对象
		//(也就是之前写的着色器)了。
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		glUseProgram(shaderProgram);
		glBindVertexArray(VAO);
		glDrawArrays(GL_TRIANGLES, 0, 3);
		//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

		// 检查并调用事件,交换缓冲
		glfwSwapBuffers(window);	
		glfwPollEvents();					
	}

	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteBuffers(1, &EBO);
	glDeleteProgram(shaderProgram);

	glfwTerminate();
	return 0;
}

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

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

相关文章

异构图注意力网络Heterogeneous Graph Attention Network ( HAN )

文章目录前言一、基础知识1.异构图&#xff08;Heterogeneous Graph&#xff09;2.元路径3.异构图注意力网络二、异构图注意力网络1.结点级别注意力&#xff08;Node-level Attention&#xff09;2.语义级别注意力&#xff08;Semantic-level Attention&#xff09;总结前言 异…

微信商城小程序怎么开发_分享微信商城小程序的搭建

如何搭建好一个微信商城&#xff1f;这三个功能要会用&#xff01; 1.定期低价秒杀&#xff0c;提高商城流量 除了通过私域流量裂变&#xff0c;低价秒杀是为商城引流提高打开率的良好手段。 以不同节日作为嘘头&#xff0c;在情人节、38妇女节、中秋国庆、七夕节等日子&…

前端框架 Nuxtjs Vue3 SEO解决方案 SSR

目录 一、Nuxtjs安装 二、路由规则 三、公共布局 四、Vue3中TypeScript的使用 一、Nuxtjs安装 参考&#xff1a;Installation Get Started with Nuxt安装 - NuxtJS | Nuxt.js 中文网Installation Get Started with Nuxt yarn create nuxt-app <项目名> 项目运行…

GAMES101 作业0 环境配置 PC下简单配置i

前言 GAMES101提供了计算机图形学相关教学知识&#xff0c;闫教授及其团队也为大家准备了相应课程作业。课程作业部署在虚拟机上&#xff0c;以便免去环境部署的麻烦。但对于一些同学来说&#xff0c;还是希望直接在WIN的VS上使用并编码&#xff0c;本文对此进行简单说明。 环…

神经网络和深度学习-后向传播back propagation

后向传播back propagation 首先我们要了解&#xff0c;前向传播&#xff0c;损失函数这些前置知识&#xff0c;下面我们给出一张神经网络的图 反向传播通过导数链式法则计算损失函数对各参数的梯度&#xff0c;并根据梯度进行参数的更新 下面举个简单的例子 我们需要知道x,y,…

Linux C网络通信过程

socket函数、sockaddr_in结构体 和 bind函数 socket函数的作用是创建一个网络文件描述符&#xff0c;程序通过这个文件描述符将数据发送到网络&#xff0c;也通过这个文件描述符从网络中接受数据。观察一下socket函数&#xff1a; int listenfd; listenfd socket(AF_INET, S…

NNDL 作业11:优化算法比较

目录 1. 编程实现图6-1&#xff0c;并观察特征 2. 观察梯度方向 3. 编写代码实现算法&#xff0c;并可视化轨迹 4. 分析上图&#xff0c;说明原理&#xff08;选做&#xff09; 5. 总结SGD、Momentum、AdaGrad、Adam的优缺点&#xff08;选做&#xff09; 6. Adam这么好&…

Python威布尔分布

文章目录威布尔分布及其性质在Python中生成威布尔分布的随机数指数分布和拉普拉斯分布的对比威布尔分布及其性质 威布尔分布&#xff0c;即Weibull distribution&#xff0c;又被译为韦伯分布、韦布尔分布等&#xff0c;是仅分布在正半轴的连续分布。 在numpy.random中&#…

python中urllib库的使用

1. 获取目标页面的源码 以获取百度页面源码为例 #使用urllib获取百度首页的源码 import urllib.request#1 定义一个url 作为需要访问的网址 url http://www.baidu.com#2 模拟浏览器向服务器发送请求 response响应 response urllib.request.urlopen(url)#3 获取响应中的页面…

Monkey测试

一、什么是 Monkey 测试 Monkey 测试是通过向系统发送伪随机的用户事件流&#xff08;如按键输入、触摸屏输入、手势输入等&#xff09;&#xff0c;实现对应用程序客户端的稳定性测试&#xff1b;通俗来说&#xff0c;Monkey 测试即“猴子测试”&#xff0c;是指像猴子一样&a…

JVM垃圾回收算法整理

JVM垃圾回收算法整理前言关键概念了解标记–清除算法复制算法标记–整理算法分代收集算法仰天大笑出门去&#xff0c;我辈岂是蓬蒿人前言 大概内容&#xff1a; jvm垃圾回收算法&#xff1a; 1、“标记–清除”算法&#xff1b;首先标记出所有需要被回收的对象&#xff0c;然…

搭建自己的SSR

Vue SSR介绍 是什么 官方文档&#xff1a;https://ssr.vuejs.org/Vue SSR&#xff08;Vue.js Server-Side Rendering&#xff09; 是 Vue.js 官方提供的一个服务端渲染&#xff08;同构应用&#xff09;解 决方案使用它可以构建同构应用还是基于原有的 Vue.js 技术栈 官方文档…

XXL-JOB逻辑自测及执行参数配置踩坑

概述 关于XXL-JOB的使用遇到的问题记录。对XXL-JOB不熟的&#xff0c;可以先参考分布式任务调度平台XXL-JOB深度实战 实战 业务DTO定义如下&#xff1a; Data public class AdAccountDTO {private String accountId;/*** yyyy-MM-dd HH:mm:ss*/private String startCreateT…

ThingBoard源码解析-缓存

配置 TB支持两种缓存&#xff1a;Caffeine和Redis,通过配置cache.type来指定使用哪种缓存。 位于 org.thingsboard.server.cache Caffeine 配置类&#xff1a;CaffeineCacheConfiguration Configuration ConditionalOnProperty(prefix "cache", value "t…

HTML CSS 个人网页设计 WEB前端大作业代码

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

【计算机毕业设计】7.线上花店系统maven源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 随着互联网突飞猛进的发展及其对人们的生活产生至关重要的影响&#xff0c;线上购花&#xff0c;送货到家的购物方式受到了越来越多顾客的接受与喜爱。线上花卉小铺的设计与实现不仅可以带来更广泛的选择与实…

餐饮业如何现业绩突破性增长?

疫情反复无常&#xff0c;餐饮人每天都面临着极大的挑战&#xff1a;无法预测的关店通知、突如其来的禁止堂食命令......餐饮店客流减少&#xff0c;业绩下滑成为不可避免的趋势。 在这种情形下&#xff0c;不少餐饮老板拒绝“躺平”&#xff0c;上演“花式自救”&#xff1a;…

cpu设计和实现(数据预取)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面说过了一条指令经过cpu处理的时候需要经历几个阶段。通过实验&#xff0c;我们发现&#xff0c;哪怕是再简单的ori指令也要经历取指、译码、执…

MyBatis是什么?使用方式?

目录 前言&#xff1a; 一、概念讲述 1.什么是MyBatis&#xff1f; 2.官网网址 二、使用方式 1.pom.xml里面添加依赖包 2.新建统一配置文件&#xff08;俗称数据库连接文件&#xff09; 3.新建立映射文件 &#xff08;俗称数据库表对应xml&#xff09; 4.建立数据库表…

ArcMap中之提取影像数据边界

1、前言 手里有一些经过裁剪的不规则多边形影像数据&#xff08;如图例所示&#xff09;&#xff0c;希望能批量获取该类影像的边界信息&#xff0c;即影像对应的面信息&#xff0c;边界线信息。这里我们提供一种利用镶嵌数据集Footprint图层的方法来获取&#xff0c;面&#…