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

games101-作业3

由于此次试验需要加载模型,涉及到本地环节,如果是windows系统,需要对main函数中的路径稍作改变:

这么写需要:
#include "windows.h"

该段代码:


#include "windows.h"
int main(int argc, const char** argv)
{std::vector<Triangle*> TriangleList;float angle = 140.0;bool command_line = false;std::string filename = "output.png";objl::Loader Loader;std::string obj_path = "\\..\\..\\test\\models\\spot\\";char pathBuf[MAX_PATH];char* p;if (GetModuleFileNameA(NULL, pathBuf, MAX_PATH)){p = strrchr(pathBuf, '\\');if (p){*p = '\0';std::string exe_path = pathBuf;obj_path = exe_path + obj_path;}}bool loadout = Loader.LoadFile("D:\\opencode\\games101\\test\\test\\models\\spot\\spot_triangulated_good.obj");

同时在windows下计算很卡,可能软件模拟计算量很大的原因;

使用c++17及以上版本,

\spot_triangulated_good.obj 中的描述了顶点信息

输入到三角形列表中: 

但是 在使用的时候:

fragment_shader 即下面的着色模型:中去如何消费这个图片


    std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = normal_fragment_shader;  用于设置着色模型

着色模型

1.normal模型
// 根据法线进行不同着色
Eigen::Vector3f normal_fragment_shader(const fragment_shader_payload& payload)
{Eigen::Vector3f return_color = (payload.normal.head<3>().normalized() + Eigen::Vector3f(1.0f, 1.0f, 1.0f)) / 2.f;Eigen::Vector3f result;result << return_color.x() * 255, return_color.y() * 255, return_color.z() * 255;return result;
}

这个函数并不是光照模型,只是根据不同法线返回不同颜色值。
第一行的代码,首先取出当前待着色像素点的法向量的X,Y,Z坐标并归一化,故此时X,Y,Z都在[-1,1]之间,加上(1.0f, 1.0f, 1.0f)后,变为[0,2],再除以2,即得[0,1],再分别乘以255即可得到各个颜色值了。

效果:

2.phong模型

Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{// 泛光、漫反射、高光系数Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);Eigen::Vector3f kd = payload.color;Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);// 灯光位置和强度auto l1 = light{{20, 20, 20}, {500, 500, 500}};auto l2 = light{{-20, 20, 0}, {500, 500, 500}};std::vector<light> lights = {l1, l2};// 光照Eigen::Vector3f amb_light_intensity{10, 10, 10};// 环境光强度Eigen::Vector3f eye_pos{0, 0, 10};// 相机位置float p = 150;// ping point的信息Eigen::Vector3f color = payload.color;Eigen::Vector3f point = payload.view_pos;// view spaceEigen::Vector3f normal = payload.normal;Eigen::Vector3f result_color = {0, 0, 0};// 光照结果Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);// 遍历每一束光for (auto& light : lights){Eigen::Vector3f l = (light.position - point).normalized(), v = (eye_pos - point).normalized();// 光照方向和观察方向Eigen::Vector3f h = (l + v).normalized();// 半程向量Eigen::Vector3f I = light.intensity;// 光强float r2 = (light.position - point).dot(light.position - point);Eigen::Vector3f Ld = kd.cwiseProduct(I / r2) * std::max(0.0f, normal.dot(l));//cwiseProduct()函数允许Matrix直接进行点对点乘法,而不用转换至ArrayEigen::Vector3f Ls = ks.cwiseProduct(I / r2) * std::pow(std::max(0.0f, normal.dot(h)), p);result_color += La + Ld + Ls;}//Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);//result_color += La;return result_color * 255.f;
}

直接根据公式写就行,这里我认为环境光应该放在循环外,不过这样渲出来的结果相比说说明偏暗,但后面的displacement又要放在外面否则偏亮。

phong模型渲染结果:

3.texture模型
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{Eigen::Vector3f return_color = {0, 0, 0};if (payload.texture){return_color = payload.texture->getColor(payload.tex_coords.x(), payload.tex_coords.y());// 获取材质颜色信息}Eigen::Vector3f texture_color;texture_color << return_color.x(), return_color.y(), return_color.z();Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);Eigen::Vector3f kd = texture_color / 255.f;// 材质颜色影响漫反射系数Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);auto l1 = light{{20, 20, 20}, {500, 500, 500}};auto l2 = light{{-20, 20, 0}, {500, 500, 500}};std::vector<light> lights = {l1, l2};Eigen::Vector3f amb_light_intensity{10, 10, 10};Eigen::Vector3f eye_pos{0, 0, 10};float p = 150;Eigen::Vector3f color = texture_color;Eigen::Vector3f point = payload.view_pos;Eigen::Vector3f normal = payload.normal;Eigen::Vector3f result_color = {0, 0, 0};Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);for (auto& light : lights){Eigen::Vector3f l = (light.position - point).normalized(), v = (eye_pos - point).normalized();// 光照方向和观察方向Eigen::Vector3f h = (l + v).normalized();// 半程向量Eigen::Vector3f I = light.intensity;// 光强float r2 = (light.position - point).dot(light.position - point);Eigen::Vector3f Ld = kd.cwiseProduct(I / r2) * std::max(0.0f, normal.dot(l));//cwiseProduct()函数允许Matrix直接进行点对点乘法,而不用转换至ArrayEigen::Vector3f Ls = ks.cwiseProduct(I / r2) * std::pow(std::max(0.0f, normal.dot(h)), p);result_color += La + Ld + Ls;}// Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);// result_color += La;return result_color * 255.f;
}

 

    Eigen::Vector3f getColor(float u, float v){// 限制(u, v)坐标范围u = std::fmin(1, std::fmax(u, 0));v = std::fmin(1, std::fmax(v, 0));auto u_img = u * (width-1);auto v_img = (1 - v) * (height-1);auto color = image_data.at<cv::Vec3b>(v_img, u_img);// 四舍五入return Eigen::Vector3f(color[0], color[1], color[2]);}

更换一个材质:

4 displacement_fragment_shader 

Eigen::Vector3f displacement_fragment_shader(const fragment_shader_payload& payload)
{Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);Eigen::Vector3f kd = payload.color;Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);auto l1 = light{{20, 20, 20}, {500, 500, 500}};auto l2 = light{{-20, 20, 0}, {500, 500, 500}};std::vector<light> lights = {l1, l2};Eigen::Vector3f amb_light_intensity{10, 10, 10};Eigen::Vector3f eye_pos{0, 0, 10};float p = 150;Eigen::Vector3f color = payload.color; Eigen::Vector3f point = payload.view_pos;Eigen::Vector3f normal = payload.normal;float kh = 0.2, kn = 0.1;float x = normal.x();float y = normal.y();float z = normal.z();Eigen::Vector3f t{ x * y / std::sqrt(x * x + z * z), std::sqrt(x * x + z * z), z*y / std::sqrt(x * x + z * z) };Eigen::Vector3f b = normal.cross(t);Eigen::Matrix3f TBN;TBN <<  t.x(), b.x(), normal.x(),t.y(), b.y(), normal.y(),t.z(), b.z(), normal.z();float u = payload.tex_coords.x();float v = payload.tex_coords.y();float w = payload.texture->width;float h = payload.texture->height;float dU = kh * kn * (payload.texture->getColor(u + 1 / w , v).norm() - payload.texture->getColor(u, v).norm());float dV = kh * kn * (payload.texture->getColor(u, v + 1 / h).norm() - payload.texture->getColor(u, v).norm());Eigen::Vector3f ln{-dU, -dV, 1};//与凹凸贴图的区别就在于这句话point += (kn * normal * payload.texture->getColor(u , v).norm());normal = (TBN * ln).normalized();Eigen::Vector3f result_color = {0, 0, 0};for (auto& light : lights){Eigen::Vector3f l = (light.position - point).normalized();      // 光Eigen::Vector3f v = (eye_pos - point).normalized();		        // 眼Eigen::Vector3f h = (l + v).normalized();                       // 半程向量double r_2 = (light.position - point).dot(light.position - point);Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r_2) * std::max(0.0f, normal.dot(l));    //cwiseProduct()函数允许Matrix直接进行点对点乘法,而不用转换至Array。Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r_2) * std::pow(std::max(0.0f, normal.dot(h)), p);result_color += (Ld + Ls);   }Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);result_color += La; return result_color * 255.f;
}

 draw函数(顶点、三角形处理阶段) 

void rst::rasterizer::draw(std::vector<Triangle*>& TriangleList) {float f1 = (50 - 0.1) / 2.0;// zfar和znear距离的一半float f2 = (50 + 0.1) / 2.0;// zfar和znear的中心z坐标Eigen::Matrix4f mvp = projection * view * model;// 计算MVP变换矩阵// 遍历每个小三角形for (const auto& t : TriangleList){Triangle newtri = *t;// 计算viewspace_pos,其中viewspace_pos的坐标是经过MV变换,没有经过P投影变换// 所以默认在相机坐标系而不是世界坐标系// 记录三角形顶点MV变换后坐标std::array<Eigen::Vector4f, 3> mm{(view * model * t->v[0]),(view * model * t->v[1]),(view * model * t->v[2])};std::array<Eigen::Vector3f, 3> viewspace_pos;// 存入viewspace_posstd::transform(mm.begin(), mm.end(), viewspace_pos.begin(), [](auto& v) {return v.template head<3>();});// 得到经过mvp后的坐标Eigen::Vector4f v[] = {mvp * t->v[0],mvp * t->v[1],mvp * t->v[2]};// 换算齐次坐标for (auto& vec : v) {vec.x() /= vec.w();vec.y() /= vec.w();vec.z() /= vec.w();}// 计算在MV转换后各顶点的法向量// 利用原来点法向量推出MV变换后法向量// 因为光线作用是在view_space下进行的Eigen::Matrix4f inv_trans = (view * model).inverse().transpose();Eigen::Vector4f n[] = {inv_trans * to_vec4(t->normal[0], 0.0f),inv_trans * to_vec4(t->normal[1], 0.0f),inv_trans * to_vec4(t->normal[2], 0.0f)};// 视口变换 得到顶点在屏幕上的坐标 即screen spacefor (auto& vert : v){vert.x() = 0.5 * width * (vert.x() + 1.0);vert.y() = 0.5 * height * (vert.y() + 1.0);// 为了Zbuffer保留Z值// (透视)投影变换最后一步是从正交投影变换到正则立方体// 而这一步就是把正则立方体的z值还原到正交投影时的z值,即原始z值vert.z() = vert.z() * f1 + f2;}// 记录经过MVP视口变换后的顶点坐标// 完成顶点变换,变换到屏幕空间for (int i = 0; i < 3; ++i){//screen space coordinatesnewtri.setVertex(i, v[i]);}// 记录顶点的法向量for (int i = 0; i < 3; ++i){//view space normalnewtri.setNormal(i, n[i].head<3>());}// 设置颜色newtri.setColor(0, 148, 121.0, 92.0);newtri.setColor(1, 148, 121.0, 92.0);newtri.setColor(2, 148, 121.0, 92.0);// 对这个小三角形进行光栅化// 传入viewspace_pos的坐标,光线的作用是在viewspace下的rasterize_triangle(newtri, viewspace_pos);}
}
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos) 
{auto v = t.toVector4(); //v[0],v[1],v[2]分别为三角形的三个顶点,是四维向量//比较三个顶点的横纵坐标,确定包围盒的边界并取整double min_x = std::min(v[0][0], std::min(v[1][0], v[2][0]));double max_x = std::max(v[0][0], std::max(v[1][0], v[2][0]));double min_y = std::min(v[0][1], std::min(v[1][1], v[2][1]));double max_y = std::max(v[0][1], std::max(v[1][1], v[2][1]));min_x = static_cast<int>(std::floor(min_x));min_y = static_cast<int>(std::floor(min_y));max_x = static_cast<int>(std::ceil(max_x));max_y = static_cast<int>(std::ceil(max_y));//此处实现的是MSAAstd::vector<Eigen::Vector2f> pos{                               //对一个像素分割四份 当然你还可以分成4x4 8x8等等甚至你还可以为了某种特殊情况设计成不规则的图形来分割单元{0.25,0.25},                //左下{0.75,0.25},                //右下{0.25,0.75},                //左上{0.75,0.75}                 //右上};for (int i = min_x; i <= max_x; ++i){for (int j = min_y; j <= max_y; ++j){int count = 0; //开始遍历四个小格子,获得平均值for (int MSAA_4 = 0; MSAA_4 < 4; ++MSAA_4){if (insideTriangle(static_cast<float>(i+pos[MSAA_4][0]), static_cast<float>(j+pos[MSAA_4][1]),t.v))++count;}if(count) //至少有一个小格子在三角形内{//此处是框架中代码,获得z,见原程序注释://    * v[i].w() is the vertex view space depth value z.//    * Z is interpolated view space depth for the current pixel//    * zp is depth between zNear and zFar, used for z-bufferauto[alpha, beta, gamma] = computeBarycentric2D(i + 0.5, j + 0.5, t.v);float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();zp *= Z;//endif (depth_buf[get_index(i, j)] > zp){depth_buf[get_index(i, j)] = zp;//更新深度//这里注意,虽然说明上说"反转了z,保证都是正数,并且越大表示离视点越远",//但经过我的查看,实际上并没有反转,因此还是按照-z近大远小来做,当然也可以在上面补一个负号不过没必要//利用重心坐标插值各种值auto interpolated_color = interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);auto interpolated_normal = interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1).normalized();auto interpolated_texcoords = interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);auto interpolated_shadingcoords = interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1);//shadingcoords是由view_pos插值得到,也就是物体表面的点在相机坐标系的位置。他们会在shader中被用到,来计算光照等信息。//此处是框架中代码,获得z,见原程序注释:fragment_shader_payload payload(interpolated_color, interpolated_normal, interpolated_texcoords, texture ? &*texture : nullptr);payload.view_pos = interpolated_shadingcoords;auto pixel_color = fragment_shader(payload);//end// 设置颜色set_pixel(Eigen::Vector2i(i, j), pixel_color * (count / 4.0));}}}}
}

