javascript - WebGL如何防止旋转速度增加?

标签 javascript webgl

我正在 WebGL 中编写简单的脚本,但我遇到了无法处理的问题。 我想在 Canvas 中以 3D 形式显示某些内容,并在用户选择旋转轴并单击“Uruchom”按钮后旋转它。

但是每次单击按钮时旋转速度都会增加。 我希望它保持不变,但不知道如何。 提前致谢。

var gl_canvas;
var gl_ctx;

var _triangleVertexBuffer;
var _triangleFacesBuffer;
var _position;
var _color;
var _PosMatrix;
var _MovMatrix;
var _ViewMatrix;
var _matrixProjection;
var _matrixMovement;
var _matrixView;

var rotationSpeed = 0.001;
var zoomRatio = -6;

var X, Y, Z;

function runWebGL () {
  getRotation();
  gl_canvas = document.getElementById("glcanvas");
  gl_ctx = gl_getContext(gl_canvas);
  gl_initShaders();
  gl_initBuffers();
  gl_setMatrix();
  gl_draw();
}

function getRotation() {
  X = document.getElementById('rotateX').checked;
  Y = document.getElementById('rotateY').checked;
  Z = document.getElementById('rotateZ').checked;
}

// ==================================================================== //

function gl_getContext (canvas) {
  try {
    var ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    ctx.viewportWidth = canvas.width;
    ctx.viewportHeight = canvas.height;
  } catch (e) {}

  if (!ctx) {
    document.write('Unable to initialize WebGL. Your browser may not support it.')
  }
  return ctx;
}

// ==================================================================== //

// Declare the shaders. They are pieces of code compiled by WebGL and
// executed on the graphics device. They are written in GLSL.
function gl_initShaders () {
  // position of the point - 0. is Z and 1. is W
  // PosMatrix is uniform variable - its value is constant while rendering an object
  // MovMatrix is the movement matrix of the triangle
  // gl_position -> we move position with MovMatrix before projecting it
  var vertexShader = "\n\
attribute vec3 position;\n\
uniform mat4 PosMatrix;\n\
uniform mat4 MovMatrix;\n\
uniform mat4 ViewMatrix; \n\
attribute vec3 color;\n\
varying vec3 vColor;\n\
void main(void) {\n\
gl_Position = PosMatrix * ViewMatrix * MovMatrix * vec4(position, 1.);\n\
vColor = color;\n\
}";

  // set black color
  var fragmentShader = "\n\
precision mediump float;\n\
varying vec3 vColor;\n\
void main(void) {\n\
gl_FragColor = vec4(vColor, 1.);\n\
}";

  // this function is used to compile a shader
  var getShader = function(source, type, typeString) {
    var shader = gl_ctx.createShader(type);
    gl_ctx.shaderSource(shader, source);
    gl_ctx.compileShader(shader);

    if (!gl_ctx.getShaderParameter(shader, gl_ctx.COMPILE_STATUS)) {
      alert('error in' + typeString);
      return false;
    }
    return shader;
  };

  // Compile the vertex and fragment shaders
  var shader_vertex = getShader(vertexShader, gl_ctx.VERTEX_SHADER, "VERTEX");
  var shader_fragment = getShader(fragmentShader, gl_ctx.FRAGMENT_SHADER, "FRAGMENT");


  // Create the Shader program.
  // Shader program is a combination of a vertex and fragment shaders.
  var SHADER_PROGRAM = gl_ctx.createProgram();
  gl_ctx.attachShader(SHADER_PROGRAM, shader_vertex);
  gl_ctx.attachShader(SHADER_PROGRAM, shader_fragment);


  // Linking of the shader program to the WebGL context - gl_ctx,
  // in order to match the shader variables to javascript variables
  gl_ctx.linkProgram(SHADER_PROGRAM);

  // Link PosMatrix\MovMatrix\ViewMatrix GLSL variables to
  // _PosMatrix\_MovMatrix\_ViewMatrix javascript variables
  // Uniforms do not need to be enabled like attributes
  _PosMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "PosMatrix");
  _MovMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "MovMatrix");
  _ViewMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "ViewMatrix");

  // position GLSL variable links to _position variable
  _position = gl_ctx.getAttribLocation(SHADER_PROGRAM, "position");    // *******
  // color GLSL variable links to _color variable
  _color = gl_ctx.getAttribLocation(SHADER_PROGRAM, "color");
  // enable GLSL attributes variables
  gl_ctx.enableVertexAttribArray(_position);
  gl_ctx.enableVertexAttribArray(_color);
  // linking is over - tells WebGL context to use SHADER_PROGRAM for rendering.
  gl_ctx.useProgram(SHADER_PROGRAM);
}

// ==================================================================== //

