在没有three.js的情况下在WebGL中移动3D对象

时间:2018-04-04 21:13:23

标签: javascript 3d webgl

我试图能够使用WebGL在3D中移动一些对象,而不使用three.js库。我得到它主要工作,我可以拖动对象,并有一个选择功能来区分对象。但是,当我想再次拖动它们时,对象总是会快速回到画布的中心,并且不会停留在之前拖动它们的位置。到目前为止,这是我的代码:

// PointLightedCube.js (c) 2012 matsuda and kanda
// Vertex shader program
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
   //  'attribute vec4 a_Color;\n' + // Defined constant in main()
  'attribute vec4 a_Normal;\n' +
  'attribute vec4 a_Color;\n' +
  'uniform float u_On;\n' +   
  'uniform mat4 u_MvpMatrix;\n' +
  'uniform mat4 u_viewMatrix;\n' +
  'uniform mat4 u_ModelMatrix;\n' +    // Model matrix
  'uniform mat4 u_NormalMatrix;\n' +   // Transformation matrix of the normal
  'uniform vec3 u_LightColor;\n' +     // Light color
  'uniform vec3 u_LightPosition;\n' +  // Position of the light source
  'uniform vec3 u_AmbientLight;\n' +   // Ambient light color
  'varying vec4 v_Color;\n' +
  'void main() {\n' +
  '  vec4 color = a_Color;\n' + // Sphere color
  '  gl_Position = u_MvpMatrix * a_Position;\n' +
     // Calculate a normal to be fit with a model matrix, and make it 1.0 in length
  '  vec3 normal = normalize(vec3(u_NormalMatrix * a_Normal));\n' +
     // Calculate world coordinate of vertex
  '  vec4 vertexPosition = u_ModelMatrix * a_Position;\n' +
     // Calculate the light direction and make it 1.0 in length
  '  vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));\n' +
     // The dot product of the light direction and the normal
  '  float nDotL = max(dot(lightDirection, normal), 0.0);\n' +
     // Calculate the color due to diffuse reflection
  '  vec3 diffuse = u_LightColor * color.rgb * nDotL;\n' +
     // Calculate the color due to ambient reflection
  '  vec3 ambient = u_AmbientLight * color.rgb;\n' +
     // Add the surface colors due to diffuse reflection and ambient reflection
  '  v_Color = vec4(diffuse + ambient, color.a);\n' + 
  '  if(u_On > 0.5){ v_Color = a_Color;};\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';

  var ration;
  var diffuseOn = true;
  var ambientOn = true;
  var redL = 0.8;
  var greenL = 0.8;
  var blueL = 0.8;

  var coneX = -1.5;
  var coneY = 1;
  var coneZ = -10;

  var cylX = 1.5;
  var cylY = -1.0;
  var cylZ = -10;

  var lconeX = 0;
  var lconeY = 0;
  var lcylX = 0;
  var lcylY = 0;

  var picked = [];
  var selected = false;
  var lastX;
  var lastY;

  var drag = false;

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;
  }

  gl.clearColor(0.6, 0.6, 0.6, 1);
  gl.enable(gl.DEPTH_TEST);

  // Get the storage locations of uniform variables and so on
  var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
  var u_LightColor = gl.getUniformLocation(gl.program, 'u_LightColor');
  var u_LightPosition = gl.getUniformLocation(gl.program, 'u_LightPosition');
  var u_AmbientLight = gl.getUniformLocation(gl.program, 'u_AmbientLight');
  var u_OnOff = gl.getUniformLocation(gl.program, 'u_AmbOn');
  gl.uniform1f(u_OnOff, 1);

  gl.uniform3f(u_LightColor, redL, greenL, blueL);
  gl.uniform3f(u_LightPosition, 0, 0, 7.0);
  gl.uniform3f(u_AmbientLight, 0.2, 0.2, 0.2);

  var modelMatrix = new Matrix4();  // Model matrix
  var viewMatrix = new Matrix4();
  var mvpMatrix = new Matrix4();   // Model view projection matrix
  var normalMatrix = new Matrix4(); // Transformation matrix for normals

  // Pass the model matrix to u_ModelMatrix
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);


  ratio = canvas.width/canvas.height;
  var g_EyeX = 0, g_EyeY = 0, g_EyeZ = 6;


  // Calculate the view projection matrix
  //viewMatrix.setPerspective(30, ratio, 1, 100);
  //viewMatrix.lookAt(g_EyeX, g_EyeY, g_EyeZ, 0, 0, 0, 0, 1, 0);
  //mvpMatrix = viewMatrix.multiply(modelMatrix);
  //gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  // Calculate the matrix to transform the normal based on the model matrix
  normalMatrix.setInverseOf(modelMatrix);
  normalMatrix.transpose();
  // Pass the transformation matrix for normals to u_NormalMatrix
  gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);

  // Clear color and depth buffer
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  document.onmouseup = function(ev) {drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);
                                        if(picked[0]){
                                            lconeX = coneX;
                                            lconeY = coneY;
                                        }
                                        else if(picked[1]){
                                            lcylX = cylX;
                                            lcylY = cylY;
                                        }
                                        picked = [];
                                        drag = false;

                                        };

  document.onkeydown = function(ev){ keydown(ev, gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected); };
  var k = 0.01;
  canvas.onmousedown = function(ev){    drag = true;
                                        var x = ev.clientX, y = ev.clientY;
                                        var rect = ev.target.getBoundingClientRect();
                                        if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
                                            lastX = x - rect.left, lastY = rect.bottom - y;
                                            console.log((lastX) + " : " + (lastY));

                                            check(gl, lastX, lastY, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);
                                            /*if(picked[0]){
                                                coneX = (currX - lastX)*k + lconeX;
                                                coneY = (currY - lastY)*k + lconeY;
                                            }
                                            else if(picked[1]){
                                                cylX = (currX - lastX)*k + lcylX;
                                                cylY = (currY - lastY)*k + lcylY;
                                            }*/
                                            //drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);
                                        }
  };
  canvas.onmousemove = function(ev){ if(drag){
                                            var x = ev.clientX, y = ev.clientY;
                                            var rect = ev.target.getBoundingClientRect();
                                                if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
                                                    var currX = x - rect.left, currY = rect.bottom - y;
                                                    //if(currX != lastX && currY != lastY){
                                                        if(picked[0]){
                                                            coneX = (currX - lastX)*k + lconeX;
                                                            coneY = (currY - lastY)*k + lconeY;
                                                        }
                                                        else if(picked[1]){
                                                            cylX = (currX - lastX)*k + lcylX;
                                                            cylY = (currY - lastY)*k + lcylY;
                                                        }
                                                    //}
                                                }
                                                drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);
                                            }
  };

  drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);
}
var angle = 0;
var scale = 1;