完整代码:vs2022

相关文章:

games101-作业3

由于此次试验需要加载模型&#xff0c;涉及到本地环节&#xff0c;如果是windows系统&#xff0c;需要对main函数中的路径稍作改变&#xff1a; 这么写需要&#xff1a; #include "windows.h" 该段代码&#xff1a; #include "windows.h" int main(int ar…...

【Block总结】高效多尺度注意力EMA,超越SE、CBAM、SA、CA等注意力|即插即用

论文信息 标题: Efficient Multi-Scale Attention Module with Cross-Spatial Learning 作者: Daliang Ouyang, Su He, Guozhong Zhang, Mingzhu Luo, Huaiyong Guo, Jian Zhan, Zhijie Huang 论文链接: https://arxiv.org/pdf/2305.13563v2 GitHub链接: https://github.co…...

Pwn 入门核心工具和命令大全

一、调试工具&#xff08;GDB 及其插件&#xff09; GDB 启动调试&#xff1a;gdb ./binary 运行程序&#xff1a;run 或 r 设置断点&#xff1a;break *0x地址 或 b 函数名 查看寄存器&#xff1a;info registers 查看内存&#xff1a;x/10wx 0x地址 &#xff08;查看 10 个 …...

探索AI(chatgpt、文心一言、kimi等)提示词的奥秘

