如何动态添加对象到场景?

How to dynamically add objects to scene?

我必须编写一个程序,我应该能够在其中单击一个按钮 "add cube" 并且可以在场景中添加任意数量的立方体。我必须能够 select 他们中的任何一个并四处走动。我设法创建了一个(硬编码的)立方体并对其应用缩放和平移。

我尝试创建一个 class "cube" 并调用初始化和渲染作为对象的一部分,但没有按预期工作。这是一个立方体及其转换的工作示例(仅注意缩放和平移工作)

HTML:

<head>
  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
  <title>Lab 0</title>

  <script id="vertex-shader" type="x-shader/x-vertex">

    attribute  vec4 vPosition;
    attribute  vec4 vColors;
    varying vec4 fragColor;

    uniform vec3 theta;
    uniform float coeff;
    uniform vec3 trCoeff;

    uniform mat4 modelView;
    uniform mat4 projection;

    vec3 angles = radians( theta );
    void main() {

      vec3 angles = radians( theta );
      vec3 c = cos( angles );
      vec3 s = sin( angles );

      // Remeber: thse matrices are column-major
      mat4 rx = mat4( 1.0,  0.0,  0.0, 0.0,
      0.0,  c.x,  s.x, 0.0,
      0.0, -s.x,  c.x, 0.0,
      0.0,  0.0,  0.0, 1.0 );

  mat4 ry = mat4( c.y, 0.0, -s.y, 0.0,
      0.0, 1.0,  0.0, 0.0,
      s.y, 0.0,  c.y, 0.0,
      0.0, 0.0,  0.0, 1.0 );


  mat4 rz = mat4( c.z, s.z, 0.0, 0.0,
      -s.z,  c.z, 0.0, 0.0,
      0.0,  0.0, 1.0, 0.0,
      0.0,  0.0, 0.0, 1.0 );

  mat4 sc = mat4(
    coeff, 0, 0, 0,
    0, coeff, 0, 0,
    0, 0, coeff, 0,
    0, 0, 0, 1
  );

  mat4 tr = mat4(
    1, 0, 0, 0,
    0, 1, 0, 0,
    0, 0, 1, 0,
    trCoeff.x, trCoeff.y, trCoeff.z, 1
  );

    fragColor = vColors;
    //projection * modelView *
    gl_Position =  tr * rz * ry * rx * sc * vPosition;
    }
  </script>

  <script id="fragment-shader" type="x-shader/x-fragment">
    precision mediump float;
    varying vec4 fragColor;

    void main() {
      gl_FragColor = vec4(fragColor);
    }
  </script>

  <script type="text/javascript" src="webgl-utils.js"></script>
  <script type="text/javascript" src="initShaders.js"></script>
  <script type="text/javascript" src="MV.js"></script>
  <script type="text/javascript" src="shape0.js"></script>
</head>

<body>
  <canvas id="gl-canvas" width="512" height="512">
    Oops ... your browser doesn't support the HTML5 canvas element
  </canvas>
</body>
<p> </p>
<button id="xButton">Rotate X</button>
<button id="yButton">Rotate Y</button>
<button id="zButton">Rotate Z</button>
<button id="stopButton">Stop Fucking Rotation</button>
<p> </p>

<button id="sUpButton">Scale Up</button>
<button id="sDownButton">Scale Down</button>
<p> </p>

<button id="leftButton">Left</button>
<button id="rightButton">Right</button>
<button id="upButton">Up</button>
<button id="downButton">Down</button>
<button id="closeButton">Closer</button>
<button id="furtherButton">Further</button>

<p> </p>
<button id="Button1">Increase Z</button>
<button id="Button2">Decrease Z</button>
<button id="Button3">Increase R</button>
<button id="Button4">Decrease R</button>

<p> </p>
<button id="Button5">Increase theta</button>
<button id="Button6">Decrease theta</button>
<button id="Button7">Increase phi</button>
<button id="Button8">Decrease phi</button>
<p> </p>

</html>

JS:

"use strict"

var canvas;
var gl;

var thetaRot = [10, 10, 10];
var thetaLoc;
var coeff = 1;
var coeffLoc;
var trCoeff = [0, 0, 0];
var trCoeffLoc;

var flag = true;
var axis = 0;

var xAxis = 0;
var yAxis = 1;
var zAxis = 2;

var near = 0.3;
var far = 3.0;
var radius = 4.0;
var theta = 0.0;
var phi = 0.0;
var dr = 5.0 * Math.PI / 180.0;

