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轴由屏幕向里。
矩阵使用列主序,是常用的行主序的转置。
位移:Translation
旋转:Rotation
缩放:Scaling
所以又称之为TRS矩阵。
矩阵接口:
使用常用的透视投影:
有了变换矩阵,可以开始渲染了。
1.扩展顶点buffer来创建立方体,并建立顶点索引buffer(前几节没有使用顶点索引)。
一个立方体需要12个顶点和36个顶点索引。
2.修改vertex shader,使用mvp矩阵变换顶点:
3.开启深度测试、背面裁剪:
4.渲染:
矩阵使用解析:
模型矩阵
立方体位于(0, 0, 0),
绕x轴旋转45度,绕y轴旋转rot度,
缩放系数为1。
视图矩阵
摄像机位于(0, 0, -3),
前方向向量(0, 0, 1),即朝向z轴正方向,
上方向向量(0, 1, 0),朝向y轴正方向。
投影矩阵
摄像机视口张角45度,
宽高比等于屏幕宽高比,
近裁剪面z=0.3,
远裁剪面z=100。
MVP
连乘得到mvp,之后传给vertex shader。
运行结果(由于上传图片大小限制,动画帧率较低):
代码下载
本节中,我们将开始进入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。
运行结果(由于上传图片大小限制,动画帧率较低):
代码下载
相关文章推荐
- ios动画学习(二)
- JavaScript RegExp 对象
- 第二章节 活动
- SpringMVC+MyBatis 导入数据到数据库
- PHP超全局变量-$_POST
- For life,For what。
- int 类的计算,不好犯这种低级错误
- [009] 百度地图API之MyLocationOverlay的使用(Android)
- mysql 小知识
- C++ 不用 < > 与 : ?运算符判断 a,b大小
- Contains Duplicate III - LeetCode 220
- [008] 百度地图API之ItemizedOverlay的使用(Android)
- s3c2440启动过程详解 (主要讲启动时nand nor的地址映射)
- UCTF WriteUp
- java web 服务器随笔(reponse)
- 开放的智力10:常识积累
- c#抽象类和接口的简单举例
- objective-C 的内存管理之-自动释放池(autorelease pool)
- Java中的垃圾回收
- c++学习的50条忠告