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

WebGL 选中一个表面

目录

选中一个表面 

示例程序(PickFace.js)

代码详解

gl.readPixels()见126行效果

gl.UNSIGNED_BYTE注意点

示例效果


选中一个表面 

​​​​​​​WebGL 选中物体_山楂树の的博客-CSDN博客可以使用同样的方法来选中物体的某一个表面。这一节在PickObject程序的基础上编写了PickFace程序,后者同样包含一个立方体,但用户可以选中立方体的某一个表面,被选中的表面会变成白色。下图显示了PickFace的运行效果。

如果你理解了PickObject的程序原理,那么PickFace就很简单了。PickObject用户在点击鼠标时,将立方体重绘为红色,然后读取鼠标点击位置的像素颜色,根据其是红色或是黑色来判断点击时鼠标是否在立方体上,即是否选中了立方体。而PickFace则更进一步,在用户点击鼠标时重绘立方体,并将“每个像素属于哪个面”的信息写入到颜色缓冲区的α分量中。下面来看一下示例程序。 

示例程序(PickFace.js)

PickFace.js的代码如下所示。为了简洁,略去了与前例相同的部分,如顶点着色器等。 

var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'attribute vec4 a_Color;\n' +'attribute float a_Face;\n' +   // 曲面编号(不能将int用于属性变量)'uniform mat4 u_MvpMatrix;\n' +'uniform int u_PickedFace;\n' + // 选定面的曲面编号'varying vec4 v_Color;\n' +'void main() {\n' +'  gl_Position = u_MvpMatrix * a_Position;\n' +'  int face = int(a_Face);\n' + // 转换为int   1.0 -> 1'  vec3 color = (face == u_PickedFace) ? vec3(1.0) : a_Color.rgb;\n' +'  if(u_PickedFace == 0) {\n' + // 如果为0,则将面编号插入alpha'    v_Color = vec4(color, a_Face/255.0);\n' + // 6.0/255.0 = 6'  } else {\n' +'    v_Color = vec4(color, a_Color.a);\n' +'  }\n' +'}\n';
var FSHADER_SOURCE ='#ifdef GL_ES\n' +'precision mediump float;\n' +'#endif\n' +'varying vec4 v_Color;\n' +'void main() {\n' +'  gl_FragColor = v_Color;\n' +'}\n';var ANGLE_STEP = 20.0; // 旋转角度(20度/秒)
function main() {var canvas = document.getElementById('webgl');var gl = getWebGLContext(canvas);if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) returnvar n = initVertexBuffers(gl);gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.enable(gl.DEPTH_TEST);var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');var u_PickedFace = gl.getUniformLocation(gl.program, 'u_PickedFace');var viewProjMatrix = new Matrix4();viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0);viewProjMatrix.lookAt(0.0, 0.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);gl.uniform1i(u_PickedFace, -1); // 默认不匹配面var currentAngle = 0.0; // 当前旋转角度canvas.onmousedown = function(ev) {   // 按下鼠标var x = ev.clientX, y = ev.clientY;var rect = ev.target.getBoundingClientRect();if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {// 如果按下的位置在<canvas>内,则更新表面var x_in_canvas = x - rect.left, y_in_canvas = rect.bottom - y;var face = checkFace(gl, n, x_in_canvas, y_in_canvas, currentAngle, u_PickedFace, viewProjMatrix, u_MvpMatrix);gl.uniform1i(u_PickedFace, face); // 将曲面编号传递给uPickedFacedraw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);}}var tick = function() {   // Start drawingcurrentAngle = animate(currentAngle);draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);requestAnimationFrame(tick, canvas);};tick();
}function initVertexBuffers(gl) {//    v6----- v5//   /|      /|//  v1------v0|//  | |     | |//  | |v7---|-|v4//  |/      |///  v2------v3var vertices = new Float32Array([   // Vertex coordinates1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,    // v0-v1-v2-v3 front1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,    // v0-v3-v4-v5 right1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,    // v0-v5-v6-v1 up-1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,    // v1-v6-v7-v2 left-1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0,    // v7-v4-v3-v2 down1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0     // v4-v7-v6-v5 back]);var colors = new Float32Array([   // Colors0.32, 0.18, 0.56,  0.32, 0.18, 0.56,  0.32, 0.18, 0.56,  0.32, 0.18, 0.56, // v0-v1-v2-v3 front0.5, 0.41, 0.69,   0.5, 0.41, 0.69,   0.5, 0.41, 0.69,   0.5, 0.41, 0.69,  // v0-v3-v4-v5 right0.78, 0.69, 0.84,  0.78, 0.69, 0.84,  0.78, 0.69, 0.84,  0.78, 0.69, 0.84, // v0-v5-v6-v1 up0.0, 0.32, 0.61,   0.0, 0.32, 0.61,   0.0, 0.32, 0.61,   0.0, 0.32, 0.61,  // v1-v6-v7-v2 left0.27, 0.58, 0.82,  0.27, 0.58, 0.82,  0.27, 0.58, 0.82,  0.27, 0.58, 0.82, // v7-v4-v3-v2 down0.73, 0.82, 0.93,  0.73, 0.82, 0.93,  0.73, 0.82, 0.93,  0.73, 0.82, 0.93, // v4-v7-v6-v5 back]);var faces = new Uint8Array([   // Faces1, 1, 1, 1,     // v0-v1-v2-v3 front2, 2, 2, 2,     // v0-v3-v4-v5 right3, 3, 3, 3,     // v0-v5-v6-v1 up4, 4, 4, 4,     // v1-v6-v7-v2 left5, 5, 5, 5,     // v7-v4-v3-v2 down6, 6, 6, 6,     // v4-v7-v6-v5 back]);var indices = new Uint8Array([   // Indices of the vertices0, 1, 2,   0, 2, 3,    // front4, 5, 6,   4, 6, 7,    // right8, 9,10,   8,10,11,    // up12,13,14,  12,14,15,    // left16,17,18,  16,18,19,    // down20,21,22,  20,22,23     // back]);var indexBuffer = gl.createBuffer();if (!initArrayBuffer(gl, vertices, gl.FLOAT, 3, 'a_Position')) return -1; // Coordinates Informationif (!initArrayBuffer(gl, colors, gl.FLOAT, 3, 'a_Color')) return -1;      // Color Informationif (!initArrayBuffer(gl, faces, gl.UNSIGNED_BYTE, 1, 'a_Face')) return -1;// Surface Informationgl.bindBuffer(gl.ARRAY_BUFFER, null);gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);return indices.length;
}function checkFace(gl, n, x, y, currentAngle, u_PickedFace, viewProjMatrix, u_MvpMatrix) {var pixels = new Uint8Array(4); // 存储像素值的数组gl.uniform1i(u_PickedFace, 0);  // 将表面编号写入a分量draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix);/* 在WebGL中,gl.readPixels()函数用于从帧缓冲区中读取像素数据(而非颜色缓冲区中)。该函数的第六个参数用于指定返回的像素数据的数据类型。当将第六个参数设置为gl.UNSIGNED_BYTE时,表示希望返回的像素数据以无符号字节的形式表示。每个像素将用一个8位无符号整数(即一个字节)来表示。使用gl.UNSIGNED_BYTE可以获得每个像素的红、绿、蓝和透明度通道的值(RGB或RGBA)的整数表示,取值范围在0到255之间。当将gl.readPixels()函数的第六个参数设置为gl.UNSIGNED_BYTE时,返回的每个像素的RGBA值都会以0到255的范围内的整数表示。每个颜色通道(红、绿、蓝和透明度)都用一个8位无符号整数(即一个字节)来表示这样的设置通常用于读取像素数据后进行图像处理、纹理操作或其他需要使用整数表示的场景。*/gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // 读取单击位置的像素值。pixels[3]是曲面数console.log(pixels); // [128, 105, 176, 2]return pixels[3];
}var g_MvpMatrix = new Matrix4(); // 模型视图投影矩阵
function draw(gl, n, currentAngle, viewProjMatrix, u_MvpMatrix) {// 计算模型视图投影矩阵并将其传递给u_MvpMatrixg_MvpMatrix.set(viewProjMatrix); g_MvpMatrix.rotate(currentAngle, 1.0, 0.0, 0.0); // 适当旋转g_MvpMatrix.rotate(currentAngle, 0.0, 1.0, 0.0);g_MvpMatrix.rotate(currentAngle, 0.0, 0.0, 1.0);gl.uniformMatrix4fv(u_MvpMatrix, false, g_MvpMatrix.elements);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);   gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);  
}var last = Date.now();  // 上次调用此函数的时间
function animate(angle) {var now = Date.now(); // 计算运行时间var elapsed = now - last;last = now;// 更新当前旋转角度(根据经过的时间进行调整)var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;return newAngle % 360;
}function initArrayBuffer (gl, data, type, num, attribute) {var buffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, buffer);gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);var a_attribute = gl.getAttribLocation(gl.program, attribute);gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);gl.enableVertexAttribArray(a_attribute);return true;
}

