WebGL编程指南 - 绘制和变换三角形

  • 三角形在三维图形学中的重要地位,以及WebGL如何绘制三角形。
  • 使用多个三角形绘制其它类型的基本图形。
  • 利用简单的方程对三角形做基本的变换,如移动、旋转和缩放。
  • 利用矩阵简化变换。

绘制多个点与缓冲区对象

相关内容:缓冲区对象:创建缓冲区对象-绑定缓冲区对象-向缓冲区对象写入数据以及类型化数组-缓冲区对象分配给attribute变量-开启attribute变量;开始绘制及着色器运行过程
相关函数:gl.createBuffer(), gl.bindBuffer(), gl.bufferData(), new Float32Array()…, gl.vertexAttribPointer()(有自动补全), gl.enableVertexAttribArray(), gl.disableVertexAttribArray(), gl.drawArrays()

之前的示例都是逐个点进行绘制,本节将讨论一次性绘制多个点的方法,作为绘制多顶点图形的基础。

可以用WebGL缓冲区对象(buffer object),它可以一次性向着色器传入多个顶点的数据。缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存其中,供顶点着色器使用。

// MultiPoint.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +'   gl_Position = a_Position;\n' +'   gl_PointSize = 10.0;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空canvasgl.clear(gl.COLOR_BUFFER_BIT)// 绘制gl.drawArrays(gl.POINTS, 0, n)
}function initVertexBuffers(gl) {let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])let n = 3 // 点的个数// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 将缓冲区对象绑定到目标gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 向缓冲区对象中写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)// 将缓冲区对象分配给a_Position变量let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')return -1}gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 连接a_Position变量与分配给它的缓冲区对象gl.enableVertexAttribArray(a_Position)return n
}

新加入的函数 initVertexBuffers() 创建了顶点缓冲器对象,将多个顶点的数据保存在缓冲区中,最后将缓冲区传给顶点着色器。函数的返回值是待绘制顶点的数量,发生错误会返回 -1

使用缓冲区对象:

缓冲区对象是WebGL系统中的一块存储区,我们可以在缓冲区对象中保存想要绘制的所有顶点的数据。如下图所示:

使用缓冲区对象向顶点着色器传入多个顶点的数据,需要五个步骤:

  • 创建缓冲区对象(gl.createBuffer())
  • 绑定缓冲区对象 ( gl.bindBuffer()  )
  • 将数据写入缓冲区对象 ( gl.bufferData() )
  • 将缓冲区对象分配给一个 attribute 变量 ( gl.vertexAttribPointer() )
  • 开启 attribute 变量 ( gl.enableVertexAttrribArray() )

创建缓冲区对象( gl.createBuffer() )

  // 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}

绑定缓冲区 (gl.bindBuffer() )

将缓冲区对象绑定到WebGL系统中已经存在的目标上,这个目标表示缓冲区对象的用途,这样 WebGL 才能够正确处理其中的内容

  // 将缓冲区对象绑定到目标gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)

示例程序中,我们将创建的缓冲区对象绑定到gl.ARRAY_BUFFER目标上,代码执行完毕后,WebGL系统内如下图所示:

向缓冲区对象写入数据(gl.bufferData() )

  // 向缓冲区对象中写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

这一步将 vertices 中的数据写入到绑定在 gl.ARRAU_BUFFER 目标中的缓冲区对象。此处不能直接向缓冲区写入数据,而实依据 target 写入数据

类型化数组

  let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])

为了优化性能,WebGL 为每种基本数据类型引入了一种特输的数组(类型化数组)提前告诉浏览器数组中的数据类型,能够更有效率地处理数据,为绘制三维图形提供了大量便利。

let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])
let vertices = new Float32Array(4)

将缓冲区对象分配给一个 attribute 变量 (gl.vertexAttribPointer() )

