如何用图像绘制等距平面? [复制]

Posted

技术标签:

【中文标题】如何用图像绘制等距平面? [复制]【英文标题】:How to draw an isometric plane with images? [duplicate] 【发布时间】:2016-07-30 02:23:00 【问题描述】:

我想创建等轴测图。地图存在等距矩形,如图:

我想将每个矩形表示为具有宽度和高度的二维矩形。 因此,例如,当您在编辑器中创建地图时,您可以绘制 2d 矩形,并且此创建可以转换为等距平面。 等轴测平面的宽度是从北到东南(从菱形顶部到右侧。) 高度是从北到西南。 (从上到左)

现在我有两个问题。

第一个问题

我正在画布上绘制这张等距地图,我正在使用屏幕外画布分别绘制每个平面。我想计算屏幕外画布的 2d 宽度和高度。我用这个数学来做到这一点:

var canvasWidth  = Math.cos(30) * planeWidth + Math.cos(30) * planeHeight;
var canvasHeight = Math.sin(30) * planeWidth + Math.cos(30) * planeHeight;
// still remember that planeWidth and planeHeight are isometric, so oblique sides

但这并没有为我提供合适的画布尺寸。 我想知道的另一件事是用像素计算斜边是否明智。

第二个问题 我想为飞机使用“纹理”图像。所以,我有这样的图像:

我的想法是存储每个图像的斜边,例如平面的宽度和高度。 当我想用特定图像绘制平面时,我可以重复该图像。

主要目标是创建具有像素精确给定宽度和高度的等距平面。并且飞机应该具有图像作为纹理。这些图像已经是等距的,因此可以像等距瓷砖一样重复和剪裁。

我的问题: 1. 我想知道使用斜边作为宽度和高度是否明智,无论如何还是想办法继续使用 2d 尺寸不是更好吗?

    在离屏画布上创建一个二维矩形,并用非等距纹理图像填充它们,然后变换该画布,这不是更聪明吗?

    如果我使用斜边来表示我的平面和图像的宽度/高度,那么这些长度是真正的长度吗?它就像一个线性函数:它只是像素。每 2 个水平,1 个垂直。一个奇数表示水平,不会给出一个整数表示垂直。 (而且我认为这会给我以后带来不必要的麻烦)。

【问题讨论】:

地图是2D 2.5D 还是3D?你考虑过使用精灵吗?看看***.com/a/36454198/2521214。也不要使用sin, cos,它们可以将舍入误差添加到最终像素输出(保持 x 不变,并将 y 除以 2)。一个好主意是将单元格/平铺/精灵大小限制为可被 2 或 4 整除以避免舍入问题。 对“重复”问题的回答没有回答这个问题,因为它没有按照这个问题的要求给出准确的等距投影(即 1 到 0.5 像素比)。跨度> 【参考方案1】:

要绘制等距平面,请使用变换矩阵设置 x 和 y 轴。

假设 x 轴沿向量 (1,0.5),y 轴沿向量 (-1,0.5),原点仍位于左上角。

这样定义的。

var xAxis = 
    x : 1,
    y : 0.5,

var yAxis = 
    x : -1,
    y : 0.5,

var origin = 
    x : 0,
    y : 0,

要设置转换,只需使用

ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);

现在所有的渲染都在那个等距平面上。

您可以将轴设置为您希望的任何等距投影。我使用的那个会增加一个像素的面积。这一切都取决于 x 和 y 轴的长度和相对方向。

更新

只是出于兴趣。

由于许多等轴测投影倾向于改变每个像素的总面积,因此我包含了一个简单的计算,无论使用何种投影,都会对像素面积进行归一化。 (对于我给出的投影,它恰好是 2 的平方根的 1)

使用xAxisyAxis 计算该投影的像素面积

var area = (xAxis.x * ( xAxis.y + yAxis.y ) + ( xAxis.x + yAxis.x ) * yAxis.y) - (xAxis.y * ( xAxis.x + yAxis.x ) + ( xAxis.y + yAxis.y ) * yAxis.x)

面积平方根上的那个就是所需的比例

var scaleBy = 1 / Math.sqrt(area);

您可以像这样直接将其应用于轴...

xAxis.x *= scaleBy;
xAxis.y *= scaleBy;
yAxis.x *= scaleBy;
yAxis.y *= scaleBy;
ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);

或在设置转换后通过ctx.scale 应用它

ctx.setTransform(xAxis.x, xAxis.y, yAxis.x, yAxis.y, origin.x, origin.y);
ctx.scale(scaleBy, scaleBy);

示例

演示展示了这种方法的简单应用(没有像素归一化)。

    
    var canvas = document.createElement("canvas"); 
    canvas.width          = window.innerWidth;
    canvas.height         = window.innerHeight;
    canvas.style.position = "absolute";
    canvas.style.left     = "0px";
    canvas.style.top      = "0px";
    document.body.appendChild(canvas);
    
function demo()
    canvas.width  = window.innerWidth;
    canvas.height = window.innerHeight;
    var ctx       = canvas.getContext("2d"); 

    ctx.font         = "80px arial black";
    ctx.lineWidth    = 8;
    ctx.lineJoin     = "round"
    ctx.strokeStyle  = "green";
    ctx.fillStyle    = "#aF6";
    ctx.textAlign    = "center";
    ctx.textBaseline = "middle";

    // the axis and origin
    var xAxis  = x : 1, y: 0.5;
    var yAxis  = x : -1, y: 0.5;
    var origin = x : 0, y : 0;

    // cludge factor dividing by two to fit the display area
    ctx.setTransform(xAxis.x / 2, xAxis.y / 2, yAxis.x / 2, yAxis.y / 2, origin.x, origin.y);

    // draw big text
    ctx.strokeText("Isometric",ctx.canvas.width/2,0);
    ctx.fillText("Isometric",ctx.canvas.width/2,0);

    // half size text
    ctx.font      = "40px arial black";
    ctx.lineWidth = "4";
    ctx.strokeText("projection",ctx.canvas.width/2,60);
    ctx.fillText("projection",ctx.canvas.width/2,60);

     // tiny text
    ctx.font      = "16px arial black";
    ctx.lineWidth = "3";
    ctx.strokeText("using 2D context transformation",ctx.canvas.width/2,100);
    ctx.fillText("using 2D context transformation",ctx.canvas.width/2,100);

    // add an image to demonstrate that the project will be applied to 
    // anything that is rendered.
    var image = new Image();
    image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
    image.addEventListener("load",function()
        ctx.drawImage(this,ctx.canvas.width-this.width,-this.height * 1.2);
    );

demo();
window.addEventListener("resize",demo);

【讨论】:

以上是关于如何用图像绘制等距平面? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何用matlab来实现绘制工业摄像机站位的

如何用android studio替换所有屏幕尺寸图像的可绘制图像?

如何用图像替换单选按钮? [复制]

急!!如何用matlab导入asc和stl格式的点云数据,并绘制图像?

即使海报图像的宽高比与其视频元素不同,如何用海报图像填充视频元素? [复制]

聚焦时如何用图像填充 LWUIT 按钮?