var angleStep = 5;
function keydown(ev, gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected) {
    //left
    if(ev.keyCode == 68) { 
      angleStep = 1;
      angle = animate(angle);
    } else 
    if (ev.keyCode == 65) { 
      angleStep = -1;
      angle = animate(angle);
    }
    //forward
    else if(ev.keyCode == 87){
        scale += 0.1;
    }
    else if(ev.keyCode == 83){
        scale = Math.max(scale-0.1, 0.1);
    }
    else { return; } // Prevent the unnecessary drawing
    drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected);    

}

function setRed(){
    var slider = document.getElementById("red");
    redL = slider.value;
}
function setGreen(){
    var slider = document.getElementById("green");
    greenL = slider.value;
}
function setBlue(){
    var slider = document.getElementById("blue");
    blueL = slider.value;
}       



function animate(angle) {
  var newAngle = angle + angleStep;
  return newAngle % 360;
}



var g_mvp = new Matrix4();
function drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, colour){
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    viewMatrix.setPerspective(30, ratio, 1, 100);
    viewMatrix.lookAt(0, 0, 6, 0, 0, 0, 0, 1, 0); 

    var n = initVertexBuffersCone(gl, colour);
    mvpMatrix = new Matrix4();
    mvpMatrix.set(viewMatrix);
    mvpMatrix.translate(0, 0, coneZ)
    mvpMatrix.scale(scale, scale, scale, 1);
    mvpMatrix.rotate(angle, 0.0, 1.0, 0.0);
    mvpMatrix.translate(0, 0, -coneZ)
    draw(gl, normalMatrix, modelMatrix, mvpMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, colour, n);

    var n = initVertexBuffersCyl(gl, colour);
    cmvpMatrix = new Matrix4();
    cmvpMatrix.set(viewMatrix);
    cmvpMatrix.translate(0, 0, cylZ);
    cmvpMatrix.scale(scale, scale, scale, 1);
    cmvpMatrix.rotate(angle, 0.0, 1.0, 0.0);
    cmvpMatrix.translate(0, 0, -cylZ);
    drawCyl(gl, normalMatrix, modelMatrix, cmvpMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, colour, n);
}