缓冲区对象准备好之后,需要获取attribute变量地址,再向attribute变量传递参数。第二章中使用了gl.vertexAttrib[1234]f[v]系列函数来传递数据,但此方法一次只能传递一个值,此时需要一次传递多个值,示例中采用gl.vertexAttribPointer()方法,它可以将缓冲区对象(实际上是缓冲区对象的引用或指针)分配给 attribute 变量

  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)

开启 attribute 变量 ( gl.enablevertexAttribArray() )

gl.enableVertexAttribArray(a_Position);

开始绘制

通过上面的函数,我们已经配置好了缓冲区和着色器,可以开始绘制:

  // 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空canvasgl.clear(gl.COLOR_BUFFER_BIT)// 绘制gl.drawArrays(gl.POINTS, 0, n)
gl.drawArrays(mode, first, count)

在建立attribute变量和缓冲区联系时,函数gl.vertexAttribPointer()中的参数size为2,表示缓冲区每个顶点有2个分量值。所以每次着色器运行前,gl_Position都被提供了两个分量(通过attribute变量a_Position),其他值按照规则填充为0.0和1.0。

Hello Triangle

画一个三角形

// MultiPoint.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main(){\n' +'   gl_Position = a_Position;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空canvasgl.clear(gl.COLOR_BUFFER_BIT)// 绘制gl.drawArrays(gl.TRIANGLES, 0, n)
}function initVertexBuffers(gl) {let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])let n = 3 // 点的个数// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 将缓冲区对象绑定到目标gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 向缓冲区对象中写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)// 将缓冲区对象分配给a_Position变量let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')return -1}gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 连接a_Position变量与分配给它的缓冲区对象gl.enableVertexAttribArray(a_Position)return n
}

相对于上一个,有以下两处修改:

  • 顶点着色器中删除了' gl_PointSize = 10.0;\n',该语句只在绘制单个点的时候才起作用;
  • gl.drawArrays()方法第一个参数改为了gl.TRIANGLES(第37行)

基本图形与 gl.drawArrays() 方法

gl.drawArrays()方法强大又灵活,可以通过第1个参数mode指定不同的值来以7种不同的方式绘制图形。WebGL可以绘制的基本图形如下:

  1. 点的顺序对图形的呈现有重要影响;
  2. WebGL只能绘制三种图形:点、线段和三角形。当然,从球体到立方体,再到游戏中的三维角色,都可以由小的三角形组成,实际上,我们可以使用以上这些最基本的图形来绘制出任何东西。

用三角形绘制矩形(HelloQuad)

矩形可以由两个三角形组成,绘制方式可以采用gl.TRAINGLES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN三种方法,第一种方法需要用到6个顶点,后两种需要4个顶点,每种方法的顶点顺序都不相同。此处采用gl.TRIANGLE_STRIP方法进行绘制,相比于上个示例,此处改动如下:

  let vertices = new Float32Array([-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5]) # 正方形的四个顶点,点的顺序可见上一节gl.TRIANGLE_STRIP图形示例let n = 4 // 点的个数// 绘制gl.drawArrays(gl.TRIANGLE_STRIP, 0, n)

移动,旋转和缩放

相关内容:1.表达式方式进行仿射变换;2.变换矩阵进行仿射变换;3.将矩阵传递给uniform变量;4.按列主序
相关函数:gl.uniformMatrix4fv()

本节将讨论如何移动(平移)、旋转和缩放三角形,这样的操作称为变换(transformations)或仿射变换(affine transformations)。

百度百科中对仿射变换的定义如下:
仿射变换,又称仿射映射,是指在几何中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。

显而易见,相关的变换通过矩阵可以简单获得。

该程序将上一节中的三角形向上向右移动了0.5哥单位

平移   

  • 显然,在WebGL系统中实现平移操作是一个逐顶点操作(per-vertex operation),我们需要在顶点着色器中为顶点坐标的每一个分量加上一个常量。
  • 这一常量对于每个顶点都是一样的,故采用uniform变量即可。

