当前位置: 首页 > news >正文

tinyrenderer笔记(Shader)

  • tinyrenderer
  • 个人代码仓库:tinyrenderer个人练习代码

前言

现在我们将所有的渲染代码都放在了 main.cpp 中,然而在 OpenGL 渲染管线中,渲染的核心逻辑是位于 shader 中的,下面是 OpenGL 的渲染管线:

image.png

蓝色是我们可以自定义的 shader,现在让我们来模仿顶点着色器与片段着色器的工作流程(忽略几何着色器)。

UniformVar

在 glsl 中,我们可以定义一些 uniform 变量,在着色器之外设置值,并在着色器之内访问。这些变量在所有着色器都可以访问。为了模拟这种变量访问模式,我定义了一个 UniformVar 结构体,内部通过 unordered_map std::string 索引实际的变量值:

template <typename T>
struct UniformVar
{std::unordered_map<std::string, T> var;void add_var(const std::string& name){var[name] = T();}T get_var(const std::string& name){if (var.find(name) == var.end()){std::cout << "[glProgram] UniformVar get_var " << name << " not found" << std::endl;return T();}return var[name];}void set_var(const std::string& name, const T& value){if (var.find(name) == var.end()){std::cout << "[glProgram] UniformVar set_var " << name << " not found" << std::endl;return;}var[name] = value;}
};

这是一个模板结构体,以满足不同类型的变量定义。

VaryingVar

在 glsl 中,顶点着色器定义的输出变量经过插值之后会输出到片段着色器中。插值是对一个图元考虑的,在我的代码中只考虑三角形图元。我们模拟 varying 变量时,需要一种辅助结构体来帮助我们插值,它会存储三角形三个顶点的属性。

template <typename T>
struct VaryingVar
{std::unordered_map<std::string, std::array<T, 3>> var;void add_var(const std::string& name){var[name] = std::array<T, 3>();}T get_var(const std::string& name, size_t index){if (var.find(name) == var.end()){std::cout << "[glProgram] VaryingVar get_var " << name << " not found" << std::endl;return T();}if (index > 3){std::cout << "[glProgram] VaryingVar get_var " << name << " index out of range" << std::endl;return T();}return var[name][index];}std::array<T, 3> get_var(const std::string& name){if (var.find(name) == var.end()){std::cout << "[glProgram] VaryingVar get_var " << name << " not found" << std::endl;return T();}return var[name];}void set_var(const std::string& name,const T& value, size_t index){if (var.find(name) == var.end()){std::cout << "[glProgram] VaryingVar set_var " << name << " not found" << std::endl;return;}if (index > 3){std::cout << "[glProgram] VaryingVar set_var " << name << " index out of range" << std::endl;return;}var[name][index] = value;}
};

glProgram

我们需要一个能够管理顶点着色器与片段着色器的类,它会使用自定义着色器的功能,并在内部做一些光栅化操作,类比于 OpenGL 的 Program,我们自定义一个 glProgram 类。

glProgram 会管理所有 uniform 与 varying 变量,并提供外部接口来访问不同类型的变量:

UniformVar<glm::mat4>   uniform_mat4;
UniformVar<float>       uniform_float;
·········
VaryingVar<Vec4f>       varying_vec4f;
VaryingVar<Vec3f>       varying_vec3f;
·········

访问通过模板函数来确定访问变量的类型:

template<typename T>
T GetUniformVar(const std::string& name)
{if constexpr (std::is_same_v<T, glm::mat4>){return uniform_mat4.get_var(name);}·········
}template<typename T>
void SetUniformVar(const std::string& name, const T& value)
{if constexpr (std::is_same_v<T, glm::mat4>){uniform_mat4.set_var(name, value);}·········
}template<typename T>
void AddUniformVar(const std::string& name)
{if constexpr (std::is_same_v<T, glm::mat4>){uniform_mat4.add_var(name);}·········
}template<typename T>
T GetVaryingVar(const std::string& name, size_t index)
{if constexpr (std::is_same_v<T, Vec4f>){return varying_vec4f.get_var(name, index);}·········
}template<typename T>
void SetVaryingVar(const std::string& name, const T& value, size_t index)
{if constexpr (std::is_same_v<T, Vec4f>){varying_vec4f.set_var(name, value, index);}·········
}template<typename T>
void AddVaryingVar(const std::string& name)
{if constexpr (std::is_same_v<T, Vec4f>){varying_vec4f.add_var(name);}·········
}

glProgram 会管理顶点着色器与片段着色器,内部会存储它们的指针:

