Reputation: 93
As shown below, all I can think of is to describe all the points on a circle and then use triangulation to draw a ring with width
I can also think of a way to use overlay. First draw a circle and then draw a circle with a smaller radius
const TAU_SEGMENTS = 360;
const TAU = Math.PI * 2;
export function arc(x0: number, y0: number, radius: number, startAng = 0, endAng = Math.PI * 2) {
const ang = Math.min(TAU, endAng - startAng);
const ret = ang === TAU ? [] : [x0, y0];
const segments = Math.round(TAU_SEGMENTS * ang / TAU);
for(let i = 0; i <= segments; i++) {
const x = x0 + radius * Math.cos(startAng + ang * i / segments);
const y = y0 + radius * Math.sin(startAng + ang * i / segments);
ret.push(x, y);
}
return ret;
}
const gl = container.current.getContext("webgl2");
const program = initProgram(gl);
const position = new Float32Array(arc(0, 0, 1).concat(...arc(0, 0, 0.9)));
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, position, gl.STATIC_DRAW);
let gl_position = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(gl_position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl_position);
gl.drawArrays(gl.LINE_STRIP, 0, position.length / 2);
The final effect of the code I wrote is as follows. May I ask how I should modify it to become the same as the above picture
Upvotes: 1
Views: 605
Reputation: 211166
You have to add a color attributes for the vertices and you have to draw a gl.TRIANGLE_STRIP
primitive instead of a gl.LINE_STRIP
primitive.
The color can be calculated from the angle. Map the angle from the range [0, PI] to the range [0, 1] and use the formula for the hue value from the HSL and HSV color space:
function HUEtoRGB(hue) {
return [
Math.min(1, Math.max(0, Math.abs(hue * 6.0 - 3.0) - 1.0)),
Math.min(1, Math.max(0, 2.0 - Math.abs(hue * 6.0 - 2.0))),
Math.min(1, Math.max(0, 2.0 - Math.abs(hue * 6.0 - 4.0)))
];
}
Create vertices in pairs for the inner and outer arcs with the corresponding color attribute:
const TAU_SEGMENTS = 360;
const TAU = Math.PI * 2;
function arc(x0, y0, innerRadius, outerRadius, startAng = 0, endAng = Math.PI * 2) {
const ang = Math.min(TAU, endAng - startAng);
const position = ang === TAU ? [] : [x0, y0];
const color = []
const segments = Math.round(TAU_SEGMENTS * ang / TAU);
for(let i = 0; i <= segments; i++) {
const angle = startAng + ang * i / segments;
const x1 = x0 + innerRadius * Math.cos(angle);
const y1 = y0 + innerRadius * Math.sin(angle);
const x2 = x0 + outerRadius * Math.cos(angle);
const y2 = y0 + outerRadius * Math.sin(angle);
position.push(x1, y1, x2, y2);
let hue = (Math.PI/2 - angle) / (2 * Math.PI);
if (hue < 0) hue += 1;
const rgb = HUEtoRGB(hue);
color.push(...rgb);
color.push(...rgb);
}
return { 'position': position, 'color': color };
}
Create a shader with a color attribute and pass the color attribute from the vertex to the fragment shader:
#version 300 es
precision highp float;
in vec2 position;
in vec4 color;
out vec4 vColor;
void main()
{
vColor = color;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
#version 300 es
precision highp float;
in vec4 vColor;
out vec4 fragColor;
void main()
{
fragColor = vColor;
}
Vertex specification:
const attributes = arc(0, 0, 0.6, 0.9);
position = new Float32Array(attributes.position);
color = new Float32Array(attributes.color);
let position_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer);
gl.bufferData(gl.ARRAY_BUFFER, position, gl.STATIC_DRAW);
let gl_position = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(gl_position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl_position);
let color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, color, gl.STATIC_DRAW);
let gl_color = gl.getAttribLocation(program, "color");
gl.vertexAttribPointer(gl_color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl_color);
Complet and runnable example:
const canvas = document.getElementById( "ogl-canvas");
const gl = canvas.getContext("webgl2");
const program = gl.createProgram();
for (let i = 0; i < 2; ++i) {
let source = document.getElementById(i==0 ? "draw-shader-vs" : "draw-shader-fs").text;
let shaderObj = gl.createShader(i==0 ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER);
gl.shaderSource(shaderObj, source);
gl.compileShader(shaderObj);
let status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
if (!status) alert(gl.getShaderInfoLog(shaderObj));
gl.attachShader(program, shaderObj);
gl.linkProgram(program);
}
status = gl.getProgramParameter(program, gl.LINK_STATUS);
if ( !status ) alert(gl.getProgramInfoLog(program));
gl.useProgram(program);
function HUEtoRGB(hue) {
return [
Math.min(1, Math.max(0, Math.abs(hue * 6.0 - 3.0) - 1.0)),
Math.min(1, Math.max(0, 2.0 - Math.abs(hue * 6.0 - 2.0))),
Math.min(1, Math.max(0, 2.0 - Math.abs(hue * 6.0 - 4.0)))
];
}
const TAU_SEGMENTS = 360;
const TAU = Math.PI * 2;
function arc(x0, y0, innerRadius, outerRadius, startAng = 0, endAng = Math.PI * 2) {
const ang = Math.min(TAU, endAng - startAng);
const position = ang === TAU ? [] : [x0, y0];
const color = []
const segments = Math.round(TAU_SEGMENTS * ang / TAU);
for(let i = 0; i <= segments; i++) {
const angle = startAng + ang * i / segments;
const x1 = x0 + innerRadius * Math.cos(angle);
const y1 = y0 + innerRadius * Math.sin(angle);
const x2 = x0 + outerRadius * Math.cos(angle);
const y2 = y0 + outerRadius * Math.sin(angle);
position.push(x1, y1, x2, y2);
let hue = (Math.PI/2 - angle) / (2 * Math.PI);
if (hue < 0) hue += 1;
const rgb = HUEtoRGB(hue);
color.push(...rgb);
color.push(...rgb);
}
return { 'position': position, 'color': color };
}
const attributes = arc(0, 0, 0.6, 0.9);
position = new Float32Array(attributes.position);
color = new Float32Array(attributes.color);
let position_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, position_buffer);
gl.bufferData(gl.ARRAY_BUFFER, position, gl.STATIC_DRAW);
let gl_position = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(gl_position, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl_position);
let color_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer);
gl.bufferData(gl.ARRAY_BUFFER, color, gl.STATIC_DRAW);
let gl_color = gl.getAttribLocation(program, "color");
gl.vertexAttribPointer(gl_color, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(gl_color);
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
//vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
vp_size = [window.innerWidth, window.innerHeight];
vp_size = [256, 256]
canvas.width = vp_size[0];
canvas.height = vp_size[1];
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, attributes.position.length / 2);
<script id="draw-shader-vs" type="x-shader/x-vertex">#version 300 es
precision highp float;
in vec2 position;
in vec4 color;
out vec4 vColor;
void main()
{
vColor = color;
gl_Position = vec4(position.xy, 0.0, 1.0);
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">#version 300 es
precision highp float;
in vec4 vColor;
out vec4 fragColor;
void main()
{
fragColor = vColor;
}
</script>
<canvas id="ogl-canvas" style="border: none"></canvas>
Upvotes: 3