That's a pretty basic WebGL question. Some more tutorials on webgl might be helpful.
WebGL only draws chunks of data. It doesn't really have a lineTo
or a moveTo
. Instead you give it buffers of data, tell it how to pull data out of those buffers, then you write a function (a vertex shader) to use that data to tell WebGL how convert it to clip space coordinates and whether to draw points, lines, or triangles with the result. You also supply a function (a fragment shader) to tell it what colors to use for the points, lines or triangles.
Basically to draw the thing you want to draw you need to generate 2 triangles for every rectangle on that sphere. In other words you need to generate 6 vertices for every rectangle. The reason is in order to draw each triangle in a different color you can't share any vertices since the colors are associated with the vertices.
So for one rectangle you need to generate these points
0--1 4
| / /|
|/ / |
2 3--5
Where 0, 1, and 2 are pink points and 3, 4, 5 are green points. 1 and 4 have the same position but because their colors are different they have to be different points. The same with points 2 and 3.
var pink = [1, 0.5, 0.5, 1];
var green = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var across = 20;
var down = 10;
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
colors.push(color[0], color[1], color[2], color[3]);
}
for (var y = 0; y < down; ++y) {
for (var x = 0; x < across; ++x) {
// for each rect we need 6 points
addPoint(x , y , pink);
addPoint(x + 1, y , pink);
addPoint(x , y + 1, pink);
addPoint(x , y + 1, green);
addPoint(x + 1, y , green);
addPoint(x + 1, y + 1, green);
}
}
Here's the sphere above rendered but without any lighting, perspective or anything.
var pink = [1, 0.5, 0.5, 1];
var green = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var across = 20;
var down = 10;
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
colors.push(color[0], color[1], color[2], color[3]);
}
for (var y = 0; y < down; ++y) {
for (var x = 0; x < across; ++x) {
// for each rect we need 6 points
addPoint(x , y , pink);
addPoint(x + 1, y , pink);
addPoint(x , y + 1, pink);
addPoint(x , y + 1, green);
addPoint(x + 1, y , green);
addPoint(x + 1, y + 1, green);
}
}
var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
var arrays = {
position: positions,
color: colors,
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
var uniforms = {
resolution: [gl.canvas.width, gl.canvas.height],
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
canvas { border: 1px solid black; }
<canvas id="c"></canvas>
<script id="vs" type="not-js">
attribute vec4 position;
attribute vec4 color;
uniform vec2 resolution;
varying vec4 v_color;
void main() {
gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1);
v_color = color;
}
</script>
<script id="fs" type="not-js">
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
If you later want to light it you'll also need normals (values that you can use to tell which direction something is facing). We can add those in by adding
var normals = [];
and inside addPoint
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
colors.push(color[0], color[1], color[2], color[3]);
normals.push(nx, ny, nz);
}
Here's a sample with hacked lighting
var pink = [1, 0.5, 0.5, 1];
var green = [0.5, 1, 0.5, 1];
var positions = [];
var colors = [];
var normals = [];
var across = 20;
var down = 10;
function addPoint(x, y, color) {
var u = x / across;
var v = y / down;
var radius = Math.sin(v * Math.PI);
var angle = u * Math.PI * 2;
var nx = Math.cos(angle);
var ny = Math.cos(v * Math.PI);
var nz = Math.sin(angle);
positions.push(
nx * radius, // x
ny, // y
nz * radius); // z
normals.push(nx, ny, nz);
colors.push(color[0], color[1], color[2], color[3]);
}
for (var y = 0; y < down; ++y) {
for (var x = 0; x < across; ++x) {
// for each rect we need 6 points
addPoint(x , y , pink);
addPoint(x + 1, y , pink);
addPoint(x , y + 1, pink);
addPoint(x , y + 1, green);
addPoint(x + 1, y , green);
addPoint(x + 1, y + 1, green);
}
}
var gl = document.getElementById("c").getContext("webgl");
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
var arrays = {
position: positions,
normal: normals,
color: colors,
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
var uniforms = {
resolution: [gl.canvas.width, gl.canvas.height],
lightDirection: [0.5, 0.5, -1],
};
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, bufferInfo);
canvas { border: 1px solid black; }
<canvas id="c"></canvas>
<script id="vs" type="not-js">
attribute vec4 position;
attribute vec4 color;
attribute vec3 normal;
uniform vec2 resolution;
varying vec4 v_color;
varying vec3 v_normal;
void main() {
gl_Position = position * vec4(resolution.y / resolution.x, 1, 1, 1);
v_color = color;
v_normal = normal;
}
</script>
<script id="fs" type="not-js">
precision mediump float;
varying vec4 v_color;
varying vec3 v_normal;
uniform vec3 lightDirection;
void main() {
float light = pow(abs(dot(v_normal, normalize(lightDirection))), 2.0);
gl_FragColor = vec4(v_color.xyz * light, v_color.a);
}
</script>
<script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
PS: The picture you posted is actually drawing more triangles per rectangle. The division between green and pink is not straight.