我正在使用glfx.js库,以便使用矩阵变换为我的图像创建透视效果。在我的应用程序中,系统的工作方式就像photoshop的智能对象(渲染平面图像并在渲染后获得透视效果)
glfx.js使用此函数canvas.perspective(before, after)
将矩阵变换应用于图像,方法是在协调图像中的4个点之前和之后进行分配,然后在后台运行Matrix命令以转换图像。 / p>
我的问题是,如果应用转换后我想要的结果图像比原始图像大(如果旋转图像就会发生),那么WebGL画布将裁剪我的图像。
请看以下小提琴:
https://jsfiddle.net/human_a/o4yrheeq/
window.onload = function() {
try {
var canvas = fx.canvas();
} catch (e) {
alert(e);
return;
}
// convert the image to a texture
var image = document.getElementById('image');
var texture = canvas.texture(image);
// apply the perspective filter
canvas.draw(texture).perspective( [0,0,774,0,0,1094,774,1094], [0,389,537,0,732,1034,1269,557] ).update();
image.src = canvas.toDataURL('image/png');
// or even if you replace the image with the canvas
// image.parentNode.insertBefore(canvas, image);
// image.parentNode.removeChild(image);
};
<script src="https://evanw.github.io/glfx.js/glfx.js"></script>
<img id="image" crossOrigin="anonymous" src="https://images.unsplash.com/photo-1485207801406-48c5ac7286b2?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=600&fit=max&s=9bb1a18da78ab0980d5e7870a236af88">
有关如何使WebGL画布适合旋转图像(不使图像变小)或以某种方式提取整个图像而不是裁剪的图像的任何想法?
答案 0 :(得分:2)
没有涵盖所有解决方案。这是因为当您从2D转换为3D时,投影图像的大小可能会无限大(近剪裁会阻止无穷大),因此无论您使用多大的图像输出,都始终可能会应用某些裁剪。
有了这个警告,大多数情况都可以避免削波。它非常简单,只需展开画布即可保存其他内容。
为了简化计算,我将after数组更改为一组标准化点(它们表示后跟数作为图像大小的比例因子)。然后我使用图像大小转换为实际像素坐标。然后从我锻炼的最小尺寸纹理需要保持原始图像和投影。
使用该信息我只需创建纹理(作为画布)绘制图像。如果需要,调整befor数组(如果某些投影点在负空间中)并应用过滤器。
所以我们有一个宽度和高度的图像对象。你有这些观点的预测。
// assuming image has been loaded and is ready
var imgW = image.naturalWidth;
var imgH = image.naturalHeight;
设置角落阵列(之前)
var before = [0, 0, imgW, 0, 0, imgH, imgW, imgH];
投射点。为了更容易处理,我已将投影点标准化为图像大小
var projectNorm = [[0, 0.3556], [0.6938, 0], [0.9457, 0.9452], [1.6395, 0.5091]];
如果要使用绝对坐标,如在小提琴后面的数组,请使用以下代码。之后的片段中的规范化相反,因此您可以跳过规范化。由于时间不够,我刚刚更新了答案。
var afterArray = [0,389,537,0,732,1034,1269,557];
projectNorm = [];
for(var i = 0; i < afterArray.length; i+= 2){
afterArray.push([afterArray[i] / before[i], afterArray[i + 1] / before[i + 1]]);
}
现在计算投影的大小。这是重要的部分,因为它可以计算出画布的大小。
var top, left, right, bottom;
top = 0;
left = 0;
bottom = imgH;
right = imgW;
var project = projectNorm.map(p => [p[0] * imgW, p[1] * imgH]);
project.forEach(p => {
top = Math.min(p[1], top);
left = Math.min(p[0], left);
bottom = Math.max(p[1], bottom);
right = Math.max(p[0], right);
});
既然已经收集了我们需要的所有数据,我们可以创建一个可以容纳投影的新图像。 (假设投影点对投影是真的)
var texture = document.createElement("canvas");
var ctx = texture.getContext("2d");
texture.width = Math.ceil(right - left);
texture.height = Math.ceil(bottom - top);
在0,0
处绘制图像ctx.setTransform(1, 0, 0, 1, left, top); // put origin so image is at 0,0
ctx.drawImage(image,0,0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
然后展平投影点阵列
var after = [];
project.forEach(p => after.push(...p));
将所有点移动到正投影空间
after.forEach((p,i) => {
if (i % 2) {
before[i] += -top;
after[i] += -top;
} else {
before[i] += -left;
after[i] += -left;
}
});
最后一步是创建glfx.js对象并应用过滤器
// create a fx canvas
var canvas = fx.canvas();
// create the texture
var glfxTexture = canvas.texture(texture);
// apply the filter
canvas.draw(glfxTexture).perspective( before, after ).update();
// show the result on the page
document.body.appendChild(canvas);
使用上述方法演示您的代码段(对图像加载稍作修改)
// To save time typing I have just kludged a simple load image wait poll
waitForLoaded();
function waitForLoaded(){
if(image.complete){
projectImage(image);
}else{
setTimeout(waitForLoaded,500);
}
}
function projectImage(image){
var imgW = image.naturalWidth;
var imgH = image.naturalHeight;
var projectNorm = [[0, 0.3556], [0.6938, 0], [0.9457, 0.9452], [1.6395, 0.5091]];
var before = [0, 0, imgW, 0, 0, imgH, imgW, imgH];
var top, left, right, bottom;
top = 0;
left = 0;
bottom = imgH;
right = imgW;
var project = projectNorm.map(p => [p[0] * imgW, p[1] * imgH]);
project.forEach(p => {
top = Math.min(p[1], top);
left = Math.min(p[0], left);
bottom = Math.max(p[1], bottom);
right = Math.max(p[0], right);
});
var texture = document.createElement("canvas");
var ctx = texture.getContext("2d");
texture.width = Math.ceil(right - left);
texture.height = Math.ceil(bottom - top);
ctx.setTransform(1, 0, 0, 1, left, top); // put origin so image is at 0,0
ctx.drawImage(image,0,0);
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
var after = [];
project.forEach(p => after.push(...p));
after.forEach((p,i) => {
if (i % 2) {
before[i] += -top;
after[i] += -top;
} else {
before[i] += -left;
after[i] += -left;
}
});
// create a fx canvas
var canvas = fx.canvas();
// create the texture
var glfxTexture = canvas.texture(texture);
// apply the filter
canvas.draw(glfxTexture).perspective( before, after ).update();
// show the result on the page
document.body.appendChild(canvas);
}
#image {
display : none;
}
<script src="https://evanw.github.io/glfx.js/glfx.js"></script>
<img id="image" crossOrigin="anonymous" src="https://images.unsplash.com/photo-1485207801406-48c5ac7286b2?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&s=9bb1a18da78ab0980d5e7870a236af88">
注意投影点(在数组之后)并不总是与投影图像的最终角点匹配。如果发生这种情况,最终图像可能会被剪裁。
注意此方法仅在前点表示原始图像的外角时才有效。如果点(之前)在图像内,则此方法可能会失败。
警告无法审核生成的图像尺寸。大图像可能会导致浏览器变得迟缓,有时会崩溃。对于生产代码,您应该尽力将图像大小保持在使用代码的设备的限制范围内。客户很少返回缓慢和/或崩溃的页面。