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
由于此次试验需要加载模型,涉及到本地环节,如果是windows系统,需要对main函数中的路径稍作改变: 这么写需要: #include "windows.h" 该段代码: #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 入门核心工具和命令大全
一、调试工具(GDB 及其插件) GDB 启动调试:gdb ./binary 运行程序:run 或 r 设置断点:break *0x地址 或 b 函数名 查看寄存器:info registers 查看内存:x/10wx 0x地址 (查看 10 个 …...

探索AI(chatgpt、文心一言、kimi等)提示词的奥秘
大家好,我是老六哥,我正在共享使用AI提高工作效率的技巧。欢迎关注我,共同提高使用AI的技能,让AI成功你的个人助理。 "AI提示词究竟是什么?" 这是许多初学者在接触AI时的共同疑问。 "我阅读了大量关于…...

利用飞书机器人进行 - ArXiv自动化检索推荐
相关作者的Github仓库 ArXivToday-Lark 使用教程 Step1 新建机器人 根据飞书官方机器人使用手册,新建自定义机器人,并记录好webhook地址,后续将在配置文件中更新该地址。 可以先完成到后续步骤之前,后续的步骤与安全相关&…...
小白爬虫冒险之反“反爬”:无限debugger、禁用开发者工具、干扰控制台...(持续更新)
背景浅谈 小白踏足JS逆向领域也有一年了,对于逆向这个需求呢主要要求就是让我们去破解**“反爬机制”**,即反“反爬”,脚本处理层面一般都是decipher网站对request设置的cipher,比如破解一个DES/AES加密拿到key。这篇文章先不去谈…...

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

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

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

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

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

【MySQL】数据类型与表约束
目录 数据类型分类 数值类型 tinyint类型 bit类型 小数类型 字符串类型 日期和时间类型 enum和set 表的约束 空属性 默认值 列描述 zerofill 主键 自增长 唯一键 外键 数据类型分类 数值类型 tinyint类型 MySQL中,整形可以是有符号和无符号的&…...
CAG技术:提升LLM响应速度与质量
标题:CAG技术:提升LLM响应速度与质量 文章信息摘要: CAG(Cache-Augmented Generation)通过预加载相关知识到LLM的扩展上下文中,显著减少了检索延迟和错误,从而提升了响应速度和质量。与传统的R…...
上位机知识篇---Linux源码编译安装链接命令
文章目录 前言第一部分:Linux源码编译安装1. 安装编译工具2. 下载源代码3. 解压源代码4. 配置5. 编译6. 测试(可选)7. 安装8. 清理(可选)9.注意事项 第二部分:链接命令硬链接(Hard Link…...

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

将ollama迁移到其他盘(eg:F盘)
文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景:在windows操作系统中进行操作 相关阅读 :本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘,所以只能安装好之后再进行手动迁移位置。 # 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是啥就不介绍了,好像是目前最好用的ai ide,下面主要是配置远程ssh连接linux机器进行qt5 c程序运行的配置过程记录。 一、c_cpp_properties.json 在项目根目录的.vscode目录里面新建c_cpp_properties.json文件,根据你的实际情况配置该文…...

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

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...