var fovy = 45.0;  // Field-of-view in Y direction angle (in degrees)
var aspect;       // Viewport aspect ratio

var mvMatrix, pMatrix;
var modelView, projection;
var eye;
const at = vec3(0.0, 0.0, 0.0);
const up = vec3(0.0, 1.0, 0.0);

var coord = {
  // For cube
  '1': vec3(0.1, 0.1, 0.1),
  '2': vec3(0.5, 0.1, 0.1),
  '3': vec3(0.5, 0.1, 0.5),
  '4': vec3(0.1, 0.1, 0.5),
  '5': vec3(0.1, 0.5, 0.1),
  '6': vec3(0.1, 0.5, 0.5),
  '7': vec3(0.5, 0.5, 0.5),
  '8': vec3(0.5, 0.5, 0.1),

}

var vertices = [
  // For cube
  coord[4], coord[3], coord[2],
  coord[2], coord[4], coord[1],
  coord[1], coord[2], coord[5],
  coord[5], coord[8], coord[2],
  coord[2], coord[3], coord[8],
  coord[8], coord[3], coord[7],
  coord[7], coord[3], coord[4],
  coord[4], coord[7], coord[6],
  coord[6], coord[4], coord[1],
  coord[1], coord[5], coord[6],
  coord[6], coord[7], coord[8],
  coord[8], coord[6], coord[5],

]

var colors = [
  // each face of the cube
  0, 0, 1, 1.0,
  0, 0, 1, 1.0,
  0, 0, 1, 1.0,
  0, 0, 1, 1.0,
  0, 0, 1, 1.0,
  0, 0, 1, 1.0,

  0, 1, 0, 1.0,
  0, 1, 0, 1.0,
  0, 1, 0, 1.0,
  0, 1, 0, 1.0,
  0, 1, 0, 1.0,
  0, 1, 0, 1.0,

  0, 0.7, 1, 1.0,
  0, 0.7, 1, 1.0,
  0, 0.7, 1, 1.0,
  0, 0.7, 1, 1.0,
  0, 0.7, 1, 1.0,
  0, 0.7, 1, 1.0,

  0.5, 0, 1, 1.0,
  0.5, 0, 1, 1.0,
  0.5, 0, 1, 1.0,
  0.5, 0, 1, 1.0,
  0.5, 0, 1, 1.0,
  0.5, 0, 1, 1.0,

  1, 0, 0, 1.0,
  1, 0, 0, 1.0,
  1, 0, 0, 1.0,
  1, 0, 0, 1.0,
  1, 0, 0, 1.0,
  1, 0, 0, 1.0,

  0.2, 0, 1, 1.0,
  0.2, 0, 1, 1.0,
  0.2, 0, 1, 1.0,
  0.2, 0, 1, 1.0,
  0.2, 0, 1, 1.0,
  0.2, 0, 1, 1.0,

]

// add vertices for cone


window.onload = init;

function init() {
  canvas = document.getElementById("gl-canvas");

  gl = WebGLUtils.setupWebGL(canvas);

  if (!gl) { alert("WebGL isn't available"); }

  aspect = canvas.width / canvas.height;

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

  gl.enable(gl.DEPTH_TEST);


  var bufferId = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
  gl.bufferData(gl.ARRAY_BUFFER, 50000, gl.STATIC_DRAW);
  gl.bufferSubData(gl.ARRAY_BUFFER, 0, flatten(vertices));

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

  var bufferColor = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, bufferColor);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

  var vColors = gl.getAttribLocation(program, "vColors");
  gl.vertexAttribPointer(vColors, 4, gl.FLOAT, false, 4 * Float32Array.BYTES_PER_ELEMENT, 0);
  gl.enableVertexAttribArray(vColors);

  thetaLoc = gl.getUniformLocation(program, "theta");


  document.getElementById("xButton").onclick = function () {
    flag = false
    axis = xAxis;
  };
  document.getElementById("yButton").onclick = function () {
    flag = false
    axis = yAxis;
  };
  document.getElementById("zButton").onclick = function () {
    flag = false
    axis = zAxis;
  };
  document.getElementById("stopButton").onclick = function () {
    flag = true;
  };


  document.getElementById("sUpButton").onclick = function () {
    coeff += 0.1;
  };
  document.getElementById("sDownButton").onclick = function () {
    coeff -= 0.1;
  };



  document.getElementById("leftButton").onclick = function () {
    trCoeff[0] -= 0.1;
  };
  document.getElementById("rightButton").onclick = function () {
    trCoeff[0] += 0.1;
  };
  document.getElementById("upButton").onclick = function () {
    trCoeff[1] += 0.1;
  };
  document.getElementById("downButton").onclick = function () {
    trCoeff[1] -= 0.1;
  };
  document.getElementById("closeButton").onclick = function () {
    trCoeff[2] += 0.1;
  };
  document.getElementById("furtherButton").onclick = function () {
    trCoeff[2] -= 0.1;
  };

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

  document.getElementById("Button1").onclick = function () { near *= 1.1; far *= 1.1; };
  document.getElementById("Button2").onclick = function () { near *= 0.9; far *= 0.9; };
  document.getElementById("Button3").onclick = function () { radius *= 2.0; };
  document.getElementById("Button4").onclick = function () { radius *= 0.5; };
  document.getElementById("Button5").onclick = function () { theta += dr; };
  document.getElementById("Button6").onclick = function () { theta -= dr; };
  document.getElementById("Button7").onclick = function () { phi += dr; };
  document.getElementById("Button8").onclick = function () { phi -= dr; };

  coeffLoc = gl.getUniformLocation(program, "coeff");
  trCoeffLoc = gl.getUniformLocation(program, "trCoeff");



  render();
}

