我试图能够使用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;
}