大家好&#xff0c;我是老六哥&#xff0c;我正在共享使用AI提高工作效率的技巧。欢迎关注我&#xff0c;共同提高使用AI的技能&#xff0c;让AI成功你的个人助理。 "AI提示词究竟是什么&#xff1f;" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…...

利用飞书机器人进行 - ArXiv自动化检索推荐

相关作者的Github仓库 ArXivToday-Lark 使用教程 Step1 新建机器人 根据飞书官方机器人使用手册&#xff0c;新建自定义机器人&#xff0c;并记录好webhook地址&#xff0c;后续将在配置文件中更新该地址。 可以先完成到后续步骤之前&#xff0c;后续的步骤与安全相关&…...

小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)

背景浅谈 小白踏足JS逆向领域也有一年了&#xff0c;对于逆向这个需求呢主要要求就是让我们去破解**“反爬机制”**&#xff0c;即反“反爬”&#xff0c;脚本处理层面一般都是decipher网站对request设置的cipher&#xff0c;比如破解一个DES/AES加密拿到key。这篇文章先不去谈…...

Ubuntu中MySQL安装-02

服务器端安装 安装服务器端&#xff1a;在终端中输入如下命令&#xff0c;回车后&#xff0c;然后按照提示输入 sudo apt-get install mysql-server 当前使用的ubuntu镜像中已经安装好了mysql服务器端&#xff0c;无需再安装&#xff0c;并且设置成了开机自启动服务器用于接…...

大数据相关职位介绍之一(数据分析,数据开发,数据产品经理,数据运营)

大数据相关职位介绍之一 随着大数据、人工智能&#xff08;AI&#xff09;和机器学习的快速发展&#xff0c;数据分析与管理已经成为各行各业的重要组成部分。从互联网公司到传统行业的数字转型&#xff0c;数据相关职位在中国日益成为推动企业创新和提升竞争力的关键力量。以…...

使用DeepSeek API生成Markdown文件

DeepSeek技术应用与代码实现 一、DeepSeek简介 DeepSeek是一款强大的人工智能写作助手&#xff0c;能够根据用户输入的提示&#xff08;Prompt&#xff09;快速生成高质量的文章。它不仅支持批量生成文章&#xff0c;还能通过智能分段、Markdown转HTML等功能优化内容。此外&…...

java多线程学习笔记

文章目录 关键词1.什么是多线程以及使用场景?2.并发与并行3.多线程实现3.1继承 Thread 类实现3.2Runnable 接口方式实现3.3Callable接口/Future接口实现3.4三种方式总结 4.常见的成员方法&#xff08;重点记忆&#xff09;94.1setName/currentThread/sleep要点4.2线程的优先级…...

Manticore Search,新一代搜索引擎之王

吊打ES&#xff0c;新一代搜索引擎之王 概述 Manticore Search 是一个开源的分布式搜索引擎&#xff0c;专注于高性能和低延迟的搜索场景。 它基于 Sphinx 搜索引擎开发&#xff0c;继承了 Sphinx 的高效索引和查询能力&#xff0c;并在分布式架构、实时搜索、易用性等方面进…...