function gl_initBuffers () {
  // Point coordinates array of the triangle
  //   var triangleVertices = [
  //      -1, -1, 0,    // bottom left
  //       0, 0, 1,      // submit color: blue
  //       1, -1, 0,    // bottom right
  //       1, 1, 1,      // submit color: white
  //       1, 1, 0,     // top right
  //       1, 0, 0       // submit color: red
  //   ];
  var triangleVertices = [
    1,0,2,    // wierzchołek #1
    0, 0, 0,       // kolor: czarny
    1,0,0,      // wierzchołek #2
    2, 0, 0,       // kolor: czerwony
    -1,0,1,       // wierzchołek #3
    1, 1, 0,       // kolor: zółty
    0,2,0,       // wierzchołek #4
    0, 1, 0      // kolor: niebieski
  ];


  // Building Vertex Buffer Object - WebGL vertex array
  _triangleVertexBuffer = gl_ctx.createBuffer();                // *******
  gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
  gl_ctx.bufferData(gl_ctx.ARRAY_BUFFER, new Float32Array(triangleVertices), gl_ctx.STATIC_DRAW);


  // Triangle faces array
  // var triangleFaces = [0, 1, 2];
  var triangleFaces = [
    0, 1, 2,       // pobierz 1, 2 i 3 wierzchołek z tablicy triangleVertices
    0, 1, 3,
    1, 2, 3,
    0, 2, 3
  ];

  _triangleFacesBuffer = gl_ctx.createBuffer();                     // *******
  gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);
  gl_ctx.bufferData(gl_ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(triangleFaces), gl_ctx.STATIC_DRAW);
}

// ==================================================================== //

function gl_setMatrix () {
  _matrixProjection = MATRIX.getProjection(40, gl_canvas.width/gl_canvas.height, 1, 100);
  _matrixMovement = MATRIX.getIdentityMatrix();
  _matrixView = MATRIX.getIdentityMatrix();

  MATRIX.translateZ(_matrixView, zoomRatio);
}

// ==================================================================== //

function gl_draw() {
  // set the color to transparent
  gl_ctx.clearColor(0.0, 0.0, 0.0, 0.0);
  // enable Depth buffer test and set depth buffer comparison function
  gl_ctx.enable(gl_ctx.DEPTH_TEST);
  gl_ctx.depthFunc(gl_ctx.LEQUAL);

  // set the clear value for the depth buffer to 1
  gl_ctx.clearDepth(1.0);

  var timeOld = 0;
  var dAngle = 0;

  var animate = function (time) {
    dAngle = rotationSpeed * (time - timeOld);

    if (X) {
      MATRIX.rotateX(_matrixMovement, dAngle);
    }
    if (Y) {
      MATRIX.rotateY(_matrixMovement, dAngle);
    }
    if (Z) {
      MATRIX.rotateZ(_matrixMovement, dAngle);
    }

    timeOld = time;

    // set the drawing area on the canvas and clear it
    gl_ctx.viewport(0.0, 0.0, gl_canvas.width, gl_canvas.height);
    gl_ctx.clear(gl_ctx.COLOR_BUFFER_BIT | gl_ctx.DEPTH_BUFFER_BIT);

    // set projection matrix. _matrixProjection is not set yet.
    // It is a javascript array of 1 dimension with 16 floats
    gl_ctx.uniformMatrix4fv(_PosMatrix, false, _matrixProjection);
    gl_ctx.uniformMatrix4fv(_MovMatrix, false, _matrixMovement);
    gl_ctx.uniformMatrix4fv(_ViewMatrix, false, _matrixView);
    // drawing is here - use these points for next drawing
    // gl_ctx.vertexAttribPointer(variable, dimension, type, normalize, total vertex size in bytes, offset)
    gl_ctx.vertexAttribPointer(_position, 3, gl_ctx.FLOAT, false, 4*6, 0);
    gl_ctx.vertexAttribPointer(_color, 3, gl_ctx.FLOAT, false, 4*4, 0);

    gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
    gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);

    // draw the triangle
    //gl_ctx.drawElements(gl_ctx.TRIANGLES, 3, gl_ctx.UNSIGNED_SHORT, 0);

    // draw cube
    gl_ctx.drawElements(gl_ctx.TRIANGLES, 12, gl_ctx.UNSIGNED_SHORT, 0);

    // drawing is finished - show the render
    gl_ctx.flush();
    // redraws the scene as soon as ready
    window.requestAnimationFrame(animate);
  };

  // launch animate for the first time
  animate(timeOld);

}