该示例与 Hellotriangle 的差别有两处:

  • 第一处在顶点着色器中,定义了uniform变量u_Translation作为平移量
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform vec4 u_Translation;\n' +'void main(){\n' +'  gl_Position = a_Position + u_Translation;\n' +'}\n'
  • 第二处在main()函数中,增加了uniform变量传值的过程
// 在x,y,z方向上平移的距离
var Tx = 0.5,Ty = 0.5,Tz = 0.5// 将平移距离传输给顶点着色器uniform变量let u_Translation = gl.getUniformLocation(gl.program, 'u_Translation')if (!u_Translation) {console.log('Failed to get the storage location of u_Translation')}gl.uniform4f(u_Translation, Tx, Ty, Tz, 0.0)

完整代码:

// TranslatedTriangle.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform vec4 u_Translation;\n' +'void main(){\n' +'  gl_Position = a_Position + u_Translation;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main(){\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 在x,y,z方向上平移的距离
var Tx = 0.5,Ty = 0.5,Tz = 0.5
// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 将平移距离传输给顶点着色器uniform变量let u_Translation = gl.getUniformLocation(gl.program, 'u_Translation')if (!u_Translation) {console.log('Failed to get the storage location of u_Translation')}gl.uniform4f(u_Translation, Tx, Ty, Tz, 0.0)// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空绘图区gl.clear(gl.COLOR_BUFFER_BIT)// 绘制三角形gl.drawArrays(gl.TRIANGLES, 0, n)
}function initVertexBuffers(gl) {// 设置类型化数组和顶点数let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])let n = 3// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 绑定缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 缓冲区写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STREAM_DRAW)let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')return -1}// 将缓冲区分配给attribute变量gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 开启attribute变量(连接)gl.enableVertexAttribArray(a_Position)return n
}

旋转

描述旋转,有以下三点需要指明:

  • 旋转轴
  • 旋转方向:逆时针或顺时针
  • 旋转角度

书中这样表述旋转操作:绕Z轴,逆时针旋转了β角度。关于“逆时针”的约定是:如果β是正值,观察者在Z轴正半轴某处,视线沿着Z轴负方向进行观察,看到的物体是逆时针旋转的,如下图所示。这种情况又可称作正旋转(positive rotation),这是本书中WebGL程序的默认设定,当然,β小于零代表顺时针旋转。

呈现旋转效果的顶点着色器

  • 顶点着色器部分进行改造,创建uniform变量u_CosB和u_SinB:(注意:vec4格式的数据与JavaScript中对象类似,每个数据都有索引)
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform float u_CosB, u_SinB;\n' +'void main(){\n' +'  gl_Position.x = a_Position.x * u_CosB + a_Position.y * u_SinB;\n' +'  gl_Position.y = a_Position.x * u_SinB + a_Position.y * u_CosB;\n' +'  gl_Position.z = a_Position.z;\n' +'  gl_Position.w = 1.0;\n' +'}\n'
  • 根据旋转角度计算三角函数,传递参数给uniform变量:(注意:JavaScript中Math.sin()和Math.cos()方法的参数为弧度制的角度。)
  // 将旋转图形所需数据传输给顶点着色器let radian = (Math.PI * ANGLE) / 180.0 // 转换为弧度制let cosB = Math.cos(radian)let sinB = Math.sin(radian)let u_CosB = gl.getUniformLocation(gl.program, 'u_CosB')let u_SinB = gl.getUniformLocation(gl.program, 'u_SinB')if (!u_CosB) {console.log('Failed to get the storage location of u_CosB')}if (!u_SinB) {console.log('Failed to get the storage location of u_SinB')}gl.uniform1f(u_CosB, cosB)gl.uniform1f(u_SinB, sinB)

变换矩阵对表达式方式的改造:

旋转:该变换矩阵进行的变换是一次旋转,所以这个矩阵又可以称为旋转矩阵(rotation matrix)

平移的矩阵:

RotateTriangle_Matrix 的代码:  (用变换矩阵实现效果)

// RotatedTriangle_Matrix.js
// 顶点着色器
var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'uniform mat4 u_xformMatrix;\n' +'void main(){\n' +'  gl_Position = u_xformMatrix * a_Position;\n' +'}\n'
// 片元着色器
var FSHADER_SOURCE ='void main(){\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + '}\n'
// 旋转角度
var ANGLE = 90.0
// 主函数
function main() {// 获取canvas元素let canvas = document.getElementById('webgl')// 获取WebGL上下文let gl = getWebGLContext(canvas)if (!gl) {console.log('Failed to get the rendering context for WebGL')return}// 初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to initialize shaders')return}// 设置顶点位置let n = initVertexBuffers(gl)if (n < 0) {console.log('Failed to set the positions of the vertices')return}// 创建旋转矩阵let radian = (Math.PI * ANGLE) / 180.0 // 转换为弧度制let cosB = Math.cos(radian)let sinB = Math.sin(radian)// 注意WebGL中矩阵是列主序的let xformMatrix = new Float32Array([cosB,    sinB,    0.0,    0.0,-sinB,    cosB,    0.0,    0.0,0.0,    0.0,    1.0,    0.0,0.0,    0.0,    0.0,    1.0,])// 将旋转图形所需数据传输给顶点着色器let u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix')if (!u_xformMatrix) {console.log('Failed to get the storage location of u_xformMatrix')}gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix)// 设置背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)// 清空绘图区gl.clear(gl.COLOR_BUFFER_BIT)// 绘制三角形gl.drawArrays(gl.TRIANGLES, 0, n)
}function initVertexBuffers(gl) {// 设置类型化数组和顶点数let vertices = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5])let n = 3// 创建缓冲区对象let vertexBuffer = gl.createBuffer()if (!vertexBuffer) {console.log('Failed to create the buffer object')return -1}// 绑定缓冲区gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)// 缓冲区写入数据gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STREAM_DRAW)let a_Position = gl.getAttribLocation(gl.program, 'a_Position')if (a_Position < 0) {console.log('Failed to get the storage location of a_Position')return -1}// 将缓冲区分配给attribute变量gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)// 开启attribute变量(连接)gl.enableVertexAttribArray(a_Position)return n
}

一般来说,我们有两种方式在数组中存储矩阵元素:按行主序(row major order)和按列主序(column major order),如下图所示:

WebGL与OpenGL一样,矩阵元素是按列主序存储在数组中的。如图中的矩阵存储在数组中的顺序就是这样的:[a, e, i, m, b, f, j, n, c, g, k, o, d, h, l, p]。与我们平时写代码时使用的那种相反

  gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix)

向 uniform 变量传递矩阵

平移矩阵

缩放矩阵:

参考:【《WebGL编程指南》读书笔记-绘制和变换三角形】_webgl绘制三角形并进行平移缩放旋转-CSDN博客

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

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

相关文章

第J6周:ResNeXt-50实战解析(pytorch版)

>- **&#x1f368; 本文为[&#x1f517;365天深度学习训练营]中的学习记录博客** >- **&#x1f356; 原作者&#xff1a;[K同学啊]** 任务&#xff1a; ●阅读ResNeXt论文&#xff0c;了解作者的构建思路 ●对比我们之前介绍的ResNet50V2、DenseNet算法 ●使用ResNeX…

LabVIEW离心泵振动监控与诊断系统

利用LabVIEW结合数据采集与处理技术&#xff0c;构建了一套高效、低成本的振动监测与诊断系统&#xff0c;有效提升了测试精度与设备可靠性。 项目背景 在化工生产中&#xff0c;离心泵作为关键设备&#xff0c;其稳定运行对保障生产安全与效率至关重要。由于传统振动测试系统…

C++ —— set系列的使用

目录 1. 序列式容器和关联式容器 2. set和multiset参考⽂档 3. set类的介绍 4. set的构造和迭代器 4.1 set的增删查 4.1.1 插入 4.1.2 查找 4.1.3 删除 5. multiset和set的差异 1. 序列式容器和关联式容器 前⾯我们已经接触过STL中的部分容器如&#xff1a;str…

大学英语四级作文模板万能句型开头主体结尾PDF

大学英语四级笔试时间越来越近了&#xff0c;报名了英语四级的宝子要抓紧咯&#xff0c;今天给大家分享一份实用的英语四级作文模板&#xff0c;看到了就赶紧背&#xff0c;不要等到考完试才后悔没背 … 万能作文模板涵盖开头、主体和结尾的写法&#xff0c;以及常用的万能句型…

股票交易基础规则系列—集合竞价撮合/价格确定原则,连续竞价价格确定原则! 集合竞价和连续竞价的价格是怎么确定的?

竞价交易的撮合原则 证券竞价交易按价格优先、时间优先的原则撮合成交。 成交时价格优先的原则为&#xff1a;较高价格买入申报优先于较低价格买入申报&#xff0c;较低价格卖出申报优先于较高价格卖出申报。——买单价高优先&#xff0c;卖单价低优先。 成交时时间优先的原…

RAID5数据恢复—raid5阵列如何重组?raid5阵列重组方法详解

RAID5数据恢复环境&#xff1a; 一台存储上有一组由12块SCSI硬盘&#xff08;11块数据盘1块热备盘&#xff09;组建的RAID5磁盘阵列&#xff0c;FreeBSD操作系统zfs文件系统。 RAID5故障&#xff1a; 其中一块盘出现故障&#xff0c;需要重组该raid5磁盘阵列。 RAID5数据恢复…

MySQL数据库操作——(4)

目录 8 视图 8.1 常见的数据库对象 8.2 视图概述 8.2.1 为什么使用视图&#xff1f; 8.2.2 视图的理解 8.3 创建视图 8.3.1 创建单表视图 8.3.2 创建多表联合视图 8.3.3 基于视图创建视图 8.4 查看视图 8.5 更新视图的数据 8.5.1 一般情况 8.6 修改、删除视图 8.…

冲锋衣市场洞察:全方位数据分析与趋势展望

冲锋衣整体数据分析 一. 概述 本报告基于从淘宝商品搜索接口和淘宝精确月销量接口中提取的数据&#xff0c;分析了前百个品牌在销售额上的占比情况。分析涵盖了销售额和占比的数据&#xff0c;为决策提供了依据。&#xff08;数据获取时间&#xff1a;2024.10.08&#xff09;…

leetcode动态规划(十)-0-1背包理论基础(一维数组)

一维dp数组&#xff08;滚动数组&#xff09; leetcode中无纯0-1背包问题&#xff0c;可从卡码网上查看题目46.0-1背包问题 一维数组来源于二维数组&#xff0c;其本质是对一维数组进行压缩了&#xff0c;压缩后需要注意在进行背包容量循环的时候采用后序遍历&#xff0c;而不…

世界500强,计算机校招月薪 1k ?

大家好&#xff0c;我是鸭鸭&#xff01; 周末闲来无事&#xff0c;鸭鸭又来互联网冲浪了&#xff0c;没想到刷到这么一张图&#xff1a; 现在校招给计算机应届生开出的薪资只有 1&#xff5e;1k 了吗&#xff1f;鸭鸭很难不怀疑这是打错字了。 根据图里信息&#xff0c;这应…

【数据库设计】逻辑结构设计

E-R实体集的转换 概念结构设计之后就是对E-R图进行逻辑结构设计&#xff1a;即将E-R图转化成关系的过程。逻辑结构设计主要用于数据库管理系统上&#xff0c;为了让数据在计算机系统中更好地表示。 此设计过程用到的数据模型有&#xff1a;除了前面讲过的关系模型还有层次模型…

