本节将加载并显示一个3D模型,
从渲染方法上讲,和上一节的立方体没有本质区别,只是顶点数据不一样。
立方体是我们自己定义顶点数据,
而3D模型,是从资源文件读取顶点数据,
所以这一节的重点在于数据读取。
从指定url加载文件二进制数据
DataLoader.LoadAsync = function(url, callback, obj) {
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = 'arraybuffer';
req.onreadystatechange = function() {
if(req.readyState == 4) {
if(req.status == 200 || req.status == 0) {
callback(new DataView(req.response), obj);
} else {
alert('Could not load ' + url + ' (' + req.status + ')');
}
}
};
req.send(null);
};
加载mesh文件
Mesh.Load = function(url, onload) {
var m = new Mesh();
m.onload = onload;
DataLoader.LoadAsync(url, on_mesh_data_load, m);
return m;
};
读取mesh数据
function on_mesh_data_load(data, mesh) {
var offset = [0];
read_string(data, offset);
var mesh_count = DataLoader.ReadInt32(data, offset);
for(var i=0; i<mesh_count; i++) {
var name = read_string(data, offset);
var position = read_vector3(data, offset);
var rotation = read_quaternion(data, offset);
var scale = read_vector3(data, offset);
var vertex_count = DataLoader.ReadInt32(data, offset);
var vertices = new Float32Array(vertex_count * 3);
for(var j=0; j<vertex_count; j++) {
var v = read_vector3(data, offset);
vertices.set(v, j*3);
}
mesh.vertices = vertices;
var color_count = DataLoader.ReadInt32(data, offset);
var colors = new Float32Array(color_count * 4);
for(var j=0; j<color_count; j++) {
//read color
var v = read_quaternion(data, offset);
colors.set(v, j*4);
}
var uv_count = DataLoader.ReadInt32(data, offset);
var uvs = new Float32Array(uv_count * 2);
for(var j=0; j<uv_count; j++) {
var x = DataLoader.ReadFloat32(data, offset);
var y = 1 - DataLoader.ReadFloat32(data, offset);
uvs.set([x, y], j*2);
}
mesh.uv = uvs;
var uv2_count = DataLoader.ReadInt32(data, offset);
var uv2s = new Float32Array(uv2_count * 2);
for(var j=0; j<uv2_count; j++) {
var x = DataLoader.ReadFloat32(data, offset);
var y = 1 - DataLoader.ReadFloat32(data, offset);
uv2s.set([x, y], j*2);
}
var normal_count = DataLoader.ReadInt32(data, offset);
var normals = new Float32Array(normal_count * 3);
for(var j=0; j<normal_count; j++) {
var v = read_vector3(data, offset);
normals.set(v, j*3);
}
var tangent_count = DataLoader.ReadInt32(data, offset);
var tangents = new Float32Array(tangent_count * 4);
for(var j=0; j<tangent_count; j++) {
//read vector4
var v = read_quaternion(data, offset);
tangents.set(v, j*4);
}
var index_count = DataLoader.ReadInt32(data, offset);
var indices = new Uint16Array(index_count);
for(var j=0; j<index_count; j++) {
var v = DataLoader.ReadUint16(data, offset);
indices[j] = v;
}
mesh.indices = indices;
var submeshs = [];
var submesh_count = DataLoader.ReadInt32(data, offset);
for(var j=0; j<submesh_count; j++) {
var sub_count = DataLoader.ReadInt32(data, offset);
var sub_indices = new Uint16Array(index_count);
for(var k=0; k<sub_count; k++) {
var v = DataLoader.ReadUint16(data, offset);
sub_indices[k] = v;
}
submeshs[j] = sub_indices;
}
break;
}
mesh.loaded = true;
mesh.onload(mesh);
};
创建顶点buffer
function create_buffer() {
mesh = Mesh.Load("ranger.mesh", function(m) {
vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(m.vertices), gl.STATIC_DRAW);
uv_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uv_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(m.uv), gl.STATIC_DRAW);
index_buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(m.indices), gl.STATIC_DRAW);
});
}
初始化:
function init_gl() {
gl.clearColor(0.0, 0.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.enable(gl.CULL_FACE);
gl.frontFace(gl.CW);
create_shader();
create_buffer();
create_texture();
}
这里新加了gl.frontFace(gl.CW)
修改三角形正面的顶点顺序为CW顺时针。
因为mesh数据是我从Unity3D导出的,Unity3D引擎使用的顶点顺序是CW,
而gl默认是使用CCW逆时针。
渲染的时候,调整一下模型的位置和角度,其它的和上一节立方体渲染完全一样:
var mat_m = Matrix4x4.TRS([0, -1.1, 0], [0, rot, 0], [1, 1, 1]);
运行结果: