WebGl:让对象一起移动和分开

时间:2017-05-06 22:10:57

标签: javascript webgl

我正在尝试制造一个机器人,它使用滑块将身体全部旋转在一起,身体部位一起移动并分开移动,即移动上臂移动整个手臂并旋转身体旋转每个身体部位。但我也希望身体部位能够分开移动,例如头部移动,下臂自行移动。

我的问题是并非我的所有物体都出现了,只有前三个物体出现,我觉得这与使用滑块的theta有关。当我移动头部时,手臂也会移动。我理解它与模型视图矩阵有关,我所做的每个转换都将继续应用于其余的转换,但是当我尝试使用pop()和push()时,它会使对象消失或冻结而不能移动。有人能指出我正确的方向吗?我包含了大部分代码,但并未包含所有变量。

var theta = [0,0,0];

function scale4(a, b, c) {
  var result = mat4();
  result[0][0] = a;
  result[1][1] = b;
  result[2][2] = c;
  return result;
}

window.onload = function init()
{
  var canvas = document.getElementById( "webgl-robot" );

  gl = WebGLUtils.setupWebGL( canvas );
  if ( !gl ) { alert( "WebGL isn't available" ); }

  gl.viewport( 0, 0, canvas.width, canvas.height );
  gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
  gl.enable( gl.DEPTH_TEST ); 

  program = initShaders( gl, "vertex-shader", "fragment-shader" ); 
  gl.useProgram( program);

  colorCube();

  program = initShaders( gl, "vertex-shader", "fragment-shader" );    
  gl.useProgram( program );


  var vBuffer = gl.createBuffer();
  gl.bindBuffer( gl.ARRAY_BUFFER, vBuffer );
  gl.bufferData( gl.ARRAY_BUFFER, flatten(points), gl.DYNAMIC_DRAW );

  var vPosition = gl.getAttribLocation( program, "vPosition" );
  gl.vertexAttribPointer( vPosition, 4, gl.FLOAT, false, 0, 0 );
  gl.enableVertexAttribArray( vPosition );

  var cBuffer = gl.createBuffer();
  gl.bindBuffer( gl.ARRAY_BUFFER, cBuffer );
  gl.bufferData( gl.ARRAY_BUFFER, flatten(colors), gl.DYNAMIC_DRAW );

  var vColor = gl.getAttribLocation( program, "vColor" );
  gl.vertexAttribPointer( vColor, 4, gl.FLOAT, false, 0, 0 );
  gl.enableVertexAttribArray( vColor );

  modelView = gl.getUniformLocation( program, "modelView" );
  projection = gl.getUniformLocation( program, "projection" );

  document.getElementById("slider1").onchange = function() {
    theta[0] = event.srcElement.value;
  };
  document.getElementById("slider2").onchange = function() {
     theta[1] = event.srcElement.value;
  };
  document.getElementById("slider3").onchange = function() {
     theta[2] =  event.srcElement.value;
  };
  document.getElementById("slider4").onchange = function() {
     theta[3] = event.srcElement.value;
  };
  document.getElementById("slider5").onchange = function() {
     theta[4] = event.srcElement.value;
  };

  modelView2 = gl.getUniformLocation( program, "modelView" );
  projection2 = gl.getUniformLocation( program, "projection" );

  modelViewMatrixLoc = gl.getUniformLocation( program, "modelViewMatrix");
  projection = gl.getUniformLocation( program, "projection" );

  projectionMatrix = ortho(-10, 10, -10, 10, -10, 10);
  gl.uniformMatrix4fv( gl.getUniformLocation(program, "projectionMatrix"),  
  false, flatten(projectionMatrix) );

  render();
}

function base() {
   var s = scale4(BASE_WIDTH, BASE_HEIGHT, BASE_WIDTH);
   var instanceMatrix = mult( translate( 0.0, 0.5 * BASE_HEIGHT, 0.0 ), s);
   var t = mult(modelViewMatrix, instanceMatrix);
   gl.uniformMatrix4fv(modelViewMatrixLoc,  false, flatten(t) );
   gl.drawArrays( gl.TRIANGLES, 0, 36 );
}

