如何将css转换3d同步到webgl



在css中,我可以使用透视+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);
<canvas id="canvas" style="height: 200px;width: 200px"></canvas>

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

最后,我应该注意的是,我已经读过WebGL 3D透视,但毕竟,我仍然对这个问题感到困惑。

我尝试将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>

最新更新