var MATRIX = {
  degToRad: function(angle) {
    return (angle*Math.PI/180);
  },

  getProjection: function(angle, a, zMin, zMax) {
    var tan = Math.tan(MATRIX.degToRad(0.5*angle)),
        A=-(zMax+zMin)/(zMax-zMin),
          B=(-2*zMax*zMin)/(zMax-zMin);

    return [
      .5/tan,          0,   0,    0,
      0,   .5*a/tan,   0,    0,
      0,          0,   A,   -1,
      0,          0,   B,    0
    ]
  },

  getIdentityMatrix: function () {
    return [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ];
  },

  // rotate movement matrix with angle around X axis
  rotateX: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem1 = movMat[1],
        matElem5 = movMat[5],
        matElem9 = movMat[9];

    movMat[1] = movMat[1]*cos - movMat[2]*sin;
    movMat[5] = movMat[5]*cos - movMat[6]*sin;
    movMat[9] = movMat[9]*cos - movMat[10]*sin;

    movMat[2] = movMat[2]*cos + matElem1*sin;
    movMat[6] = movMat[6]*cos + matElem5*sin;
    movMat[10] = movMat[10]*cos + matElem9*sin;
  },

  // rotate movement matrix with angle around Y axis
  rotateY: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem0 = movMat[0],
        matElem4 = movMat[4],
        matElem8 = movMat[8];

    movMat[0] = movMat[0]*cos + movMat[2]*sin;
    movMat[4] = movMat[4]*cos + movMat[6]*sin;
    movMat[8] = movMat[8]*cos + movMat[10]*sin;

    movMat[2] = movMat[2]*cos - matElem0*sin;
    movMat[6] = movMat[6]*cos - matElem4*sin;
    movMat[10] = movMat[10]*cos - matElem8*sin;
  },

  // rotate movement matrix with angle around Z axis
  rotateZ: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem0 = movMat[0],
        matElem4 = movMat[4],
        matElem8 = movMat[8];

    movMat[0] = movMat[0]*cos - movMat[1]*sin;
    movMat[4] = movMat[4]*cos - movMat[5]*sin;
    movMat[8] = movMat[8]*cos - movMat[9]*sin;

    movMat[1] = movMat[1]*cos + matElem0*sin;
    movMat[5] = movMat[5]*cos + matElem4*sin;
    movMat[9] = movMat[9]*cos + matElem8*sin;
  },

  // translate movement matrix by trans along Z axis
  translateZ: function (movMat, trans) {
    movMat[14] += trans;
  }
};
<div>
  <form class="rotationCheckboxes">
    <input type="checkbox" id="rotateX"> Rotate X&nbsp;&nbsp;&nbsp;
    <input type="checkbox" id="rotateY"> Rotate Y&nbsp;&nbsp;&nbsp;
    <input type="checkbox" id="rotateZ"> Rotate Z
  </form>
  <br />
  <input type="button" value="run" onclick="runWebGL()"/>
  <br />
  <canvas id="glcanvas" width="500" height="300">Brak wsparcia dla elementu HTML canvas.</canvas>
</div>

缺少动画函数调用之前的某些条件?

最佳答案

每次调用 runWebGL 时,您都会创建另一个 requestAnimationFrame 循环。这意味着每次您单击run时,渲染循环gl_draw每帧都会执行更多次。

我想到了 2 个解决方案

  1. 与输入事件分开运行循环

    换句话说,只需启动循环并保持其运行 并分别更新你的旋转变量。仅调用 runWebGL 一次。

    在您的例子中,我刚刚添加了一个名为 runWebGL 的组件,并将所有复选框更改为在单击时调用 getRotation

    示例:

var gl_canvas;
var gl_ctx;

var _triangleVertexBuffer;
var _triangleFacesBuffer;
var _position;
var _color;
var _PosMatrix;
var _MovMatrix;
var _ViewMatrix;
var _matrixProjection;
var _matrixMovement;
var _matrixView;

var rotationSpeed = 0.001;
var zoomRatio = -6;

var X, Y, Z;

function runWebGL () {
  getRotation();
  gl_canvas = document.getElementById("glcanvas");
  gl_ctx = gl_getContext(gl_canvas);
  gl_initShaders();
  gl_initBuffers();
  gl_setMatrix();
  gl_draw();
}

function getRotation() {
  X = document.getElementById('rotateX').checked;
  Y = document.getElementById('rotateY').checked;
  Z = document.getElementById('rotateZ').checked;
}

// ==================================================================== //

function gl_getContext (canvas) {
  try {
    var ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
    ctx.viewportWidth = canvas.width;
    ctx.viewportHeight = canvas.height;
  } catch (e) {}

  if (!ctx) {
    document.write('Unable to initialize WebGL. Your browser may not support it.')
  }
  return ctx;
}

// ==================================================================== //

