WebGL Easy Tutorial (VII): Draw a rectangle body

1 Overview

On the tutorial "Easy Tutorial the WebGL (f): The first three-dimensional example (using the projective transformation model view)" by using the projective transformation model view, a set of triangles drawn from far and near. But this example is too simple, these coordinates are the coordinates of the triangle remains between -1 and 1, in any case are very easy to set parameters, and may not be very in-depth understanding of the model-view projection transformation.

In this tutorial more step, draw a slightly more complex entity - a rectangular body. In many cases rectangular body can be used for three-dimensional object bounding box, the bounding box is particularly useful in many cases, especially when a UI interaction, so long as the parameters can be set bounding box saw, which must be a three-dimensional object can be seen . For a better understanding of the projective transformation model view, coordinates of the rectangle body is intentionally set to a relatively large float.

2. Example

Improved tutorial on the JS code to obtain a new code as follows:

// 顶点着色器程序
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' + // attribute variable
  'attribute vec4 a_Color;\n' +
  'uniform mat4 u_MvpMatrix;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_Position = u_MvpMatrix * a_Position;\n' + // Set the vertex coordinates of the point
  '  v_Color = a_Color;\n' +
  '}\n';

// 片元着色器程序
var FSHADER_SOURCE =
  'precision mediump float;\n' +
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  gl_FragColor = v_Color;\n' +
  '}\n';

//定义一个矩形体:混合构造函数原型模式
function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) {
  this.minX = minX;
  this.maxX = maxX;
  this.minY = minY;
  this.maxY = maxY;
  this.minZ = minZ;
  this.maxZ = maxZ;
}

Cuboid.prototype = {
  constructor: Cuboid,
  CenterX: function () {
    return (this.minX + this.maxX) / 2.0;
  },
  CenterY: function () {
    return (this.minY + this.maxY) / 2.0;
  },
  CenterZ: function () {
    return (this.minZ + this.maxZ) / 2.0;
  },
  LengthX: function () {
    return (this.maxX - this.minX);
  },
  LengthY: function () {
    return (this.maxY - this.minY);
  }
}

var currentAngle = [35.0, 30.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis])
var curScale = 1.0;   //当前的缩放比例

function main() {
  // 获取 <canvas> 元素
  var canvas = document.getElementById('webgl');

  // 获取WebGL渲染上下文
  var 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 intialize shaders.');
    return;
  }

  // 设置顶点位置
  var cuboid = new Cuboid(399589.072, 400469.072, 3995118.062, 3997558.062, 732, 1268);
  var n = initVertexBuffers(gl, cuboid);
  if (n < 0) {
    console.log('Failed to set the positions of the vertices');
    return;
  }

  // 指定清空<canvas>的颜色
  gl.clearColor(0.0, 0.0, 0.0, 1.0);

  // 开启深度测试
  gl.enable(gl.DEPTH_TEST);

  //绘制函数
  var tick = function () {
    //设置MVP矩阵
    setMVPMatrix(gl, canvas, cuboid);

    //清空颜色和深度缓冲区
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    //绘制矩形体
    gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

    //请求浏览器调用tick
    requestAnimationFrame(tick);  
  };

  //开始绘制
  tick();

  // 绘制矩形体
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}

//设置MVP矩阵
function setMVPMatrix(gl, canvas, cuboid) {
  // Get the storage location of u_MvpMatrix
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  if (!u_MvpMatrix) {
    console.log('Failed to get the storage location of u_MvpMatrix');
    return;
  }

  //模型矩阵
  var modelMatrix = new Matrix4();
  modelMatrix.scale(curScale, curScale, curScale);
  modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 
  modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 
  modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ());

  //投影矩阵
  var fovy = 60;
  var near = 1;
  var projMatrix = new Matrix4();
  projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000);

  //计算lookAt()函数初始视点的高度
  var angle = fovy / 2 * Math.PI / 180.0;  
  var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle;

  //视图矩阵  
  var viewMatrix = new Matrix4();  // View matrix   
  viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0);

  //MVP矩阵
  var mvpMatrix = new Matrix4();
  mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);

  //将MVP矩阵传输到着色器的uniform变量u_MvpMatrix
  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
}