代码详解

首先,顶点着色器中添加了attribute变量a_Face,它表示立方体各表面的编号,即当前顶点属于哪个表面(第4行)。鼠标被点击时,这个值就会被“编码”成颜色值的α分量。initVertexBuffers()函数(第63行)建立了表面编号数组faces,数组中的每个元素对应一个顶点(第87行)。比如,顶点v0-v1-v2-v3定义了1号表面,而顶点v0-v3-v4-v5定义了2号表面,等等。数组faces前4个元素都是1,表示前4个顶点都属于1号表面,以此类推(第88行)。

当某个表面被选中时,就通过u_PickedFace变量来通知顶点着色器这个表面被选中了(第6行)。这样顶点着色器就可以将这个表面绘制成白色,用户就获得了反馈,知道这个表面确实被选中了。

在正常情况下(即不在鼠标被点击的那一刻),顶点着色器会比较当前被选中的表面编号u_PickedFace和当前顶点的表面编号a_Face,如果它们相等,即当前顶点属于被选中的表面,就将color赋为白色,如果不相等,就将其赋为顶点原来的颜色a_Color。此处必须将float类型的a_Face转化为int类型,再与u_PickedFace进行比较,因为attribute变量只能是float类型的。在鼠标点击的那一刻,u_PickedFace被设为0,我们将a_Face的值写入到颜色的α分量中。