// Declare the shaders. They are pieces of code compiled by WebGL and
// executed on the graphics device. They are written in GLSL.
function gl_initShaders () {
  // position of the point - 0. is Z and 1. is W
  // PosMatrix is uniform variable - its value is constant while rendering an object
  // MovMatrix is the movement matrix of the triangle
  // gl_position -> we move position with MovMatrix before projecting it
  var vertexShader = "\n\
attribute vec3 position;\n\
uniform mat4 PosMatrix;\n\
uniform mat4 MovMatrix;\n\
uniform mat4 ViewMatrix; \n\
attribute vec3 color;\n\
varying vec3 vColor;\n\
void main(void) {\n\
gl_Position = PosMatrix * ViewMatrix * MovMatrix * vec4(position, 1.);\n\
vColor = color;\n\
}";

  // set black color
  var fragmentShader = "\n\
precision mediump float;\n\
varying vec3 vColor;\n\
void main(void) {\n\
gl_FragColor = vec4(vColor, 1.);\n\
}";

  // this function is used to compile a shader
  var getShader = function(source, type, typeString) {
    var shader = gl_ctx.createShader(type);
    gl_ctx.shaderSource(shader, source);
    gl_ctx.compileShader(shader);

    if (!gl_ctx.getShaderParameter(shader, gl_ctx.COMPILE_STATUS)) {
      alert('error in' + typeString);
      return false;
    }
    return shader;
  };

  // Compile the vertex and fragment shaders
  var shader_vertex = getShader(vertexShader, gl_ctx.VERTEX_SHADER, "VERTEX");
  var shader_fragment = getShader(fragmentShader, gl_ctx.FRAGMENT_SHADER, "FRAGMENT");


  // Create the Shader program.
  // Shader program is a combination of a vertex and fragment shaders.
  var SHADER_PROGRAM = gl_ctx.createProgram();
  gl_ctx.attachShader(SHADER_PROGRAM, shader_vertex);
  gl_ctx.attachShader(SHADER_PROGRAM, shader_fragment);


  // Linking of the shader program to the WebGL context - gl_ctx,
  // in order to match the shader variables to javascript variables
  gl_ctx.linkProgram(SHADER_PROGRAM);

  // Link PosMatrix\MovMatrix\ViewMatrix GLSL variables to
  // _PosMatrix\_MovMatrix\_ViewMatrix javascript variables
  // Uniforms do not need to be enabled like attributes
  _PosMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "PosMatrix");
  _MovMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "MovMatrix");
  _ViewMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "ViewMatrix");

  // position GLSL variable links to _position variable
  _position = gl_ctx.getAttribLocation(SHADER_PROGRAM, "position");    // *******
  // color GLSL variable links to _color variable
  _color = gl_ctx.getAttribLocation(SHADER_PROGRAM, "color");
  // enable GLSL attributes variables
  gl_ctx.enableVertexAttribArray(_position);
  gl_ctx.enableVertexAttribArray(_color);
  // linking is over - tells WebGL context to use SHADER_PROGRAM for rendering.
  gl_ctx.useProgram(SHADER_PROGRAM);
}

// ==================================================================== //

function gl_initBuffers () {
  // Point coordinates array of the triangle
  //   var triangleVertices = [
  //      -1, -1, 0,    // bottom left
  //       0, 0, 1,      // submit color: blue
  //       1, -1, 0,    // bottom right
  //       1, 1, 1,      // submit color: white
  //       1, 1, 0,     // top right
  //       1, 0, 0       // submit color: red
  //   ];
  var triangleVertices = [
    1,0,2,    // wierzchołek #1
    0, 0, 0,       // kolor: czarny
    1,0,0,      // wierzchołek #2
    2, 0, 0,       // kolor: czerwony
    -1,0,1,       // wierzchołek #3
    1, 1, 0,       // kolor: zółty
    0,2,0,       // wierzchołek #4
    0, 1, 0      // kolor: niebieski
  ];


  // Building Vertex Buffer Object - WebGL vertex array
  _triangleVertexBuffer = gl_ctx.createBuffer();                // *******
  gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
  gl_ctx.bufferData(gl_ctx.ARRAY_BUFFER, new Float32Array(triangleVertices), gl_ctx.STATIC_DRAW);


  // Triangle faces array
  // var triangleFaces = [0, 1, 2];
  var triangleFaces = [
    0, 1, 2,       // pobierz 1, 2 i 3 wierzchołek z tablicy triangleVertices
    0, 1, 3,
    1, 2, 3,
    0, 2, 3
  ];

  _triangleFacesBuffer = gl_ctx.createBuffer();                     // *******
  gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);
  gl_ctx.bufferData(gl_ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(triangleFaces), gl_ctx.STATIC_DRAW);
}

// ==================================================================== //

function gl_setMatrix () {
  _matrixProjection = MATRIX.getProjection(40, gl_canvas.width/gl_canvas.height, 1, 100);
  _matrixMovement = MATRIX.getIdentityMatrix();
  _matrixView = MATRIX.getIdentityMatrix();

  MATRIX.translateZ(_matrixView, zoomRatio);
}

// ==================================================================== //