function head() {
   var s = scale4(HEAD_WIDTH, HEAD_HEIGHT, HEAD_WIDTH);
   var instanceMatrix = mult(translate( 0.0, 0.5 * HEAD_HEIGHT, 0.0 ),s);    
   var t = mult(modelViewMatrix, instanceMatrix);
   gl.uniformMatrix4fv( modelViewMatrixLoc,  false, flatten(t) );
   gl.drawArrays( gl.TRIANGLES, 0, 36 );
}

function leftUpperArm()
{
   var s = scale4(LEFT_UPPER_WIDTH, LEFT_UPPER_HEIGHT, LEFT_UPPER_WIDTH);
   var instanceMatrix = mult( translate( 0.0, 0.5 * LEFT_UPPER_HEIGHT, 0.0 ), 
   s);
   var t = mult(modelViewMatrix, instanceMatrix);
   gl.uniformMatrix4fv( modelViewMatrixLoc,  false, flatten(t) );
   gl.drawArrays( gl.TRIANGLES, 0, 36 );
}

function leftLowerArm()
{
   var s = scale4(LEFT_LOWER_WIDTH, LEFT_LOWER_HEIGHT, LEFT_LOWER_WIDTH);
   var instanceMatrix = mult( translate( 0.0, 0.5 * LEFT_LOWER_HEIGHT, 0.0 ), 
    s);
   var t = mult(modelViewMatrix, instanceMatrix);
   gl.uniformMatrix4fv( modelViewMatrixLoc,  false, flatten(t) );
   gl.drawArrays( gl.TRIANGLES, 0, 36 );
}

function rightUpperArm()
{
   var s = scale4(RIGHT_UPPER_WIDTH, RIGHT_UPPER_HEIGHT, RIGHT_UPPER_WIDTH);
   var instanceMatrix = mult( translate( -9.3, 0.5 * RIGHT_UPPER_HEIGHT, 0.0 
   ), s);
   var t = mult(modelViewMatrix, instanceMatrix);
   gl.uniformMatrix4fv( modelViewMatrixLoc,  false, flatten(t) );
   gl.drawArrays( gl.TRIANGLES, 0, 36 );
}

function render() {

  gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );

  modelViewMatrix = rotate(theta[Base], 0, 1, 0 );
  base();

  modelViewMatrix = mult(modelViewMatrix, translate(0.0, BASE_HEIGHT, 0.0)); 
  modelViewMatrix = mult(modelViewMatrix, rotate(theta[Head], 0, 0, 1 ));
  head();

  modelViewMatrix  = mult(modelViewMatrix, translate(1.3, -0.7, 0.0));
  modelViewMatrix  = mult(modelViewMatrix, rotate(theta[LeftUpper], 1, 0, 0) 
  );
  leftUpperArm();

  modelViewMatrix = mult(modelViewMatrix, translate(0.0, LEFT_UPPER_HEIGHT, 
  0.0));   
  modelViewMatrix = mult(modelViewMatrix, rotate(theta[LeftLower], 0, 0, 1 ));
  leftLowerArm();

  modelViewMatrix  = mult(modelViewMatrix, translate(5.3, -0.7, 0.0));
  modelViewMatrix  = mult(modelViewMatrix, rotate(theta[RightUpper], 1, 0, 0) 
  );
  rightUpperArm();

  requestAnimFrame(render);
}

1 个答案:

答案 0 :(得分:2)

使用scenegraph和/或matrix stack来制作/移动/制作类似内容(分层模型)的常规方法。

使用矩阵堆栈从根部开始(可能是腰部,可能是脚之间的点)然后你走过角色的树,乘以矩阵

 root
   waist
     left thigh
       left lower leg
          left foot
     right thigh
       right lower leg
          right foot
   stomach
     chest
       left upper arm
          left forearm
             left hand
       right upper arm
          right forearm
             right hand
       neck
         head
           left eye
           right eye

在树中的每个点,您保存当前模型矩阵(将其推入堆栈),然后将其传递给子项以添加其方向。

这样,如果移动/旋转胸部,部件树(手臂,颈部,头部)中较深处的一切都会自动移动/旋转。

您可以使用矩阵堆栈在下面的示例中看到。只有胸部是动画的,但由于矩阵堆叠,手臂和头部随胸部移动。



const m4 = twgl.m4;
const v3 = twgl.v3;
const gl = document.querySelector("canvas").getContext("webgl");

const vs = `
attribute vec4 position;
attribute vec3 normal;

uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_model;

varying vec3 v_normal;

void main() {
   gl_Position = u_projection * u_view * u_model * position;
   v_normal = mat3(u_model) * normal; // better to use inverse-transpose-model
}
`