【MySQL】数据类型与表约束

目录 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 字符串类型 日期和时间类型 enum和set 表的约束 空属性 默认值 列描述 zerofill 主键 自增长 唯一键 外键 数据类型分类 数值类型 tinyint类型 MySQL中&#xff0c;整形可以是有符号和无符号的&…...

CAG技术:提升LLM响应速度与质量

标题&#xff1a;CAG技术&#xff1a;提升LLM响应速度与质量 文章信息摘要&#xff1a; CAG&#xff08;Cache-Augmented Generation&#xff09;通过预加载相关知识到LLM的扩展上下文中&#xff0c;显著减少了检索延迟和错误&#xff0c;从而提升了响应速度和质量。与传统的R…...

上位机知识篇---Linux源码编译安装链接命令

文章目录 前言第一部分&#xff1a;Linux源码编译安装1. 安装编译工具2. 下载源代码3. 解压源代码4. 配置5. 编译6. 测试&#xff08;可选&#xff09;7. 安装8. 清理&#xff08;可选&#xff09;9.注意事项 第二部分&#xff1a;链接命令硬链接&#xff08;Hard Link&#xf…...

科研绘图系列:R语言绘制线性回归连线图(line chart)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理画图保存图片系统信息参考介绍 科研绘图系列:R语言绘制线性回归连线图(line chart) 加载R包 library(tidyverse) library(ggthemes) libra…...

将ollama迁移到其他盘(eg:F盘)

文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景&#xff1a;在windows操作系统中进行操作 相关阅读 &#xff1a;本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘&#xff0c;所以只能安装好之后再进行手动迁移位置。 # 1.迁移Ollama可…...

Oracle 创建用户和表空间

Oracle 创建用户和表空间 使用sys 账户登录 建立临时表空间 --建立临时表空间 CREATE TEMPORARY TABLESPACE TEMP_POS --创建名为TEMP_POS的临时表空间 TEMPFILE /oracle/oradata/POS/TEMP_POS.DBF -- 临时文件 SIZE 50M -- 其初始大小为50M AUTOEXTEND ON -- 支持…...

cursor ide配置远程ssh qt c++开发环境过程记录

cursor是啥就不介绍了&#xff0c;好像是目前最好用的ai ide&#xff0c;下面主要是配置远程ssh连接linux机器进行qt5 c程序运行的配置过程记录。 一、c_cpp_properties.json 在项目根目录的.vscode目录里面新建c_cpp_properties.json文件&#xff0c;根据你的实际情况配置该文…...

yolov5错误更改与相关参数详解(train.py)

1.错误更改 main中相关参数 if __name__ __main__:parser argparse.ArgumentParser()parser.add_argument(--weights, typestr, default, helpinitial weights path)parser.add_argument(--cfg, typestr, defaultmodels/yolov5s.yaml, helpmodel.yaml path)parser.add_arg…...

Python设计模式 - 组合模式

