initiate a number of vertices/triangles for vertex shader to use - javascript

I've been playing around with vertexshaderart.com and I'd like to use what I learned on a separate website. While I have used shaders before, some effects achieved on the site depend on having access to vertices/lines/triangles. While passing vertices is easy enough (at least it was with THREE.js, though it is kind of an overkill for simple shaders, but in some cases in need shader materials too), creating triangles seems a bit more complex.
I can't figure it out from the source, how exactly are triangles created there, when you switch the mode here?
I'd like to replicate that behavior but I honestly have no idea how to approach it. I could just create a number of triangles through THREE but with so many individual objects performance takes a hit rapidly. Are the triangles created here separate entities or are they a part of one geometry?

vertexshaderart.com is more of a puzzle, toy, art box, creative coding experiment than an example of the good WebGL. The same is true of shadertoy.com. An example like this is beautiful but it runs at 20fps in it's tiny window and about 1fps fullscreen on my 2014 Macbook Pro and yet my MBP can play beautiful games with huge worlds rendered fullscreen at 60fps. In other words, the techniques are more for art/fun/play/mental exercise and for the fun of trying to make things happen with extreme limits than to actually be good techniques.
The point I'm trying to make is both vertexshaderart and shadertoy are fun but impractical.
The way vertexshaderart works is it provides a count vertexId that counts vertices. 0 to N where N is the count setting the top of the UI. For each count you output gl_Position and a v_color (color).
So, if you want to draw something you need to provide the math to generate vertex positions based on the count. For example let's do it using Canvas 2D first
Here's a fake JavaScript vertex shader written in JavaScript that given nothing but vertexId will draw a grid 1 unit high and N units long where N = the number of vertices (vertexCount) / 6.
function ourPseudoVertexShader(vertexId, time) {
// let's compute an infinite grid of points based off vertexId
var x = Math.floor(vertexId / 6) + (vertexId % 2);
var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;
// color every other triangle red or green
var triangleId = Math.floor(vertexId / 3);
var color = triangleId % 2 ? "#F00" : "#0F0";
return {
x: x * 0.2,
y: y * 0.2,
color: color,
};
}
We call it from a loop supplying vertexId
for (var count = 0; count < vertexCount; count += 3) {
// get 3 points
var position0 = ourPseudoVertexShader(count + 0, time);
var position1 = ourPseudoVertexShader(count + 1, time);
var position2 = ourPseudoVertexShader(count + 2, time);
// draw triangle
ctx.beginPath();
ctx.moveTo(position0.x, position0.y);
ctx.lineTo(position1.x, position1.y);
ctx.lineTo(position2.x, position2.y);
ctx.fillStyle = position0.color;
ctx.fill();
}
If you run it here you'll see a grid 1 unit high and N units long. I've set the canvas origin so 0,0 is in the center just like WebGL and so the canvas is addressed +1 to -1 across and +1 to -1 down
var vertexCount = 100;
function ourPseudoVertexShader(vertexId, time) {
// let's compute an infinite grid of points based off vertexId
var x = Math.floor(vertexId / 6) + (vertexId % 2);
var y = (Math.floor(vertexId / 2) + Math.floor(vertexId / 3)) % 2;
// color every other triangle red or green
var triangleId = Math.floor(vertexId / 3);
var color = triangleId % 2 ? "#F00" : "#0F0";
return {
x: x * 0.2,
y: y * 0.2,
color: color,
};
}
var ctx = document.querySelector("canvas").getContext("2d");
requestAnimationFrame(render);
function render(time) {
time *= 0.001;
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.save();
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.scale(ctx.canvas.width / 2, -ctx.canvas.height / 2);
// lets assume triangles
for (var count = 0; count < vertexCount; count += 3) {
// get 3 points
var position0 = ourPseudoVertexShader(count + 0, time);
var position1 = ourPseudoVertexShader(count + 1, time);
var position2 = ourPseudoVertexShader(count + 2, time);
// draw triangle
ctx.beginPath();
ctx.moveTo(position0.x, position0.y);
ctx.lineTo(position1.x, position1.y);
ctx.lineTo(position2.x, position2.y);
ctx.fillStyle = position0.color;
ctx.fill();
}
ctx.restore();
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<canvas width="500" height="200"></canvas>
Doing the same thing in WebGL means making a buffer with the count
var count = [];
for (var i = 0; i < vertexCount; ++i) {
count.push(i);
}
Then putting that count in a buffer and using that as an attribute for a shader.
Here's the equivalent shader to the fake shader above
attribute float vertexId;
uniform float time;
varying vec4 v_color;
void main() {
// let's compute an infinite grid of points based off vertexId
float x = floor(vertexId / 6.) + mod(vertexId, 2.);
float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);
// color every other triangle red or green
float triangleId = floor(vertexId / 3.);
v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));
gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}
If we run that we'll get the same result
var vs = `
attribute float vertexId;
uniform float vertexCount;
uniform float time;
varying vec4 v_color;
void main() {
// let's compute an infinite grid of points based off vertexId
float x = floor(vertexId / 6.) + mod(vertexId, 2.);
float y = mod(floor(vertexId / 2.) + floor(vertexId / 3.), 2.);
// color every other triangle red or green
float triangleId = floor(vertexId / 3.);
v_color = mix(vec4(0, 1, 0, 1), vec4(1, 0, 0, 1), mod(triangleId, 2.));
gl_Position = vec4(x * 0.2, y * 0.2, 0, 1);
}
`;
var fs = `
precision mediump float;
varying vec4 v_color;
void main() {
gl_FragColor = v_color;
}
`;
var vertexCount = 100;
var gl = document.querySelector("canvas").getContext("webgl");
var count = [];
for (var i = 0; i < vertexCount; ++i) {
count.push(i);
}
var bufferInfo = twgl.createBufferInfoFromArrays(gl, {
vertexId: { numComponents: 1, data: count, },
});
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);
var uniforms = {
time: 0,
vertexCount: vertexCount,
};
requestAnimationFrame(render);
function render(time) {
uniforms.time = time * 0.001;
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
requestAnimationFrame(render);
}
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<canvas width="500" height="200"></canvas>
Everything else on vertexshartart is just creative math to make interesting patterns. You can use time to do animation. a texture with sound data is also provided.
There are some tutorials here
So, in answer to your question, when you switch modes (triangles/lines/points) on vertexshaderart.com all that does is change what's passed to gl.drawArrays (gl.POINTS, gl.LINES, gl.TRIANGLES). The points themselves are generated in the vertex shader like the example above.
So that leaves the question, what specific effect are you trying to achieve. Then we can know what to suggest to achieve it. You might want to ask a new question for that (so that this answer still matches the question above)

