我正在尝试使用2D渲染上下文在HTML5 Canvas中制作一种相机。正如您在下面的图片中看到的,这是我正在努力实现的目标:
每当我移动相机时,我已经完成了红色框可以在相机中心旋转的位置,[编辑],这是一个简化的例子:
*Within the requestAnimationFrame (game loop)*
...
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
ctx.save();
// draw camera eye.
ctx.lineWidth = 3;
ctx.fillStyle = "black";
ctx.beginPath();
ctx.moveTo(ctx.canvas.width / 2 - 50, ctx.canvas.height / 2);
ctx.lineTo(ctx.canvas.width / 2 + 50, ctx.canvas.height / 2);
ctx.moveTo(ctx.canvas.width / 2, ctx.canvas.height / 2 - 50);
ctx.lineTo(ctx.canvas.width / 2, ctx.canvas.height / 2 + 50);
ctx.stroke();
// Rotate by camera's center.
ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2);
ctx.rotate(worldRotate);
ctx.translate(worldPos.x, worldPos.y);
//
// ADD WORLD ENTITIES BELOW to be viewed by the camera.
//
ctx.fillStyle = "red";
ctx.fillRect(-5, -5, 10, 10);
ctx.restore();
...
(编辑:将变换对象更改为ctx以进一步简化代码。)
变量worldRotate和worldPos可以通过输入进行测试。
尝试向右或向左移动,看看会发生什么。并尝试向右旋转相机,使红色框与x轴对齐,然后移动相机,看看它是如何完美地像我想要的相机一样工作。
(注意:推荐的浏览器测试它是IE10,最新的Chrome,最新的Firefox,最新的歌剧和桌面游戏5 +)。
问题,当我移动相机时(通过更改worldPos,使红色框离开相机),进行旋转(使用worldRotate),然后再次移动相机,红色物体总是朝着紫色箭头而不是橙色箭头移动。 无论相机的中心是否旋转,我都希望红色框朝橙色箭头移动。
编辑:据我所知,很明显旋转会导致平移变得混乱,因为旋转会改变红框的坐标系,但我仍然不知道如何处理它,至少让翻译像橙色箭头一样,无论红色框的当前旋转坐标系如何。
对此有何解决方案?谢谢。
答案 0 :(得分:4)
[由OP进一步澄清编辑
好的,我现在对你的问题有了更好的理解。您希望在您的世界中的物体上提供相机镜头透视。
如果您的相机在X方向上与您的世界物体相比仅 ,则可以简单地使用视差。您可以通过在画布上应用2个图层来实现此操作,这些图层在X方向上以不同的速度滚动。这是全景视差。因此,对于每个帧,绘制2个图层,如此伪代码:
// Move the back layer
backLayerX += 3;
ctx.save();
ctx.translate(backLayerX,backLayerY);
// draw the objects in your back layer now
ctx.translate(-backLayerX,-backLayerY);
ctx.restore();
// Move the front layer faster than the back layer
frontLayerX += 10;
ctx.save();
ctx.translate(frontLayerX,frontLayerY);
// draw the objects in your front layer now
ctx.translate(frontLayerX,frontLayerY);
ctx.restore();
如果您沿X方向移动相机本身,您还必须改变前后层的平移速度。
如果您的相机也相对于您的世界物体在Z方向(相机更近)上看,则该过程太多更复杂。
例如,如果你靠近雕像,雕像似乎变得更大 - ctx.scale(2,2)。背景风景也变得更大,但不是那么快 - ctx.scale(1.25,1.25)。
然后,如果你也向左迈出一步,那么雕像的左侧略大于雕像的右侧 - 背景偏斜。背景也有所偏差,但非常非常轻微。
然后,如果你还站在椅子上(改变Y方向),你会得到一种不同的倾斜。
你的相机甚至不能用雕像移动,因为你的雕像是2D-只是雕像的纸板切口。从相机的角度来看,雕像实际上会消失!
我希望我已经为你开始了你的项目,但是完整的答案是在Stackoverflow的答案之外还有一个很好的答案 - 这是一个关于计算机图形学的大学课程。
我有一个在线教程推荐给你。 Wolfgang Hurst在Perspective上发表了一篇优秀的讲座。这是他在13个在线大学课程中的第7讲。您可以通过两种方式查看他的讲座。广泛地查看以获得提供相机视角所涉及的概念。或者深入查看并获得数学算法:http://www.youtube.com/watch?v=q_HA-x5AujI&list=PLDFA8FCF0017504DE
祝你好运和学习...... :)
[以下内容解决OP的原始问题]
这是一个关于如何在画布中成功旋转任何内容的快速教程
整个世界围绕X / Y“登记点”旋转。
您可以通过在ctx.rotate(角度)之前执行ctx.translate(X,Y)来设置注册点。
在此旋转空间中绘制“世界”对象后,您必须通过执行翻译(-X,-Y)重新设置注册点。
所以世界轮换是这样的:
这是了解轮换和注册点的绝佳方法:
这是代码和小提琴:http://jsfiddle.net/m1erickson/nj29x/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:30px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var camX=100;
var camY=100;
var camRadius=40;
var camDegrees=0;
draw();
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawWorldRect(75,75,30,20,0,"blue","A");
drawWorldRect(100,100,30,20,15,"red","B");
drawCamera();
}
function drawWorldRect(x,y,width,height,angleDegrees,fillstyle,letter){
var cx=x+width/2;
var cy=y+height/2;
ctx.save();
ctx.translate(cx, cy);
ctx.rotate( (Math.PI / 180) * angleDegrees);
ctx.fillStyle = fillstyle;
ctx.fillRect(-width/2, -height/2, width, height);
ctx.fillStyle="white";
ctx.font = '18px Verdana';
ctx.fillText(letter, -6, 7);
ctx.restore();
}
function drawCamera(){
ctx.save();
ctx.lineWidth = 3;
ctx.beginPath();
// draw camera cross-hairs
ctx.strokeStyle = "#dddddd";
ctx.moveTo(camX,camY-camRadius);
ctx.lineTo(camX,camY+camRadius);
ctx.moveTo(camX-camRadius,camY);
ctx.lineTo(camX+camRadius,camY);
ctx.arc(camX, camY, camRadius, 0 , 2 * Math.PI, false);
ctx.stroke();
// draw camera site
ctx.fillStyle = "#222222";
ctx.beginPath();
ctx.arc(camX, camY-camRadius, 3, 0 , 2 * Math.PI, false);
ctx.fill()
ctx.restore();
}
function rotate(){
ctx.save();
ctx.translate(camX,camY);
ctx.rotate(Math.PI/180*camDegrees);
ctx.translate(-camX,-camY);
draw();
ctx.restore();
}
$("#rotateCW").click(function(){ camDegrees+=30; rotate(); });
$("#rotateCCW").click(function(){ camDegrees-=30; rotate(); });
$("#camUp").click(function(){ camY-=20; draw(); });
$("#camDown").click(function(){ camY+=20; draw(); });
$("#camLeft").click(function(){ camX-=20; draw(); });
$("#camRight").click(function(){ camX+=20; draw(); });
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=400 height=300></canvas><br/>
<button id="camLeft">Camera Left</button>
<button id="camRight">Camera Right</button>
<button id="camUp">Camera Up.</button>
<button id="camDown">Camera Down</button><br/>
<button id="rotateCW">Rotate Clockwise</button>
<button id="rotateCCW">Rotate CounterClockwise</button>
</body>
</html>
答案 1 :(得分:2)
使用旋转角度的切线计算x和y指向移动世界中心的位置。我有一个展示这个概念的例子,它不是一个最终的解决方案,而是一个可以解决的起点。把它想象成“粗剪”(没有时间完全抛光)。例如,您可以看到,旋转的旋转点不会移动到摄像机镜头正下方的位置,因此旋转不会直接发生在摄像机下方。
这是一个解释几何/三角函数如何工作的图表:
这是代码片段和指向功能齐全的jsfiddle的链接。键盘命令与示例中的相同。
http://jsfiddle.net/ricksuggs/X8rYF/
//s (down)
if (keycode === 83) {
event.preventDefault();
canvas.getContext('2d').clearRect(-200,-200,400,400);
canvas.getContext('2d').translate(2*Math.tan(rotationSum), -2);
drawGrid();
drawCamera();
}
// <-- (rotate left)
if (keycode === 37) {
event.preventDefault();
canvas.getContext('2d').clearRect(-200,-200,400,400);
rotationSum += rotation;
console.log('rotationSum: ' + rotationSum);
canvas.getContext('2d').rotate(-rotation);
drawGrid();
drawCamera();
}
答案 2 :(得分:0)
订单应该是这样的:
Ctx.save()
Ctx.translate(x, y) //to the center (Or top left of object to rotate)
Ctx.rotates(radian)
Ctx.translate(-x, -y) //important to move the context back!
Ctx.draw(...)
Ctx.restore()