const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 400;
const gl = canvas.getContext("webgl");
const latB = 16, lonB = 16, r = 1.0;
const pos = [], idx = [];
for (let lat = 0; lat <= latB; lat++) {
const theta = (lat * Math.PI) / latB;
const sT = Math.sin(theta), cT = Math.cos(theta);
for (let lon = 0; lon <= lonB; lon++) {
const phi = (lon * 2 * Math.PI) / lonB;
pos.push(r*Math.cos(phi)*sT, r*cT, r*Math.sin(phi)*sT);
}
}
const vpr = lonB + 1;
for (let lat = 0; lat <= latB; lat++) {
const rs = lat * vpr;
for (let lon = 0; lon < lonB; lon++)
idx.push(rs+lon, rs+lon+1);
}
for (let lon = 0; lon <= lonB; lon++)
for (let lat = 0; lat < latB; lat++) {
const f = lat*vpr+lon;
idx.push(f, f+vpr);
}
const vsrc = `
attribute vec3 aPos;
uniform mat4 uProj, uMV;
void main() { gl_Position = uProj * uMV * vec4(aPos, 1); }`;
const fsrc = `
precision mediump float;
uniform vec4 uColor;
void main() { gl_FragColor = uColor; }`;
function mkShader(src, type) {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
return s;
}
const prog = gl.createProgram();
gl.attachShader(prog, mkShader(vsrc, gl.VERTEX_SHADER));
gl.attachShader(prog, mkShader(fsrc, gl.FRAGMENT_SHADER));
gl.linkProgram(prog);
gl.useProgram(prog);
const posBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(pos), gl.STATIC_DRAW);
const idxBuf = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, idxBuf);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(idx), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(prog, "aPos");
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 3, gl.FLOAT, false, 0, 0);
const uProj = gl.getUniformLocation(prog, "uProj");
const uMV = gl.getUniformLocation(prog, "uMV");
const uColor = gl.getUniformLocation(prog, "uColor");
const f = 1/Math.tan(45*Math.PI/360);
const proj = new Float32Array(16);
proj[0]=f; proj[5]=f;
proj[10]=(100.1)/(0.1-100); proj[11]=-1;
proj[14]=(2*100*0.1)/(0.1-100);
gl.clearColor(0.02, 0.02, 0.02, 1);
gl.enable(gl.DEPTH_TEST);
let angle = 0;
const total = idx.length;
let drawn = 0;
function frame() {
angle += 0.01;
const c = Math.cos(angle), s = Math.sin(angle);
const mv = new Float32Array([
c,0,s,0, 0,1,0,0, -s,0,c,0, 0,0,-3.5,1
]);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.uniformMatrix4fv(uProj, false, proj);
gl.uniformMatrix4fv(uMV, false, mv);
gl.uniform4f(uColor, 0, 1, 0.25, 1);
if (drawn < total) drawn += 4;
const count = Math.min(drawn, total);
gl.drawElements(gl.LINES, count, gl.UNSIGNED_SHORT, 0);
canvas._raf = requestAnimationFrame(frame);
}
frame();
return canvas;