OpenGL光照之颜色

news2025/2/28 13:46:34

文章目录

  • 创建一个光照场景

 现实世界中有无数种颜色,每一个物体都有它们自己的颜色。我们需要使用(有限的)数值来模拟真实世界中(无限)的颜色,所以并不是所有现实世界中的颜色都可以用数值来表示的。然而我们仍能通过数值来表现出非常多的颜色,甚至你可能都不会注意到与现实的颜色有任何的差异。颜色可以数字化的由红色(Red)、绿色(Green)和蓝色(Blue)三个分量组成,它们通常被缩写为RGB。仅仅用这三个值就可以组合出任意一种颜色。例如,要获取一个珊瑚红(Coral)色的话,我们可以定义这样的一个颜色向量:

glm::vec3 coral(1.0f, 0.5f, 0.31f);

 我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色,而是它所反射的(Reflected)颜色。换句话说,那些不能被物体所吸收(Absorb)的颜色(被拒绝的颜色)就是我们能够感知到的物体的颜色。例如,太阳光能被看见的白光其实是由许多不同的颜色组合而成的(如下图所示)。如果我们将白光照在一个蓝色的玩具上,这个蓝色的玩具会吸收白光中除了蓝色以外的所有子颜色,不被吸收的蓝色光被反射到我们的眼中,让这个玩具看起来是蓝色的。下图显示的是一个珊瑚红的玩具,它以不同强度反射了多个颜色。
在这里插入图片描述
 你可以看到,白色的阳光实际上是所有可见颜色的集合,物体吸收了其中的大部分颜色。它仅反射了代表物体颜色的部分,被反射颜色的组合就是我们所感知到的颜色(此例中为珊瑚红)。
 这些颜色反射的定律被直接地运用在图形领域。当我们在OpenGL中创建一个光源时,我们希望给光源一个颜色。在上一段中我们有一个白色的太阳,所以我们也将光源设置为白色。当我们把光源的颜色与物体的颜色值相乘,所得到的就是这个物体所反射的颜色(也就是我们所感知到的颜色)。让我们再次审视我们的玩具(这一次它还是珊瑚红),看看如何在图形学中计算出它的反射颜色。我们将这两个颜色向量作分量相乘,结果就是最终的颜色向量了:
glm::vec3 lightColor(1.0f, 1.0f, 1.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);
我们可以看到玩具的颜色吸收了白色光源中很大一部分的颜色,但它根据自身的颜色值对红、绿、蓝三个分量都做出了一定的反射。这也表现了现实中颜色的工作原理。由此,我们可以定义物体的颜色为物体从一个光源反射各个颜色分量的大小。现在,如果我们使用绿色的光源又会发生什么呢?

glm::vec3 lightColor(0.0f, 1.0f, 0.0f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);

 可以看到,并没有红色和蓝色的光让我们的玩具来吸收或反射。这个玩具吸收了光线中一半的绿色值,但仍然也反射了一半的绿色值。玩具现在看上去是深绿色(Dark-greenish)的。我们可以看到,如果我们用绿色光源来照射玩具,那么只有绿色分量能被反射和感知到,红色和蓝色都不能被我们所感知到。这样做的结果是,一个珊瑚红的玩具突然变成了深绿色物体。现在我们来看另一个例子,使用深橄榄绿色(Dark olive-green)的光源:

glm::vec3 lightColor(0.33f, 0.42f, 0.18f);
glm::vec3 toyColor(1.0f, 0.5f, 0.31f);
glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

 可以看到,我们可以使用不同的光源颜色来让物体显现出意想不到的颜色。有创意地利用颜色其实并不难。

创建一个光照场景

 我们需要制作一个场景,其中有一个正方体用来替代光源,默认为白光。
main.cpp

#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include <iostream>
#include <cmath> 
#include "../shader.h"
#include "../stb_image.h"
#include "../camera.h"
#include <glm/glm.hpp> 
#include <glm/gtc/matrix_transform.hpp> 
#include <glm/gtc/type_ptr.hpp>


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
};

const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
// camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);


float ratio = 0.5;
void processInput(GLFWwindow* window);
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
float deltaTime = 0.0f; // 距离上一帧的时间间隔 
float lastFrame = 0.0f; // 上一帧发生的时间
bool firstMouse = true;
float yaw = -90.0f;
float pitch = 0.0f;
float lastX = 800.0f / 2.0;
float lastY = 600.0 / 2.0;
float fov = 45.0f;


int main() {
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__ 
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
	GLFWwindow* window = glfwCreateWindow(800, 600, "LearnOpenGL", NULL, NULL);
	if (window == NULL) {
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}
	//GLFW将窗口的上下文设置为当前线程的上下文
	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	//GLAD
	// glad: 加载所有OpenGL函数指针
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
		std::cout << "Failed to initialize GLAD" << std::endl;
		return -1;
	}

	Shader ourShader("shaders/shader.vs","shaders/shader.fs");
	Shader lightShader("shaders/shader.vs", "shaders/lightShader.fs");

	//创建VBO和VAO对象,并赋予ID
	unsigned int VBO, VAO;
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	//绑定VBO和VAO对象
	glBindVertexArray(VAO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	//为当前绑定到target的缓冲区对象创建一个新的数据存储。
	//如果data不是NULL,则使用来自此指针的数据初始化数据存储
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//告知Shader如何解析缓冲里的属性值
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
	//开启VAO管理的第一个属性值
	glEnableVertexAttribArray(0);

	//告知Shader如何解析缓冲里的属性值
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
	//开启VAO管理的第一个属性值
	glEnableVertexAttribArray(1);

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);

	stbi_set_flip_vertically_on_load(true);
	unsigned int texture, texture1;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);

	// 加载并生成纹理 
	int width, height, nrChannels;
	unsigned char* data = stbi_load("../pics/container.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);

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

		//float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
		//glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	}
	else 
	{
			std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	glGenTextures(1, &texture1);
	glBindTexture(GL_TEXTURE_2D, texture1);
	data = stbi_load("../pics/awesomeface.png", &width, &height, &nrChannels, 0);
	if (data)
	{
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
		glGenerateMipmap(GL_TEXTURE_2D);

		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_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	}
	else
	{
		std::cout << "Failed to load texture" << std::endl;
	}
	stbi_image_free(data);

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, texture1);
	glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

	ourShader.use();
	ourShader.setInt("texture1", 0);
	ourShader.setInt("texture2", 1);

	// 渲染循环
	while (!glfwWindowShouldClose(window)) {
		processInput(window);

		float currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;

		//glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //状态设置
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //状态使用


		ourShader.use();
		glm::mat4 model = glm::mat4(1.0f);
		// pass projection matrix to shader (note that in this case it could change every frame)
		glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
		ourShader.setMat4("projection", projection);

		// camera/view transformation
		glm::mat4 view = camera.GetViewMatrix();
		ourShader.setMat4("view", view);
		ourShader.setFloat("ratio", ratio);

		glEnable(GL_DEPTH_TEST);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		// glfw: 交换缓冲区和轮询IO事件(按键按下/释放、鼠标移动等)

		glBindVertexArray(VAO);

		ourShader.setMat4("model", model);
		ourShader.setVec3("objectColor", 1.0f, 0.5f, 0.31f);
		ourShader.setVec3("lightColor", 1.0f, 1.0f, 1.0f);
		glDrawArrays(GL_TRIANGLES, 0, 36);


		lightShader.use();
		model = glm::mat4(1.0f);
		model = glm::translate(model, lightPos);
		model = glm::scale(model, glm::vec3(0.2f));

		lightShader.setMat4("model", model);
		lightShader.setMat4("projection", projection);
		lightShader.setMat4("view", view);
		glDrawArrays(GL_TRIANGLES, 0, 36);

		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	// glfw: 回收前面分配的GLFW先关资源. 
	glfwTerminate();
	glDeleteVertexArrays(1, &VAO);
	glDeleteBuffers(1, &VBO);
	glDeleteProgram(ourShader.ID);

	return 0;
}

void processInput(GLFWwindow* window) 
{
	float cameraSpeed = 2.5f * deltaTime;
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(RIGHT, deltaTime);

}

void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
	glViewport(0, 0, width, height);
}