//
function initVertexBuffers(gl, cuboid) {
  // Create a cube
  //    v6----- v5
  //   /|      /|
  //  v1------v0|
  //  | |     | |
  //  | |v7---|-|v4
  //  |/      |/
  //  v2------v3
  // 顶点坐标和颜色
  var verticesColors = new Float32Array([
    cuboid.maxX, cuboid.maxY, cuboid.maxZ, 1.0, 1.0, 1.0,  // v0 White
    cuboid.minX, cuboid.maxY, cuboid.maxZ, 1.0, 0.0, 1.0,  // v1 Magenta
    cuboid.minX, cuboid.minY, cuboid.maxZ, 1.0, 0.0, 0.0,  // v2 Red
    cuboid.maxX, cuboid.minY, cuboid.maxZ, 1.0, 1.0, 0.0,  // v3 Yellow
    cuboid.maxX, cuboid.minY, cuboid.minZ, 0.0, 1.0, 0.0,  // v4 Green
    cuboid.maxX, cuboid.maxY, cuboid.minZ, 0.0, 1.0, 1.0,  // v5 Cyan
    cuboid.minX, cuboid.maxY, cuboid.minZ, 0.0, 0.0, 1.0,  // v6 Blue
    cuboid.minX, cuboid.minY, cuboid.minZ, 1.0, 0.0, 1.0   // v7 Black
  ]);

  //顶点索引
  var indices = new Uint8Array([
    0, 1, 2, 0, 2, 3,    // 前
    0, 3, 4, 0, 4, 5,    // 右
    0, 5, 6, 0, 6, 1,    // 上
    1, 6, 7, 1, 7, 2,    // 左
    7, 4, 3, 7, 3, 2,    // 下
    4, 7, 6, 4, 6, 5     // 后
  ]);

  //
  var FSIZE = verticesColors.BYTES_PER_ELEMENT;   //数组中每个元素的字节数

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

  // 将缓冲区对象绑定到目标
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexColorBuffer);
  // 向缓冲区对象写入数据
  gl.bufferData(gl.ARRAY_BUFFER, verticesColors, gl.STATIC_DRAW);

  //获取着色器中attribute变量a_Position的地址 
  var 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;
  }
  // 将缓冲区对象分配给a_Position变量
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, FSIZE * 6, 0);

  // 连接a_Position变量与分配给它的缓冲区对象
  gl.enableVertexAttribArray(a_Position);

  //获取着色器中attribute变量a_Color的地址 
  var a_Color = gl.getAttribLocation(gl.program, 'a_Color');
  if (a_Color < 0) {
    console.log('Failed to get the storage location of a_Color');
    return -1;
  }
  // 将缓冲区对象分配给a_Color变量
  gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
  // 连接a_Color变量与分配给它的缓冲区对象
  gl.enableVertexAttribArray(a_Color);

  // 将顶点索引写入到缓冲区对象
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

  return indices.length;
}

Process consistent with this code JS code on a colored portion also remain unchanged. It should be mainly concerned with two things: draw objects and settings MVP matrix by the vertex index.

2.1. Vertex indices draw

If a rectangular body is drawn by previous knowledge, a rectangular shape has six faces, each have two triangles, each triangle has three points, it means that the apex 36 is defined. But we know a rectangular body just to have 8 vertices on it, the definition of 36 vertices means that the memory and memory waste. To solve this problem, WebGL provides a method drawn through vertex index: gl.drawElements (). Its function is as follows:
1

In the present example, description of a first object defines a rectangular body, and according to a parameter, which defines the vertex array, comprising the XYZ information and color information.

//定义一个矩形体:混合构造函数原型模式
function Cuboid(minX, maxX, minY, maxY, minZ, maxZ) {
  this.minX = minX;
  this.maxX = maxX;
  this.minY = minY;
  this.maxY = maxY;
  this.minZ = minZ;
  this.maxZ = maxZ;
}

Cuboid.prototype = {
  constructor: Cuboid,
  CenterX: function () {
    return (this.minX + this.maxX) / 2.0;
  },
  CenterY: function () {
    return (this.minY + this.maxY) / 2.0;
  },
  CenterZ: function () {
    return (this.minZ + this.maxZ) / 2.0;
  },
  LengthX: function () {
    return (this.maxX - this.minX);
  },
  LengthY: function () {
    return (this.maxY - this.minY);
  }
}

//...

// 顶点坐标和颜色
var verticesColors = new Float32Array([
  cuboid.maxX, cuboid.maxY, cuboid.maxZ, 1.0, 1.0, 1.0,  // v0 White
  cuboid.minX, cuboid.maxY, cuboid.maxZ, 1.0, 0.0, 1.0,  // v1 Magenta
  cuboid.minX, cuboid.minY, cuboid.maxZ, 1.0, 0.0, 0.0,  // v2 Red
  cuboid.maxX, cuboid.minY, cuboid.maxZ, 1.0, 1.0, 0.0,  // v3 Yellow
  cuboid.maxX, cuboid.minY, cuboid.minZ, 0.0, 1.0, 0.0,  // v4 Green
  cuboid.maxX, cuboid.maxY, cuboid.minZ, 0.0, 1.0, 1.0,  // v5 Cyan
  cuboid.minX, cuboid.maxY, cuboid.minZ, 0.0, 0.0, 1.0,  // v6 Blue
  cuboid.minX, cuboid.minY, cuboid.minZ, 1.0, 0.0, 1.0   // v7 Black
]);

//...

As with the previous code and the color of the vertex arrays are passed to the vertex buffer objects. The difference is that here also defines a vertex index array:

//顶点索引
var indices = new Uint8Array([
  0, 1, 2, 0, 2, 3,    // 前
  0, 3, 4, 0, 4, 5,    // 右
  0, 5, 6, 0, 6, 1,    // 上
  1, 6, 7, 1, 7, 2,    // 左
  7, 4, 3, 7, 3, 2,    // 下
  4, 7, 6, 4, 6, 5     // 后
]);

This array truly rectangular body defines the drawing order of triangles, the apex of each triangle instead of by the index value of the vertex array to WebGL to identify, as shown:
2

Similarly, the vertex index array should be transmitted to an object buffer. But does not bind to the gl.ARRAY_BUFFER and bound to gl.ELEMENT_ARRAY_BUFFER. This parameter represents the contents of the buffer is the index data of the vertices. Related code is as follows:

// 创建缓冲区对象
var indexBuffer = gl.createBuffer();

//...

// 将顶点索引写入到缓冲区对象
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);

Finally, by the above gl.drawElements () function is plotted:

// 绘制矩形体
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);

By drawing a three-dimensional object vertex indices can obviously save memory and memory overhead, the more common point three-dimensional object is, the higher should be used in this way.

2.2. MVP matrix settings

Also provided is placed in a matrix MVP setMVPMatrix () function.

2.2.1 The model matrix

var currentAngle = [35.0, 30.0]; // 绕X轴Y轴的旋转角度 ([x-axis, y-axis])
var curScale = 1.0;   //当前的缩放比例

//...

//模型矩阵
var modelMatrix = new Matrix4();
modelMatrix.scale(curScale, curScale, curScale);
modelMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0); // Rotation around x-axis 
modelMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0); // Rotation around y-axis 
modelMatrix.translate(-cuboid.CenterX(), -cuboid.CenterY(), -cuboid.CenterZ());

In the model matrix, the first level of the center of the rectangular body to the origin of the coordinate system, then rotated 35 degrees about the X axis, rotated 30 degrees about the Y axis, and finally held constant scaling.

2.2.2. The projection matrix

In general, the perspective projection matrix parameters is not easy to set, typically an empirical value may be set to fixed (not absolute).

//投影矩阵
var fovy = 60;
var near = 1;
var projMatrix = new Matrix4();
projMatrix.setPerspective(fovy, canvas.width / canvas.height, 1, 10000);

2.2.3. View matrix

Then through the front of the parameter setting view matrix, so that the view can be displayed exactly rectangular body:

//计算lookAt()函数初始视点的高度
var angle = fovy / 2 * Math.PI / 180.0;  
var eyeHight = (cuboid.LengthY() * 1.2) / 2.0 / angle;

//视图矩阵  
var viewMatrix = new Matrix4();  // View matrix   
viewMatrix.lookAt(0, 0, eyeHight, 0, 0, 0, 0, 1, 0);

Of LookAt () function, it has been the observation point is the origin of the coordinate system, that is, the center position of the rectangular body (rectangular body has been translated a); a direction generally experience a default value (0,1,0); so the key is to find a viewpoint position, it is further, as the high position.

Then the vertical field angle perspective projection setting, can be determined as high, as shown:

It is evident that, when the tangent of the center of the light shines bounding box, bounding box half the length of the Y direction, divided by a high viewpoint that fovy half. This is the origin of the code above to obtain the eyeHight.

2.2.4. MVP Matrix

The model matrix, view matrix, a projection matrix cascaded to give MVP matrix:

//MVP矩阵
var mvpMatrix = new Matrix4();
mvpMatrix.set(projMatrix).multiply(viewMatrix).multiply(modelMatrix);

3. Results

Open the corresponding HTML browser, you may see a colored rectangular body. Results are as follows:
3

4. Reference

Originally part of the code and illustrations from "WebGL Programming Guide," the source link: address . Follow-up will continue to update the contents of this shared directory.

Guess you like

Origin www.cnblogs.com/charlee44/p/11628462.html