DJ's WebGL Tutorial 005--3D渲染与所需矩阵变换
来源:互联网 发布:redis 数据库设计实例 编辑:程序博客网 时间:2024/05/17 07:46
在前面几节,我们省略了顶点的矩阵变换,直接使用的屏幕空间坐标进行渲染。
本节中,我们将开始进入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。
运行结果(由于上传图片大小限制,动画帧率较低):
代码下载
- DJ's WebGL Tutorial 005--3D渲染与所需矩阵变换
- DJ's WebGL Tutorial 001--渲染准备
- DJ's WebGL Tutorial 002--渲染循环、显示FPS
- DJ's WebGL Tutorial 003--渲染一个三角形
- DJ's WebGL Tutorial 007--骨骼动画
- DJ's WebGL Tutorial 004--带贴图的矩形
- DJ's WebGL Tutorial 006--模型加载、显示
- 3D渲染管线中的变换矩阵及推导过程
- 3D变换矩阵
- 3D坐标系、矩阵变换、视景体与裁剪
- 3D坐标系、矩阵变换、视景体与裁剪 (转载)
- 3D坐标系、矩阵变换、视景体与裁剪
- 3D坐标系、矩阵变换、视景体与裁剪
- 3D坐标系、矩阵变换、视景体与裁剪
- 使用WebGL 2.0更快地渲染 3D
- HTML5 WEBGL学习2 实时3D渲染
- WebGL 入门-WebGL简介与3D图形学
- WebGL学习系列-基础矩阵变换
- int 类的计算,不好犯这种低级错误
- [008] 百度地图API之ItemizedOverlay的使用(Android)
- poj 1703 Find them, Catch them 【带权并查集】
- UCTF WriteUp
- String对象不可改变的特性
- DJ's WebGL Tutorial 005--3D渲染与所需矩阵变换
- [009] 百度地图API之MyLocationOverlay的使用(Android)
- PHP超全局变量-$_POST
- 计算机网络学习(4)
- python中函数返回值为func 和func() 的区别
- twemproxy
- Twitter“鲸鱼”故障技术剖析
- [010] 百度地图API之根据经纬度查询地址信息(Android)
- jdbc AbstractDao的一些代码片段