Related

WEBGL applying animation to only specific objects among others

The objective of this work is to produce a big white circle in the center of the Canvas. Then on the circumference of the big circle, there will be 10 small circles of different colors. These 10 small circles will keep increasing in size.
My current work will cause the big white circle to increase in size too. How should I fix the size of the big white circle?
The only difference between draw_circle() and draw_growing_circle is that the later one has gl.uniform1f(u_SizeChange, currentSize); in the function.
I think it might be caused by how I initialize the VSHADER but I have no idea how to change it.
ps. In my codes, I only tried 2 small circles.
Thanks
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform float u_SizeChange;\n' +
'void main() {\n' +
' gl_Position.x = u_SizeChange * a_Position.x;\n' +
' gl_Position.y = u_SizeChange * a_Position.y;\n' +
' gl_Position.z = u_SizeChange * a_Position.z;\n' +
' gl_Position.w = 1.0;\n' +
'}\n';
var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec4 u_FragColor;\n' +
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' +
'}\n';
// Growing rate (size change/second)
var GROWING_RATE = 0.1;
function main() {
// Retrieve <canvas> element
var canvas = document.getElementById('webgl');
// Get the rendering context for WebGL
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// Initialize shaders
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
// Specify the color for clearing <canvas>
console.log('run');
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Get storage location of u_SizeChange
var u_SizeChange = gl.getUniformLocation(gl.program, 'u_SizeChange');
if (!u_SizeChange) {
console.log('Failed to get the storage location of u_SizeChange');
return;
}
// Current size
var currentSize = 1.0;
var cx = 0;
var cy = 0;
var r = 200;
// Start drawing
var tick = function() {
currentSize = animate(currentSize); // Update the size
// draw(gl, n,currentSize, u_SizeChange); // Draw the square
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
draw_circle(gl, 0, 0, r/400,[1.0,1.0,1.0,1.0]); // Draw the Disk
draw_growing_circle(gl, cx+r*Math.sin(10)/400, cy+r*Math.cos(10)/400, 20/400,[Math.random(),Math.random(),Math.random(),1],currentSize, u_SizeChange); //Draw the bacteria
draw_growing_circle(gl, cx+r*Math.sin(20)/400, cy+r*Math.cos(20)/400, 20/400,[Math.random(),Math.random(),Math.random(),1],currentSize, u_SizeChange);
requestAnimationFrame(tick, canvas); // Request that the browser calls tick
};
tick();
}
var g_last = Date.now();
function animate(size) {
// Calculate the elapsed time
var now = Date.now();
var elapsed = now - g_last;
g_last = now;
// Update the current size (adjusted by the elapsed time)
var newSize = size + (GROWING_RATE * elapsed) / 100.0;
return newSize;
}
function draw_growing_circle(gl, x, y, r, color,currentSize,u_SizeChange){
//Define the geometry and store it in buffer objects
//Represent a circle disk through 360 trangles
n = 3*360; // number of total vertices
var vertices = [];
for (var i = 1; i <= 360; i++) {
vertices.push(x);
vertices.push(y);
vertices.push(x+r*Math.sin(i-1));
vertices.push(y+r*Math.cos(i-1));
vertices.push(x+r*Math.sin(i));
vertices.push(y+r*Math.cos(i));
}
// Create a new buffer object
var vertex_buffer = gl.createBuffer();
// Bind an empty array buffer to it
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
// Pass the vertices data to the buffer
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
//Get the attribute location
var coord = gl.getAttribLocation(gl.program, "a_Position");
if(coord < 0) {
console.log('Failed to get the storage location of coord');
return -1;
}
// Get the storage location of u_FragColor
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
if (!u_FragColor) {
console.log('Failed to get the storage location of u_FragColor');
return;
}
//point an attribute to the currently bound VBO
gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0);
//Enable the attribute
gl.enableVertexAttribArray(coord);
//Drawing the required object (triangle)
// Enable the depth test
gl.enable(gl.DEPTH_TEST);
// Draw the triangles
// Pass the color of a point to u_FragColor variable
var rgba = color;
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
gl.uniform1f(u_SizeChange, currentSize);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
// Unbind the buffer
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
WebGL is a state machine, when you set a uniform it remains set until you set it to a different value, so when you draw your non-growing circle you want to make sure it has the corresponding value. Generating the geometry every frame is the wrong way of doing things as you only need to generate it once, but I'm getting a homework vibe from your questions so hopefully this topic will be addressed.

Addressing Texel in ThreeJS DataTexture

I'm looking to compute texel references for a THREE.DataTexture in Javascript for use in a fragment shader. I've succeeded in computing screen space coordinates of points and passing them to a shader in a uniform float array of x and y values, and then referencing those points by indices in my shader. I now want to render too many points to pass the coordinates in a uniform float array so I'd like to use a DataTexture and write the coordinates in the RG values of RGBA texels.
Referencing this question I am using the following method:
var tDataWidth = points.length;
var tData = new Uint8Array( Math.pow(tDataWidth, 2) );
var texelSize = 1.0 / tDataWidth;
var texelOffset = new THREE.Vector2(0.5 * texelSize, 0.5 * texelSize);
for(var i = 0; i < points.length; i++){
//convert data to 0-1, then to 0-255
//inverse is to divide by 255 then multiply by width, height respectively
tData[i * 4] = Math.round(255 * (points[i].x / window.innerWidth));
tData[i * 4 + 1] = Math.round(255 * ((window.innerHeight - points[i].y) / window.innerHeight));
tData[i * 4 + 2] = 0;
tData[i * 4 + 3] = 0;
//calculate UV texel coordinates here
//Correct after edit
var u = ((i % tDataWidth) / tDataWidth) + texelOffset;
var v = (Math.floor(i / tDataWidth) + texelOffset);
var vUV = new THREE.Vector2(u, v);
//this function inserts the reference to the texel at the index into the shader
//referenced in the frag shader:
//cvec = texture2D(tData, index);
shaderInsert += ShaderInsert(vUV, screenPos.x, window.innerHeight - screenPos.y);
}
var dTexture = new THREE.DataTexture( sdfUItData, tDataWidth, tDataWidth, THREE.RGBAFormat, THREE.UnsignedByteType );
//I think this is necessary
dTexture.magFilter = THREE.NearestFilter;
dTexture.needsUpdate = true;
//update uniforms of shader to get this DataTexture
renderer.getUniforms("circles")["tData"].value = dTexture;
//return string insert of circle
//I'm editing the shader through javascript then recompiling it
//There's more to it in the calling function, but this is the relevant part I think
...
ShaderInsert(index){
var circle = "\n\tvIndex = vec2(" + String(index.x) + ", " + String(index.y) + ");\n";
circle += "\tcvec = texture2D(tData, vIndex);\n";
circle += "\tcpos = vec2( (cvec.r / 255.0) * resolution.x, (cvec.y / 255.0) * resolution.y);\n";
circle += "\tc = circleDist(translate(p, cpos), 7.0);\n";
circle += "\tm = merge(m, c);";
return(circle);
}
Any help on where I'm going wrong? Right now output is all in the lower left corner, so (0, window.innerHeight) as far as I can tell. Thanks!
So the answer is actually straightforward. In the fragment shader rgba values are 0.0 - 1.0, so there's no need to divide by 255 as I was doing in the fragment shader.
I'd also like to say that I discovered the Spector.js Chrome extension which allows one to view all webgl calls and buffers. Pretty cool!
If anyone wants to learn more about how the drawing functions work in the fragment shader, it's all in this awesome shader which I did not write:
https://www.shadertoy.com/view/4dfXDn
<3

