我正在與HTML5
和canvas
一起工作。我已經畫出了一個2D圓圈。現在我想用一個顏色給圓圈加陰影,但陰影看起來像一個3D圓圈。用畫布可以做到這一點嗎?謝謝。如何遮擋畫布中的圓圈
回答
As @ danday74說,您可以使用漸變向您的圓圈添加深度。
您還可以使用陰影將深度添加到您的圈子。
下面是說明3D甜甜圈證明的概念:
我讓你來設計你的期望的繞圈
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var PI=Math.PI;
drawShadow(150,150,120,50);
function drawShadow(cx,cy,r,strokewidth){
ctx.save();
ctx.strokeStyle='white';
ctx.lineWidth=5;
ctx.shadowColor='black';
ctx.shadowBlur=15;
//
ctx.beginPath();
ctx.arc(cx,cy,r-5,0,PI*2);
ctx.clip();
//
ctx.beginPath();
ctx.arc(cx,cy,r,0,PI*2);
ctx.stroke();
//
ctx.beginPath();
ctx.arc(cx,cy,r-strokewidth,0,PI*2);
ctx.stroke();
ctx.shadowColor='rgba(0,0,0,0)';
//
ctx.beginPath();
ctx.arc(cx,cy,r-strokewidth,0,PI*2);
ctx.fillStyle='white'
ctx.fill();
//
ctx.restore();
}
body{ background-color: white; }
canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
是的。謝謝你的回答。這非常有幫助。 – Gamsh
假煙霧和鏡子
假球上的燈光。我猜這是一個球體,你說的圓圈,你可能是一個甜甜圈。這項技術也適用於甜甜圈。
所以照明。
Phong光照
最基本的照明模型是蓬(從存儲器)。它使用入射光線和表面法線之間的角度(從90度表面出來的一條線)。反射光量是該角度時間光強度的餘弦。
球體一個容易
由於球是對稱的,這允許我們使用徑向漸變爲在球體上的每個像素,並用於與所述光的球體應用該值直接開銷這產生一個完美的蓬用很少的努力就可以遮蔽球體。
這樣做的代碼。 x,y是球體的中心,r是半徑。當您從球體中心移出時,光線與表面法線之間的角度很容易計算。它從零開始,以Math.PI/2(90度)結束。所以反射值就是該角度的餘弦。
var grd = ctx.createRadialGradient(x,y,0,x,y,r);
var step = (Math.PI/2)/r;
for(var i = 0; i < (Math.PI/2); i += step){
var c = "" + Math.floor(Math.max(0,255 * Math.abs(Math.cos(i)));
grd.addColorStop(i/(Math.PI/2),"rgba("+c+","+c+","+c+","1)");
}
該代碼創建一個適合圓的漸變。
國防部荷馬食品
要爲您需要修改我甜甜圈做。甜甜圈具有內,外半徑(R1,R2),所以內部的for循環修改我
var ii = (i/(Math.PI/2)); // normalise i
ii *= r2; // scale to outer edge
ii = ((r1+r2)/2)-ii; // get distance from center line
ii = ii/((r2-r1)/2); // normalise to half the width;
ii = ii * Math.PI * (1/2); // scale to get the surface norm on the donut.
// use ii as the surface normal to calculate refelected light
var c = "" + Math.floor(Math.max(0,255 * Math.abs(Math.cos(ii)));
Phong光照吮吸
通過Phong光照吸大的時候,也不會做。這也不允許偏離球體中心或甚至部分落在球體後面的燈。
我們需要添加偏心光線的能力。幸運的是,徑向梯度可以抵消
var grd = ctx.createRadialGradient(x,y,0,x,y,r);
前3個數字是梯度的起始圓,可以放在任何位置。問題在於,當我們移動起始位置時,phong底紋模型會分崩離析。爲了解決這個問題,有一點點菸霧和鏡子可以讓眼睛相信大腦想要的東西。
根據光線離中心的距離,我們調整徑向漸變上每個色阻的脫落,亮度,擴展和角度。
鏡面高光
這提高了一點,但仍然不是最好的。照明的另一個重要組成部分是鏡面反射(高光)。這取決於反射光和眼睛之間的角度。由於我們不想做所有這些(JavaScript很慢),我們將通過對phong底紋的輕微修改來對其進行整理。我們只是將表面法線乘以大於1的值。雖然不完美,但它效果很好。
表面性能和環境
接着光被着色,該球體具有依賴於頻率的反射品質和存在環境光爲好。我們不想模擬所有這些東西,所以我們需要一種方法來僞造它。
這可以通過合成來完成(用於幾乎所有的3D電影製作)。我們一次構建一層照明。 2D API爲我們提供了合成操作,因此我們可以創建多個漸變並對它們進行分層。
有很多數學參與,但我儘量保持它儘可能簡單。
的演示
下面演示並球體的實時陰影(將工作在所有徑向對稱的對象)除了爲畫布和鼠標演示有兩個部分的主循環執行一些設置代碼通過分層lights
和函數createGradient
的合成創建了漸變。
所使用的燈可以在對象lights
中找到,並具有各種屬性來控制圖層。第一層應該使用comp = source-in
和lum = 1
,否則最終會顯示背景。所有其他層燈可以是你想要的。
該標誌spec
告訴着色器燈是高光的,並且必須包含specPower > 1
,因爲我沒有審查它的存在。
燈的顏色在陣列中,代表紅色,綠色和藍色。值可以大於256且小於0,因爲自然界中的光具有巨大的動態範圍,並且某些效果需要將入射光升高到RGB像素的255限制以上。
我給分層結果添加了最後的「乘法」。這是煙霧和鏡像方法中的魔法。
如果你喜歡代碼玩的價值和層次。移動鼠標更改光源位置。
這不是真正的照明它是假的,但只要它看起來好,誰在乎。笑
UPDATE
發現一個錯誤,以便固定它,而我在這裏,改變了代碼,以隨機的燈光,當你點擊鼠標左鍵。這樣您就可以看到將ctx.globalCompositeOperation
與梯度組合使用時可以實現的照明範圍。
var demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
if(typeof mouse !== "undefined"){ // if the mouse exists
if(mouse.removeMouse !== undefined){
mouse.removeMouse(); // remove prviouse events
}
}else{
var mouse;
}
var canvasMouseCallBack = undefined; // if needed
mouse = (function(){
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 0, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function() { return this.interfaceId++; }, // For UI functions
startMouse:undefined,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(mouse); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
mouse.mouseEvents.forEach(
function(n){
element.addEventListener(n, mouseMove);
}
);
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.removeMouse = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(
function(n){
mouse.element.removeEventListener(n, mouseMove);
}
);
canvasMouseCallBack = undefined;
}
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
// draws the circle
function drawCircle(c){
ctx.beginPath();
ctx.arc(c.x,c.y,c.r,0,Math.PI*2);
ctx.fill();
}
function drawCircle1(c){
ctx.beginPath();
var x = c.x;
var y = c.y;
var r = c.r * 0.95;
ctx.moveTo(x,y - r);
ctx.quadraticCurveTo(x + r * 0.8, y - r , x + r *1, y - r/10);
ctx.quadraticCurveTo(x + r , y + r/3 , x , y + r/3);
ctx.quadraticCurveTo(x - r , y + r/3 , x - r , y - r /10 );
ctx.quadraticCurveTo(x - r * 0.8, y - r , x , y- r);
ctx.fill();
}
function drawShadowShadow(circle,light){
var x = light.x; // get the light position as we will modify it
var y = light.y;
var r = circle.r * 1.1;
var vX = x - circle.x; // get the vector to the light source
var vY = y - circle.y;
var dist = -Math.sqrt(vX*vX+vY*vY)*0.3;
var dir = Math.atan2(vY,vX);
lx = Math.cos(dir) * dist + circle.x; // light canb not go past radius
ly = Math.sin(dir) * dist + circle.y;
var grd = ctx.createRadialGradient(lx,ly,r * 1/4 ,lx,ly,r);
grd.addColorStop(0,"rgba(0,0,0,1)");
grd.addColorStop(1,"rgba(0,0,0,0)");
ctx.fillStyle = grd;
drawCircle({x:lx,y:ly,r:r})
}
// 2D light simulation. This is just an approximation and does not match real world stuff
// based on Phong shading.
// x,y,r descript the imagined sphere
// light is the light source
// ambient is the ambient lighting
// amount is the amount of this layers effect has on the finnal result
function createGradient(circle,light,ambient,amount){
var r,g,b; // colour channels
var x = circle.x; // get lazy coder values
var y = circle.y;
var r = circle.r;
var lx = light.x; // get the light position as we will modify it
var ly = light.y;
var vX = light.x - x; // get the vector to the light source
var vY = light.y - y;
// get the distance to the light source
var dist = Math.sqrt(vX*vX+vY*vY);
// id the light is a specular source then move it to half its position away
dist *= light.spec ? 0.5 : 1;
// get the direction of the light source.
var dir = Math.atan2(vY,vX);
// fix light position
lx = Math.cos(dir)*dist+x; // light canb not go past radius
ly = Math.sin(dir)*dist+y;
// add some dimming so that the light does not wash out.
dim = 1 - Math.min(1,(dist/(r*4)));
// add a bit of pretend rotation on the z axis. This will bring in a little backlighting
var lightRotate = (1-dim) * (Math.PI/2);
// spread the light a bit when near the edges. Reduce a bit for spec light
var spread = Math.sin(lightRotate) * r * (light.spec ? 0.5 : 1);
// create a gradient
var grd = ctx.createRadialGradient(lx,ly,spread,x,y,r + dist);
// use the radius to workout what step will cover a pixel (approx)
var step = (Math.PI/2)/r;
// for each pixel going out on the radius add the caclualte light value
for(var i = 0; i < (Math.PI/2); i += step){
if(light.spec){
// fake spec light reduces dim fall off
// light reflected has sharper falloff
// do not include back light via Math.abs
r = Math.max(0,light.col[0] * Math.cos((i + lightRotate)*light.specPower) * 1-(dim * (1/3)));
g = Math.max(0,light.col[1] * Math.cos((i + lightRotate)*light.specPower) * 1-(dim * (1/3)));
b = Math.max(0,light.col[2] * Math.cos((i + lightRotate)*light.specPower) * 1-(dim * (1/3)));
}else{
// light value is the source lum * the cos of the angle to the light
// Using the abs value of the refelected light to give fake back light.
// add a bit of rotation with (lightRotate)
// dim to stop washing out
// then clamp so does not go below zero
r = Math.max(0,light.col[0] * Math.abs(Math.cos(i + lightRotate)) * dim);
g = Math.max(0,light.col[1] * Math.abs(Math.cos(i + lightRotate)) * dim);
b = Math.max(0,light.col[2] * Math.abs(Math.cos(i + lightRotate)) * dim);
}
// add ambient light
if(light.useAmbient){
r += ambient[0];
g += ambient[1];
b += ambient[2];
}
// add the colour stop with the amount of the effect we want.
grd.addColorStop(i/(Math.PI/2),"rgba("+Math.floor(r)+","+Math.floor(g)+","+Math.floor(b)+","+amount+")");
}
//return the gradient;
return grd;
}
// define the circles
var circles = [
{
x: canvas.width * (1/2),
y: canvas.height * (1/2),
r: canvas.width * (1/8),
}
]
function R(val){
return val * Math.random();
}
var lights;
function getLights(){
return {
ambient : [10,30,50],
sources : [
{
x: 0, // position of light
y: 0,
col : [R(255),R(255),R(255)], // RGB intensities can be any value
lum : 1, // total lumanance for this light
comp : "source-over", // composite opperation
spec : false, // if true then use a pretend specular falloff
draw : drawCircle,
useAmbient : true,
},{ // this light is for a little accent and is at 180 degree from the light
x: 0,
y: 0,
col : [R(255),R(255),R(255)],
lum : R(1),
comp : "lighter",
spec : true, // if true then you MUST inclue spec power
specPower : R(3.2),
draw : drawCircle,
useAmbient : false,
},{
x: canvas.width,
y: canvas.height,
col : [R(1255),R(1255),R(1255)],
lum : R(0.5),
comp : "lighter",
spec : false,
draw : drawCircle,
useAmbient : false,
},{
x: canvas.width/2,
y: canvas.height/2 + canvas.width /4,
col : [R(155),R(155),R(155)],
lum : R(1),
comp : "lighter",
spec : true, // if true then you MUST inclue spec power
specPower : 2.32,
draw : drawCircle,
useAmbient : false,
},{
x: canvas.width/3,
y: canvas.height/3,
col : [R(1255),R(1255),R(1255)],
lum : R(0.2),
comp : "multiply",
spec : false,
draw : drawCircle,
useAmbient : false,
},{
x: canvas.width/2,
y: -100,
col : [R(2255),R(2555),R(2255)],
lum : R(0.3),
comp : "lighter",
spec : false,
draw : drawCircle1,
useAmbient : false,
}
]
}
}
lights = getLights();
/** FrameUpdate.js begin **/
var w = canvas.width;
var h = canvas.height;
var cw = w/2;
var ch = h/2;
ctx.font = "20px Arial";
ctx.textAlign = "center";
function update(){
ctx.setTransform(1,0,0,1,0,0);
ctx.fillStyle = "#A74"
ctx.fillRect(0,0,w,h);
ctx.fillStyle = "black";
ctx.fillText("Left click to change lights", canvas.width/2, 20)
// set the moving light source to that of the mouse
if(mouse.buttonRaw === 1){
mouse.buttonRaw = 0;
lights = getLights();
}
lights.sources[0].x = mouse.x;
lights.sources[0].y = mouse.y;
if(lights.sources.length > 1){
lights.sources[1].x = mouse.x;
lights.sources[1].y = mouse.y;
}
drawShadowShadow(circles[0],lights.sources[0])
//do each sphere
for(var i = 0; i < circles.length; i ++){
// for each sphere do the each light
var cir = circles[i];
for(var j = 0; j < lights.sources.length; j ++){
var light = lights.sources[j];
ctx.fillStyle = createGradient(cir,light,lights.ambient,light.lum);
ctx.globalCompositeOperation = light.comp;
light.draw(circles[i]);
}
}
ctx.globalCompositeOperation = "source-over";
if(!STOP && (mouse.buttonRaw & 4)!== 4){
requestAnimationFrame(update);
}else{
if(typeof log === "function"){
log("DONE!")
}
STOP = false;
var can = document.getElementById("canv");
if(can !== null){
document.body.removeChild(can);
}
}
}
if(typeof clearLog === "function"){
clearLog();
}
update();
}
var STOP = false; // flag to tell demo app to stop
function resizeEvent(){
var waitForStopped = function(){
if(!STOP){ // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped,200);
}
STOP = true;
setTimeout(waitForStopped,100);
}
window.addEventListener("resize",resizeEvent);
demo();
/** FrameUpdate.js end **/
有用的光線投射信息! :-) – markE
- 1. 在佈局中畫圓圈
- 2. 如何在畫布上移動圓圈?
- 3. 如何遮擋Wordpress?
- 4. 如何清除HTML5畫布中的圓弧或圓圈?
- 5. 更改畫布中圓圈的起點?
- 6. 在畫布中心繪製圓圈
- 7. PCL:textureMeshwithMultipleCameras()如何處理遮擋?
- 8. 如何遮擋Swing組件?
- 9. 爲什麼畫布圓圈看起來不像一個圓圈?
- 10. 如何清除畫布中圓圈的前一個位置?
- 11. 如何在畫布上旋轉圓圈動畫
- 12. android如何在畫布上畫圓圈,矩形?
- 13. 我的帆布disapears因爲遮擋
- 14. 背景遮擋
- 15. 如何在opengles中畫圓圈
- 16. 動畫 - 在iOS中繪製圓圈 - 未完成圓圈動畫
- 17. Tango AR Camera中的遮擋
- 18. Javascript畫布繪製矩形或圓圈
- 19. Javascript HTML5畫布繪製透明圓圈
- 20. 在畫布上繪製交互圓圈
- 21. 如何在Android中通過畫布繪製圓圈?
- 22. 如何在android中繪製聯鎖圓圈使用畫布
- 23. 如何在HTML5畫布上繪製圓圈作爲單獨的畫布?
- 24. 如何畫一個圓圈上的Android
- 25. pygame圓圈動畫
- 26. 如何在3D物體遮擋畫布的情況下停止對畫布的反應
- 27. 在iOS中畫圓圈
- 28. 如何使用畫布連接具有線條的圓圈
- 29. 如何獲取在畫布上點擊的圓圈
- 30. 如何在tkinter的畫布上做圓圈?
是否使用WebGL的? –
沒有不使用WebGL。這可能嗎? – Gamsh