function gl_draw() {
  // set the color to transparent
  gl_ctx.clearColor(0.0, 0.0, 0.0, 0.0);
  // enable Depth buffer test and set depth buffer comparison function
  gl_ctx.enable(gl_ctx.DEPTH_TEST);
  gl_ctx.depthFunc(gl_ctx.LEQUAL);

  // set the clear value for the depth buffer to 1
  gl_ctx.clearDepth(1.0);

  var timeOld = 0;
  var dAngle = 0;

  var animate = function (time) {
    dAngle = rotationSpeed * (time - timeOld);

    if (X) {
      MATRIX.rotateX(_matrixMovement, dAngle);
    }
    if (Y) {
      MATRIX.rotateY(_matrixMovement, dAngle);
    }
    if (Z) {
      MATRIX.rotateZ(_matrixMovement, dAngle);
    }

    timeOld = time;

    // set the drawing area on the canvas and clear it
    gl_ctx.viewport(0.0, 0.0, gl_canvas.width, gl_canvas.height);
    gl_ctx.clear(gl_ctx.COLOR_BUFFER_BIT | gl_ctx.DEPTH_BUFFER_BIT);

    // set projection matrix. _matrixProjection is not set yet.
    // It is a javascript array of 1 dimension with 16 floats
    gl_ctx.uniformMatrix4fv(_PosMatrix, false, _matrixProjection);
    gl_ctx.uniformMatrix4fv(_MovMatrix, false, _matrixMovement);
    gl_ctx.uniformMatrix4fv(_ViewMatrix, false, _matrixView);
    // drawing is here - use these points for next drawing
    // gl_ctx.vertexAttribPointer(variable, dimension, type, normalize, total vertex size in bytes, offset)
    gl_ctx.vertexAttribPointer(_position, 3, gl_ctx.FLOAT, false, 4*6, 0);
    gl_ctx.vertexAttribPointer(_color, 3, gl_ctx.FLOAT, false, 4*4, 0);

    gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
    gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);

    // draw the triangle
    //gl_ctx.drawElements(gl_ctx.TRIANGLES, 3, gl_ctx.UNSIGNED_SHORT, 0);

    // draw cube
    gl_ctx.drawElements(gl_ctx.TRIANGLES, 12, gl_ctx.UNSIGNED_SHORT, 0);

    // drawing is finished - show the render
    gl_ctx.flush();
    // redraws the scene as soon as ready
    window.requestAnimationFrame(animate);
  };

  // launch animate for the first time
  animate(timeOld);

}

var MATRIX = {
  degToRad: function(angle) {
    return (angle*Math.PI/180);
  },

  getProjection: function(angle, a, zMin, zMax) {
    var tan = Math.tan(MATRIX.degToRad(0.5*angle)),
        A=-(zMax+zMin)/(zMax-zMin),
          B=(-2*zMax*zMin)/(zMax-zMin);

    return [
      .5/tan,          0,   0,    0,
      0,   .5*a/tan,   0,    0,
      0,          0,   A,   -1,
      0,          0,   B,    0
    ]
  },

  getIdentityMatrix: function () {
    return [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    ];
  },

  // rotate movement matrix with angle around X axis
  rotateX: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem1 = movMat[1],
        matElem5 = movMat[5],
        matElem9 = movMat[9];

    movMat[1] = movMat[1]*cos - movMat[2]*sin;
    movMat[5] = movMat[5]*cos - movMat[6]*sin;
    movMat[9] = movMat[9]*cos - movMat[10]*sin;

    movMat[2] = movMat[2]*cos + matElem1*sin;
    movMat[6] = movMat[6]*cos + matElem5*sin;
    movMat[10] = movMat[10]*cos + matElem9*sin;
  },

  // rotate movement matrix with angle around Y axis
  rotateY: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem0 = movMat[0],
        matElem4 = movMat[4],
        matElem8 = movMat[8];

    movMat[0] = movMat[0]*cos + movMat[2]*sin;
    movMat[4] = movMat[4]*cos + movMat[6]*sin;
    movMat[8] = movMat[8]*cos + movMat[10]*sin;

    movMat[2] = movMat[2]*cos - matElem0*sin;
    movMat[6] = movMat[6]*cos - matElem4*sin;
    movMat[10] = movMat[10]*cos - matElem8*sin;
  },

  // rotate movement matrix with angle around Z axis
  rotateZ: function(movMat, angle) {
    var sin = Math.sin(angle);
    var cos = Math.cos(angle);
    var matElem0 = movMat[0],
        matElem4 = movMat[4],
        matElem8 = movMat[8];

    movMat[0] = movMat[0]*cos - movMat[1]*sin;
    movMat[4] = movMat[4]*cos - movMat[5]*sin;
    movMat[8] = movMat[8]*cos - movMat[9]*sin;

    movMat[1] = movMat[1]*cos + matElem0*sin;
    movMat[5] = movMat[5]*cos + matElem4*sin;
    movMat[9] = movMat[9]*cos + matElem8*sin;
  },

  // translate movement matrix by trans along Z axis
  translateZ: function (movMat, trans) {
    movMat[14] += trans;
  }
};

runWebGL();
<div>
  <form class="rotationCheckboxes">
    <input type="checkbox" id="rotateX" onclick="getRotation()"> Rotate X&nbsp;&nbsp;&nbsp;
    <input type="checkbox" id="rotateY" onclick="getRotation()"> Rotate Y&nbsp;&nbsp;&nbsp;
    <input type="checkbox" id="rotateZ" onclick="getRotation()"> Rotate Z
  </form>
  <br />
  <canvas id="glcanvas" width="500" height="300">Brak wsparcia dla elementu HTML canvas.</canvas>