How to get the 2d dimensions of the object being drawn for hit test on webgl after model view transform

I follow webgl fundamentals and draw 2d object and use matrices to scale vertices and render.
Before render I pass width/height that set as vertices to render a quad. This defines the size of the object. But in the vertex shader I apply transformation to these vertices like so:
in vec2 aPosition;
in vec2 aTexCoord;
out vec2 vQuadCoord;
uniform mat3 uMatrix;
void main() {
vec2 position = (uMatrix * vec3(aPosition, 1)).xy;
vQuadCoord = aTexCoord;
gl_Position = vec4(position, 0, 1);
}
This matrix controls translate/rotate/scale of the object. After render, I want to know the bounds of this object. But especially after scaling I can't know the bounds. If I translate this object (with matrices) at x,y it's position is known, but if I scale this object, x is shifted to the left, by unkown amount. webgl fundamentals don't mention about this topic, what is a good approach to detect the bounds of the object and transform precisely because I also have problems with the pivot, i might ask as another question.
You need to convert the mouse coordinates to clip space and then multiply them by the inverse of the matrix. this will give you mouse cooordinates that are relative to the values of aPosition.
After that it's up to you. If the values (the vertices) fed to aPosition are a rectangle than you can just check the transformed point against that rectangle. If they are a more complicated shape like a star then you'll need to make your own function to do point in star or point in triangle and check each triangle but at least after the transformation the mouse position is in coordinates relative to your vertices. You could also compute at init time the bounding box of the vertices and use that to test against the transformed point.
function main() {
const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) {
return alert('need WebGL2');
}
const vs = `#version 300 es
in vec2 aPosition;
uniform mat3 uMatrix;
void main() {
vec2 position = (uMatrix * vec3(aPosition, 1)).xy;
gl_Position = vec4(position, 0, 1);
}
`;
const fs = `#version 300 es
precision mediump float;
uniform vec4 color;
out vec4 outColor;
void main() {
outColor = color;
}
`;
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);
// create a quad that starts at 0,0 and is 20 units wide and 10 tall
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
aPosition: {
numComponents: 2,
data: [
0, 0,
0, 10,
20, 0,
20, 0,
0, 10,
20, 10,
],
}
});
const vao = twgl.createVAOFromBufferInfo(gl, programInfo, bufferInfo);
let mouseClipX = 0;
let mouseClipY = 0;
const infoElem = document.querySelector('#info');
function render(time) {
t = time / 1000;
twgl.resizeCanvasToDisplaySize(gl.canvas);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(programInfo.program);
gl.bindVertexArray(vao);
let mat = m3.projection(gl.canvas.width, gl.canvas.height);
mat = m3.translate(
mat,
150 + Math.sin(t * 0.1) * 100,
75 + Math.cos(t * 0.2) * 50);
mat = m3.rotate(mat, t * 0.3);
mat = m3.scale(
mat,
2 + Math.sin(t * 0.4) * 0.5,
2 + Math.cos(t * 0.5) * 0.5);
// convert clipspace mouse to aPosition relative values
// 'mat' takes aPosition and converts to clip space
// so the inverse of 'mat' would take clip space and
// convert back to aPosition space.
const invMat = m3.inverse(mat);
const p = m3.transformPoint(invMat, [mouseClipX, mouseClipY]);
// now check in aPosition space. It's a 20x10 rect starting at 0,0 so
const inbox = p[0] >= 0 && p[0] < 20 &&
p[1] >= 0 && p[1] < 10;
twgl.setUniforms(programInfo, {
uMatrix: mat,
color: inbox ? [1, 0, 0, 1] : [0, 0, 1, 1],
});
twgl.drawBufferInfo(gl, bufferInfo);
infoElem.textContent = inbox ? 'mouse in rect' : 'no hit';
requestAnimationFrame(render);
}
requestAnimationFrame(render);
gl.canvas.addEventListener('mousemove', (event) => {
// convert canvas relative mouse coordinates to clip space
mouseClipX = (event.offsetX / gl.canvas.clientWidth ) * 2 - 1;
mouseClipY = (event.offsetY / gl.canvas.clientHeight) * -2 + 1; // note we flip Y
});
}
main();
canvas { border: 1px solid black; }
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="https://webgl2fundamentals.org/webgl/resources/m3.js"></script>
<canvas></canvas>
<pre id="info"></pre>

