1542605579614-4723a488-9f04-4ab6-83da-c4b2cdc4a634.png

    在学习三维之前让我们先看一看二维,还请见谅。这个主题对有些人来说可能过于简单,但还是准备在几篇文章中加以阐述。

    平移就是普通意义的“移动”物体。用第一篇文章中的代码,你可以改变传递给 setRectangle() 的值,移动矩形的位置。这里有个例子基于前一个例子

    首先我们来定义一些变量存储矩形的平移,宽,高和颜色。

    1. var translation = [0, 0];
    2. var width = 100;
    3. var height = 30;
    4. var color = [Math.random(), Math.random(), Math.random(), 1];

    然后定义一个方法重绘所有东西,我们可以在更新变换之后调用这个方法。

    1. // 绘制场景
    2. function drawScene() {
    3. webglUtils.resizeCanvasToDisplaySize(gl.canvas);
    4. // 告诉WebGL如何从裁剪空间对应到像素
    5. gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    6. // 清空画布
    7. gl.clear(gl.COLOR_BUFFER_BIT);
    8. // 使用我们的程序
    9. gl.useProgram(program);
    10. // 启用属性
    11. gl.enableVertexAttribArray(positionLocation);
    12. // 绑定位置缓冲
    13. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    14. // 设置矩形参数
    15. setRectangle(gl, translation[0], translation[1], width, height);
    16. // 告诉属性怎么从positionBuffer中读取数据 (ARRAY_BUFFER)
    17. var size = 2; // 每次迭代运行提取两个单位数据
    18. var type = gl.FLOAT; // 每个单位的数据类型是32位浮点型
    19. var normalize = false; // 不需要归一化数据
    20. var stride = 0; // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
    21. var offset = 0; // 从缓冲起始位置开始读取
    22. gl.vertexAttribPointer(
    23. positionLocation, size, type, normalize, stride, offset)
    24. // 设置分辨率
    25. gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
    26. // 设置颜色
    27. gl.uniform4fv(colorLocation, color);
    28. // 绘制矩形
    29. var primitiveType = gl.TRIANGLES;
    30. var offset = 0;
    31. var count = 6;
    32. gl.drawArrays(primitiveType, offset, count);
    33. }

    在下方的例子中,我添加了一对滑块,当它们值改变时会更新translation[0]translation[1] 并且调用drawScene方法。拖动滑块来平移矩形。

    image.png

    CodePen 地址

    到目前为止还不错!但是想象一下如果对一个更复杂的图形做类似操作怎么办。

    假设我们想绘制一个由六个三角形组成的 ‘F’ ,像这样

    polygon-f.svg

    接着当前的代码我们需要修改 setRectangle(),像这样

    1. // 在缓冲存储构成 'F' 的值
    2. function setGeometry(gl, x, y) {
    3. var width = 100;
    4. var height = 150;
    5. var thickness = 30;
    6. gl.bufferData(
    7. gl.ARRAY_BUFFER,
    8. new Float32Array([
    9. // 左竖
    10. x, y,
    11. x + thickness, y,
    12. x, y + height,
    13. x, y + height,
    14. x + thickness, y,
    15. x + thickness, y + height,
    16. // 上横
    17. x + thickness, y,
    18. x + width, y,
    19. x + thickness, y + thickness,
    20. x + thickness, y + thickness,
    21. x + width, y,
    22. x + width, y + thickness,
    23. // 中横
    24. x + thickness, y + thickness * 2,
    25. x + width * 2 / 3, y + thickness * 2,
    26. x + thickness, y + thickness * 3,
    27. x + thickness, y + thickness * 3,
    28. x + width * 2 / 3, y + thickness * 2,
    29. x + width * 2 / 3, y + thickness * 3,
    30. ]),
    31. gl.STATIC_DRAW);
    32. }

    你可能发现这样做可能并不好,如果我们想绘制一个含有成百上千个线条的几何图形,将会有很复杂的代码。最重要的是,每次绘制 JavaScript 都要更新所有点。

    这里有个简单的方式,上传几何体然后在着色器中进行平移。

    这是新的着色器

    1. attribute vec2 a_position;
    2. uniform vec2 u_resolution;
    3. uniform vec2 u_translation;
    4. void main() {
    5. // 加上平移量
    6. vec2 position = a_position + u_translation;
    7. // 从像素坐标转换到 0.0 到 1.0
    8. vec2 zeroToOne = position / u_resolution;
    9. ...
    10. </code></pre><div>重构一下代码,首先我们只需要设置一次几何体。</div>
    11. <pre data-syntax=""><code>// 在缓冲存储构成 'F' 的值
    12. function setGeometry(gl) {
    13. gl.bufferData(
    14. gl.ARRAY_BUFFER,
    15. new Float32Array([
    16. // 左竖
    17. 0, 0,
    18. 30, 0,
    19. 0, 150,
    20. 0, 150,
    21. 30, 0,
    22. 30, 150,
    23. // 上横
    24. 30, 0,
    25. 100, 0,
    26. 30, 30,
    27. 30, 30,
    28. 100, 0,
    29. 100, 30,
    30. // 中横
    31. 30, 60,
    32. 67, 60,
    33. 30, 90,
    34. 30, 90,
    35. 67, 60,
    36. 67, 90,
    37. ]),
    38. gl.STATIC_DRAW);
    39. }
    40. </code></pre>

    然后我们只需要在绘制前更新u_translation为期望的平移量。

    1. ...
    2. var translationLocation = gl.getUniformLocation(
    3. program, "u_translation");
    4. ...
    5. // 创建一个存放位置信息的缓冲
    6. var positionBuffer = gl.createBuffer();
    7. // 绑定到 ARRAY_BUFFER (简单的理解为 ARRAY_BUFFER = positionBuffer)
    8. gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    9. // 将几何数据存到缓冲
    10. setGeometry(gl);
    11. ...
    12. // 绘制场景
    13. function drawScene() {
    14. ...
    15. // 设置平移
    16. gl.uniform2fv(translationLocation, translation);
    17. // 绘制矩形
    18. var primitiveType = gl.TRIANGLES;
    19. var offset = 0;
    20. var count = 18;
    21. gl.drawArrays(primitiveType, offset, count);
    22. }

    注意到setGeometry只调用了一次,它不在drawScene内部了。

    image.png

    CodePen 地址

    现在当我们绘制时,WebGL几乎做了所有事情,我们做的仅仅是设置平移然后让它绘制,即使我们的几何体有成千上万个点,主要的代码还是保持不变。你可以对比上方例子中使用 JavaScript 更新所有点的情况。

    我希望这个例子不会过于简单,请继续阅读,我们会用更好的方式实现平移。下一篇将介绍二维旋转。