main()函数为u_PickedFace指定初始值-1(第40行)。按照faces变量的定义(第87行),立方体各表面中没有哪一个编号是-1,所以一开始没有任何表面被选中,每一个面的颜色都是初始颜色。

gl.readPixels()见126行效果

鼠标被点击时,u_PickedFace变量变为了0,顶点着色器就在颜色缓冲区中将每个面绘制成非1的α值,并且α的值取决于表面编号。最关键的逻辑发生在鼠标点击事件的响应函数中:在获取了鼠标点击位置后,调用checkFace()函数并传入u_PickedFace变量,也就是着色器中同名变量的存储地址(第49行)。

checkFace()函数的任务是,根据点击位置返回选中表面的编号(第113行)。该函数首先将u_PickedFace变量设为0(第115行),然后立刻调用draw()函数(在颜色缓冲区中,最终没有显示在屏幕上)进行绘制,此时每个表面的α值就取决于表面的编号。然后,从颜色缓冲区获取鼠标点击处的像素值(第125行),通过pixels[3]获取表面编号(即α分量,索引为3)。checkFace()函数返回选中的表面编号,执行流程回到鼠标点击事件响应函数中,用选中的表面编号重新绘制立方体(第50~51行),并在屏幕上显示出来,如前所述。

gl.UNSIGNED_BYTE注意点

    在WebGL中,gl.readPixels()函数用于从帧缓冲区中读取像素数据(而非颜色缓冲区中)。该函数的第六个参数用于指定返回的像素数据的数据类型。

    当将第六个参数设置为gl.UNSIGNED_BYTE时,表示希望返回的像素数据以无符号字节的形式表示。每个像素将用一个8位无符号整数(即一个字节)来表示。

    使用gl.UNSIGNED_BYTE可以获得每个像素的红、绿、蓝和透明度通道的值(RGB或RGBA)的整数表示,取值范围在0到255之间。(见 gl.readPixels()见126行效果 上图)

    这样的设置通常用于读取像素数据后进行图像处理、纹理操作或其他需要使用整数表示的场景。

