Cześć,
Chce wygenerować kulę ziemską z trójkątów. Napisałem skrypt, który oblicza współrzędne wierzchołków dla trójkątów, dodaje indeksy dla wierzchołków, a także liczy normalne dla każdego trójkąta. Problem jest taki, że skrypt działa w kilku procentach, bo rysuje się tylko kawałek tej kuli.
Czy jest jakieś ograniczenczenie w ilości rysowanych elementów?
Skrypt:
window.onload = function() {
var gl = document.getElementById("webgl_canvas").getContext("experimental-webgl");
// Create vertex shader
var vertexShaderCode = document.querySelector("#vs").textContent;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderCode);
gl.compileShader(vertexShader);
// Create fragment shader
var fragmentShaderCode = document.querySelector("#fs").textContent;
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderCode);
gl.compileShader(fragmentShader);
// Create program
var program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
// Create buffer for positions of vertices
var posLoc = gl.getAttribLocation(program, "pos");
gl.enableVertexAttribArray(posLoc);
// Create buffer for position of vertices
var posBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
// ########################
var phi = 0; // angle of rotation begin since 0, than phi+=10
var theta = -90; // range betwen 90 and -90
var radius = 1;
var vertices = [];
var indices = [];
var normals = [];
function sphere2cartesian(phi, theta, radius){
var x = radius * Math.cos(theta*(Math.PI/180)) * Math.cos(phi*(Math.PI/180));
var y = radius * Math.cos(theta*(Math.PI/180)) * Math.sin((phi*Math.PI/180));
var z = radius * Math.sin(theta*(Math.PI/180));
// console.log(phi, theta, radius);
// console.log(x, ' \t', y, '\t', z);
// var x = radius * Math.sin(theta*Math.PI/180) * Math.cos(phi*Math.PI/180);
// var y = radius * Math.sin(theta*Math.PI/180) * Math.sin(phi*Math.PI/180);
// var z = radius * Math.cos(theta*Math.PI/180);
return [x, y, z];
}
function find_normal(x1, x3, x4, y1, y3, y4, z1, z3, z4){
var r1x = x1-x4;
var r2x = x3-x4;
var r1y = y1-y4;
var r2y = y3-y4;
var r1z = z1-z4;
var r2z = z3-z4;
//cross product
var nx = (r1y*r2z)-(r1z*r2y);
var ny = -((r1x*r2z)-(r1z*r2x));
var nz = (r1x*r2y)-(r1y*r2x);
// change wector length to 1 unit
var n_len = Math.sqrt(Math.pow(nx, 2)+Math.pow(ny, 2)+Math.pow(nz, 2));
return [nx/n_len, ny/n_len, nz/n_len];
}
var index = 0;
for (var theta=-90; theta<90; theta+=10){
for (var phi=0; phi<360; phi+=10){
// generate vertices
// We need many vertices, because each vertex need
// own value of normal and UV
var xyz = sphere2cartesian(phi, theta, radius);
var x1 = xyz[0];
var y1 = xyz[1];
var z1 = xyz[2];
phi+=10;
var xyz = sphere2cartesian(phi, theta, radius);
var x2 = xyz[0];
var y2 = xyz[1];
var z2 = xyz[2];
theta+=10;
var xyz = sphere2cartesian(phi, theta, radius);
var x3 = xyz[0];
var y3 = xyz[1];
var z3 = xyz[2];
phi-=10;
var xyz = sphere2cartesian(phi, theta, radius);
var x4 = xyz[0];
var y4 = xyz[1];
var z4 = xyz[2];
theta-=10;
vertices.push(x1, y1, z1);
vertices.push(x2, y2, z2);
vertices.push(x3, y3, z3);
vertices.push(x4, y4, z4);
// adding indices
indices.push(index, index+1, index+2);
indices.push(index, index+2, index+3);
index += 4;
// adding normals
var normal = find_normal(x1, x3, x4, y1, y3, y4, z1, z3, z4);
normals.push(normal[0], normal[1], normal[2]);
normals.push(normal[0], normal[1], normal[2]);
normals.push(normal[0], normal[1], normal[2]);
normals.push(normal[0], normal[1], normal[2]);
}
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.vertexAttribPointer(posLoc, 3, gl.FLOAT, false, 0, 0);
// Create buffer for UV coordinates
var uvLoc = gl.getAttribLocation(program, "uv");
gl.enableVertexAttribArray(uvLoc);
var uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
// ###################
// generate UVS
var texture_size_row = 1/36;
var texture_size_col = 1/18;
var uvs = [];
for (var texture_y=1; texture_y>=0; texture_y-=texture_size_col){
for (var texture_x=0; texture_x<1; texture_x+=texture_size_row){
uvs.push(texture_x, texture_y);
uvs.push((texture_x+texture_size_row), texture_y);
uvs.push((texture_x+texture_size_row), (texture_y-texture_size_col));
uvs.push(texture_x, (texture_y-texture_size_col));
}
}
// ###################
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(uvs), gl.STATIC_DRAW);
gl.vertexAttribPointer(uvLoc, 2, gl.FLOAT, false, 0, 0);
// Create buffer for vertex normals
var normalLoc = gl.getAttribLocation(program, "normal");
gl.enableVertexAttribArray(normalLoc);
var normalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, true, 0, 0);
// Create index buffer
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices), gl.STATIC_DRAW);
// gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.STATIC_DRAW);
// Create and load image used as texture
var image = new Image();
image.src = "./globe_texture.jpg";
image.onload = function() {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.generateMipmap(gl.TEXTURE_2D);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
var samplerLoc = gl.getUniformLocation(program, "sampler");
gl.uniform1i(samplerLoc, 0); // nula odpovídá gl.TEXTURE0
};
// Create matrix for model
var modelMatrix = mat4.create();
mat4.scale(modelMatrix, modelMatrix, vec3.fromValues(0.8, 0.8, 0.8));
var modelLocation = gl.getUniformLocation(program, "modelMatrix");
gl.uniformMatrix4fv(modelLocation, false, modelMatrix);
// Create matrix for view
var viewMatrix = mat4.create();
mat4.translate(viewMatrix, viewMatrix, vec3.fromValues(0, 0, -5));
var viewLocation = gl.getUniformLocation(program, "viewMatrix");
gl.uniformMatrix4fv(viewLocation, false, viewMatrix);
// Create matrix for projection
var projMatrix = mat4.create();
mat4.perspective(projMatrix, Math.PI/3, 1, 0.1, 100);
var projLocation = gl.getUniformLocation(program, "projMatrix");
gl.uniformMatrix4fv(projLocation, false, projMatrix);
// Create matrix for transformation of normal vectors
var normalMatrix = mat3.create();
var normalLocation = gl.getUniformLocation(program, "normalMatrix");
mat3.normalFromMat4(normalMatrix, modelMatrix);
gl.uniformMatrix3fv(normalLocation, false, normalMatrix);
// Enable depth test
gl.enable(gl.DEPTH_TEST);
// Create polyfill to make it working in the most modern browsers
window.requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| function(cb) { setTimeout(cb, 1000/60); };
var render = function() {
mat4.rotateX(modelMatrix, modelMatrix, 0.005);
mat4.rotateY(modelMatrix, modelMatrix, 0.01);
gl.uniformMatrix4fv(modelLocation, false, modelMatrix);
mat3.normalFromMat4(normalMatrix, modelMatrix);
gl.uniformMatrix3fv(normalLocation, false, normalMatrix);
gl.clear(gl.COLOR_BUFFER_BIT, gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0);
requestAnimationFrame(render);
}
render();
}