Javascript 使用 HTML5 Canvas 进行图像处理和纹理映射?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4774172/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:01:37  来源:igfitidea点击:

Image manipulation and texture mapping using HTML5 Canvas?

javascripthtml3dtexture-mappinghtml5-canvas

提问by pimvdb

In a 3D engine I'm working on I've succesfully managed to draw a cube in 3D. The only method to fill the sides is using either a solid color or gradient as far as I'm concerned. To make things more exciting, I'd really love to implement texture mapping using a simple bitmap.

在我正在使用的 3D 引擎中,我成功地绘制了一个 3D 立方体。就我而言,填充侧面的唯一方法是使用纯色或渐变色。为了让事情更令人兴奋,我真的很喜欢使用简单的位图来实现纹理映射。

The point is that I can hardly find any articles or code samples on the subject of image manipulation in JavaScript. Moreover, image support in HTML5 canvas seems to be restricted to cropping.

关键是我几乎找不到任何关于 JavaScript 图像处理主题的文章或代码示例。此外,HTML5 画布中的图像支持似乎仅限于裁剪。

How could I go about stretching a bitmap so that a rectangular bitmap can fill up a unregular cube face? In 2D, a projected square cube face is, due to perspective, not of a square shape, so I'll have to stretch it to make it fit in any quadrilateral.

我怎样才能拉伸位图,以便矩形位图可以填充不规则的立方体面?在 2D 中,由于透视的原因,投影的方形立方体面不是方形,因此我必须拉伸它以使其适合任何四边形。

Hopefully this image clarifies my point. The left face is now filled up with a white/black gradient. How could I fill it with a bitmap, after it has been texture-mapped?

希望这张图片可以澄清我的观点。左面现在填充了白色/黑色渐变。在纹理映射之后,如何用位图填充它?

Cube

立方体

Does anyone have any tips on perspective texture mapping (or image manipulation at all) using JavaScript and HTML5 Canvas?

有没有人对使用 JavaScript 和 HTML5 Canvas 的透视纹理映射(或根本图像处理)有任何提示?

Edit:I got it working, thanks to 6502!

编辑:我让它工作了,感谢 6502!

It is, however, rather CPU intensive so I'd love to hear any optimization ideas.

然而,它是 CPU 密集型的,所以我很想听听任何优化的想法。

Result using 6502's technique- Texture image used

结果用6502的技术-使用的纹理图像

回答by 6502

I think you will never get an accurate result... I spent some time investigating how to do 3d graphics using canvas 2d context and I found it viable to do texture mapping gouraud shading by computing appropriate 2d gradients and matrices:

我认为你永远不会得到准确的结果......我花了一些时间研究如何使用画布 2d 上下文制作 3d 图形,我发现通过计算适当的 2d 梯度和矩阵来进行纹理映射 gouraud 着色是可行的:

  • Solid polygons are of course easy
  • Gouraud filling is possible only on one component (i.e. you cannot have a triangle where every vertex is an arbitrary RGB filled with bilinear interpolation, but you can do that filling using for example three arbitrary shades of a single color)
  • Linear texture mapping can be done using clipping and image drawing
  • 实心多边形当然很容易
  • Gouraud 填充只能在一个组件上进行(即你不能有一个三角形,其中每个顶点都是一个用双线性插值填充的任意 RGB,但你可以使用例如单一颜色的三个任意阴影进行填充)
  • 可以使用裁剪和图像绘制来完成线性纹理映射

I would implement perspective-correct texture mapping using mesh subdivision (like on PS1).

我将使用网格细分(如在 PS1 上)实现透视正确的纹理映射。

However I found many problems... for example image drawing with a matrix transform (needed for texture mapping) is quite inaccurate on chrome and IMO it's impossible to get a pixel-accurate result; in general there is no way to turn off antialiasing when drawing on a canvas and this means you will get visible see-through lines when subdividing in triangles. I also found multipass rendering working really bad on chrome (probably because of how hw-accellerated rendering is implemented).

但是我发现了很多问题......例如,使用矩阵变换(纹理映射所需)的图像绘制在 chrome 和 IMO 上非常不准确,不可能获得像素准确的结果;一般来说,在画布上绘图时无法关闭抗锯齿,这意味着在细分三角形时您将获得可见的透明线条。我还发现多通道渲染在 chrome 上的工作非常糟糕(可能是因为硬件加速渲染的实现方式)。

In general this kind of rendering is surely a stress for web browsers and apparently these use cases (strange matrices for example) are not tested very well. I was even able to get Firefox crashing so bad that it took down the whole X susbsystem on my Ubuntu.

