WebGL层次模型——多节点模型

news2024/9/27 7:21:05

目录

多节点模型

MultiJointModel中的层次结构

控制各部件旋转角度的变量

示例程序——共用顶点数据,通过模型矩阵缩放实现(MultiJointModel.js) 

MultiJointModel.js(按键响应部分) 

MultiJointModel.js(绘制模型部分) 

MultiJointModel.js(绘制部件drawBox())

示例程序——各自对应各自顶点数据实现(MultiJointModel_segment.js)

代码详解 

示例效果


多节点模型

本文将把WebGL层次模型——单节点模型_山楂树の的博客-CSDN博客JointMode扩展为MultiJointModel,后者绘制一个具有多个关节的完整的机器人手臂,包括基座(base)、上臂(arm1)、前臂(arm2)、手掌(palm)、两根手指(finger1 & finger2),全部可以通过键盘来控制。arm1和arm2的连接关节joint1位于arm1顶部,arm2和palm的连接关节joint2位于arm2顶部,finger1和finger2位于palm一端,如下图所示。

MultiJointModel中的层次结构

用户可以通过键盘操纵机器人手臂,arm1和arm2的操作和JointModel一样,此外,还可以使用x和z键旋转joint2(腕关节),使用C和V键旋转finger1和finger2。控制这些小部件旋转角度的全局变量,如下图所示。 

控制各部件旋转角度的变量

我们将用如下两种方式实现 

示例程序——共用顶点数据,通过模型矩阵缩放实现(MultiJointModel.js) 

示例程序MultiJointModelJointModel相比,主要有两处不同:keydown()函数响应更多的按键情况,draw()函数绘制各部件的逻辑更复杂了。首先来看keydown()函数,如下所示:

var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec4 a_Normal;\n' +
  'uniform mat4 u_MvpMatrix;\n' +
  'uniform mat4 u_NormalMatrix;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\n' +
  '  vec3 lightDirection = normalize(vec3(0.0, 0.5, 0.7));\n' + // 归一化光线方向
  '  vec4 color = vec4(1.0, 0.4, 0.0, 1.0);\n' +
  '  vec3 normal = normalize((u_NormalMatrix * a_Normal).xyz);\n' +
  '  float nDotL = max(dot(normal, lightDirection), 0.0);\n' +
  '  v_Color = vec4(color.rgb * nDotL + vec3(0.1), color.a);\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';

