WebGL 实践篇(三)—— 二维图形的平移、旋转、缩放

一 平移

(1)平移直接体现在代码当中

在二维当中,平移相当于就是改变x,y的位置。

    function setRectangle(gl, x, y, width, height) {
      var x1 = x;
      var x2 = x + width;
      var y1 = y;
      var y2 = y + height;
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        x1, y1,
        x2, y1,
        x1, y2,
        x1, y2,
        x2, y1,
        x2, y2
      ]), gl.STATIC_DRAW);
    }

在这段代码当中,要实现矩形的移动,就改变x,y的值即可。x,y的值通过外界可变的方式传入,每改变一次x,y的值就要进行一次重绘矩形,refreshDraw()是对渲染代码进行封装,滑块引入webgl-lessons-ui.js库进行操作。

    var translation = [10, 10];
    var width = 100;
    var height = 30;
    var color = [Math.random(), Math.random(), Math.random(), 1];

    refreshDraw();

    webglLessonsUI.setupSlider("#x", { slide: updatePosition(0), max: webgl.canvas.width });
    webglLessonsUI.setupSlider("#y", { slide: updatePosition(1), max: webgl.canvas.height });

    function updatePosition(index) {
      return function (event, ui) {
        translation[index] = ui.value;
        refreshDraw();
      }
    }

结果如下:

 (2)平移体现在着色器当中

如果对一个复杂图形进行平移的话,像上述那种代码就比较复杂与繁琐。将平移项加入到着色器当中能够简化代码。比如F的绘制,只需要知道"F"形状的长宽即可,偏移量可不体现在setGeometry()这个函数当中:

    function setRectangle(gl) {
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        // 左竖
        0, 0,
        30, 0,
        0, 150,
        0, 150,
        30, 0,
        30, 150,

        // 长横
        30, 0,
        100, 0,
        30, 30,
        30, 30,
        100, 0,
        100, 30,

        // 短横
        30, 60,
        67, 60,
        30, 90,
        30, 90,
        67, 60,
        67, 90,
      ]), gl.STATIC_DRAW);
    }

偏移量(u_translation)体现在着色器中:

  <script id="vertex-shader" type="nojs">
    attribute vec2 a_position;
    uniform vec2 u_resolution;
    uniform vec2 u_translation;

    void main(){
      vec2 position = a_position + u_translation;
      vec2 zeroToOne = position / u_resolution;
      vec2 zeroToTwo = zeroToOne * 2.0;
      vec2 clipSpace = zeroToTwo - 1.0;

      gl_Position = vec4(clipSpace * vec2(1,-1),0,1);
    }
  </script>

之后的代码都差不多,需要注意的是参数单个分别传入与一起以数组的形式传入的区别:

      webgl.uniform2f(resolutionUniformLocation, webgl.canvas.width, webgl.canvas.height);
      webgl.uniform2fv(translationUniformLocation, translation);
      webgl.uniform4fv(colorUniformLocation, color);

还要注意最后绘制的个数为18个,因为有6个三角形构成了”F“,而每个三角形有3个顶点。

      webgl.drawArrays(webgl.TRIANGLES, 0, 18);

以及还要注意一下,加入这行代码与不加的区别:

      webglUtils.resizeCanvasToDisplaySize(webgl.canvas);

不加上述那行代码的结果如下:

 加了的结果:

 二 旋转

有两种理解方式,一是将原来的坐标点乘以单位圆中的某一点来获得旋转后的坐标点,二是根据正余弦来求解旋转后的坐标点。其实二者算是同一种方式,只不过单位圆算是正余弦之后得到的结果了。

利用单位圆(其中中间的加减符号与旋转方向有关,该公式一般作顺时针旋转):

rotatedX = a_position.x * u_rotation.y + a_position.y * u_rotation.x;
rotatedY = a_position.y * u_rotation.y - a_position.x * u_rotation.x;

利用正余弦(其中中间的加减符号与旋转方向有关,也在一定程度上与传入的角度有关,该公式一般作逆时针旋转):

rotatedX = a_position.x * u_cosB - a_position.y * u_sinB;
rotatedY = a_position.y * u_cosB + a_position.x * u_sinB;

旋转量(u_rotation)也体现在着色器当中:

  <script id="vertex-shader" type="nojs">
    attribute vec2 a_position;
    uniform vec2 u_resolution;
    uniform vec2 u_translation;
    uniform vec2 u_rotation;

    void main(){
      vec2 rotatedPosition = vec2(
        a_position.x * u_rotation.y + a_position.y * u_rotation.x,
        a_position.y * u_rotation.y - a_position.x * u_rotation.x);
      vec2 position = rotatedPosition + u_translation;

      vec2 zeroToOne = position / u_resolution;

      vec2 zeroToTwo = zeroToOne * 2.0;

      vec2 clipSpace = zeroToTwo - 1.0;

      gl_Position = vec4(clipSpace * vec2(1,-1),0,1);
    }
  </script>

获得属性位置、传参以及加入旋转角度的滑块(代码借助正余弦方式进行实现):

    var rotationUniformLocation = webgl.getUniformLocation(program, "u_rotation");
    var rotation = [0, 1];
    webglLessonsUI.setupSlider("#rotation", { slide: updateAngle, max: 360 });

    function updateAngle(event, ui) {
      var angleInDegrees = 360 - ui.value;
      var angleInRadians = angleInDegrees * Math.PI / 180;

      rotation[0] = Math.sin(angleInRadians);
      rotation[1] = Math.cos(angleInRadians);

      refreshDraw();
    }

    //不要忘记设置旋转参数
    webgl.uniform2fv(rotationUniformLocation, rotation);

结果如下:

 三 缩放

缩放跟平移的原理差不多。

缩放大小(u_scale)体现在顶点着色器当中:

  <script id="vertex-shader" type="x-shader/x-vertex">
    attribute vec2 a_position;
    uniform vec2 u_resolution;
    uniform vec2 u_translation;
    uniform vec2 u_rotation;
    uniform vec2 u_scale;

    void main(){
      vec2 scaledPosition = a_position * u_scale;

      vec2 rotatedPosition = vec2(
        scaledPosition.x * u_rotation.y + scaledPosition.y * u_rotation.x,
        scaledPosition.y * u_rotation.y - scaledPosition.x * u_rotation.x);
      vec2 position = rotatedPosition + u_translation;

      vec2 zeroToOne = position / u_resolution;

      vec2 zeroToTwo = zeroToOne * 2.0;

      vec2 clipSpace = zeroToTwo - 1.0;

      gl_Position = vec4(clipSpace * vec2(1,-1),0,1);
    }
  </script>

获得属性位置、传参以及加入缩放大小的滑块:

    var scaleUniformLocation = webgl.getUniformLocation(program, "u_scale")

    webglLessonsUI.setupSlider("#scalex", { value: scale[0], slide: updateScale(0), min: -5, max: 5, step: 0.01, precision: 2 });
    webglLessonsUI.setupSlider("#scaley", { value: scale[1], slide: updateScale(1), min: -5, max: 5, step: 0.01, precision: 2 });

      webgl.uniform2fv(scaleUniformLocation, scale);

    function updateScale(index) {
      return function (event, ui) {
        scale[index] = ui.value;
        refreshDraw();
      }
    }

    //不要忘记设置缩放参数
    webgl.uniform2fv(scaleUniformLocation, scale);

结果如下(x为负值向左缩放,y为负值向上缩放):

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值