定义 组合模式&#xff08;Composite Pattern&#xff09; 是一种结构型设计模式&#xff0c;主要意图是将对象组织成树形结构以表示"部分-整体"的层次结构。这种模式能够使客户端统一对待单个对象和组合对象&#xff0c;从而简化了客户端代码。 组合模式有透明组合…...

css粘性定位超出指定宽度失效问题

展示效果 解决办法&#xff1a;外层容器添加display:grid即可 完整代码 <template><div class"box"><div class"line" v-for"items in 10"><div class"item" v-for"item in 8">drgg</div>&…...

Windows 程序设计6:错误码的查看

文章目录 前言一、说明二、使用GetLastError找到错误的原因三、使用错误码的宏总结 前言 Windows 程序设计6&#xff1a;错误码的查看。 一、说明 有时写的代码单纯看是没有问题的&#xff0c;但是执行起来就会崩溃。因此要养成判断函数执行是否成功的习惯&#xff0c;除非这…...

doris: CSV导入数据

本文介绍如何在 Doris 中导入 CSV 格式的数据文件。Doris 支持灵活的 CSV 格式配置&#xff0c;包括自定义分隔符、字段包围符等&#xff0c;并提供多种导入方式以满足不同场景的数据导入需求。 导入方式​ Doris 支持以下方式导入 CSV 格式数据&#xff1a; Stream LoadBro…...

FastStone Image Viewer图像处理软件安装步骤(百度网盘链接)

软件简介&#xff1a;一款小巧便捷的添加水印、特效、图片处理软件&#xff0c;让使用者可以通过它的操作界面来浏览图片&#xff0c;且还支持了幻灯播放的功能&#xff0c;让使用者能够轻松的浏览目录中的所有图片。 网盘链接&#xff1a;https://pan.baidu.com/s/1Zvrx7fXwb6…...

Kafka 深入服务端 — 时间轮

Kafka中存在大量的延迟操作&#xff0c;比如延时生产、延时拉取和延时删除等。Kafka基于时间轮概念自定义实现了一个用于延时功能的定时器&#xff0c;来完成这些延迟操作。 1 时间轮 Kafka没有使用基于JDK自带的Timer或DelayQueue来实现延迟功能&#xff0c;因为它们的插入和…...

网络爬虫学习:应用selenium获取Edge浏览器版本号,自动下载对应版本msedgedriver,确保Edge浏览器顺利打开。

一、前言 我从24年11月份开始学习网络爬虫应用开发&#xff0c;经过2个来月的努力&#xff0c;于1月下旬完成了开发一款网络爬虫软件的学习目标。这里对本次学习及应用开发进行一下回顾总结。 前几天我已经发了一篇日志&#xff08;网络爬虫学习&#xff1a;应用selenium从搜…...

【go语言】结构体

一、type 关键字的用法 在 go 语言中&#xff0c;type 关键字用于定义新的类型&#xff0c;他可以用来定义基础类型、结构体类型、接口类型、函数类型等。通过 type 关键字&#xff0c;我们可以为现有类型创建新的类型别名或者自定义新的类型。 1.1 类型别名 使用 type 可以为…...

Spring Boot是什么及其优点

简介 Spring Boot是基于Spring框架开发的全新框架&#xff0c;其设计目的是简化Spring应用的初始化搭建和开发过程。 Spring Boot整合了许多框架和第三方库配置&#xff0c;几乎可以达到“开箱即用”。 优点 可快速构建独立的Spring应用。 直接嵌入Tomcat、Jetty和Underto…...

谷氨酸:大脑功能的多面手

标题&#xff1a;谷氨酸&#xff1a;大脑功能的多面手 文章信息摘要&#xff1a; 谷氨酸是大脑中最主要的兴奋性神经递质&#xff0c;参与了90%以上的神经元激活&#xff0c;在蛋白质合成、味觉&#xff08;鲜味&#xff09;以及神经可塑性中发挥重要作用。它与GABA、多巴胺等…...

SpringCloudGateWay和Sentinel结合做黑白名单来源控制

假设我们的分布式项目&#xff0c;admin是8087&#xff0c;gateway是8088&#xff0c;consumer是8086 我们一般的思路是我们的请求必须经过我们的网关8088然后网关转发到我们的分布式项目&#xff0c;那我要是没有处理我们绕过网关直接访问项目8087和8086不也是可以&#xff1…...