function draw(gl, normalMatrix, modelMatrix, mvpMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, colour, n){ 
console.log(coneX + " ... " + coneY);
  mvpMatrix.translate(coneX, coneY, coneZ);
  mvpMatrix.translate(-lconeX, -lconeY, 0);

  gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);

  if(colour){
    var temp = gl.getUniformLocation(gl.program, 'u_On');
    gl.uniform1f(temp, 1);
  }
  else{
    var temp = gl.getUniformLocation(gl.program, 'u_On');
    gl.uniform1f(temp, 0);
  }
  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}


function drawCyl(gl, normalMatrix, modelMatrix, cmvpMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, colour, n){
  cmvpMatrix.translate(cylX, cylY, cylZ);
  cmvpMatrix.translate(-lcylX, -lcylY, 0);

  gl.uniformMatrix4fv(u_MvpMatrix, false, cmvpMatrix.elements);

  if(colour){
    var temp = gl.getUniformLocation(gl.program, 'u_On');
    gl.uniform1f(temp, 1);
  }
  else{
    var temp = gl.getUniformLocation(gl.program, 'u_On');
    gl.uniform1f(temp, 0);
  }

  gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_SHORT, 0);
}

function initVertexBuffersCone(gl, colour) { // Create a sphere
  var SPHERE_DIV = 13;

  var i, ai, si, ci;
  var j, aj, sj, cj;
  var p1, p2;

  var positions = [1, 0, 0];
  var indices = [];
  var color = [];

  var startX = -1;


  // Generate coordinates
  for (j = 0; j <= SPHERE_DIV; j++) {
    aj = (Math.PI/180) * (360/SPHERE_DIV);
    sj = Math.sin(aj);
    cj = Math.cos(aj);
    for(i = 0; i <= SPHERE_DIV;i++){
        ai = i * 2 * Math.PI / SPHERE_DIV;
        si = Math.sin(ai);
        ci = Math.cos(ai);

        positions.push(startX);
        positions.push(ci);
        positions.push(si);

    }

  }

  for (j = 0; j <= SPHERE_DIV; j++) {
    for(i = 0; i <= SPHERE_DIV;i++){

        if(!colour){
            color.push(0.6);
            color.push(0.4);
            color.push(0.2);
        }
        else{
            color.push(1.0);
            color.push(0.0);
            color.push(0.0);
        }
    }

  }

  var start = 0;
  // Generate indices
  for (j = 0; j < SPHERE_DIV; j++) {
    for(i = 0; i < SPHERE_DIV;i++){

        indices.push(0);
        indices.push(start+1);
        indices.push(start+2)

        start += 1;
    }
  }

  indices.push(0);
  indices.push(start);
  indices.push(start+1);


  // Write the vertex property to buffers (coordinates and normals)
  // Same data can be used for vertex and normal
  // In order to make it intelligible, another buffer is prepared separately
  if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3))  return -1;
  if (!initArrayBuffer(gl, 'a_Color', new Float32Array(color), gl.FLOAT, 3)) return -1;  
  // Unbind the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // 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, new Uint16Array(indices), gl.STATIC_DRAW);

  return indices.length;
}
function initVertexBuffersCyl(gl, colour) { // Create a sphere
  var SPHERE_DIV = 20;

  var i, ai, si, ci;
  var j, aj, sj, cj;
  var p1, p2;

  var positions = [];
  var indices = [];
  var color = [];

  var startX = -1;


  // Generate coordinates
  for (j = 0; j <= SPHERE_DIV; j++) {
    aj = (Math.PI/180) * (360/SPHERE_DIV);
    sj = Math.sin(aj);
    cj = Math.cos(aj);
    for(i = 0; i <= SPHERE_DIV;i++){
        ai = i * 2 * Math.PI / SPHERE_DIV;
        si = Math.sin(ai);
        ci = Math.cos(ai);

        positions.push(si);
        positions.push(ci);
        positions.push(0);

        positions.push(si);
        positions.push(ci);
        positions.push(1);

    }
  }

  for (j = 0; j <= SPHERE_DIV; j++) {
    aj = (Math.PI/180) * (360/SPHERE_DIV);
    sj = Math.sin(aj);
    cj = Math.cos(aj);
    for(i = 0; i <= SPHERE_DIV;i++){
        ai = i * 2 * Math.PI / SPHERE_DIV;
        si = Math.sin(ai);
        ci = Math.cos(ai);

        if(!colour){
            if(i % 3 == 0 && i % 2 == 0){
                color.push(1.0); //r
                color.push(0.0); //g
                color.push(0.0); //b
                color.push(0.0); //r
                color.push(0.0); //g
                color.push(1.0); //b
            }
            else if(i % 3 == 0){
                color.push(0.0); //r
                color.push(1.0); //g
                color.push(0.0); //b
                color.push(0.0); //r
                color.push(1.0); //g
                color.push(1.0); //b
            }
            else{
                color.push(0.2); //r
                color.push(0.58); //g
                color.push(0.82); //b

                color.push(0.32); //r
                color.push(0.18); //g
                color.push(0.56); //b
            }
        }
        else{
            color.push(0.5); //r
            color.push(0.0); //g
            color.push(0.0); //b
            color.push(0.5); //r
            color.push(0.0); //g
            color.push(0.0); //b
        }
    }
  }



  start = 0;
  // Generate indices
  for (j = 0; j < SPHERE_DIV; j++) {
    for(i = 0; i < SPHERE_DIV;i++){

        indices.push(start);
        indices.push(start+1);
        indices.push(start+2)

        indices.push(start+1);
        indices.push(start+2)
        indices.push(start+3);


        start += 2;
    }
  }

  // Write the vertex property to buffers (coordinates and normals)
  // Same data can be used for vertex and normal
  // In order to make it intelligible, another buffer is prepared separately
  if (!initArrayBuffer(gl, 'a_Position', new Float32Array(positions), gl.FLOAT, 3)) return -1;
  if (!initArrayBuffer(gl, 'a_Normal', new Float32Array(positions), gl.FLOAT, 3))  return -1;
  if (!initArrayBuffer(gl, 'a_Color', new Float32Array(color), gl.FLOAT, 3)) return -1;  

  // Unbind the buffer object
  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  // 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, new Uint16Array(indices), gl.STATIC_DRAW);

  return indices.length;
}

function check(gl, x, y, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected){
  selected = true;  
  drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected); // Draw cube with red

  // Read pixel at the clicked position
  var pixels = new Uint8Array(4); // Array for storing the pixel value
  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
  console.log("C: " + pixels[0]);
  if (pixels[0] == 255) // The mouse in on cube if R(pixels[0]) is 255
    picked[0] = true;
  else if(pixels[0] == 128){
      picked[1] = true;
  }
  selected = false;
  drawMain(gl, normalMatrix, modelMatrix, viewMatrix, u_ModelMatrix, u_MvpMatrix, u_NormalMatrix, selected); // Draw the cube
  return picked;
}

function initArrayBuffer(gl, attribute, data, type, num) {
  // Create a buffer object
  var buffer = gl.createBuffer();
  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);

  gl.bindBuffer(gl.ARRAY_BUFFER, null);

  return true;
}

0 个答案:

没有答案