在webGL中绘制参数形状(不含three.js)

时间:2015-11-18 07:03:28

标签: javascript html5 graphics opengl-es webgl

我编写了一个程序,只使用HTML5和常规Javascript绘制一些参数形状(球体,圆环和圆柱体)。我正在尝试转换此代码,以便它使用WebGL,使用三角形条实现形状,如this tutorial中所述。我的困惑之处在于三角带甚至是如何用于创建球体的。我之前做的方式只是计算根据for循环中的纬度线绘制每个水平切片或圆的位置,并嵌套在该循环内部是计算该圆上的每个点。在生成所有这些点之后,我将它们传递给一个函数,该函数将所有顶点添加到一个数组中,该数组返回并传递给使用moveTo()lineTo()绘制的曲线绘制函数以便绘制每个点之间的线。问题是,当使用三角形形状时,我不知道webGL中moveTo()lineTo()的等价物是什么。如何将我的实现转换为WebGL?

以下是我原始实现中的一些代码:

//Calculates point on sphere
   function spherePoint(uv) {
      var u = uv[0];
      var v = uv[1];
      var phi = -Math.PI/2 + Math.PI * v;
      var theta = 2 * Math.PI * u;
      return [ Math.cos(phi) * Math.cos(theta),
               Math.cos(phi) * Math.sin(theta),
               Math.sin(phi)];
   }

// Takes the parametric function as an argument and constructs 3D shape

   function makeShape(num_u, num_v, eq, possRad) {
      var shell = [];
      for (var j = 0 ; j <= num_v ; j++) {
          var v = j / num_v;
          shell.push([]);
         for (var i = 0 ; i <= num_u ; i++) {
             var u = i / num_u;
             var p = eq([u, v], possRad);
            shell[j].push(p);
         }
      }
      return shell;
   }
// Used to create shapes to render parametric surface

   function renderShape(shape) {
       var num_j = shape.length;
       var num_i = shape[0].length;
       for (var j = 0 ; j < num_j - 1 ; j++)
           for (var i = 0 ; i < num_i - 1 ; i++) {

              plotCurve([shape[j][i],
                          shape[j + 1][i],
                          shape[j + 1][i + 1],
                          shape[j][i + 1]]);
          }
   }
 //plot curve on canvas
   function plotCurve(C) {
      g.beginPath();
      for (var i = 0 ; i < C.length ; i++)
         if (i == 0)
            moveTo(C[i]);
         else
            lineTo(C[i]);
      g.stroke();
   }

  function moveTo(p) {
      var q = m.transform(p);  // APPLY 3D MATRIX TRANFORMATION
      var xy = viewport(q);    // APPLY VIEWPORT TRANSFORM
      g.moveTo(xy[0], xy[1]);
   }

   function lineTo(p) {
      var q = m.transform(p);  // APPLY 3D MATRIX TRANFORMATION
      var xy = viewport(q);    // APPLY VIEWPORT TRANSFORM
      g.lineTo(xy[0], xy[1]);
   }

webGL版本应该看起来像我想的那样:enter image description here

普通的Javascript版本如下所示: enter image description here

2 个答案:

答案 0 :(得分:2)

这是一个非常基本的WebGL问题。 Some more tutorials on webgl might be helpful

WebGL仅绘制大量数据。它实际上没有lineTomoveTo。相反,你给它缓冲数据,告诉它如何从这些缓冲区中提取数据,然后你编写一个函数(一个顶点着色器)来使用该数据告诉WebGL如何将它转换为剪辑空间坐标以及是否绘制点,线,或带有结果的三角形。您还提供了一个函数(片段着色器)来告诉它用于点,线或三角形的颜色。

基本上要绘制想要绘制的东西,您需要为该球体上的每个矩形生成2个三角形。换句话说,您需要为每个矩形生成6个顶点。原因是为了绘制不同颜色的每个三角形,您不能共享任何顶点,因为颜色与顶点相关联。

因此,对于一个矩形,您需要生成这些点

0--1 4
| / /|
|/ / |
2 3--5

其中0,1和2是粉红点,3,4,5是绿点。 1和4具有相同的位置,但由于它们的颜色不同,它们必须是不同的点。与第2点和第3点相同。

var pink   = [1, 0.5, 0.5, 1];
var green  = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var across = 20;
var down = 10;

function addPoint(x, y, color) {
  var u = x / across;
  var v = y / down;
  var radius = Math.sin(v * Math.PI);
  var angle = u * Math.PI * 2;
  var nx = Math.cos(angle);
  var ny = Math.cos(v * Math.PI);
  var nz = Math.sin(angle);
  positions.push(
     nx * radius,   // x
     ny,            // y
     nz * radius);  // z
  colors.push(color[0], color[1], color[2], color[3]);
}

for (var y = 0; y < down; ++y) {
  for (var x = 0; x < across; ++x) {
    // for each rect we need 6 points
    addPoint(x    , y    , pink);
    addPoint(x + 1, y    , pink);
    addPoint(x    , y + 1, pink);

    addPoint(x    , y + 1, green);
    addPoint(x + 1, y    , green);
    addPoint(x + 1, y + 1, green);
  }
}