void mouse_callback(GLFWwindow* window, double xposIn, double yposIn)
{
	float xpos = static_cast<float>(xposIn);
	float ypos = static_cast<float>(yposIn);

	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}

	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top

	lastX = xpos;
	lastY = ypos;

	camera.ProcessMouseMovement(xoffset, yoffset);

}

void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
	fov -= (float)yoffset;
	if (fov < 1.0f) fov = 1.0f;
	if (fov > 75.0f) fov = 75.0f;
}

lightShader.fs

#version 330 core 
out vec4 FragColor; 

void main() { 
	FragColor = vec4(1.0); 
}

shader.fs

#version 330 core 
out vec4 FragColor; 
uniform vec3 objectColor; 
uniform vec3 lightColor; 
void main() { 
	FragColor = vec4(lightColor * objectColor, 1.0); 
}

shader.vs

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;
uniform float offsetX;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos.x,aPos.y,aPos.z, 1.0);
    TexCoord = aTexCoord;
}

参考地址

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

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

相关文章

物联网Lora模块从入门到精通(四)对某些端口的初始化

一、前言 由于程序设计开发具有的不确定性&#xff0c;我们常常需要初始化某些特定的引脚&#xff0c;并读取引脚电平状态或向引脚输出高低电平。 二、代码实现 快速找到端口的初始化语句&#xff1a; 首先&#xff0c;找到board.c文件&#xff0c;在下图的位置&#xff0c;我…

【算法系列专栏介绍】

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级论点蓝色&#xff1a;用来标记二级论点 决定开一个算法专栏&#xff0c;希望能帮助大…

什么是域控服务器?域控服务器功能?部署域控需要考虑因素?域控组策略功能?

一、什么是域控制服务器&#xff1f; 域控制器&#xff08;Domain Controller&#xff09;是在Windows Server操作系统上运行的一个服务角色&#xff0c;它用于管理和控制一个或多个计算机的安全策略、用户身份验证和授权等任务。域控制器通常是用于企业网络中的主要身份验证和…

性能测试从0到1实战,超详细性能测试计划编写汇总...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、测试背景 首先…

yt-dlp 使用教程

参考&#xff1a;yt-dlp 使用教程 下载yt-dlp.exe&#xff0c;地址&#xff1a;Releases yt-dlp/yt-dlp GitHub windows下载.exe版本&#xff0c;放到指定路径下&#xff0c;我的是C:\Users\bellychang\Downloads 查看视频所有分辨率 yt-dlp.exe --proxy socks5://127.0.0.…

github action 基于个人项目实践

前言: DevOps 和 Jenkins 作为一名开发&#xff0c;虽然也没有经常听到 Devops &#xff08;研发和运维一体化&#xff09;这个概念&#xff0c;但日常工作中已经无处不在地用着 DevOps 工具。自研也好&#xff0c;基于开源项目改造也好&#xff0c;互联网公司基本都会有自已的…

强化学习Q-learning实践

1. 引言 前篇文章介绍了强化学习系统红的基本概念和重要组成部分&#xff0c;并解释了Q-learning算法相关的理论知识。本文的目标是在Python3中实现该算法&#xff0c;并将其应用于实际的实验中。 闲话少说&#xff0c;我们直接开始吧&#xff01; 2. Taxi-v3 Env 为了使本文…

一文讲完Java常用设计模式(23种)

介绍 设计模式的起源可以追溯到20世纪80年代&#xff0c;当时面向对象编程开始流行。在这个时期&#xff0c;一些软件开发者开始注意到他们在不同的项目中遇到了相同的问题&#xff0c;并且他们开始寻找可重用的解决方案。这些解决方案被称为设计模式。最早提出设计模式的人是…

centos7的docker安装与简单介绍

docker的基本组成&#xff08;三要素&#xff09; 镜像容器仓库 理解&#xff1a;镜像可以理解成一个类&#xff0c;容器就是用这个类new出来的对象&#xff0c;仓库就是放镜像文件的。docker本身是容器运行载体或管理引擎 安装 安装gcc yum -y install gcc安装需要的软件…

Vcpkg介绍及使用

Vcpkg用于在Windows、Linux、Mac上管理C和C库&#xff0c;极大简化了第三方库的安装&#xff0c;它由微软开源&#xff0c;源码地址&#xff1a;https://github.com/Microsoft/vcpkg&#xff0c;最新发布版本为2023.04.15 Release&#xff0c;它的license为MIT。 在windows上安…

[解决方案]springboot怎么接受encode后的参数(参数通过=拼接)

springboot怎么接受encode后的参数(拼接& springboot怎么接受encode后的参数(拼接&)问题出现原因发送encode后的值在postman里面的情况这个时候该如何接受呢&#xff08;encode后的值接受&#xff09;controller层的代码用到的工具类CRequest springboot怎么接受encode…

软考A计划-系统架构师-官方考试指定教程-(14/15)

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

数组删除元素使用remove最优的方法

Array.prototype.remove function(from, to) { var rest this.slice((to || from) 1 || this.length); this.length from < 0 ? this.length from : from; return this.push.apply(this, rest); };

Anaconda安装及入门教程(Windows、Ubuntu)

文章目录 安装Anaconda3UbuntuWindows 使用换源设置不自动启用conda环境显示环境创建环境激活环境查找某个包的版本安装某个版本的包 虚拟环境中安装包删除虚拟环境删除特定的包复制环境设置代理UbuntuWindows 使用 conda-pack 离线导入、导出环境安装conda-pack导出导入 安装A…

简单使用nacos、openFeign和Sentinel(建议看源码和截图一起看)

1、Nacos 1、下载nacos&#xff0c;可以从结尾获取压缩包和源码 2、下方图例是两个服务程序注册成功到注册中心&#xff0c;并且配置从配置中心拉取&#xff0c;成功访问数据库 3、nacos中配置项里的内容 spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driv…

在linux上做移动开发必须知道这五个

导读随着越来越多的人依靠手机进行各种业务&#xff0c;移动应用开发的重要性也在不断增加。虽然他们与桌面应用程序有很多相似之处&#xff0c;但移动应用程序本身也具有一系列挑战和特殊性。因此&#xff0c;希望在当前市场找到有利就业的程序员将需要利用和发展当前需求的技…

第五章 结构化设计

结构化设计的概念 1. 设计的定义 一种软件开发活动&#xff0c;定义实现需求规约所需的软件结构。 结构化设计分为&#xff1a; (1)总体设计&#xff1a;确定系统的整体模块结构&#xff0c;即系统实现所需要的软件模块以及这些模块之间的调用关系。 (2)详细设计&#xff1a;…

从Referer到XMLHttpRequest:探究Web安全中的重要知识点

目录 Referer 概念 Referrer-policy&#xff08;可以一定程度上防御CSRF攻击&#xff09; 同源 iframe sandbox(沙箱): cookie的原理&#xff1a; 如何设置Referrer&#xff1f; 盗链 盗链的工作原理 三种情况下可以引用图片&#xff1a; XMLHTTPRequest AJAX&…

初出茅庐的小李博客之STM32F103实现CAN通信

CAN通信基础知识 参考上一篇博客 https://editor.csdn.net/md/?articleId131026450 原理图 转换芯片是 TJA1050 代码实现思路 发送思路&#xff1a;定时发送 按键测试发送 接收思路&#xff1a;中断接收 CAN代码实现 第一步 定义了两个全局变量TxMessage和RxMessage&am…

Unity如何实现Microphone判断录入音频的频率是低音还是高音

前言 Unity中使用Microphone可以通过麦克风录制AudioClip音频,我们可以通过它实现录音功能,然后可以通过录入的音频数据对音频进行分析,比如音量大小,频率高低,等等。 我们今天就来分析一下音频的高音低音。 如何判断高音低音 科普:一般人们习惯将音响划分一定的频段…