function render() {
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  if (!flag) {
    thetaRot[axis] += 2.0;
  }
  gl.uniform3fv(thetaLoc, thetaRot);
  gl.uniform1f(coeffLoc, coeff);
  gl.uniform3fv(trCoeffLoc, trCoeff);


  eye = vec3(radius * Math.sin(theta) * Math.cos(phi),
    radius * Math.sin(theta) * Math.sin(phi), radius * Math.cos(theta));
  mvMatrix = lookAt(eye, at, up);
  pMatrix = perspective(fovy, aspect, near, far);

  gl.uniformMatrix4fv(modelView, false, flatten(mvMatrix));
  gl.uniformMatrix4fv(projection, false, flatten(pMatrix));

  gl.drawArrays(gl.TRIANGLES, 0, vertices.length);

  requestAnimFrame(render);
}

我希望设法向场景中添加无限数量的对象(或者直到我 运行 内存不足)并一次对一个对象应用转换。

WebGL 只是绘制像素。它没有 "Scene" 和 "objects" 的概念。所有这些都 100% 取决于您。如何表示对象以及如何表示场景完全取决于您。一个例子

const scene = [];

function addNewObjectToScene() {
  const obj = {
     x: Math.random() * 400,
     y: Math.random() * 400,
  }; 
  scene.push(obj);
}

现在添加按钮或计时器或任何您想调用的东西addNewObjectToScene

在您自己的代码中,您将有一个 renderdrawScene 函数遍历 "scene" 并绘制每个对象

function render() {
   for (const object of scene) {
     gl.useProgram(whateverShaderProgramCurrentObjectNeeds);

     // setBuffersAndAttributes for object
     // for each attribute in object
     //   gl.bindBuffer(gl.ARRAY_BUFFER, bufferForThisAttribute)
     //   gl.enableVertexAttribArray(locationForThisAttrubute);
     //   gl.vertexAttribPointer(locationForThisAttibute, other settings for this attribute)

     // set any uniforms. This is probably where you'd use x and y from the code above
     // example:
     //   gl.uniform2f(offsetUniformLocation, object.x, object.y);

     // now draw
     // gl.drawArrays or gl.drawElements with how ever many vertices are in the current object.

   }
}

如何组织 "scene" 以及对象中的内容完全取决于您。 Three.js 从一个名为 THREE.Object3D 的对象创建一个场景,每个对象都包含 "children" 的数组。这样就形成了一个场景图。

其他应用做不同的事情。

能够select一个也是一个太大的话题。 "select" 是什么意思。您可以使用 HTML <select> 表单从下拉列表中选择您想要 select 的对象吗?也许有一个 <input type="text"> 元素,您可以在其中键入要 select?

的对象的名称

如果您想单击对象,那么现在您的主题太宽泛了,无法涵盖。您可以为每个对象存储碰撞数据。您可以计算数据所形成的每个三角形的位置,并查看鼠标是否命中。您可以使用 GPU 拾取,使用不同的着色器以不同的颜色绘制场景,然后查看鼠标下方的像素以查看它有效地告诉您想要选择的对象是什么颜色。但所有这些都取决于您如何创建场景。

建议你read some tutorials on WebGL and on drawing multiple things and maybe a scene graph