双十一该买什么比较好?双十一推荐好物清单分享

​是不是很多朋友跟我一样&#xff0c;已经为双11做好了准备&#xff0c;打算开启买买买的节奏&#xff01;作为一名家居兼数码博主&#xff0c;每年双11的时候都会疯狂囤很多物品&#xff0c;所以今天就跟大家来分享一下&#xff0c;我的双11购物清单&#xff0c;也给大家参考…

数据结构部分混淆

1.随机存储和顺序存储&#xff1a; 随机存取&#xff1a;数组&#xff0c;当存储器中的数据被读取或写入时&#xff0c;所需要的时间与该数据所在的物理地址无关 顺序存取&#xff1a;链表&#xff0c;当存储器中的数据被读取或写入时&#xff0c;所需要的时间与该数据所在的物…

Java:IDEA生成JavaDoc文档

有些开源项目并未直接提供api&#xff0c;但是通过从github、gitee下载项目可以手动生成Java文档。 步骤如下。 使用IDEA打开项目。 选择生成Java Doc文档&#xff1a; 第一步&#xff1a;选择生成JavaDoc文档的范围&#xff0c;我只对一个源文件生成Doc&#xff0c;所以选择…

鸿蒙开发超好用的 UI 组件和工具类库 BasicLibrary

大家好&#xff0c;我是 V 哥。你在学习HarmonyOS NEXT 开发吗&#xff0c;今天 V 哥给你推荐一款超好用的三方库BasicLibrary&#xff0c;BasicLibrary 是一个基于 API 11 封装的基本库&#xff0c;旨在提升鸿蒙开发效率。它包含了一些常用的 UI 组件和实用工具类&#xff0c;…

Java基础:面向对象编程8

1 Java 注解 1.1 概念 定义&#xff1a;注解&#xff08;Annotation&#xff09;是在 Java 1.5 时引入的概念&#xff0c;同 class 和 interface 一样&#xff0c;属于一种类型。作用&#xff1a;注解提供了一系列数据用来装饰程序代码&#xff08;类、方法、字段等&#xff…

git配置以及如何删除git

你努力学习和充实自己&#xff0c;除了提升自身的价值&#xff0c;最实际的是当遇到有喜欢的人和事的时候&#xff0c;除了一片真心&#xff0c;还有拿得出手的东西 作用 记录开发的历史&#xff0c;每次记录就是一个版本&#xff0c;而且可以回到历史的某个版本可以实现多人合…

浅谈普通学历编码学生代码编程之路--24程序员节

#1024程序员节 | 征文# 今夕已是2024年&#xff0c;楼主是一名大四学生 &#xff0c;即将进入社会&#xff0c;曾经总是拿码农来开程序员的玩笑 &#xff0c;也是也是幻想着成为一名码农啊&#xff0c;先说说个人学习Java之路 &#xff0c;从步入大学开始就是主学Java&#xff…

【MyBatis】MyBatis缓存原理详解

目录 一、简介 二、缓存类介绍 2.1 PerpetualCache 2.2 LruCache 2.3 BlockingCache 三、CacheKey 3.1 总结&#xff1a;二级缓存命中条件 四、一级缓存 五、二级缓存 5.1 二级缓存如何组装这些装饰器&#xff1f;&#xff08;解析SQL映射文件中的标签&#xff0c;初…

2025青海选调生报名指南及照片要求

第一步&#xff1a;用户信息注册&#xff0c;仔细阅读公告内容 第二步&#xff1a;人脸识别认证、上传照片 第三步&#xff1a;确认基本信息、照片确认 第四步&#xff1a;填写报考信息&#xff0c;请务必于11月5日18:00前提交 第五步&#xff1a;打印报名表&#xff0c;下载《…