基于p5.js的3D画布网格对齐问题及网格图片添加需求
透视网格对齐与网格内2D图片实现方案
一、修复Y轴透视网格对齐问题
透视下的网格横线不能用均分间距,近大远小的视觉效果要求横线间距从远到近逐步递增。结合你已有的四个顶点坐标,可通过非线性插值调整Y轴分割点的位置:
核心思路
放弃均分的t值(即t = i / 行数),改用增长速率先慢后快的函数生成t,比如平方根函数Math.sqrt(i / 行数),以此模拟透视效果。如果匹配度不够,可调整指数(如Math.pow(i / 行数, 0.6)或Math.pow(i / 行数, 1.5)),直到横线与背景图完全对齐。
代码示例(React + p5.js)
// 假设你已定义四个顶点坐标 const topLeft = createVector(100, 50); const topRight = createVector(700, 50); const bottomLeft = createVector(50, 550); const bottomRight = createVector(750, 550); const numRows = 14; // Y轴14格 const numCols = 12; // X轴12格 function draw() { background(0); // 先绘制背景图(你已实现的部分) // image(backgroundImg, 0, 0, width, height); // 绘制透视横线 for (let i = 0; i <= numRows; i++) { // 用平方根函数生成透视化的t值,可按需调整指数 const t = Math.sqrt(i / numRows); // 计算当前横线的左右端点 const leftX = lerp(topLeft.x, bottomLeft.x, t); const leftY = lerp(topLeft.y, bottomLeft.y, t); const rightX = lerp(topRight.x, bottomRight.x, t); const rightY = lerp(topRight.y, bottomRight.y, t); // 绘制横线 line(leftX, leftY, rightX, rightY); } // 保留你已对齐的竖线绘制逻辑 for (let j = 0; j <= numCols; j++) { const t = j / numCols; const topX = lerp(topLeft.x, topRight.x, t); const topY = lerp(topLeft.y, topRight.y, t); const bottomX = lerp(bottomLeft.x, bottomRight.x, t); const bottomY = lerp(bottomLeft.y, bottomRight.y, t); line(topX, topY, bottomX, bottomY); } }
二、在网格框中添加2D图片
完全可以实现,利用p5.js的texture()方法将图片映射到透视网格框的四边形上,步骤如下:
1. 预加载图片
确保图片在绘制前加载完成,可通过p5的preload函数或React状态管理加载状态:
let gridImg; // 单张示例图片,多图可改用数组存储 function preload() { gridImg = loadImage('path/to/your/image.png'); }
2. 绘制带图片的网格框
在draw函数中遍历每个网格框,计算其四个顶点坐标后,用纹理映射绘制图片:
function draw() { // ... 先绘制背景图和网格线 ... // 遍历所有网格框 for (let i = 0; i < numRows; i++) { for (let j = 0; j < numCols; j++) { // 计算当前网格上下两行的透视t值 const tTop = Math.sqrt(i / numRows); const tBottom = Math.sqrt((i + 1) / numRows); // 计算当前网格左右两列的插值s值 const sLeft = j / numCols; const sRight = (j + 1) / numCols; // 计算网格框的四个顶点坐标 const tl = createVector( lerp(lerp(topLeft.x, topRight.x, sLeft), lerp(bottomLeft.x, bottomRight.x, sLeft), tTop), lerp(lerp(topLeft.y, topRight.y, sLeft), lerp(bottomLeft.y, bottomRight.y, sLeft), tTop) ); const tr = createVector( lerp(lerp(topLeft.x, topRight.x, sRight), lerp(bottomLeft.x, bottomRight.x, sRight), tTop), lerp(lerp(topLeft.y, topRight.y, sRight), lerp(bottomLeft.y, bottomRight.y, sRight), tTop) ); const br = createVector( lerp(lerp(topLeft.x, topRight.x, sRight), lerp(bottomLeft.x, bottomRight.x, sRight), tBottom), lerp(lerp(topLeft.y, topRight.y, sRight), lerp(bottomLeft.y, bottomRight.y, sRight), tBottom) ); const bl = createVector( lerp(lerp(topLeft.x, topRight.x, sLeft), lerp(bottomLeft.x, bottomRight.x, sLeft), tBottom), lerp(lerp(topLeft.y, topRight.y, sLeft), lerp(bottomLeft.y, bottomRight.y, sLeft), tBottom) ); // 绘制带纹理的网格框 push(); beginShape(); texture(gridImg); // 对应图片的UV坐标(0,0为左上角,1,1为右下角) vertex(tl.x, tl.y, 0, 0); vertex(tr.x, tr.y, 0, 1); vertex(br.x, br.y, 0, 1); vertex(bl.x, bl.y, 0, 0); endShape(CLOSE); pop(); } } }
说明
- 如果每个网格需要不同图片,可将图片存入数组,通过
i和j索引获取对应图片。 - 若图片拉伸效果不符合预期,可调整UV坐标(比如
vertex(tl.x, tl.y, 0, 0)改为vertex(tl.x, tl.y, 0.2, 0.2))实现局部裁剪。
内容的提问来源于stack exchange,提问作者mr.asahd
相关产品推荐
相关产品推荐

