您的位置:首页 > 其它

DJ's WebGL Tutorial 005--3D渲染与所需矩阵变换

2015-06-01 22:46 579 查看
在前面几节,我们省略了顶点的矩阵变换,直接使用的屏幕空间坐标进行渲染。

本节中,我们将开始进入3D世界,渲染一个旋转的3D立方体。

一个3D模型要显示到屏幕上,顶点要经过以下坐标变换:

本地坐标->世界坐标

世界坐标->视图坐标

视图坐标->屏幕坐标

这就涉及到3个变换矩阵:Model,View,Projection。

由于WebGL不提供矩阵相关API,所以,我写了一个Matrix4x4类,同时还提供了所需的Vector3、Quaternion类。

在讲矩阵之前,我们先约定:

使用左手坐标系,x轴向右,y轴向上,z轴由屏幕向里。

矩阵使用列主序,是常用的行主序的转置。

Matrix Model

模型矩阵可以分解为三个矩阵:

位移:Translation

旋转:Rotation

缩放:Scaling

所以又称之为TRS矩阵。

矩阵接口:

Matrix4x4.TRS = function(vect, vecr, vecs) {
var t = Matrix4x4.Translation(vect);
var r = Matrix4x4.Rotation(vecr);
var s = Matrix4x4.Scaling(vecs);

return t.multiply(r).multiply(s);
};


Matrix View

视图矩阵描述了摄像机的位置和朝向:

Matrix4x4.LookTo = function(eye_position, to_direction, up_direction) {
var m = Matrix4x4.Identity();
var array = m.array;

var zaxis = new Vector3([-to_direction[0], -to_direction[1], -to_direction[2]]);
zaxis.normalize();

var xaxis = zaxis.multiply(new Vector3(up_direction));
xaxis.normalize();

var yaxis = xaxis.multiply(zaxis);

var xa = xaxis.array;
var ya = yaxis.array;
var za = zaxis.array;
var eye = new Vector3(eye_position);

array[0] = xa[0]; array[1] = xa[1]; array[2] = xa[2]; array[3] = -xaxis.dot(eye);
array[4] = ya[0]; array[5] = ya[1]; array[6] = ya[2]; array[7] = -yaxis.dot(eye);
array[8] = za[0]; array[9] = za[1]; array[10] = za[2]; array[11] = -zaxis.dot(eye);
array[12] = 0; array[13] = 0; array[14] = 0; array[15] = 1.0;

return m;
};


Matrix Projection

投影矩阵描述了摄像机的投影参数,

使用常用的透视投影:

Matrix4x4.Perspective = function(fov, aspect, z_near, z_far) {
var m = Matrix4x4.Identity();
var array = m.array;

var y_scale = 1 / Math.tan(Math.PI / 180 * fov / 2);
var x_scale = y_scale / aspect;

array[0] = x_scale;
array[5] = y_scale;
array[10] = (z_near + z_far) / (z_near - z_far);
array[11] = 2 * z_near * z_far / (z_near - z_far);
array[14] = -1.0;
array[15] = 0;

return m;
};


有了变换矩阵,可以开始渲染了。

1.扩展顶点buffer来创建立方体,并建立顶点索引buffer(前几节没有使用顶点索引)。

一个立方体需要12个顶点和36个顶点索引。

function create_buffer() {
var vertices = [
-0.5, 0.5, -0.5,
-0.5, -0.5, -0.5,
0.5, -0.5, -0.5,
0.5, 0.5, -0.5,

-0.5, 0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5,
0.5, 0.5, 0.5,

-0.5, 0.5, 0.5,
0.5, 0.5, 0.5,
-0.5, -0.5, 0.5,
0.5, -0.5, 0.5
];

var uvs = [
0.0, 0.0,
0.0, 1.0,
1.0, 1.0,
1.0, 0.0,

1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,

0.0, 1.0,
1.0, 1.0,
0.0, 0.0,
1.0, 0.0
];

var indices = [
0, 1, 2,
0, 2, 3,

4, 6, 5,
4, 7, 6,

3, 2, 6,
3, 6, 7,

4, 5, 1,
4, 1, 0,

8, 0, 3,
8, 3, 9,

10, 2, 1,
10, 11, 2
];

vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

uv_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uv_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvs), gl.STATIC_DRAW);

index_buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
}


2.修改vertex shader,使用mvp矩阵变换顶点:

var shader_src_vs = "\
uniform mat4 u_mat_mvp;\
attribute vec4 a_position;\
attribute vec2 a_uv;\
varying vec2 v_uv;\
void main()\
{\
vec4 pos = a_position * u_mat_mvp;\
v_uv = a_uv;\
gl_Position = pos;\
}\
";


3.开启深度测试、背面裁剪:

function init_gl() {
gl.clearColor(0.0, 0.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);

create_shader();
create_buffer();
create_texture();
}


4.渲染:

function render() {
update_fps();

gl.clear(gl.COLOR_BUFFER_BIT);

if (texture != null) {
gl.useProgram(program);

var index_mvp = gl.getUniformLocation(program, "u_mat_mvp");
var index_pos = gl.getAttribLocation(program, "a_position");
var index_uv = gl.getAttribLocation(program, "a_uv");
var index_tex = gl.getUniformLocation(program, "u_tex");

rot += 1;
var mat_m = Matrix4x4.TRS([0, 0, 0], [45, rot, 0], [1, 1, 1]);
var mat_v = Matrix4x4.LookTo([0, 0, -3], [0, 0, 1], [0, 1, 0]);
var ratio = gl.drawingBufferWidth / gl.drawingBufferHeight;
var mat_p = Matrix4x4.Perspective(45, ratio, 0.3, 100);

var mat_mvp = mat_p.multiply(mat_v).multiply(mat_m);

gl.uniformMatrix4fv(index_mvp, false, mat_mvp.array);

gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.enableVertexAttribArray(index_pos);
gl.vertexAttribPointer(index_pos, 3, gl.FLOAT, false, 0, 0);

gl.bindBuffer(gl.ARRAY_BUFFER, uv_buffer);
gl.enableVertexAttribArray(index_uv);
gl.vertexAttribPointer(index_uv, 2, gl.FLOAT, false, 0, 0);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(index_tex, 0);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 0);
}

window.requestAnimationFrame(render);
}


矩阵使用解析:

模型矩阵

var mat_m = Matrix4x4.TRS([0, 0, 0], [45, rot, 0], [1, 1, 1]);


立方体位于(0, 0, 0),

绕x轴旋转45度,绕y轴旋转rot度,

缩放系数为1。

视图矩阵

var mat_v = Matrix4x4.LookTo([0, 0, -3], [0, 0, 1], [0, 1, 0]);


摄像机位于(0, 0, -3),

前方向向量(0, 0, 1),即朝向z轴正方向,

上方向向量(0, 1, 0),朝向y轴正方向。

投影矩阵

var ratio = gl.drawingBufferWidth / gl.drawingBufferHeight;


var mat_p = Matrix4x4.Perspective(45, ratio, 0.3, 100);


摄像机视口张角45度,

宽高比等于屏幕宽高比,

近裁剪面z=0.3,

远裁剪面z=100。

MVP

var mat_mvp = mat_p.multiply(mat_v).multiply(mat_m);


连乘得到mvp,之后传给vertex shader。

运行结果(由于上传图片大小限制,动画帧率较低):



代码下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: