【问题标题】:How to sync css transform 3d to webgl?如何将 css 转换 3d 同步到 webgl?
【发布时间】:2021-08-19 17:12:43
【问题描述】:

在css中我可以使用perspective + matrix3d来实现3d变换的透视效果。

例如:

transform: perspective(500px) rotateY(45deg);

等于

matrix3d(0.707107, 0, -0.707107, 0.00141421, 0, 1, 0, 0, 0.707107, 0, 0.707107, -0.00141421, 0, 0, 0, 1)

<div style="display:inline-block;background: linear-gradient(#F00, #00F);width:100px;height:100px;transform:perspective(300px)rotateY(45deg)"></div>

<div style="display:inline-block;background: linear-gradient(#F00, #00F);width:100px;height:100px;transform:matrix3d(0.707107, 0, -0.707107, 0.00141421, 0, 1, 0, 0, 0.707107, 0, 0.707107, -0.00141421, 0, 0, 0, 1)"></div>

同样在webgl中使用这个矩阵,渲染了一个矩形(2个三角形),但是没有透视的效果。

function initShaders(gl, vshader, fshader) {
    let program = createProgram(gl, vshader, fshader);
    if (!program) {
      return false;
    }

    gl.useProgram(program);
    gl.program = program;

    return true;
  }


  function createProgram(gl, vshader, fshader) {
    let vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
    let fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
    if (!vertexShader || !fragmentShader) {
      return null;
    }

    let program = gl.createProgram();
    if (!program) {
      return null;
    }

    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);

    let linked = gl.getProgramParameter(program, gl.LINK_STATUS);
    if (!linked) {
      let error = gl.getProgramInfoLog(program);
      gl.deleteProgram(program);
      gl.deleteShader(fragmentShader);
      gl.deleteShader(vertexShader);
      return null;
    }
    return program;
  }

  function loadShader(gl, type, source) {
    let shader = gl.createShader(type);
    if (shader == null) {
      return null;
    }

    gl.shaderSource(shader, source);
    gl.compileShader(shader);

    let compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
    if (!compiled) {
      let error = gl.getShaderInfoLog(shader);
      console.error('Failed to compile shader: ' + error);
      gl.deleteShader(shader);
      return null;
    }

    return shader;
  }

  let canvas = document.querySelector('#canvas');
  let gl = canvas.getContext('webgl');
  gl.clearColor(0.0, 0.0, 0.0, 0.0);
  gl.clear(gl.COLOR_BUFFER_BIT);

  let vSource = `attribute vec4 a_position;
        attribute vec4 a_color;
        varying vec4 v_color;
        uniform mat4 u_matrix;

        void main() {
         gl_Position = u_matrix * a_position;
         v_color = a_color;
        }`;

  let fSource = `precision mediump float;

    varying vec4 v_color;

    void main() {
        gl_FragColor = v_color;
    }`;
  if (!initShaders(gl, vSource, fSource)) {
    console.error('init error');
  }

  let pointBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    -0.5, -0.5,
    0.5, -0.5,
    -0.5, 0.5,
    0.5, -0.5,
    -0.5, 0.5,
    0.5, 0.5,
  ]), gl.STATIC_DRAW);
  let a_position = gl.getAttribLocation(gl.program, 'a_position');
  gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_position);

  let colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0, 0, 1, 1,
    0, 0, 1, 1,
    1, 0, 0, 1,
    0, 0, 1, 1,
    1, 0, 0, 1,
    1, 0, 0, 1,
  ]), gl.STATIC_DRAW);
  let a_color = gl.getAttribLocation(gl.program, 'a_color');
  gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_color);
  let u_matrix = gl.getUniformLocation(gl.program, 'u_matrix');

  // same matrix as css above
  gl.uniformMatrix4fv(u_matrix, false, new Float32Array([
    0.707107, 0, -0.707107, 0.00141421, 0, 1, 0, 0, 0.707107, 0, 0.707107, -0.00141421, 0, 0, 0, 1
  ]));
  gl.drawArrays(gl.TRIANGLES, 0, 6);