VertexShader*   m_vertexShader;
FragmentShader* m_fragmentShader;
// 注册顶点着色器
void glProgram::RegisterVertexShader(VertexShader* vertexShader)
{m_vertexShader = vertexShader;vertexShader->SetProgram(this);vertexShader->ConfirmVaryingVar();
}
// 注册片段着色器
void glProgram::RegisterFragmentShader(FragmentShader* fragmentShader)
{m_fragmentShader = fragmentShader;fragmentShader->SetProgram(this);
}

会提供一个函数来确定视口:

glm::mat4 m_viewport;
void glProgram::SetViewPort(int width, int height)
{m_viewport = glm::viewport(width, height);
}

最重要的是提供一个函数来绘制一个三角形(功能后续讲解):

void Draw(TGAImage& target, float* zbuffer, const std::array<VertexInfoBase*, 3>& vertexInfos);// 绘制一个三角面

IShader

这是最基本的着色器类,它会派生一个顶点着色器与片段着色器。所有着色器可以获得全局变量的值,这个值在着色器之外设置。所以 IShader 会为子类提供一个访问 uniform 变量的方法:

class IShader
{friend class glProgram;
public:void SetProgram(glProgram* program){m_program = program;}
protected:template<typename T>T GetUniformVar(const std::string& name){return m_program->GetUniformVar<T>(name);}glProgram* m_program;
};

SetProgram 会在注册着色器的时候调用,保存 glProgram 的指针用于访问 uniform 或者 varying 变量。

VertexShader

顶点着色器基类,下面是顶点着色器的功能:

The main goal of the vertex shader is to transform the coordinates of the vertices. The secondary goal is to prepare data for the fragment shader.

顶点着色器的主要目标是转换顶点的坐标。次要目标是准备片段着色器所需的数据。

最核心的就是这个函数:

virtual Vec4f vertex(VertexInfoBase* vertexInfo) = 0;

会接受一个 VertexInfoBase 类型的变量,然后返回经过 MVP 变换后的顶点坐标。VertexInfoBase 是什么?在 OpenGL 中,我们可以定义一些缓冲区来为顶点着色器输入一些变量(坐标、法线、纹理坐标),每个顶点的数据不一样。VertexInfoBase 就是我用来模仿为顶点着色器输入变量的方法:

// 顶点所需的信息
struct VertexInfoBase
{size_t index;
};

保存了当前顶点在三角形的 index,对于各种不同的自定义着色器,我们所需的顶点信息不同,可以通过继承 VertexInfoBase 来添加一些属性:

struct PhongVertexInfo : VertexInfoBase
{Vec3f location;Vec2f textureCoord;Vec3f normal;
};

顶点着色器会定义一些 varying 变量,插值后给片段着色器使用:

virtual void ConfirmVaryingVar() = 0;// 定义varying变量template<typename T>
void AddVaryingVar(const std::string& name)
{m_program->AddVaryingVar<T>(name);
}
template<typename T>
void SetVaryingVar(const std::string& name, size_t index, const T& value)
{m_program->SetVaryingVar<T>(name, value, index);
}

FragmentShader

对于片段着色器,它的功能如下:

The main goal of the fragment shader - is to determine the color of the current pixel. Secondary goal - we can discard current pixel by returning true.

片段着色器的主要目标是确定当前像素的颜色。次要目标——我们可以通过返回 true 来丢弃当前像素。

最核心的函数是这个:

virtual bool fragment(Vec3f barycentric_coords, TGAColor& color) = 0;

会接受当前像素坐标对应的重心坐标,用于插值得到当前像素的属性。为什么要这么做?其实是我还没想到简易的架构,可以不输入重心坐标而自动获得插值后的属性,所以需要用一个函数显式获得:

template<typename T>
T GetInterVaryingVar(const std::string& name, const Vec3f& barycentric_coords)
{std::array<T, 3> varyings;T res{};for (int i = 0; i < 3; i++){varyings[i] = m_program->GetVaryingVar<T>(name, i);res = res + varyings[i] * barycentric_coords[i];}return res;
}

Draw

最后来介绍 glProgramDraw 函数,其实这里面的代码就是将之前 triangle 函数与 drawModel 的功能合并。

void Draw(TGAImage& target, float* zbuffer, const std::array<VertexInfoBase*, 3>& vertexInfos);// 绘制一个三角面

首先会调用顶点着色器获得顶点对应的屏幕坐标:

Vec3f viewport_coords[3];
for (int i = 0;i < 3;i++)
{// 顶点着色器返回投影坐标vertexInfos[i]->index = i;Vec4f coord = m_vertexShader->vertex(vertexInfos[i]);// 透视除法coord = coord / coord.w;// 视口变换viewport_coords[i] = m_viewport * coord;
}

然后就是计算三个顶点的包围盒:

// 包围盒范围
Vec2f bboxmin(width - 1, height - 1);
Vec2f bboxmax(0, 0);
bboxmin.x = std::min({ bboxmin.x, viewport_coords[0].x, viewport_coords[1].x, viewport_coords[2].x});
bboxmin.y = std::min({ bboxmin.y, viewport_coords[0].y, viewport_coords[1].y, viewport_coords[2].y });
bboxmax.x = std::max({ bboxmax.x, viewport_coords[0].x, viewport_coords[1].x, viewport_coords[2].x });
bboxmax.y = std::max({ bboxmax.y, viewport_coords[0].y, viewport_coords[1].y, viewport_coords[2].y });

遍历包围盒的每个像素:

for (int x = bboxmin.x; x <= bboxmax.x; x++)
{for (int y = bboxmin.y; y <= bboxmax.y; y++){······}
}

循环内先获得当前像素的重心坐标,然后插值获得深度值:

Vec3f P = Vec3f(x, y, 0);
Vec3f bc_screen = glm::barycentric(viewport_coords[0], viewport_coords[1], viewport_coords[2], P);
if (bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0) continue;
if (x < 0 || x >= width || y < 0 || y >= height)
{continue;
}
// 重心坐标插值
P.z = bc_screen.x * viewport_coords[0].z + bc_screen.y * viewport_coords[1].z + bc_screen.z * viewport_coords[2].z;
if (P.z < 0 || P.z > 1 || zbuffer[x + y * width] < P.z)
{continue;
}

深度测试与裁剪判断通过之后,就会调用片段着色器来获得当前像素的颜色值:

TGAColor color;
bool bDiscard = m_fragmentShader->fragment(bc_screen, color);
if (!bDiscard)
{zbuffer[x + y * width] = P.z;target.set(x, y, color);
}

main

在框架的基础上,来看看我们实现之前的 Phong Shading 需要干些什么。

首先定义每个顶点所需的属性:

struct PhongVertexInfo : VertexInfoBase
{Vec3f location;// 位置Vec2f textureCoord;// 纹理坐标Vec3f normal;// 法线
};

来看看顶点着色器:

class PhongVertexShader : public VertexShader
{
public:// 确定有哪些 Varying 变量virtual void ConfirmVaryingVar() override{AddVaryingVar<Vec2f>("textureCoord");AddVaryingVar<Vec3f>("normal");}virtual Vec4f vertex(VertexInfoBase* vertexInfos) override{// 解码顶点属性实际类型auto* info = static_cast<PhongVertexInfo*>(vertexInfos);// 获得矩阵glm::mat4 modelMatrix = GetUniformVar<glm::mat4>("Model");glm::mat4 viewMatrix = GetUniformVar<glm::mat4>("View");glm::mat4 proMatrix = GetUniformVar<glm::mat4>("Proj");// 设置 Varying 变量SetVaryingVar<Vec2f>("textureCoord", info->index, info->textureCoord);glm::mat3 normalMatrix = modelMatrix.inverse().transpose().to_mat3();Vec3f normal = normalMatrix * info->normal;SetVaryingVar<Vec3f>("normal", info->index, normal);return proMatrix * viewMatrix * modelMatrix * Vec4f(info->location, 1.f);}
};

再看看片段着色器:

class PhongFragmentShader : public FragmentShader
{
public:virtual bool fragment(Vec3f barycentric_coords, TGAColor& color) override{Vec3f light_dir = GetUniformVar<Vec3f>("lightDir");// 光线方向TGAImage* tex = GetUniformVar<TGAImage*>("texture");// 纹理贴图glm::mat4 modelMatrix = GetUniformVar<glm::mat4>("Model");light_dir = modelMatrix * Vec4f(light_dir, 0.f);light_dir.normalize();Vec3f normal = GetInterVaryingVar<Vec3f>("normal", barycentric_coords);// 获得插值后的法线normal.normalize();// 插值之后记得归一化Vec2f textureCoord = GetInterVaryingVar<Vec2f>("textureCoord", barycentric_coords);// 获得插值后的纹理坐标TGAColor diffuse = tex->texture(textureCoord.x, textureCoord.y);// 采样得到纹理颜色float intensity = std::max(0.f, -normal.dot(light_dir));// 计算光照强度color = TGAColor(diffuse.r * intensity, diffuse.g * intensity, diffuse.b * intensity, 255);// 设置颜色return false;// do not discard the pixel}
};

在着色器之外呢?

先注册片段着色器与顶点着色器、设置视口:

phongProgram->RegisterVertexShader(vertexShader);
phongProgram->RegisterFragmentShader(fragmentShader);
phongProgram->SetViewPort(width, height);

确定有哪些 uniform 变量:

phongProgram->AddUniformVar<glm::mat4>("Model");
phongProgram->AddUniformVar<glm::mat4>("View");
phongProgram->AddUniformVar<glm::mat4>("Proj");
phongProgram->AddUniformVar<Vec3f>("lightDir");
phongProgram->AddUniformVar<TGAImage*>("texture");

设置 uniform 变量:

glm::mat4 viewMatrix = glm::lookAt(Vec3f(0, 0, 2), Vec3f(0, 0, 0), Vec3f(0, 1, 0));
glm::mat4 projMatrix = glm::perspective(45, float(width) / height, 0.1f, 100.f);
glm::mat4 modelMatrix;
modelMatrix = glm::translate(modelMatrix, Vec3f(-0.5, 0, 0));
modelMatrix = glm::rotate(modelMatrix, 45, Vec3f(0, 1, 0));
modelMatrix = glm::scale(modelMatrix, Vec3f(0.5, 0.5, 0.5));
phongProgram->SetUniformVar<glm::mat4>("Model", modelMatrix);
phongProgram->SetUniformVar<glm::mat4>("View", viewMatrix);
phongProgram->SetUniformVar<glm::mat4>("Proj", projMatrix);
phongProgram->SetUniformVar<Vec3f>("lightDir", light_dir);
phongProgram->SetUniformVar<TGAImage*>("texture", texture);

遍历模型的每个三角面并绘制:

std::array<VertexInfoBase*, 3> vertexInfos;
for (int i = 0; i < model->nfaces(); i++)
{std::vector<int> face_v = model->face_v(i);std::vector<int> face_vt = model->face_vt(i);std::vector<int> face_vn = model->face_vn(i);for (int j = 0; j < 3; j++){auto* info = new PhongVertexInfo();info->location = Vec4f(model->vert(face_v[j]), 1.f);info->textureCoord = model->vertTexture(face_vt[j]);info->normal = model->vertNormal(face_vn[j]);vertexInfos[j] = info;}phongProgram->Draw(result, zbuffer, vertexInfos);
}

over!来看看效果:

image.png

效果还不错,整个框架是我花了大概一天时间写出来的,还有很多不成熟的地方,大家可以在这上面自己改进改进,代码仓库在文章开头。

本次代码提交记录:

image.png

  1. 提交的代码,片段着色器处 light_dir 忘记乘上模型变换矩阵了
  2. 这个版本的 LookAt 函数存在错误!2025-4-29 16.23 提交修复

参考

