首页 > 解决方案 > 如何动态地将对象添加到场景中?

问题描述

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

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

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

我希望设法将无限数量的对象(或直到我用完 ram)添加到场景中,并一次将转换应用于一个对象。

标签: webgl

解决方案


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

const scene = [];

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

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

在您自己的代码中,您将有一个renderordrawScene函数来遍历您的“场景”并绘制每个对象

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.

   }
}

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

其他应用程序做不同的事情。

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

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

建议你阅读一些关于 WebGL绘制多个东西的教程,也许是一个场景图


推荐阅读