这里是上面渲染的球体,但没有任何光线,透视或任何东西。

&#13;
&#13;
var pink   = [1, 0.5, 0.5, 1];
var green  = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var across = 20;
var down = 10;

function addPoint(x, y, color) {
  var u = x / across;
  var v = y / down;
  var radius = Math.sin(v * Math.PI);
  var angle = u * Math.PI * 2;
  var nx = Math.cos(angle);
  var ny = Math.cos(v * Math.PI);
  var nz = Math.sin(angle);
  positions.push(
    nx * radius,   // x
    ny,            // y
    nz * radius);  // z
  colors.push(color[0], color[1], color[2], color[3]);
}

for (var y = 0; y < down; ++y) {
  for (var x = 0; x < across; ++x) {
    // for each rect we need 6 points
    addPoint(x    , y    , pink);
    addPoint(x + 1, y    , pink);
    addPoint(x    , y + 1, pink);

    addPoint(x    , y + 1, green);
    addPoint(x + 1, y    , green);
    addPoint(x + 1, y + 1, green);
  }
}

var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
 
var arrays = {
  position: positions,
  color: colors,
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

var uniforms = {
  resolution: [gl.canvas.width, gl.canvas.height],
};

gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
&#13;
canvas { border: 1px solid black; }
&#13;
<canvas id="c"></canvas>  

<script id="vs" type="not-js">
attribute vec4 position;
attribute vec4 color;

uniform vec2 resolution;

varying vec4 v_color;

void main() {
  gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1);
  v_color = color;
}
</script>
<script id="fs" type="not-js">
precision mediump float;

varying vec4 v_color;

void main() {
  gl_FragColor = v_color;
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
&#13;
&#13;
&#13;

如果您以后想点亮它,您还需要法线(您可以使用这些值来判断某些方向正面)。我们可以通过添加

来添加它们
var normals = [];

addPoint

function addPoint(x, y, color) {
  var u = x / across;
  var v = y / down;
  var radius = Math.sin(v * Math.PI);
  var angle = u * Math.PI * 2;
  var nx = Math.cos(angle);
  var ny = Math.cos(v * Math.PI);
  var nz = Math.sin(angle);
  positions.push(
     nx * radius,   // x
     ny,            // y
     nz * radius);  // z
  colors.push(color[0], color[1], color[2], color[3]);
  normals.push(nx, ny, nz);
}

这是一个带有黑客照明的样本

&#13;
&#13;
var pink   = [1, 0.5, 0.5, 1];
var green  = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var normals = [];
var across = 20;
var down = 10;

function addPoint(x, y, color) {
  var u = x / across;
  var v = y / down;
  var radius = Math.sin(v * Math.PI);
  var angle = u * Math.PI * 2;
  var nx = Math.cos(angle);
  var ny = Math.cos(v * Math.PI);
  var nz = Math.sin(angle);
  positions.push(
    nx * radius,   // x
    ny,            // y
    nz * radius);  // z
  normals.push(nx, ny, nz);
  colors.push(color[0], color[1], color[2], color[3]);
}

for (var y = 0; y < down; ++y) {
  for (var x = 0; x < across; ++x) {
    // for each rect we need 6 points
    addPoint(x    , y    , pink);
    addPoint(x + 1, y    , pink);
    addPoint(x    , y + 1, pink);

    addPoint(x    , y + 1, green);
    addPoint(x + 1, y    , green);
    addPoint(x + 1, y + 1, green);
  }
}

var gl = document.getElementById("c").getContext("webgl");
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
 
var arrays = {
  position: positions,
  normal: normals,
  color: colors,
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);

var uniforms = {
  resolution: [gl.canvas.width, gl.canvas.height],
  lightDirection: [0.5, 0.5, -1],
};

gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
&#13;
canvas { border: 1px solid black; }
&#13;
<canvas id="c"></canvas>  

<script id="vs" type="not-js">
attribute vec4 position;
attribute vec4 color;
attribute vec3 normal;

uniform vec2 resolution;

varying vec4 v_color;
varying vec3 v_normal;

void main() {
  gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1);
  v_color = color;
  v_normal = normal;
}
</script>
<script id="fs" type="not-js">
precision mediump float;

varying vec4 v_color;
varying vec3 v_normal;

uniform vec3 lightDirection;

void main() {
  float light = pow(abs(dot(v_normal, normalize(lightDirection))), 2.0);
  gl_FragColor = vec4(v_color.xyz * light, v_color.a);
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
&#13;
&#13;
&#13;

PS:您发布的picture实际上是每个矩形绘制更多三角形。绿色和粉红色之间的区分并不直。

答案 1 :(得分:1)

gl.LINES很好,几乎画了连线。因此lineTo(x,y,z)只会向用于存储行数据的VBO添加一个顶点。 moveTo(x,y,z)只是为了创建一个&#34; break&#34;线之间。这可以通过新的drawArrays调用来完成。