Get color of the texture at UV coordinate

I am using three v.73
I have UV coordinate from intersection of raycaster.
Also I have texture of this object. How can I get color (RGB or RGBA) of used texture at the UV coordinate?
I have tried to use get pixel of Image from texture, but it was using a lot of memory
If you want it to be fast keep your texture's images around. At init time for each image you're making a texture from also make a copy of its data with something like
// make the canvas same size as the image
some2dCanvasCtx.canvas.width = img.width;
some2dCanvasCtx.canvas.height = img.height;
// draw the image into the canvas
some2dCanvasCtx.drawImage(img, 0, 0);
// copy the contents of the canvas
var texData = some2dCanvasCtx.getImageData(0, 0, img.width, img.height);
Now if you have a UV coord you can just look it up
var tx = Math.min(emod(u, 1) * texData.width | 0, texData.width - 1);
var ty = Math.min(emod(v, 1) * texData.height | 0, texData.height - 1);
var offset = (ty * texData.width + tx) * 4;
var r = texData.data[offset + 0];
var g = texData.data[offset + 1];
var b = texData.data[offset + 2];
var a = texData.data[offset + 3];
// this is only needed if your UV coords are < 0 or > 1
// if you're using CLAMP_TO_EDGE then you'd instead want to
// clamp the UVs to 0 to 1.
function emod(n, m) {
return ((n % m) + m) % m;
}
Otherwise you can ask WebGL for the color of the texture. Use tx and ty from above. See this answer.