function main() {
  var canvas = document.getElementById('webgl');
  var gl = getWebGLContext(canvas);
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return
  var 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_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix'); // 用于计算法向量的矩阵
  // 计算视图投影矩阵
  var viewProjMatrix = new Matrix4();
  viewProjMatrix.setPerspective(50.0, canvas.width / canvas.height, 1.0, 100.0);
  viewProjMatrix.lookAt(20.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
  // 注册按键时要调用的事件处理程序
  document.onkeydown = function (ev) { keydown(ev, gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); };
  draw(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); // 绘制立方体
}

var ANGLE_STEP = 3.0;     // 旋转角度的增量(度)
var g_arm1Angle = 90.0;   // 手臂1的旋转角度(度)
var g_joint1Angle = 45.0; // 关节1的旋转角度(度)
var g_joint2Angle = 0.0;  // 关节2的旋转角度(度)
var g_joint3Angle = 0.0;  // 关节3的旋转角度(度)
function keydown(ev, gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix) {
  switch (ev.keyCode) {
    case 40: // Up arrow key -> the positive rotation of joint1 around the z-axis
      if (g_joint1Angle < 135.0) g_joint1Angle += ANGLE_STEP;
      break;
    case 38: // Down arrow key -> the negative rotation of joint1 around the z-axis
      if (g_joint1Angle > -135.0) g_joint1Angle -= ANGLE_STEP;
      break;
    case 39: // Right arrow key -> the positive rotation of arm1 around the y-axis
      g_arm1Angle = (g_arm1Angle + ANGLE_STEP) % 360;
      break;
    case 37: // Left arrow key -> the negative rotation of arm1 around the y-axis
      g_arm1Angle = (g_arm1Angle - ANGLE_STEP) % 360;
      break;
    case 90: // 'z'key -> the positive rotation of joint2
      g_joint2Angle = (g_joint2Angle + ANGLE_STEP) % 360;
      break;
    case 88: // 'x'key -> the negative rotation of joint2
      g_joint2Angle = (g_joint2Angle - ANGLE_STEP) % 360;
      break;
    case 86: // 'v'key -> the positive rotation of joint3
      if (g_joint3Angle < 60.0) g_joint3Angle = (g_joint3Angle + ANGLE_STEP) % 360;
      break;
    case 67: // 'c'key -> the nagative rotation of joint3
      if (g_joint3Angle > -60.0) g_joint3Angle = (g_joint3Angle - ANGLE_STEP) % 360;
      break;
    default: return; // Skip drawing at no effective action
  }
  // Draw the robot arm
  draw(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix);
}

function initVertexBuffers(gl) {
  // 坐标(一侧长度为1,原点位于底部中心的立方体)
  var vertices = new Float32Array([
    0.5, 1.0, 0.5, -0.5, 1.0, 0.5, -0.5, 0.0, 0.5, 0.5, 0.0, 0.5, // v0-v1-v2-v3 front
    0.5, 1.0, 0.5, 0.5, 0.0, 0.5, 0.5, 0.0, -0.5, 0.5, 1.0, -0.5, // v0-v3-v4-v5 right
    0.5, 1.0, 0.5, 0.5, 1.0, -0.5, -0.5, 1.0, -0.5, -0.5, 1.0, 0.5, // v0-v5-v6-v1 up
    -0.5, 1.0, 0.5, -0.5, 1.0, -0.5, -0.5, 0.0, -0.5, -0.5, 0.0, 0.5, // v1-v6-v7-v2 left
    -0.5, 0.0, -0.5, 0.5, 0.0, -0.5, 0.5, 0.0, 0.5, -0.5, 0.0, 0.5, // v7-v4-v3-v2 down
    0.5, 0.0, -0.5, -0.5, 0.0, -0.5, -0.5, 1.0, -0.5, 0.5, 1.0, -0.5  // v4-v7-v6-v5 back
  ]);
  var normals = new Float32Array([
    0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
    1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
    0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
    -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
    0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
    0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0  // v4-v7-v6-v5 back
  ]);
  var indices = new Uint8Array([
    0, 1, 2, 0, 2, 3,    // front
    4, 5, 6, 4, 6, 7,    // right
    8, 9, 10, 8, 10, 11,    // up
    12, 13, 14, 12, 14, 15,    // left
    16, 17, 18, 16, 18, 19,    // down
    20, 21, 22, 20, 22, 23     // back
  ]);
  // 将顶点属性写入缓冲区(坐标和法线)
  if (!initArrayBuffer(gl, 'a_Position', vertices, gl.FLOAT, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', normals, gl.FLOAT, 3)) return -1;
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  var indexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
  return indices.length;
}

function initArrayBuffer(gl, attribute, data, type, num) {
  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;
}

// 坐标变换矩阵
var g_modelMatrix = new Matrix4(), g_mvpMatrix = new Matrix4();
function draw(gl, n, viewProjMatrix, u_MvpMatrix, u_NormalMatrix) {
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // 基座
  var baseHeight = 2.0;
  g_modelMatrix.setTranslate(0.0, -12.0, 0.0);
  drawBox(gl, n, 10.0, baseHeight, 10.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix);

  // Arm1
  var arm1Length = 10.0;
  g_modelMatrix.translate(0.0, baseHeight, 0.0);     // Move onto the base
  g_modelMatrix.rotate(g_arm1Angle, 0.0, 1.0, 0.0);  // Rotate around the y-axis
  drawBox(gl, n, 3.0, arm1Length, 3.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); // Draw

  // Arm2
  var arm2Length = 10.0;
  g_modelMatrix.translate(0.0, arm1Length, 0.0);       // Move to joint1
  g_modelMatrix.rotate(g_joint1Angle, 0.0, 0.0, 1.0);  // Rotate around the z-axis
  drawBox(gl, n, 4.0, arm2Length, 4.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix); // Draw

  // A palm
  var palmLength = 2.0;
  g_modelMatrix.translate(0.0, arm2Length, 0.0);       // Move to palm
  g_modelMatrix.rotate(g_joint2Angle, 0.0, 1.0, 0.0);  // Rotate around the y-axis
  drawBox(gl, n, 2.0, palmLength, 6.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix);  // Draw

  // 移动至手掌中心
  g_modelMatrix.translate(0.0, palmLength, 0.0);

  // 第一个手指
  pushMatrix(g_modelMatrix); // 此操作防止第二个手指依赖第一个手指的模型矩阵,如旋转平移大变换或小变换的操作
  g_modelMatrix.translate(0.0, 0.0, 2.0);
  g_modelMatrix.rotate(g_joint3Angle, 1.0, 0.0, 0.0);  // Rotate around the x-axis
  drawBox(gl, n, 1.0, 2.0, 1.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix);
  g_modelMatrix = popMatrix();

  // 第二个手指
  g_modelMatrix.translate(0.0, 0.0, -2.0);
  g_modelMatrix.rotate(-g_joint3Angle, 1.0, 0.0, 0.0);  // Rotate around the x-axis
  drawBox(gl, n, 1.0, 2.0, 1.0, viewProjMatrix, u_MvpMatrix, u_NormalMatrix);
}

var g_matrixStack = []; // 用于存储矩阵的数组
function pushMatrix(m) { // 将指定的矩阵存储到数组中
  var m2 = new Matrix4(m);
  g_matrixStack.push(m2);
}

function popMatrix() { // 删除最后一个矩阵
  return g_matrixStack.pop();
}

var g_normalMatrix = new Matrix4();  // 法向量变换的矩阵
//绘制长方体
function drawBox(gl, n, width, height, depth, viewProjMatrix, u_MvpMatrix, u_NormalMatrix) {
  pushMatrix(g_modelMatrix);   // 每次绘制部件前,保存绘制前的模型矩阵,因为会有缩放操作,以防影响下个部件的大小
  // 长宽高缩放对应系数
  g_modelMatrix.scale(width, height, depth);
  // 计算模型视图投影矩阵并将其传递给u_MvpMatrix
  g_mvpMatrix.set(viewProjMatrix);
  g_mvpMatrix.multiply(g_modelMatrix);
  gl.uniformMatrix4fv(u_MvpMatrix, false, g_mvpMatrix.elements);
  // 计算模型运动后,法向量的值
  g_normalMatrix.setInverseOf(g_modelMatrix); // 逆旋转
  g_normalMatrix.transpose(); // 转置矩阵
  gl.uniformMatrix4fv(u_NormalMatrix, false, g_normalMatrix.elements);
  // 绘制立方体
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
  g_modelMatrix = popMatrix();   // 释放绘制前保存的模型矩阵供下个部件使用
}

MultiJointModel.js(按键响应部分) 

本例的keydown()函数,除了需要在方向键被按下时作出响应,更新g_arm1Angle和g_jointAngle变量(就像JointModel中一样),还需要在Z键、X键、V键和C键被按下时做出响应(第61、64、67和70行),更新g_joint2Angle和g_joint3Angle变量。在此之后,就调用draw()函数,把整个模型画出来。

模型的各个部件base、arm1、arm2、palm、finger1和finger2等虽然都是立方体,但是长宽高各不相同。所以本例扩展了drawBox()函数,添加了3个参数:

新增加的3个参数表示部件的宽度、高度和长度(深度),drawBox()会根据这3个参数,将部件分毫不差地绘制出来。

MultiJointModel.js(绘制模型部分) 

draw()函数的任务和JointModel中的相同,就是对每个部件进行:(1)平移,(2)旋转,(3)绘制。首先,base不会旋转,所以只需要将其移动到合适的位置(第132行),再调用drawBox()进行绘制。通过向drawBox()传入参数,我们指定base的宽度是10,高度是2,长度是10,即一个扁平的基座。 

然后,按照arm1、arm2和palm这些部件在模型中的层次顺序,对每一个部件都进行上述三个步骤,这与JointModel中的是一样的。

比较麻烦的是finger1和finger2,因为它们并不是上下层的关系,而是都连接在palm上,此时要格外注意计算模型矩阵的过程。首先来看finger1,它相对于palm原点沿Z轴平移了2.0单位,并且可以绕X轴旋转,我们执行上述三个步骤。相关代码如下所示:

接着看finger2,如果遵循上述同样的步骤,沿z轴平移-2.0个单位并绕X轴旋转(这是finger2相对palm的位置)就会出现问题。在将模型矩阵“沿z轴平移-2.0个单位”之前,模型矩阵实际上处于绘制finger1的状态,这会导致finger2连接在finger1而不是palm上,使得finger1转动带动finger2。 

所以,我们需要在绘制finger1之前,先将模型矩阵保存起来;绘制完finger1后,再将保存的模型矩阵取出来作为当前的模型矩阵,并继续绘制finger2。可以使用一个栈结构来完成这项操作:调用pushMatrix()并将模型矩阵g_modelMatrix作为参数传入,将当时模型矩阵的状态保存起来(第157行),然后在绘制完finger1后,调用popMatrix()获取之前保存的矩阵,并赋给g_modelMatrix(第161行),使模型矩阵又回到绘制finger1之前的状态,在此基础上绘制finger2。

pushMatrix()函数和popMatrix()函数如下所示,它们使用全局变量g_matrixStack来存储矩阵(第169行),前者向栈中压入一个矩阵,而后者从栈中弹出一个。

只要栈足够深,用这种方法就可以绘制任意复杂的层次结构模型。我们只需要按照层次顺序,从高到低绘制部件,并在绘制“具有兄弟部件”的部件前将模型矩阵压入栈,绘制完再弹出即可。

MultiJointModel.js(绘制部件drawBox())

最后看一下drawBox()函数,该函数的任务是绘制机器人手臂的一个部件,它接收若干个参数:

参数width、height和depth分别表示待绘制部件的宽度、高度和深度。其他的参数与JointMode.js中无异:参数viewMatrix表示视图矩阵,u_MvpMatrix表示模型试图投影矩阵,u_NormalMatrix表示用来计算变换后的法向量矩阵,后两者将被传给顶点着色器中相应的同名uniform变量。 

此外,与JointMode不同的是,本例中部件的三维模型是标准化的立方体,其边长为1,原点位于底面。drawBox()函数的定义如下所示:

如你所见,drawBox()函数首先将模型矩阵乘以由width、height和depth参数生成的缩放矩阵,使绘制出的立方体尺寸与设想的一样。然后使用pushMatrix()函数将模型矩阵压入栈中(第182行),使用popMatrix()再重新获得之(第195行)。如果不这样做,当绘制arm2的时候,对arm1的拉伸效果还会仍然留在模型矩阵中,并影响arm2的绘制。所以,在第195行执行之后,模型矩阵又回到了第182行的状态。 

虽然pushMatrix()函数和popMatrix()函数使代码变得更复杂了,但这是值得的,因为你只用了一组顶点数据就绘制了好几个大小位置各不相同的立方体部件。或者,我们也可以对每一个部件都单独使用一组顶点数据,接下来就看看如何实现。

示例程序——各自对应各自顶点数据实现(MultiJointModel_segment.js)

这一节将换一种方式来绘制机器人手臂,那就是,对每一个部件,都定义一组顶点数据,并存储在一个单独的缓冲区对象中。通常,一个部件的顶点数据包括坐标、法向量、索引值等,但是这里的每个部件都是立方体,所以你可以让各部件共享法向量和索引值,而仅仅为各个部件单独定义顶点坐标。每个部件的顶点坐标数据分别存储在对应的缓冲区中,在绘制整条机器人手臂时轮流使用。如下显示了程序的代码。

// MultiJointModel_segment.js (c) 2012 matsuda
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'attribute vec4 a_Normal;\n' +
  'uniform mat4 u_MvpMatrix;\n' +
  'uniform mat4 u_NormalMatrix;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\n' +
  // The followings are some shading calculation to make the arm look three-dimensional
  '  vec3 lightDirection = normalize(vec3(0.0, 0.5, 0.7));\n' + // Light direction
  '  vec4 color = vec4(1.0, 0.4, 0.0, 1.0);\n' +  // Robot color
  '  vec3 normal = normalize((u_NormalMatrix * a_Normal).xyz);\n' +
  '  float nDotL = max(dot(normal, lightDirection), 0.0);\n' +
  '  v_Color = vec4(color.rgb * nDotL + vec3(0.1), color.a);\n' +
  '}\n';

// Fragment shader program
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';

function main() {
  // Retrieve <canvas> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Set the vertex information
  var n = initVertexBuffers(gl);
  if (n < 0) {
    console.log('Failed to set the vertex information');
    return;
  }

  // Set the clear color and enable the depth test
  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);

  // Get the storage locations of attribute and uniform variables
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  if (a_Position < 0 || !u_MvpMatrix || !u_NormalMatrix) {
    console.log('Failed to get the storage location of attribute or uniform variable');
    return;
  }

  // Calculate the view projection matrix
  var viewProjMatrix = new Matrix4();
  viewProjMatrix.setPerspective(50.0, canvas.width / canvas.height, 1.0, 100.0);
  viewProjMatrix.lookAt(20.0, 10.0, 30.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

  // Register the event handler to be called on key press
  document.onkeydown = function(ev){ keydown(ev, gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix); };

  draw(gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
}

var ANGLE_STEP = 3.0;     // The increments of rotation angle (degrees)
var g_arm1Angle = 90.0;   // The rotation angle of arm1 (degrees)
var g_joint1Angle = 45.0; // The rotation angle of joint1 (degrees)
var g_joint2Angle = 0.0;  // The rotation angle of joint2 (degrees)
var g_joint3Angle = 0.0;  // The rotation angle of joint3 (degrees)

function keydown(ev, gl, o, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
  switch (ev.keyCode) {
    case 40: // Up arrow key -> the positive rotation of joint1 around the z-axis
      if (g_joint1Angle < 135.0) g_joint1Angle += ANGLE_STEP;
      break;
    case 38: // Down arrow key -> the negative rotation of joint1 around the z-axis
      if (g_joint1Angle > -135.0) g_joint1Angle -= ANGLE_STEP;
      break;
    case 39: // Right arrow key -> the positive rotation of arm1 around the y-axis
      g_arm1Angle = (g_arm1Angle + ANGLE_STEP) % 360;
      break;
    case 37: // Left arrow key -> the negative rotation of arm1 around the y-axis
      g_arm1Angle = (g_arm1Angle - ANGLE_STEP) % 360;
      break;
    case 90: // 'z'key -> the positive rotation of joint2
      g_joint2Angle = (g_joint2Angle + ANGLE_STEP) % 360;
      break; 
    case 88: // 'x'key -> the negative rotation of joint2
      g_joint2Angle = (g_joint2Angle - ANGLE_STEP) % 360;
      break;
    case 86: // 'v'key -> the positive rotation of joint3
      if (g_joint3Angle < 60.0)  g_joint3Angle = (g_joint3Angle + ANGLE_STEP) % 360;
      break;
    case 67: // 'c'key -> the nagative rotation of joint3
      if (g_joint3Angle > -60.0) g_joint3Angle = (g_joint3Angle - ANGLE_STEP) % 360;
      break;
    default: return; // Skip drawing at no effective action
  }
  // Draw
  draw(gl, o, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
}

var g_baseBuffer = null;     // Buffer object for a base
var g_arm1Buffer = null;     // Buffer object for arm1
var g_arm2Buffer = null;     // Buffer object for arm2
var g_palmBuffer = null;     // Buffer object for a palm
var g_fingerBuffer = null;   // Buffer object for fingers

function initVertexBuffers(gl){
  // Vertex coordinate (prepare coordinates of cuboids for all segments)
  var vertices_base = new Float32Array([ // Base(10x2x10)
     5.0, 2.0, 5.0, -5.0, 2.0, 5.0, -5.0, 0.0, 5.0,  5.0, 0.0, 5.0, // v0-v1-v2-v3 front
     5.0, 2.0, 5.0,  5.0, 0.0, 5.0,  5.0, 0.0,-5.0,  5.0, 2.0,-5.0, // v0-v3-v4-v5 right
     5.0, 2.0, 5.0,  5.0, 2.0,-5.0, -5.0, 2.0,-5.0, -5.0, 2.0, 5.0, // v0-v5-v6-v1 up
    -5.0, 2.0, 5.0, -5.0, 2.0,-5.0, -5.0, 0.0,-5.0, -5.0, 0.0, 5.0, // v1-v6-v7-v2 left
    -5.0, 0.0,-5.0,  5.0, 0.0,-5.0,  5.0, 0.0, 5.0, -5.0, 0.0, 5.0, // v7-v4-v3-v2 down
     5.0, 0.0,-5.0, -5.0, 0.0,-5.0, -5.0, 2.0,-5.0,  5.0, 2.0,-5.0  // v4-v7-v6-v5 back
  ]);

  var vertices_arm1 = new Float32Array([  // Arm1(3x10x3)
     1.5, 10.0, 1.5, -1.5, 10.0, 1.5, -1.5,  0.0, 1.5,  1.5,  0.0, 1.5, // v0-v1-v2-v3 front
     1.5, 10.0, 1.5,  1.5,  0.0, 1.5,  1.5,  0.0,-1.5,  1.5, 10.0,-1.5, // v0-v3-v4-v5 right
     1.5, 10.0, 1.5,  1.5, 10.0,-1.5, -1.5, 10.0,-1.5, -1.5, 10.0, 1.5, // v0-v5-v6-v1 up
    -1.5, 10.0, 1.5, -1.5, 10.0,-1.5, -1.5,  0.0,-1.5, -1.5,  0.0, 1.5, // v1-v6-v7-v2 left
    -1.5,  0.0,-1.5,  1.5,  0.0,-1.5,  1.5,  0.0, 1.5, -1.5,  0.0, 1.5, // v7-v4-v3-v2 down
     1.5,  0.0,-1.5, -1.5,  0.0,-1.5, -1.5, 10.0,-1.5,  1.5, 10.0,-1.5  // v4-v7-v6-v5 back
  ]);

  var vertices_arm2 = new Float32Array([  // Arm2(4x10x4)
     2.0, 10.0, 2.0, -2.0, 10.0, 2.0, -2.0,  0.0, 2.0,  2.0,  0.0, 2.0, // v0-v1-v2-v3 front
     2.0, 10.0, 2.0,  2.0,  0.0, 2.0,  2.0,  0.0,-2.0,  2.0, 10.0,-2.0, // v0-v3-v4-v5 right
     2.0, 10.0, 2.0,  2.0, 10.0,-2.0, -2.0, 10.0,-2.0, -2.0, 10.0, 2.0, // v0-v5-v6-v1 up
    -2.0, 10.0, 2.0, -2.0, 10.0,-2.0, -2.0,  0.0,-2.0, -2.0,  0.0, 2.0, // v1-v6-v7-v2 left
    -2.0,  0.0,-2.0,  2.0,  0.0,-2.0,  2.0,  0.0, 2.0, -2.0,  0.0, 2.0, // v7-v4-v3-v2 down
     2.0,  0.0,-2.0, -2.0,  0.0,-2.0, -2.0, 10.0,-2.0,  2.0, 10.0,-2.0  // v4-v7-v6-v5 back
  ]);

  var vertices_palm = new Float32Array([  // Palm(2x2x6)
     1.0, 2.0, 3.0, -1.0, 2.0, 3.0, -1.0, 0.0, 3.0,  1.0, 0.0, 3.0, // v0-v1-v2-v3 front
     1.0, 2.0, 3.0,  1.0, 0.0, 3.0,  1.0, 0.0,-3.0,  1.0, 2.0,-3.0, // v0-v3-v4-v5 right
     1.0, 2.0, 3.0,  1.0, 2.0,-3.0, -1.0, 2.0,-3.0, -1.0, 2.0, 3.0, // v0-v5-v6-v1 up
    -1.0, 2.0, 3.0, -1.0, 2.0,-3.0, -1.0, 0.0,-3.0, -1.0, 0.0, 3.0, // v1-v6-v7-v2 left
    -1.0, 0.0,-3.0,  1.0, 0.0,-3.0,  1.0, 0.0, 3.0, -1.0, 0.0, 3.0, // v7-v4-v3-v2 down
     1.0, 0.0,-3.0, -1.0, 0.0,-3.0, -1.0, 2.0,-3.0,  1.0, 2.0,-3.0  // v4-v7-v6-v5 back
  ]);

  var vertices_finger = new Float32Array([  // Fingers(1x2x1)
     0.5, 2.0, 0.5, -0.5, 2.0, 0.5, -0.5, 0.0, 0.5,  0.5, 0.0, 0.5, // v0-v1-v2-v3 front
     0.5, 2.0, 0.5,  0.5, 0.0, 0.5,  0.5, 0.0,-0.5,  0.5, 2.0,-0.5, // v0-v3-v4-v5 right
     0.5, 2.0, 0.5,  0.5, 2.0,-0.5, -0.5, 2.0,-0.5, -0.5, 2.0, 0.5, // v0-v5-v6-v1 up
    -0.5, 2.0, 0.5, -0.5, 2.0,-0.5, -0.5, 0.0,-0.5, -0.5, 0.0, 0.5, // v1-v6-v7-v2 left
    -0.5, 0.0,-0.5,  0.5, 0.0,-0.5,  0.5, 0.0, 0.5, -0.5, 0.0, 0.5, // v7-v4-v3-v2 down
     0.5, 0.0,-0.5, -0.5, 0.0,-0.5, -0.5, 2.0,-0.5,  0.5, 2.0,-0.5  // v4-v7-v6-v5 back
  ]);

  // Normal
  var normals = new Float32Array([
     0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0,  0.0, 0.0, 1.0, // v0-v1-v2-v3 front
     1.0, 0.0, 0.0,  1.0, 0.0, 0.0,  1.0, 0.0, 0.0,  1.0, 0.0, 0.0, // v0-v3-v4-v5 right
     0.0, 1.0, 0.0,  0.0, 1.0, 0.0,  0.0, 1.0, 0.0,  0.0, 1.0, 0.0, // v0-v5-v6-v1 up
    -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
     0.0,-1.0, 0.0,  0.0,-1.0, 0.0,  0.0,-1.0, 0.0,  0.0,-1.0, 0.0, // v7-v4-v3-v2 down
     0.0, 0.0,-1.0,  0.0, 0.0,-1.0,  0.0, 0.0,-1.0,  0.0, 0.0,-1.0  // v4-v7-v6-v5 back
  ]);

  // Indices of the vertices
  var indices = new Uint8Array([
     0, 1, 2,   0, 2, 3,    // front
     4, 5, 6,   4, 6, 7,    // right
     8, 9,10,   8,10,11,    // up
    12,13,14,  12,14,15,    // left
    16,17,18,  16,18,19,    // down
    20,21,22,  20,22,23     // back
  ]);

  // Write coords to buffers, but don't assign to attribute variables
  g_baseBuffer = initArrayBufferForLaterUse(gl, vertices_base, 3, gl.FLOAT);
  g_arm1Buffer = initArrayBufferForLaterUse(gl, vertices_arm1, 3, gl.FLOAT);
  g_arm2Buffer = initArrayBufferForLaterUse(gl, vertices_arm2, 3, gl.FLOAT);
  g_palmBuffer = initArrayBufferForLaterUse(gl, vertices_palm, 3, gl.FLOAT);
  g_fingerBuffer = initArrayBufferForLaterUse(gl, vertices_finger, 3, gl.FLOAT);
  if (!g_baseBuffer || !g_arm1Buffer || !g_arm2Buffer || !g_palmBuffer || !g_fingerBuffer) return -1;

  // Write normals to a buffer, assign it to a_Normal and enable it
  if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;

  // Write the indices to the buffer object
  var indexBuffer = gl.createBuffer();
  if (!indexBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

function initArrayBufferForLaterUse(gl, data, num, type){
  var buffer = gl.createBuffer();   // Create a buffer object
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return null;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  // Store the necessary information to assign the object to the attribute variable later
  buffer.num = num;
  buffer.type = type;

  return buffer;
}

function initArrayBuffer(gl, attribute, data, num, type){
  var buffer = gl.createBuffer();   // Create a buffer object
  if (!buffer) {
    console.log('Failed to create the buffer object');
    return false;
  }
  // Write date into the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  // Assign the buffer object to the attribute variable
  var a_attribute = gl.getAttribLocation(gl.program, attribute);
  if (a_attribute < 0) {
    console.log('Failed to get the storage location of ' + attribute);
    return false;
  }
  gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  gl.enableVertexAttribArray(a_attribute);

  return true;
}


// Coordinate transformation matrix
var g_modelMatrix = new Matrix4(), g_mvpMatrix = new Matrix4();

function draw(gl, n, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  // Draw a base
  var baseHeight = 2.0;
  g_modelMatrix.setTranslate(0.0, -12.0, 0.0);
  drawSegment(gl, n, g_baseBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
 
  // Arm1
  var arm1Length = 10.0;
  g_modelMatrix.translate(0.0, baseHeight, 0.0);     // Move onto the base
  g_modelMatrix.rotate(g_arm1Angle, 0.0, 1.0, 0.0);  // Rotate around the y-axis
  drawSegment(gl, n, g_arm1Buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix); // Draw

  // Arm2
  var arm2Length = 10.0;
  g_modelMatrix.translate(0.0, arm1Length, 0.0);       // Move to joint1
  g_modelMatrix.rotate(g_joint1Angle, 0.0, 0.0, 1.0);  // Rotate around the z-axis
  drawSegment(gl, n, g_arm2Buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix); // Draw

  // A palm
  var palmLength = 2.0;
  g_modelMatrix.translate(0.0, arm2Length, 0.0);       // Move to palm
  g_modelMatrix.rotate(g_joint2Angle, 0.0, 1.0, 0.0);  // Rotate around the y-axis
  drawSegment(gl, n, g_palmBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);  // Draw

  // Move to the center of the tip of the palm
  g_modelMatrix.translate(0.0, palmLength, 0.0);

  // Draw finger1
  pushMatrix(g_modelMatrix);
    g_modelMatrix.translate(0.0, 0.0, 2.0);
    g_modelMatrix.rotate(g_joint3Angle, 1.0, 0.0, 0.0);  // Rotate around the x-axis
    drawSegment(gl, n, g_fingerBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
  g_modelMatrix = popMatrix();

  // Finger2
  g_modelMatrix.translate(0.0, 0.0, -2.0);
  g_modelMatrix.rotate(-g_joint3Angle, 1.0, 0.0, 0.0);  // Rotate around the x-axis
  drawSegment(gl, n, g_fingerBuffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix);
}

var g_matrixStack = []; // Array for storing a matrix
function pushMatrix(m) { // Store the specified matrix to the array
  var m2 = new Matrix4(m);
  g_matrixStack.push(m2);
}

function popMatrix() { // Retrieve the matrix from the array
  return g_matrixStack.pop();
}

var g_normalMatrix = new Matrix4();  // Coordinate transformation matrix for normals

// Draw segments
function drawSegment(gl, n, buffer, viewProjMatrix, a_Position, u_MvpMatrix, u_NormalMatrix) {
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  // Assign the buffer object to the attribute variable
  gl.vertexAttribPointer(a_Position, buffer.num, buffer.type, false, 0, 0);
  // Enable the assignment of the buffer object to the attribute variable
  gl.enableVertexAttribArray(a_Position);

  // Calculate the model view project matrix and pass it to u_MvpMatrix
  g_mvpMatrix.set(viewProjMatrix);
  g_mvpMatrix.multiply(g_modelMatrix);
  gl.uniformMatrix4fv(u_MvpMatrix, false, g_mvpMatrix.elements);
  // Calculate matrix for normal and pass it to u_NormalMatrix
  g_normalMatrix.setInverseOf(g_modelMatrix);
  g_normalMatrix.transpose();
  gl.uniformMatrix4fv(u_NormalMatrix, false, g_normalMatrix.elements);
  // Draw
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}

代码详解 

 示例程序的关键点是:(1)为每个部件单独创建一个缓冲区,在其中存储顶点的坐标数据;(2)绘制部件之前,将相应缓冲区对象分配给a_Position变量;(3)开启a_Position变量并绘制该部件。

main()函数的流程很简单,包括初始化缓冲区(第47行),获取a_Position的存储地址(第58行),然后调用draw()函数进行绘制(第73行)等。

接着来看initVertxBuffers()函数(第121行),该函数之前定义了若干全局变量,表示存储各个部件顶点坐标数据的缓冲区对象(第115~119行)。本例与MultiJoint-Model.js的主要区别在顶点坐标上(第123行),我们不再使用一个立方体经过不同变换来绘制不同的部件,而是将每个部件的顶点坐标分开定义在不同的数组中(比如base立方体的顶点坐标定义在vertice_base中,arm1立方体的顶点坐标定义在vertices_arm1中,等等)。真正创建这些缓冲区对象是由initArrayBufferForLaterUse()函数完成的(第189~193行)。该函数定义如下: 

initArrayBufferForLaterUse()函数首先创建了缓冲区对象(第212行),然后向其中写入数据(第218~219行)。注意,函数并没有将缓冲区对象分配给attribute变量(gl.vertexAttribPointer())或开启attribute变量(gl.enableVertexAttribArray()),这两个步骤将留到真正进行绘制之前再完成。另外,为了便于将缓冲区分配给attribute变量,我们手动为其添加了两个属性num和type(第222~223行)。

这里利用了JavaScript的一个有趣的特性,就是可以自由地为对象添加新的属性。你可以直接通过属性名为对象添加新属性,并向其赋值。如你所见,我们为缓冲区对象添加了新的num属性并保存其中顶点的个数(第222行),添加了type属性以保存数据类型(第223行)。当然,也可以通过相同的方式访问这些属性。注意,在使用JavaScript的这项特性时应格外小心,如果不小心拼错了属性名,浏览器也不会报错。同样你也应该记得,这样做会增加性能开销。

最后,调用draw()函数绘制整个模型(第311行),与MultiJointModel中一样。但是调用drawSegments()函数的方式与前例调用drawBox()函数的方式有所不同,第3个参数是存储了顶点坐标数据的缓冲区对象,如下所示。 

drawSegments()函数的定义在第311行,它将缓冲区对象分配给a_Position变量(第314行)并开启之(第316行),然后调用gl.drawElements()进行绘制操作(第327行)。这里使用了之前为缓冲区对象添加的num和type属性。

 这一次,你不必再像前例中那样,在绘制每个部件时对模型矩阵进行缩放操作了,因为每个部件的顶点坐标都已经事先定义好了。同样也没必要再使用栈来管理模型矩阵,所以pushMatrix()函数和popMatrix()也不需要了。

示例效果

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

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

相关文章

CentOS 7.5 centos failed to load selinux policy 错误解决方法

这是个 selinux 使能导致的&#xff0c; 关闭即可 在进入到内核选中界面&#xff0c;选中要启动的内核&#xff0c; 按键盘 e 就会进入启动参数界面 进入启动参数界面如图&#xff0c;按上下键找到 UTF8 UTF8如图&#xff0c; 添加 selinux0 添加完成如图&#xff0c; 按 ctr…

编译工具:CMake(八) | cmake 常用指令

编译工具&#xff1a;CMake&#xff08;八&#xff09; | cmake 常用指令 基本指令 基本指令 ADD_DEFINITIONS向 C/C编译器添加-D 定义&#xff0c;比如:ADD_DEFINITIONS(-DENABLE_DEBUG-DABC)&#xff0c;参数之间用空格分割。 如果你的代码中定义了#ifdef ENABLE_DEBUG #end…

OpenCV实现“蓝线挑战“特效

原理 算法原理可以分为三个流程&#xff1a; 1、将视频&#xff08;图像&#xff09;从&#xff08;顶->底&#xff09;或&#xff08;左->右&#xff09;逐行&#xff08;列&#xff09;扫描图像。 2、将扫描完成的行&#xff08;列&#xff09;像素重新生成定格图像…

【JAVA-Day31】深入解析冒泡、选择和插入排序在数组排序中的应用

深入解析冒泡、选择和插入排序在数组排序中的应用 深入解析冒泡、选择和插入排序在数组排序中的应用摘要引言冒泡排序&#xff1a;基本原理和应用场景冒泡排序算法的工作原理什么情况下选择冒泡排序冒泡排序的时间复杂度和空间复杂度 选择排序&#xff1a;精确排序策略和最佳实…

chroma 向量库使用;查询、更新与插入

参考: https://docs.trychroma.com/usage-guide https://blog.csdn.net/weixin_46515328/article/details/131855650 chroma 支持保存文档、向量&#xff0c;和向量的ids值 ##安装 pip install chromadb1、使用 import chromadb##这句可以数据库默认时刻保存&#xff0c;启动…

岭回归与LASSO回归:解析两大经典线性回归方法

文章目录 &#x1f34b;引言&#x1f34b;岭回归&#xff08;Ridge Regression&#xff09;&#x1f34b;实战---岭回归&#x1f34b;LASSO回归&#xff08;LASSO Regression&#xff09;&#x1f34b;实战---LASSO回归&#x1f34b;岭回归和LASSO哪个更容易是直线&#x1f34b…

Cannot read properties of null bug解决方法

项目场景&#xff1a; vue3element plusts。 问题描述 本地运行没有任何错误&#xff0c;在生产环境中&#xff0c;当点击按钮&#xff0c;展示el-dialog时第一次正常&#xff0c;关闭时报错 原因分析&#xff1a; 定位到el-dialog组件&#xff0c;最初认为是dialog中table依…

IMAGEBIND: One Embedding Space To Bind Them All论文笔记

论文https://arxiv.org/pdf/2305.05665.pdf代码https://github.com/facebookresearch/ImageBind 1. Motivation 像CLIP这一类的方法只能实现Text-Image这两个模态的 Embedding 对齐&#xff0c;本文提出的ImageBind能够实现六个模态&#xff08;images, text, audio, depth, t…

进阶指针(一)

✨博客主页&#xff1a;小钱编程成长记 &#x1f388;博客专栏&#xff1a;进阶C语言 进阶指针&#xff08;一&#xff09; 0.回顾初阶指针1.字符指针1.1 相关面试题 2.数组指针3.指针数组3.1 数组指针的定义3.2 &数组名VS数组名3.3 数组指针的使用 4.数组传参和指针传参4.…

linux下解决tomcat错误问题

错误一&#xff1a; Linux下Tomcat启动报错&#xff1a;Neither the JAVA_HOME nor the JRE_HOME environment variable is defined 原因&#xff1a;可能是Linux环境变了&#xff0c;需要在catalina.sh文件里指定JDK路径 解决方式&#xff1a; 在/bin/catalina.sh配置文件中加…

基于springboot+vue的爱心助农网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Linux离线安装telnet

TELNET rmp地址 链接: https://pan.baidu.com/s/1Yl3zt1p3zjp2fYTwByf7dQ?pwdgdcg 提取码: gdcg 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 命令 rpm -ivh xinetd-2.3.15-14.el7.x86_64.rpm rpm -ivh telnet-0.17-65.el7_8.x86_64.rpm rpm -ivh telnet-…

autoware.ai docker安装

1.进行docker安装 sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo apt-key fingerprint 0EBFCD88sudo add-apt-reposi…

Unity实战(11):项目非启动状态下使用代码批量替换材质

目录 前言 配置环境 一、场景准备 二、代码演示 三、效果呈现 四、关于Resources.Load()的说明 前言 本文内容为unity在编辑状态&#xff08;非启动状态&#xff09;下使用代码批量替换材质&#xff0c;该方法也适用于其他在编辑状态下对物体的操作需求。 配置环境 win1…

Mysql003:用户管理

前言&#xff1a;本章节讲解的是mysql中的用户管理&#xff0c;包括&#xff08;管理数据用户&#xff09;、&#xff08;控制数据库的访问权限&#xff09;。 目录 1. 查询用户 2. 创建用户 3. 修改用户密码 4. 删除用户 5. 权限控制 1. 查询用户 在mysql数据库中&#xff0…

华为手机如何开启设置健康使用手机模式限制孩子玩手机时间?

华为手机如何开启设置健康使用手机模式限制孩子玩手机时间&#xff1f; 1、在手机上找到「设置」并点击打开&#xff1b; 2、在设置内找到「健康使用手机」并点击进入&#xff1b; 3、开启健康使用手机后&#xff0c;选择孩子使用&#xff1b; 4、在健康使用手机内&#xff0c…

已解决 Java Error: Exception in thread ‘main‘ java.lang.ClassNotFoundException

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…

Godot配置C#语言编写脚本(使用VSCode作为外部编辑器)

文章目录 Godot部分查看VSCode的所在位置配置外部编辑器 配置VSCode编写脚本中文注释 其他文章字符编码 Godot部分 打开编辑器-编辑器设置&#xff1b; 查看VSCode的所在位置 右键单击你的VScode快捷方式&#xff0c;选择属性。 这里的目标就是你的VSCode所在的位置。 配…

并发编程——synchronized

文章目录 原子性、有序性、可见性原子性有序性可见性 synchronized使用synchronized锁升级synchronized-ObjectMonitor 原子性、有序性、可见性 原子性 数据库事务的原子性&#xff1a;是一个最小的执行的单位&#xff0c;一次事务的多次操作要么都成功&#xff0c;要么都失败…

【探索Linux】—— 强大的命令行工具 P.9(进程地址空间)

阅读导航 前言一、内存空间分布二、什么是进程地址空间1. 概念2. 进程地址空间的组成 三、进程地址空间的设计原理1. 基本原理2. 虚拟地址空间 概念 大小和范围 作用 虚拟地址空间的优点 3. 页表 四、为什么要有地址空间五、总结温馨提示 前言 前面我们讲了C语言的基础知识&am…