</div>

  • 在开始新循环之前停止旧循环

    您可以通过保存请求的 ID 并调用 cancelAnimationFrame 来取消 requestAnimationFrame 请求。

    在本例中,我添加了一个全局变量

    var requestId;
    

    我改为调用requestAnimationFrame来保存id

    requestId = window.requestAnimationFrame(animate);
    

    在 runWebGL 的顶部,我将其更改为取消旧请求(如果有)

    if (requestId) {
      window.cancelAnimationFrame(requestId);
      requestId = undefined;
    }
    

    但请注意,这个示例仍然很糟糕,您不希望每次用户更改某些内容时都创建缓冲区、着色器等。您确实想要解决方案 #1

  • 示例:

    var gl_canvas;
    var gl_ctx;
    
    var _triangleVertexBuffer;
    var _triangleFacesBuffer;
    var _position;
    var _color;
    var _PosMatrix;
    var _MovMatrix;
    var _ViewMatrix;
    var _matrixProjection;
    var _matrixMovement;
    var _matrixView;
    
    var rotationSpeed = 0.001;
    var zoomRatio = -6;
    var requestId;              // ----ADDED----!
    
    var X, Y, Z;
    
    function runWebGL () {
      // stop old loop        
      if (requestId) {
        window.cancelAnimationFrame(requestId);
        requestId = undefined;
      }
      
      getRotation();
      gl_canvas = document.getElementById("glcanvas");
      gl_ctx = gl_getContext(gl_canvas);
      gl_initShaders();
      gl_initBuffers();
      gl_setMatrix();
      gl_draw();
    }
    
    function getRotation() {
      X = document.getElementById('rotateX').checked;
      Y = document.getElementById('rotateY').checked;
      Z = document.getElementById('rotateZ').checked;
    }
    
    // ==================================================================== //
    
    function gl_getContext (canvas) {
      try {
        var ctx = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
        ctx.viewportWidth = canvas.width;
        ctx.viewportHeight = canvas.height;
      } catch (e) {}
    
      if (!ctx) {
        document.write('Unable to initialize WebGL. Your browser may not support it.')
      }
      return ctx;
    }
    
    // ==================================================================== //
    
    // Declare the shaders. They are pieces of code compiled by WebGL and
    // executed on the graphics device. They are written in GLSL.
    function gl_initShaders () {
      // position of the point - 0. is Z and 1. is W
      // PosMatrix is uniform variable - its value is constant while rendering an object
      // MovMatrix is the movement matrix of the triangle
      // gl_position -> we move position with MovMatrix before projecting it
      var vertexShader = "\n\
    attribute vec3 position;\n\
    uniform mat4 PosMatrix;\n\
    uniform mat4 MovMatrix;\n\
    uniform mat4 ViewMatrix; \n\
    attribute vec3 color;\n\
    varying vec3 vColor;\n\
    void main(void) {\n\
    gl_Position = PosMatrix * ViewMatrix * MovMatrix * vec4(position, 1.);\n\
    vColor = color;\n\
    }";
    
      // set black color
      var fragmentShader = "\n\
    precision mediump float;\n\
    varying vec3 vColor;\n\
    void main(void) {\n\
    gl_FragColor = vec4(vColor, 1.);\n\
    }";
    
      // this function is used to compile a shader
      var getShader = function(source, type, typeString) {
        var shader = gl_ctx.createShader(type);
        gl_ctx.shaderSource(shader, source);
        gl_ctx.compileShader(shader);
    
        if (!gl_ctx.getShaderParameter(shader, gl_ctx.COMPILE_STATUS)) {
          alert('error in' + typeString);
          return false;
        }
        return shader;
      };
    
      // Compile the vertex and fragment shaders
      var shader_vertex = getShader(vertexShader, gl_ctx.VERTEX_SHADER, "VERTEX");
      var shader_fragment = getShader(fragmentShader, gl_ctx.FRAGMENT_SHADER, "FRAGMENT");
    
    
      // Create the Shader program.
      // Shader program is a combination of a vertex and fragment shaders.
      var SHADER_PROGRAM = gl_ctx.createProgram();
      gl_ctx.attachShader(SHADER_PROGRAM, shader_vertex);
      gl_ctx.attachShader(SHADER_PROGRAM, shader_fragment);
    
    
      // Linking of the shader program to the WebGL context - gl_ctx,
      // in order to match the shader variables to javascript variables
      gl_ctx.linkProgram(SHADER_PROGRAM);
    
      // Link PosMatrix\MovMatrix\ViewMatrix GLSL variables to
      // _PosMatrix\_MovMatrix\_ViewMatrix javascript variables
      // Uniforms do not need to be enabled like attributes
      _PosMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "PosMatrix");
      _MovMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "MovMatrix");
      _ViewMatrix = gl_ctx.getUniformLocation(SHADER_PROGRAM, "ViewMatrix");
    
      // position GLSL variable links to _position variable
      _position = gl_ctx.getAttribLocation(SHADER_PROGRAM, "position");    // *******
      // color GLSL variable links to _color variable
      _color = gl_ctx.getAttribLocation(SHADER_PROGRAM, "color");
      // enable GLSL attributes variables
      gl_ctx.enableVertexAttribArray(_position);
      gl_ctx.enableVertexAttribArray(_color);
      // linking is over - tells WebGL context to use SHADER_PROGRAM for rendering.
      gl_ctx.useProgram(SHADER_PROGRAM);
    }
    
    // ==================================================================== //
    
    function gl_initBuffers () {
      // Point coordinates array of the triangle
      //   var triangleVertices = [
      //      -1, -1, 0,    // bottom left
      //       0, 0, 1,      // submit color: blue
      //       1, -1, 0,    // bottom right
      //       1, 1, 1,      // submit color: white
      //       1, 1, 0,     // top right
      //       1, 0, 0       // submit color: red
      //   ];
      var triangleVertices = [
        1,0,2,    // wierzchołek #1
        0, 0, 0,       // kolor: czarny
        1,0,0,      // wierzchołek #2
        2, 0, 0,       // kolor: czerwony
        -1,0,1,       // wierzchołek #3
        1, 1, 0,       // kolor: zółty
        0,2,0,       // wierzchołek #4
        0, 1, 0      // kolor: niebieski
      ];
    
    
      // Building Vertex Buffer Object - WebGL vertex array
      _triangleVertexBuffer = gl_ctx.createBuffer();                // *******
      gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
      gl_ctx.bufferData(gl_ctx.ARRAY_BUFFER, new Float32Array(triangleVertices), gl_ctx.STATIC_DRAW);
    
    
      // Triangle faces array
      // var triangleFaces = [0, 1, 2];
      var triangleFaces = [
        0, 1, 2,       // pobierz 1, 2 i 3 wierzchołek z tablicy triangleVertices
        0, 1, 3,
        1, 2, 3,
        0, 2, 3
      ];
    
      _triangleFacesBuffer = gl_ctx.createBuffer();                     // *******
      gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);
      gl_ctx.bufferData(gl_ctx.ELEMENT_ARRAY_BUFFER, new Uint16Array(triangleFaces), gl_ctx.STATIC_DRAW);
    }
    
    // ==================================================================== //
    
    function gl_setMatrix () {
      _matrixProjection = MATRIX.getProjection(40, gl_canvas.width/gl_canvas.height, 1, 100);
      _matrixMovement = MATRIX.getIdentityMatrix();
      _matrixView = MATRIX.getIdentityMatrix();
    
      MATRIX.translateZ(_matrixView, zoomRatio);
    }
    
    // ==================================================================== //
    
    function gl_draw() {
      // set the color to transparent
      gl_ctx.clearColor(0.0, 0.0, 0.0, 0.0);
      // enable Depth buffer test and set depth buffer comparison function
      gl_ctx.enable(gl_ctx.DEPTH_TEST);
      gl_ctx.depthFunc(gl_ctx.LEQUAL);
    
      // set the clear value for the depth buffer to 1
      gl_ctx.clearDepth(1.0);
    
      var timeOld = 0;
      var dAngle = 0;
    
      var animate = function (time) {
        dAngle = rotationSpeed * (time - timeOld);
    
        if (X) {
          MATRIX.rotateX(_matrixMovement, dAngle);
        }
        if (Y) {
          MATRIX.rotateY(_matrixMovement, dAngle);
        }
        if (Z) {
          MATRIX.rotateZ(_matrixMovement, dAngle);
        }
    
        timeOld = time;
    
        // set the drawing area on the canvas and clear it
        gl_ctx.viewport(0.0, 0.0, gl_canvas.width, gl_canvas.height);
        gl_ctx.clear(gl_ctx.COLOR_BUFFER_BIT | gl_ctx.DEPTH_BUFFER_BIT);
    
        // set projection matrix. _matrixProjection is not set yet.
        // It is a javascript array of 1 dimension with 16 floats
        gl_ctx.uniformMatrix4fv(_PosMatrix, false, _matrixProjection);
        gl_ctx.uniformMatrix4fv(_MovMatrix, false, _matrixMovement);
        gl_ctx.uniformMatrix4fv(_ViewMatrix, false, _matrixView);
        // drawing is here - use these points for next drawing
        // gl_ctx.vertexAttribPointer(variable, dimension, type, normalize, total vertex size in bytes, offset)
        gl_ctx.vertexAttribPointer(_position, 3, gl_ctx.FLOAT, false, 4*6, 0);
        gl_ctx.vertexAttribPointer(_color, 3, gl_ctx.FLOAT, false, 4*4, 0);
    
        gl_ctx.bindBuffer(gl_ctx.ARRAY_BUFFER, _triangleVertexBuffer);
        gl_ctx.bindBuffer(gl_ctx.ELEMENT_ARRAY_BUFFER, _triangleFacesBuffer);
    
        // draw the triangle
        //gl_ctx.drawElements(gl_ctx.TRIANGLES, 3, gl_ctx.UNSIGNED_SHORT, 0);
    
        // draw cube
        gl_ctx.drawElements(gl_ctx.TRIANGLES, 12, gl_ctx.UNSIGNED_SHORT, 0);
    
        // drawing is finished - show the render
        gl_ctx.flush();
        // redraws the scene as soon as ready
        requestId = window.requestAnimationFrame(animate);
      };
    
      // launch animate for the first time
      animate(timeOld);
    
    }
    
    var MATRIX = {
      degToRad: function(angle) {
        return (angle*Math.PI/180);
      },
    
      getProjection: function(angle, a, zMin, zMax) {
        var tan = Math.tan(MATRIX.degToRad(0.5*angle)),
            A=-(zMax+zMin)/(zMax-zMin),
              B=(-2*zMax*zMin)/(zMax-zMin);
    
        return [
          .5/tan,          0,   0,    0,
          0,   .5*a/tan,   0,    0,
          0,          0,   A,   -1,
          0,          0,   B,    0
        ]
      },
    
      getIdentityMatrix: function () {
        return [
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1
        ];
      },
    
      // rotate movement matrix with angle around X axis
      rotateX: function(movMat, angle) {
        var sin = Math.sin(angle);
        var cos = Math.cos(angle);
        var matElem1 = movMat[1],
            matElem5 = movMat[5],
            matElem9 = movMat[9];
    
        movMat[1] = movMat[1]*cos - movMat[2]*sin;
        movMat[5] = movMat[5]*cos - movMat[6]*sin;
        movMat[9] = movMat[9]*cos - movMat[10]*sin;
    
        movMat[2] = movMat[2]*cos + matElem1*sin;
        movMat[6] = movMat[6]*cos + matElem5*sin;
        movMat[10] = movMat[10]*cos + matElem9*sin;
      },
    
      // rotate movement matrix with angle around Y axis
      rotateY: function(movMat, angle) {
        var sin = Math.sin(angle);
        var cos = Math.cos(angle);
        var matElem0 = movMat[0],
            matElem4 = movMat[4],
            matElem8 = movMat[8];
    
        movMat[0] = movMat[0]*cos + movMat[2]*sin;
        movMat[4] = movMat[4]*cos + movMat[6]*sin;
        movMat[8] = movMat[8]*cos + movMat[10]*sin;
    
        movMat[2] = movMat[2]*cos - matElem0*sin;
        movMat[6] = movMat[6]*cos - matElem4*sin;
        movMat[10] = movMat[10]*cos - matElem8*sin;
      },
    
      // rotate movement matrix with angle around Z axis
      rotateZ: function(movMat, angle) {
        var sin = Math.sin(angle);
        var cos = Math.cos(angle);
        var matElem0 = movMat[0],
            matElem4 = movMat[4],
            matElem8 = movMat[8];
    
        movMat[0] = movMat[0]*cos - movMat[1]*sin;
        movMat[4] = movMat[4]*cos - movMat[5]*sin;
        movMat[8] = movMat[8]*cos - movMat[9]*sin;
    
        movMat[1] = movMat[1]*cos + matElem0*sin;
        movMat[5] = movMat[5]*cos + matElem4*sin;
        movMat[9] = movMat[9]*cos + matElem8*sin;
      },
    
      // translate movement matrix by trans along Z axis
      translateZ: function (movMat, trans) {
        movMat[14] += trans;
      }
    };
    <div>
      <form class="rotationCheckboxes">
        <input type="checkbox" id="rotateX"> Rotate X&nbsp;&nbsp;&nbsp;
        <input type="checkbox" id="rotateY"> Rotate Y&nbsp;&nbsp;&nbsp;
        <input type="checkbox" id="rotateZ"> Rotate Z
      </form>
      <br />
      <input type="button" value="run" onclick="runWebGL()"/>
      <br />
      <canvas id="glcanvas" width="500" height="300">Brak wsparcia dla elementu HTML canvas.</canvas>
    </div>

    关于javascript - WebGL如何防止旋转速度增加?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41928392/

    相关文章:

    MediaWiki 中的 3D 对象查看器?

    javascript - 如果我在字符串(如 "123")之前添加 + 而不是在其之后添加,它如何转换为数字?

    Javascript 变量范围问题 - 错误 : TypeError: this. 图形未定义

    javascript - 将所有 "\n"替换为 ","

    javascript - 添加到 YouTube 的 Canvas 不可见

    Three.js 法线贴图渲染方式不同 windows/mac

    android - 移动设备上的全屏、全 dpi WebGL Canvas

    javascript - 在javascript中解析json编码数组

    javascript - sheetJs - 空单元格没有输出

    javascript - Canvas 快速纹理映射