&lt;canvas id="canvas" style="height: 200px;width: 200px"&gt;&lt;/canvas&gt;

我应该如何在webgl中获得一个与css变换矩阵具有等效效果的矩阵?

最后,我应该注意到我已经阅读了WebGL 3D Perspective,但毕竟,我仍然对这个问题感到困惑。

【问题讨论】:

    标签: javascript html css webgl css-transforms


    【解决方案1】:

    我尝试将WebGL 3D Perspective 中的方法和您的矩阵结合起来,fudgeFactor 的因子是 100 倍(webgl 中为-0.2,css 中为-0.002)。进一步探索表明,这个因素与图像大小有关,对于一个 200px * 200px 的 DOM,fudgeFactor 在 webgl 中为 -0.1 而在 css 中为 -0.002。

    var m4 = {
    
      projection: function(width, height, depth) {
        // Note: This matrix flips the Y axis so 0 is at the top.
        return [
           2 / width, 0, 0, 0,
           0, -2 / height, 0, 0,
           0, 0, 2 / depth, 0,
          -1, 1, 0, 1,
        ];
      },
    
      multiply: function(a, b) {
        var a00 = a[0 * 4 + 0];
        var a01 = a[0 * 4 + 1];
        var a02 = a[0 * 4 + 2];
        var a03 = a[0 * 4 + 3];
        var a10 = a[1 * 4 + 0];
        var a11 = a[1 * 4 + 1];
        var a12 = a[1 * 4 + 2];
        var a13 = a[1 * 4 + 3];
        var a20 = a[2 * 4 + 0];
        var a21 = a[2 * 4 + 1];
        var a22 = a[2 * 4 + 2];
        var a23 = a[2 * 4 + 3];
        var a30 = a[3 * 4 + 0];
        var a31 = a[3 * 4 + 1];
        var a32 = a[3 * 4 + 2];
        var a33 = a[3 * 4 + 3];
        var b00 = b[0 * 4 + 0];
        var b01 = b[0 * 4 + 1];
        var b02 = b[0 * 4 + 2];
        var b03 = b[0 * 4 + 3];
        var b10 = b[1 * 4 + 0];
        var b11 = b[1 * 4 + 1];
        var b12 = b[1 * 4 + 2];
        var b13 = b[1 * 4 + 3];
        var b20 = b[2 * 4 + 0];
        var b21 = b[2 * 4 + 1];
        var b22 = b[2 * 4 + 2];
        var b23 = b[2 * 4 + 3];
        var b30 = b[3 * 4 + 0];
        var b31 = b[3 * 4 + 1];
        var b32 = b[3 * 4 + 2];
        var b33 = b[3 * 4 + 3];
        return [
          b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
          b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
          b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
          b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
          b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
          b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
          b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
          b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
          b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
          b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
          b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
          b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
          b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
          b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
          b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
          b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
        ];
      },
    
      translation: function(tx, ty, tz) {
        return [
           1,  0,  0,  0,
           0,  1,  0,  0,
           0,  0,  1,  0,
           tx, ty, tz, 1,
        ];
      },
    
      xRotation: function(angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);
    
        return [
          1, 0, 0, 0,
          0, c, s, 0,
          0, -s, c, 0,
          0, 0, 0, 1,
        ];
      },
    
      yRotation: function(angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);
    
        return [
          c, 0, -s, 0,
          0, 1, 0, 0,
          s, 0, c, 0,
          0, 0, 0, 1,
        ];
      },
    
      zRotation: function(angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);
    
        return [
           c, s, 0, 0,
          -s, c, 0, 0,
           0, 0, 1, 0,
           0, 0, 0, 1,
        ];
      },
    
      scaling: function(sx, sy, sz) {
        return [
          sx, 0,  0,  0,
          0, sy,  0,  0,
          0,  0, sz,  0,
          0,  0,  0,  1,
        ];
      },
    
      translate: function(m, tx, ty, tz) {
        return m4.multiply(m, m4.translation(tx, ty, tz));
      },
    
      xRotate: function(m, angleInRadians) {
        return m4.multiply(m, m4.xRotation(angleInRadians));
      },
    
      yRotate: function(m, angleInRadians) {
        return m4.multiply(m, m4.yRotation(angleInRadians));
      },
    
      zRotate: function(m, angleInRadians) {
        return m4.multiply(m, m4.zRotation(angleInRadians));
      },
    
      scale: function(m, sx, sy, sz) {
        return m4.multiply(m, m4.scaling(sx, sy, sz));
      },
    
    };
    function initShaders(gl, vshader, fshader) {
        let program = createProgram(gl, vshader, fshader);
        if (!program) {
          return false;
        }
    
        gl.useProgram(program);
        gl.program = program;
    
        return true;
      }
    
    
      function createProgram(gl, vshader, fshader) {
        let vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
        let fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
        if (!vertexShader || !fragmentShader) {
          return null;
        }
    
        let program = gl.createProgram();
        if (!program) {
          return null;
        }
    
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
    
        let linked = gl.getProgramParameter(program, gl.LINK_STATUS);
        if (!linked) {
          let error = gl.getProgramInfoLog(program);
          gl.deleteProgram(program);
          gl.deleteShader(fragmentShader);
          gl.deleteShader(vertexShader);
          return null;
        }
        return program;
      }
    
      function loadShader(gl, type, source) {
        let shader = gl.createShader(type);
        if (shader == null) {
          return null;
        }
    
        gl.shaderSource(shader, source);
        gl.compileShader(shader);
    
        let compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
        if (!compiled) {
          let error = gl.getShaderInfoLog(shader);
          console.error('Failed to compile shader: ' + error);
          gl.deleteShader(shader);
          return null;
        }
    
        return shader;
      }
    
      let canvas = document.querySelector('#canvas');
      let gl = canvas.getContext('webgl');
      gl.clearColor(0.0, 0.0, 0.0, 0.0);
      gl.clear(gl.COLOR_BUFFER_BIT);
    
      let vSource = `attribute vec4 a_position;
            attribute vec4 a_color;
            varying vec4 v_color;
            uniform mat4 u_matrix;
    
            void main() {
             gl_Position = u_matrix * a_position;
             v_color = a_color;
            }`;
    
      let fSource = `precision mediump float;
    
        varying vec4 v_color;
    
        void main() {
            gl_FragColor = v_color;
        }`;
      if (!initShaders(gl, vSource, fSource)) {
        console.error('init error');
      }
    
      let pointBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, pointBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        -0.5, -0.5,
        0.5, -0.5,
        -0.5, 0.5,
        0.5, -0.5,
        -0.5, 0.5,
        0.5, 0.5,
      ]), gl.STATIC_DRAW);
      let a_position = gl.getAttribLocation(gl.program, 'a_position');
      gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(a_position);
    
      let colorBuffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0, 0, 1, 1,
        0, 0, 1, 1,
        1, 0, 0, 1,
        0, 0, 1, 1,
        1, 0, 0, 1,
        1, 0, 0, 1,
      ]), gl.STATIC_DRAW);
      let a_color = gl.getAttribLocation(gl.program, 'a_color');
      gl.vertexAttribPointer(a_color, 4, gl.FLOAT, false, 0, 0);
      gl.enableVertexAttribArray(a_color);
      let u_matrix = gl.getUniformLocation(gl.program, 'u_matrix');
      
      function makeZToWMatrix(fudgeFactor) {
        return [
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, fudgeFactor,
          0, 0, 0, 1,
        ];
      }
    
      // same matrix as css above
      gl.uniformMatrix4fv(u_matrix, false, new Float32Array(m4.multiply(makeZToWMatrix(-100/200), m4.yRotate([
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ], 80 / 180 * Math.PI))));
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    #canvas {
     position: absolute;
     left: -50px;
     top: -50px;
    }
    <canvas id="canvas" style="height: 200px;width: 200px"></canvas>
    <div style="position: absolute; left: 0; top: 0; opacity: 0.5; display:inline-block;background: blue;width:100px;height:100px;transform:perspective(200px) rotateY(80deg)"></div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多