const fs = `
precision mediump float;

varying vec3 v_normal;

uniform vec3 u_lightDir;
uniform vec3 u_color;

void main() {
  float light = dot(normalize(v_normal), u_lightDir) * .5 + .5;
  gl_FragColor = vec4(u_color * light, 1);
}
`;

// compiles shaders, links program, looks up attributes
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// calls gl.createBuffer, gl.bindBuffer, gl.bufferData
const cubeBufferInfo = twgl.primitives.createCubeBufferInfo(gl, 1);

const stack = [];

const color = [1, .5, .3];
const lightDir = v3.normalize([1, 5, 10]);
 
function render(time) {
   time *= 0.001;
   
   twgl.resizeCanvasToDisplaySize(gl.canvas);
   gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
   
   gl.enable(gl.DEPTH_TEST);
   gl.enable(gl.CULL_FACE);
   
   gl.useProgram(programInfo.program);
   
   const fov = Math.PI * .25;
   const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
   const zNear = 0.01;
   const zFar = 100;
   const projection = m4.perspective(fov, aspect, zNear, zFar);
   
   const cameraPosition = [1, 4, 10]
   const target = [0, 3, 0];
   const up = [0, 1, 0];
   const camera = m4.lookAt(cameraPosition, target, up);
   
   const view = m4.inverse(camera);
   
   // make base position of robot
   let m = m4.translation([0, 0, 0]);
   pushMatrix(m);
   {
      // move up to waist
      m = m4.translate(m, [0, 3, 0]);
      drawCube(projection, view, m);
      
      pushMatrix(m);
      {
         // move to left thigh
         m = m4.translate(m, [1.1, -.5, 0]);
         drawCube(projection, view, m);
         
         pushMatrix(m);
         {
           // move to left foot
           m = m4.translate(m, [0, -1.1, 0]);
           drawCube(projection, view, m);
         }
         m = popMatrix(m);
      }
      m = popMatrix(m);
      
      pushMatrix(m);
      {
         // move to right thigh
         m = m4.translate(m, [-1.1, -.5, 0]);
         drawCube(projection, view, m);
         
         pushMatrix(m);
         {
           // move to right foot
           m = m4.translate(m, [0, -1.1, 0]);
           drawCube(projection, view, m);
         }
         m = popMatrix(m);
      }
      m = popMatrix(m);
      
      pushMatrix(m);
      {
        // move to chest
        m = m4.translate(m, [0, 1.1, 0]);
        m = m4.rotateY(m, Math.sin(time) * .6);
        drawCube(projection, view, m);

        pushMatrix(m);
        {
          // move to left arm
          m = m4.translate(m, [-1.1, 0, 0]);
          drawCube(projection, view, m);
        }
        m = popMatrix(m);
      
        pushMatrix(m);
        {
          // move to right arm
          m = m4.translate(m, [1.1, 0, 0]);
          drawCube(projection, view, m);
        }
        m = popMatrix(m);
        
        pushMatrix(m);
        {
          // move to head
          m = m4.translate(m, [0, 1.1, 0]);
          drawCube(projection, view, m);
        }
        m = popMatrix(m);
     }
     m = popMatrix(m);
   }
   m = popMatrix();

   requestAnimationFrame(render);
}
requestAnimationFrame(render);

function pushMatrix(m) {
  stack.push(m);
}

function popMatrix() {
  return stack.pop();
}

function drawCube(projection, view, model) {
  // there is no reason to call these each time since we're always
  // using the same cube but a real robot would probably
  // have different parts for each section: 
  //   leg, arm, left hand, right foot...
  
  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
   twgl.setBuffersAndAttributes(gl, programInfo, cubeBufferInfo);
   
   // calls gl.uniformXXX
   twgl.setUniforms(programInfo, {
      u_color: color,
      u_lightDir: lightDir,
      u_projection: projection,
      u_view: view,
      u_model: model,
   });
   
   // calls gl.drawArrays or gl.drawElements
   twgl.drawBufferInfo(gl, cubeBufferInfo);
}

body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }

<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
<canvas></canvas>
&#13;
&#13;
&#13;

scenegraph是一个组织工具,所以不是编写上面示例中看到的所有代码,而是通过编写一些遍历场景图并计算基础的代码来更加概括。