示例效果

相关文章:

WebGL 选中一个表面

目录 选中一个表面 示例程序&#xff08;PickFace.js&#xff09; 代码详解 gl.readPixels()见126行效果 gl.UNSIGNED_BYTE注意点 示例效果 选中一个表面 ​​​​​​​WebGL 选中物体_山楂树の的博客-CSDN博客可以使用同样的方法来选中物体的某一个表面。这一节在Pi…...

open ai chartgpt 安装插件 txyz.ai

1 chatgpt 页面 左下角 用户 -> setting 2 3...

【算法思想】贪心

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

freeswitch-01

文章目录 1. 电话实现技术2. 模拟信号与数字信号2.1 模拟信号2.2 数字信号 3. PCM4. 局间中继与电路复用技术5. 信令5.1 定义5.2 分类5.2.1 功能分类5.2.2 工作区域分类5.2.3 信道分类 5.3 用户线信令5.4 局间信令5.5 七号信令5.6 H.323与SIP信令 6. 媒体6.1 定义 7. 电路交换与…...

Zookeeper-集群介绍与核心理论

Zookeeper集群 4.Zookeeper集群4.1) 介绍4.2) 核心理论 4.Zookeeper集群 4.1) 介绍 Leader选举&#xff1a; Serverid&#xff1a;服务器ID。比如有三台服务器&#xff0c;编号分别是1,2,3。编号越大在选择算法中的权重越大。Zxid&#xff1a;数据ID。服务器中存放的最大数据…...

动态分配的内存位置在哪里?

在C++中,动态分配的内存位于称为堆(Heap)的内存区域。以下是一些关于堆和其他相关内存区域的基本信息: 堆(Heap): 这是一个用于动态内存分配的内存区域。使用new(C++)或malloc(C)等函数从堆中分配内存,并使用delete(C++)或free(C)释放这些内存。堆的大小通常受…...

Vue3中的Ref与Reactive:深入理解响应式编程

前言 Vue 3是一个功能强大的前端框架&#xff0c;它引入了一些令人兴奋的新特性&#xff0c;其中最引人注目的是ref和reactive。这两个API是Vue 3中响应式编程的核心&#xff0c;本文将深入探讨它们的用法和差异。 什么是响应式编程&#xff1f; 在Vue中&#xff0c;响应式编…...

Windows10/11显示文件扩展名 修改文件后缀名教程

前言 写这篇文章的原因是由于我分享的教程中的文件、安装包基本都是存在阿里云盘的&#xff0c;下载后需要改后缀名才能使用。 但是好多同学不会改。。 Windows 10 随便打开一个文件夹&#xff0c;在上方工具栏点击 “查看”点击 “查看” 后下方会显示更详细的工具栏然后点…...

【C++】手撕string(string的模拟实现)

手撕string目录&#xff1a; 一、 Member functions 1.1 constructor 1.2 Copy constructor&#xff08;代码重构&#xff1a;传统写法和现代写法&#xff09; 1.3 operator&#xff08;代码重构&#xff1a;现代写法超级牛逼&#xff09; 1.4 destructor 二、Other mem…...