I need to warp a long image into a 2d circle (webGL). Distortion is expected

I need to take a long (max resolution) image and wrap it into a circle. So imagine bending a steel bar so that it is now circular with each end touching.
I have been banging my head against threejs for the last 8 hours and have so far managed to apply the image as a texture on a circle geometry, but can't figure out how to apply the texture to a long mesh and then warp that mesh appropriately. The warping doesn't need to be (and shouldn't be) animated. What we basically have is a 360 panoramic image that we need to "flatten" into a top-down view.
In lieu of sharing my code (as it's not significantly different), I've so far been playing around with this tutorial:
http://www.johannes-raida.de/tutorials/three.js/tutorial06/tutorial06.htm
And I do (I think) understand the broad strokes at this point.
Other things I've tried is to use just canvas to slice the image up into strips and warp each strip... this was horribly slow and I couldn't get that to work properly either!
Any help/suggestions?
Here's also a shader version: Shadertoy - Circle Distortion
This is the actual code:
#define dPI 6.28318530718 // 2*PI
#define sR 0.3 // small radius
#define bR 1.0 // big radius
void main(void)
{
// calc coordinates on the canvas
vec2 uv = gl_FragCoord.xy / iResolution.xy*2.-vec2(1.);
uv.x *= iResolution.x/iResolution.y;
// calc if it's in the ring area
float k = 0.0;
float d = length(uv);
if(d>sR && d<bR)
k = 1.0;
// calc the texture UV
// y coord is easy, but x is tricky, and certain calcs produce artifacts
vec2 tUV = vec2(0.0,0.0);
// 1st version (with artifact)
//tUV.x = atan(uv.y,uv.x)/dPI;
// 2nd version (more readable version of the 3rd version)
//float disp = 0.0;
//if(uv.x<0.0) disp = 0.5;
//tUV.x = atan(uv.y/uv.x)/dPI+disp;
// 3rd version (no branching, ugly)
tUV.x = atan(uv.y/uv.x)/dPI+0.5*(1.-clamp(uv.x,0.0,1.0)/uv.x);
tUV.y = (d-sR)/(bR-sR);
// output pixel
vec3 col = texture2D(iChannel0, tUV).rgb;
gl_FragColor = vec4(col*k,1.);
}
So you could draw rectangle on the canvas and add this shader code.
I hope this helps.
So here's a function using canvas's context2d that does the job.
The idea is to go around all the circle by a small angular step and to draw a thin slice of 'texture' along the circle radius.
To make it faster, only way i see is to compute by hand the transform to do one single setTransform instead of all this stuff.
The step count is optimal with step = atan(1, radius)
(if you do the scheme it's obvious : to go one y up when you're radius far from the center then tan = 1/radius => step angle = atan(1, radius).)
fiddle is here :
http://jsfiddle.net/gamealchemist/hto1s6fy/
A small example with a cloudy landscape :
// draw the part of img defined by the rect (startX, startY, endX, endY) inside
// the circle of center (cx,cy) between radius (innerRadius -> outerRadius)
// - no check performed -
function drawRectInCircle(img, cx, cy, innerRadius, outerRadius, startX, startY, endX, endY) {
var angle = 0;
var step = 1 * Math.atan2(1, outerRadius);
var limit = 2 * Math.PI;
ctx.save();
ctx.translate(cx, cy);
while (angle < limit) {
ctx.save();
ctx.rotate(angle);
ctx.translate(innerRadius, 0);
ctx.rotate(-Math.PI / 2);
var ratio = angle / limit;
var x = startX + ratio * (endX - startX);
ctx.drawImage(img, x, startY, 1, (endY - startY), 0, 0, 1, (outerRadius - innerRadius));
ctx.restore();
angle += step;
}
ctx.restore();
}

Categories

Resources