  • tinyrenderer

相关文章:

tinyrenderer笔记(Shader)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码 前言 现在我们将所有的渲染代码都放在了 main.cpp 中&#xff0c;然而在 OpenGL 渲染管线中&#xff0c;渲染的核心逻辑是位于 shader 中的&#xff0c;下面是 OpenGL 的渲染管线&#xff1a; 蓝色是我们可以自…...

【奔跑吧!Linux 内核(第二版)】第1章:Linux 系统基础知识

笨叔 陈悦. 奔跑吧 Linux 内核&#xff08;第2版&#xff09; [M]. 北京: 人民邮电出版社, 2020. 文章目录 Linux 系统的发展历史Linux 发行版Red Hat LinuxDebian LinuxSuSE Linux优麒麟 Linux Linux 内核介绍宏内核和微内核Linux 内核概貌 Linux 系统的发展历史 Linux 系统诞…...

​Spring + Shiro 整合的核心要点及详细实现说明

在 Spring 项目中集成 Apache Shiro 可以实现轻量级的安全控制&#xff08;认证、授权、会话管理等&#xff09;。以下是 ​Spring Shiro 整合的核心要点及详细实现说明&#xff1a; 一、Spring 与 Shiro 整合的核心组件 ​组件​​作用​ShiroFilterFactoryBean创建 Shiro 过…...

已经写好论文的AI率降低

视频演示 https://www.bilibili.com/video/BV1v4VpzgEdc 提示词 你是我专门请来的“降维写作助手”&#xff0c;专门干一件事&#xff1a;把 AI 写得太“像 AI”的文字改得更像人写的。我们主要是处理论文、创作类内容&#xff0c;目标就是&#xff1a;不让检测工具一眼识破…...

AI教你学VUE——Deepseek版

一、基础阶段&#xff1a;打好Web开发基础 HTML/CSS基础 学习HTML标签语义化、CSS布局&#xff08;Flex/Grid&#xff09;、响应式设计&#xff08;媒体查询、REM/VW单位&#xff09;。资源推荐&#xff1a; MDN Web文档&#xff08;免费&#xff09;&#xff1a;HTML | CSS实战…...

卷积神经网络基础(五)

6.3 Softmax-with-Loss 层 我们最后介绍输出层的softmax函数&#xff0c;之前我们知道softmax函数会将输入值正规化之后再输出。在手写数字识别的例子中&#xff0c;softmax层的输出如下&#xff1a; 输入图像通过Affi ne层和ReLU层进行转换&#xff0c;10个输入通过Softmax层…...

Go语言——string、数组、切片以及map

一、string、数组、切片代码 package mainimport "fmt"// 定义结构体 type student struct {id intname stringage intscore float32 }func main() {// 使用var声明切片var slice1 []intslice1 append(slice1, 1)slice1 append(slice1, 2)slice1 append(sl…...

线性回归有截距

In [ ]: ∑ i 1 m ( y i − x i T w ) 2 \sum _{i1}^{m}(y_{i}-x_{i}^{T}w)^{2} i1∑m​(yi​−xiT​w)2 w ^ ( X T X ) − 1 X T y \hat {w}(X^{T}X)^{-1}X^{T}y w^(XTX)−1XTy In [ ]: 1 &#xff03;如果有截距&#xff0c;求解时&#xff0c;需要梯度下降法求解w 和b …...

【基础】Python包管理工具uv使用全教程

一、uv简介 uv 是由 Astral&#xff08;前身为 Basis&#xff09;团队开发的 Python 包安装器和解析器&#xff0c;完全使用 Rust 语言编写。与传统 Python 工具不同&#xff0c;uv 将多个工具的功能整合到一个高性能的解决方案中&#xff0c;旨在提供更现代、更高效的 Python…...

事务(transaction)-上

事务概述 食物是一个最小的工作单元。在数据库当中&#xff0c;事务表示一件完整的事儿。一个业务的完成可能需要多条DML语句共同配合才能完成&#xff0c;例如转账业务&#xff0c;需要执行两条DML语句&#xff0c;先更新张三账户的余额&#xff0c;再更新李四账户的余额&…...

Python训练打卡Day17

无监督算法中的聚类 知识点 聚类的指标聚类常见算法&#xff1a;kmeans聚类、dbscan聚类、层次聚类三种算法对应的流程 实际在论文中聚类的策略不一定是针对所有特征&#xff0c;可以针对其中几个可以解释的特征进行聚类&#xff0c;得到聚类后的类别&#xff0c;这样后续进行解…...

【爬虫】码上爬第6题-倚天剑

堆栈入手&#xff1a; 全部复制的话&#xff0c;注意修改一些必要在地方&#xff1a; 通过s函数来获取请求头的加密参数 通过xxxxoooo来获取解密后的数据 js代码关键点&#xff1a; python代码我推荐使用这个网站&#xff1a; Convert curl commands to code 根据生成的代码…...

自定义SpringBoot Starter-笔记

SpringBoot Starter的介绍参考&#xff1a; Spring Boot Starter简介-笔记-CSDN博客。这里介绍如何自定义一个springBoot Starter。 1. 项目结构 创建一个 Maven 项目&#xff0c;结构如下&#xff1a; custom-spring-boot-starter-demo/ ├── custom-hello-jdk/ # jdk模…...

一周学会Pandas2 Python数据处理与分析-Pandas2数据类型转换操作

锋哥原创的Pandas2 Python数据处理与分析 视频教程&#xff1a; 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili Pandas 提供了灵活的方法来处理数据类型转换&#xff0c;以下是常见操作及代码示例&#xff1a; 1. 查看数据类型 …...

Java中常见的问题

1. SSO中的Cookie/Token生成与安全传递 生成Cookie/Token&#xff1a; Cookie&#xff1a;服务器通过Set-Cookie响应头生成&#xff0c;包含用户ID、过期时间等&#xff0c;需设置HttpOnly和Secure属性防止XSS和中间人攻击。Token&#xff08;如JWT&#xff09;&#xff1a;使…...

【JEECG】BasicTable内嵌Table表格错位

功能说明&#xff1a; 解决代码生成后&#xff0c;本地内嵌Table表格样式错位。 优化前&#xff1a; 优化后&#xff1a; 解决方法&#xff1a; 对应的List.vue页面增加css样式调整。 <style lang"less" scoped>//内嵌表格margin边距覆盖:deep(.ant-table-…...

人工智能 计算智能模糊逻辑讲解

引言 在计算智能&#xff08;Computational Intelligence&#xff09;领域&#xff0c;模糊逻辑&#xff08;Fuzzy Logic&#xff09;作为一种处理不确定性与模糊性信息的数学工具&#xff0c;自 1965 年由洛夫特扎德&#xff08;Lotfi Zadeh&#xff09;提出以来&#xff0c;…...

基于SSM实现的健身房系统功能实现一

一、前言介绍&#xff1a; 1.1 项目摘要 随着社会的快速发展和人们健康意识的不断提升&#xff0c;健身行业也在迅速扩展。越来越多的人加入到健身行列&#xff0c;健身房的数量也在不断增加。这种趋势使得健身房的管理变得越来越复杂&#xff0c;传统的手工或部分自动化的管…...

spring详解-循环依赖的解决

Spring循环依赖 重点提示&#xff1a; 本文都快写完了&#xff0c;发现“丈夫” 的英文是husband… 在“②有AOP循环依赖” 改过来了&#xff0c;前面用到的位置太多了就没改。我是说怎么idea的hansband英文下面怎么有波浪线。各位能够理解意思就行&#xff0c;英文拼写不要过…...

【大模型面试每日一题】Day 10:混合精度训练如何加速大模型训练?可能出现什么问题?如何解决?

【大模型面试每日一题】Day 10&#xff1a;混合精度训练如何加速大模型训练&#xff1f;可能出现什么问题&#xff1f;如何解决&#xff1f; &#x1f4cc; 题目重现 &#x1f31f;&#x1f31f; 面试官&#xff1a;混合精度训练如何加速大模型训练&#xff1f;可能出现什么问…...

[学习]RTKLib详解:rtkcmn.c与rtkpos.c

文章目录 Part A、Rrtkcmn.c一、总体功能二、关键API列表三、核心算法实现四、函数功能与参数说明1. uniqnav2. lsq3. filter4. matmul5. satazel6. ionmapf7. geodist8. timeadd9. dgetrf_ / dgetri_&#xff08;LAPACK接口&#xff09; 五、工作流程说明4.1 模块在RTKLib中的…...

cookie/session的关系

什么是cookie&#xff0c;session 我们平时去医院看病时&#xff0c;从进医院那一刻&#xff0c;我们最开始要做的就是挂号&#xff08;需要我们填写表格&#xff0c;记录一些核心信息&#xff0c;医生会把这些信息录入电脑&#xff0c;并给我办一个就诊卡&#xff0c;卡里面只…...

Linux(十四)进程间通信(IPC),管道

一、进程间通信 &#xff08;一&#xff09;系统介绍进程间通信 进程间通信&#xff08;IPC&#xff09;介绍 小编插入的这篇文章详细介绍了进程间通信的一些内容&#xff0c;大家可以一起学习。 &#xff08;二&#xff09;进程间通信的方法 1、管道 2、信号量 3、共享…...

Nmap 工具的详细使用教程

Nmap&#xff08;Network Mapper&#xff09;是一款开源且功能强大的网络扫描和安全审计工具。它被广泛用于网络发现、端口扫描、操作系统检测、服务版本探测以及漏洞扫描等。 官方链接: Nmap 官方网站: https://nmap.org/Nmap 官方文档 (英文): https://nmap.org/book/man.h…...

Vue 自定义指令输入校验过滤

/*** 过滤字符串* param {*} filterCharRule* param {*} newVal* returns*/ function filterCharForValue(filterCharRule, newVal) {if(!filterCharRule || !newVal) returnconst isArray filterCharRule instanceof Arrayconst isRegExp filterCharRule instanceof RegExpi…...

OpenGl实战笔记(2)基于qt5.15.2+mingw64+opengl实现纹理贴图

一、作用原理 1、作用&#xff1a;将一张图片&#xff08;纹理&#xff09;映射到几何体表面&#xff0c;提升视觉真实感&#xff0c;不增加几何复杂度。 2、原理&#xff1a;加载图片为纹理 → 上传到 GPU&#xff1b;为顶点设置纹理坐标&#xff08;如 0~1 范围&#xff09;&…...

tinyrenderer笔记(透视矫正)

tinyrenderer个人代码仓库&#xff1a;tinyrenderer个人练习代码 引言 还要从上一节知识说起&#xff0c;在上一节中我为了调试代码&#xff0c;换了一个很简单的正方形 obj 模型&#xff0c;配上纹理贴图与法线贴图进行渲染&#xff0c;得了下面的结果&#xff1a; what&…...

c++类【发展】

类的静态成员&#xff08;用static声明的成员&#xff09;,在声明之外用例单独的语句进行初始化&#xff0c;初始化时&#xff0c;不再需要用static进行限定。在方法文件中初始化。以防重复。 特殊成员函数 复制构造函数&#xff1a; 当使用一个对象来初始化另一个对象…...

玛格丽特鸡尾酒评鉴,玛格丽特酒的寓意和象征

玛格丽特鸡尾酒会有独特的风味&#xff0c;而且还会有一个比较吸引人的背后故事。在目前的鸡尾酒界就会占据着很重要的地位&#xff0c;不仅是味蕾的盛宴&#xff0c;同样也会拥有深厚的情感。 玛格丽特由龙舌兰酒、柠檬汁和君度橙酒调制而成&#xff0c;将三者巧妙地结合在一起…...

关于Java多态简单讲解

面向对象程序设计有三大特征&#xff0c;分别是封装&#xff0c;继承和多态。 这三大特性相辅相成&#xff0c;可以使程序员更容易用编程语言描述现实对象。 其中多态 多态是方法的多态&#xff0c;是通过子类通过对父类的重写&#xff0c;实现不同子类对同一方法有不同的实现…...

SecureCrt设置显示区域横列数

1. Logical rows //逻辑行调显示区域高度的 一般超过50就全屏了 2. Logical columns //逻辑列调显示区域宽度的 3. Scrollback buffer //缓冲区大小...

【PhysUnits】1 SI Prefixes 实现解析(prefix.rs)

一、源码 // prefix.rs //! SI Prefixes (国际单位制词头) //! //! 提供所有标准SI词头用于单位转换&#xff0c;仅处理10的幂次 //! //! Provides all standard SI prefixes for unit conversion, handling only powers of 10.use typenum::{Z0, P1, P2, P3, P6, P9, P12, …...

【Python】--实现多进程

import multiprocessing import time # 1.定义好函数 # codeing def coding():for i in range(10):print(f正在编写第{i}行代码)time.sleep(0.2)# music def music():for i in range(10):print(f正在听第{i}首歌曲)time.sleep(0.2)单任务 # 单任务--时间为4s多 if __name__ _…...

计算机视觉与深度学习 | 基于数字图像处理的裂缝检测与识别系统(matlab代码)

🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅🍅 基于数字图像处理的裂缝检测与识别系统 🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦🥦**系统架构设计****1. 图像预处理**目标:消除噪声+增强裂缝特征**2. 图像分割**目标:提取裂缝区域**3. 特征…...

嵌入式MCU语音识别算法及实现方案

在嵌入式MCU&#xff08;微控制器单元&#xff09;中实现语音识别&#xff0c;由于资源限制&#xff08;如处理能力、内存、功耗等&#xff09;&#xff0c;通常需要轻量级算法和优化技术。以下是常见的语音识别算法及实现方案&#xff1a; 一、传统语音识别算法 动态时间规整&…...

【C++核心技术深度解析:从继承多态到STL容器 】

一、C继承机制&#xff1a;代码复用与层次设计 1. 继承基础概念 什么是继承&#xff1f; 继承是面向对象编程的核心机制&#xff0c;通过class Derived : public Base让子类&#xff08;派生类&#xff09;复用父类&#xff08;基类&#xff09;的属性和方法&#xff0c;同时…...

【C/C++】new关键字解析

&#x1f4d8; C 中 new 关键字详解笔记 &#x1f539; 什么是 new&#xff1f; new 是 C 中用于动态内存分配的关键字&#xff0c;它在堆内存中为对象或变量分配空间&#xff0c;并返回对应类型的指针。 与 C 语言中的 malloc 相比&#xff0c;new 更安全、更方便&#xff…...

C++高性能内存池

目录 1. 项目介绍 1. 这个项目做的是什么? 2. 该项目要求的知识储备 2. 什么是内存池 1. 池化技术 2. 内存池 3. 内存池主要解决的问题 4.malloc 3. 先设计一个定长的内存池 4.高并发内存池 -- 整体框架设计 5. 高并发内存池 -- thread cache 6. 高并发内存池 -- …...

chili3d调试笔记12 deepwiki viewport

xiangechen/chili3d | DeepWiki viewport阅读 &#x1f9e0;deep 我要把模型投影成dxf导出有什么办法 引用lookat 截图是如何实现的 明天接着搞 ----------------------------------------------------------------...

前端取经路——JavaScript修炼:悟空的九大心法

大家好&#xff0c;我是老十三&#xff0c;一名前端开发工程师。JavaScript如同孙悟空的七十二变&#xff0c;变化多端却又充满威力。本篇文章我将带你攻克JS中最令人头疼的九大难题&#xff0c;从闭包陷阱到原型链继承&#xff0c;从异步编程到性能优化。每个难题都配有实战代…...

从零实战:在Xilinx Zynq PS端移植VxWorks 6.9系统

一、环境准备与工具链搭建 1.1 硬件配置清单 开发板: Zynq-7000系列(推荐ZedBoard或ZCU102)调试工具: USB-JTAG调试器(如Xilinx Platform Cable USB II)存储介质: SD卡(建议Class 10以上)1.2 软件环境 工具版本作用Vivado2022.1FPGA硬件设计Vitis2022.1系统集成开发Wind…...

网工实验——RIP配置

网络拓扑图 配置 1.为每台设备配置ip地址 AR4 <Huawei>u t m <Huawei>sys [Huawei]sysname AR4 [AR4]int g0/0/0 [AR4-GigabitEthernet0/0/0]ip address 172.16.1.1 24 [AR4-GigabitEthernet0/0/0]q#下面配置换回口&#xff0c;模拟网 [AR4]int LoopBack 0 [AR4…...

前端流行框架Vue3教程:14. 组件传递Props效验

(4) 组件传递Props效验 Vue组件可以更细致地声明对传入的props的校验要求 ComponentA.vue <script> import ComponentB from ./ComponentB.vue; export default {components: {ComponentB},data() {return {title: 标题}} } </script> <template><h3&g…...

电子电器架构 --- 网关ECU中采用多CPU解决方案来实现网关功能

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…...

关于tftpboot的用法

TFTPBOOT 是一个常用于嵌入式系统或网络设备中的命令&#xff0c;用于通过 TFTP 协议从网络上启动操作系统镜像或引导文件。这个命令通常在设备启动时执行&#xff0c;允许设备通过网络从 TFTP 服务器下载启动镜像或其他必要的文件&#xff0c;而不需要从本地存储中启动。 一般…...

团队协作的润滑剂——GitHub与协作流程

各位代码界的社交恐惧症患者们&#xff0c;今天我们要聊的是如何假装自己很会团队协作——使用GitHub&#xff01;这就像程序员版的"相亲平台"&#xff0c;只不过在这里&#xff0c;你展示的不是自拍和收入&#xff0c;而是代码和commit记录&#xff08;后者往往更令…...

数据库复习

DML操作包括&#xff1a; SELECT INSERT UPDATE DELETE MERGE 返回字符串长度&#xff1a;length() 查询记录&#xff1a;SELECT 增&#xff08;INSERT&#xff09;、删&#xff08;DELETE&#xff09;、改&#xff08;UPDATE&#xff09;、查&#xff08;SELECT&#…...

AI与机器学习、深度学习在气候变化预测中的应用与实践

前言&#xff1a; 全球气候变化是现代社会面临的最重要的环境挑战之一&#xff0c;影响了气温、降水、海平面、农业、生态系统等多个方面。气候变化的驱动因素主要包括温室气体排放、气溶胶浓度、火灾频发、海冰融化、叶绿素变化、农业变化和生态环境变化等。这些因素在全球范围…...

Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收

Laravel 12 基于 EMQX 实现 MQTT 消息发送与接收 要在 Laravel 12 中实现基于 EMQX 的 MQTT 消息发送与接收&#xff0c;你可以按照以下步骤操作&#xff1a; 1. 安装必要的依赖包 首先安装 MQTT 客户端库&#xff1a; composer require php-mqtt/client2. 配置 EMQX 连接 …...

论广告系统对存算分离架构的应用

辅助论点 辅助论点一&#xff1a;存算分离架构起源于数据库领域&#xff0c;并不是在线系统。 存算分离的架构源于Google的Spanner数据库&#xff0c;这个数据库采用了KV做存储层&#xff0c;OLAP做计算层的分离式设计&#xff0c;其目的是能快速伸缩计算资源&#xff0c;且节…...