用python3编译cv_bridge

文章目录 概要依赖工作空间编译可能遇到的问题error: option --install-layout not recognized概要 当我在编写一个使用传感器图像传输和OpenCV4的ROS包时,从构建到编译代码的一切都很顺利。当我开始运行节点本身时,问题出现了,它给出了以下错误: Assertion failed (tlsSl…...

招商信诺人寿基于 Apache Doris 统一 OLAP 技术栈实践

本文导读&#xff1a; 当前&#xff0c;大数据、人工智能、云计算等技术应用正在推动保险科技发展&#xff0c;加速保险行业数字化进程。在这一背景下&#xff0c;招商信诺不断探索如何将多元数据融合扩充&#xff0c;以赋能代理人掌握更加详实的用户线索&#xff0c;并将智能…...

我的python安装在哪儿了?python安装路径怎么查?

对于 Python 开发者来说&#xff0c;Windows 系统中的 Python 安装路径是非常重要的。在本文中&#xff0c;我们将从多个方面探究如何查看 Python 安装路径&#xff0c;并提供代码示例。 一、使用文件浏览器查看 Python 安装路径 在 Windows 系统中&#xff0c;我们可以使用文…...

视频汇聚/安防监控平台EasyCVR指定到新的硬盘进行存储录像,如何自动挂载该磁盘?

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…...

读博时的建议或心得

https://www.zhihu.com/question/32210068/answer/264273093 读论文&#xff1a;一开始读论文&#xff0c;一定要读顶会顶刊的&#xff0c;以后也一直要这样。如此&#xff0c;一方面保持了研究的水准&#xff0c;时刻提醒自己&#xff1a;我就是混这个层次的。另一方面&#…...

3分钟,免费制作一个炫酷实用的数据可视化大屏!

在当前大数据时代背景下&#xff0c;数据已成为在工业革命中如同煤炭、石油一般宝贵的资源。但是由于数据越来越庞大、越来越复杂&#xff0c;导致数据的可读性也越来越低。因此&#xff0c;对数据可视化的需求也越来越高&#xff0c;需要解决的问题也越来越复杂&#xff0c;而…...

自费访学|金融公司高管赴世界名校伯克利交流

R经理决定抽出一年时间&#xff0c;自费赴美国访学&#xff0c;向国外导师请教&#xff0c;探讨了解不同社会环境下&#xff0c;各种经济及社会现象的产生和发展&#xff0c;在思维碰撞中提升自身的国际视野。最终我们为其联系到世界名校-加州大学伯克利分校&#xff0c;导师为…...

Databend 开源周报第112期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 理解用户自定义…...

如何学习maya mel语言的经验分享

一、前言 总结一下这十几年来学习和使用mel语言的一些经验&#xff0c;供初学朋参考&#xff0c;哈哈。 这里不说深奥理论&#xff0c;只是朴实经历陈述。 其实&#xff0c;早在2003年&#xff0c;最初接触maya时&#xff0c;就已经涉及到mel的学习&#xff0c;当时在大学里接…...

睿趣科技:新手抖音开店卖什么产品好

抖音已经成为了一款年轻人热爱的社交媒体应用&#xff0c;同时也成为了一种全新的电商平台。对于新手来说&#xff0c;抖音开店卖什么产品是一个备受关注的问题。在这篇文章中&#xff0c;我们将探讨一些适合新手的产品选择&#xff0c;帮助他们在抖音上开店获得成功。 流行时尚…...

【新版】系统架构设计师 - 案例分析 - 架构设计<Web架构>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 案例分析 - 架构设计&#xff1c;Web架构&#xff1e;Web架构知识点单台机器 到 数据库与Web服务器分离应用服务器集群负载均衡负载均衡技术静态与动态算法Session共享机制有状态与无状态 持久化技…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

C++实现分布式网络通信框架RPC(2)——rpc发布端

有了上篇文章的项目的基本知识的了解&#xff0c;现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...