一般来说,这种渲染肯定会给 Web 浏览器带来压力,而且显然这些用例(例如奇怪的矩阵)没有经过很好的测试。我什至让 Firefox 崩溃得如此严重,以至于它在我的 Ubuntu 上关闭了整个 X 子系统。

You can see the results of my efforts hereor as a video here... IMO is surely impressing that this can be done in a browser without using 3D extensions, but I don't think current problems will be fixed in the future.

你可以看到我的努力的结果,在这里或视频在这里...... IMO无疑是令人印象深刻,这可以在浏览器中完成,无需使用3D扩展,但我不认为目前的问题将被固定在未来。

Anyway the basic idea used to draw an image so that the 4 corners ends up in specific pixels position is to draw two triangles, each of which will use bilinear interpolation.

无论如何,用于绘制图像以使 4 个角最终位于特定像素位置的基本思想是绘制两个三角形,每个三角形都将使用双线性插值。

In the following code I assume you have a picture object textureand 4 corners each of which is an object with fields x,y,u,vwhere x,yare pixel coordinates on the target canvas and u,vare pixel coordinates on texture:

在下面的代码中,我假设您有一个图片对象texture和 4 个角,每个角都是一个对象,x,y,u,v其中x,y的字段是目标画布u,v上的像素坐标,并且是 上的像素坐标texture

function textureMap(ctx, texture, pts) {
    var tris = [[0, 1, 2], [2, 3, 0]]; // Split in two triangles
    for (var t=0; t<2; t++) {
        var pp = tris[t];
        var x0 = pts[pp[0]].x, x1 = pts[pp[1]].x, x2 = pts[pp[2]].x;
        var y0 = pts[pp[0]].y, y1 = pts[pp[1]].y, y2 = pts[pp[2]].y;
        var u0 = pts[pp[0]].u, u1 = pts[pp[1]].u, u2 = pts[pp[2]].u;
        var v0 = pts[pp[0]].v, v1 = pts[pp[1]].v, v2 = pts[pp[2]].v;

        // Set clipping area so that only pixels inside the triangle will
        // be affected by the image drawing operation
        ctx.save(); ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x1, y1);
        ctx.lineTo(x2, y2); ctx.closePath(); ctx.clip();

        // Compute matrix transform
        var delta = u0*v1 + v0*u2 + u1*v2 - v1*u2 - v0*u1 - u0*v2;
        var delta_a = x0*v1 + v0*x2 + x1*v2 - v1*x2 - v0*x1 - x0*v2;
        var delta_b = u0*x1 + x0*u2 + u1*x2 - x1*u2 - x0*u1 - u0*x2;
        var delta_c = u0*v1*x2 + v0*x1*u2 + x0*u1*v2 - x0*v1*u2
                      - v0*u1*x2 - u0*x1*v2;
        var delta_d = y0*v1 + v0*y2 + y1*v2 - v1*y2 - v0*y1 - y0*v2;
        var delta_e = u0*y1 + y0*u2 + u1*y2 - y1*u2 - y0*u1 - u0*y2;
        var delta_f = u0*v1*y2 + v0*y1*u2 + y0*u1*v2 - y0*v1*u2
                      - v0*u1*y2 - u0*y1*v2;

        // Draw the transformed image
        ctx.transform(delta_a/delta, delta_d/delta,
                      delta_b/delta, delta_e/delta,
                      delta_c/delta, delta_f/delta);
        ctx.drawImage(texture, 0, 0);
        ctx.restore();
    }
}

Those ugly strange formulas for all those "delta" variables are used to solve two linear systems of three equations in three unknowns using Cramer'smethod and Sarrusscheme for 3x3 determinants.

所有那些“delta”变量的那些丑陋的奇怪公式用于使用Cramer方法和3x3 行列式的Sarrus方案来求解三个未知数中三个方程的两个线性系统。

More specifically we are looking for the values of a, b, ... fso that the following equations are satisfied

更具体地说,我们正在寻找a, b, ...的值,f以便满足以下等式

a*u0 + b*v0 + c = x0
a*u1 + b*v1 + c = x1
a*u2 + b*v2 + c = x2

d*u0 + e*v0 + f = y0
d*u1 + e*v1 + f = y1
d*u2 + e*v2 + f = y2

deltais the determinant of the matrix

delta是矩阵的行列式

u0  v0  1
u1  v1  1
u2  v2  1

and for example delta_ais the determinant of the same matrix when you replace the first column with x0, x1, x2. With these you can compute a = delta_a / delta.

例如delta_a,当您用x0, x1,替换第一列时,相同矩阵的行列式x2。有了这些你可以计算a = delta_a / delta