如何在p5.js中结合UV坐标使用bezierVertex?
在p5.js中为贝塞尔曲线顶点(bezierVertex)添加UV纹理映射
问题背景
开发需要将UV坐标映射到自定义贝塞尔形状的项目时,发现vertex()函数支持传入u、v参数实现纹理映射,但bezierVertex()的官方文档并未提及UV参数。在GitHub相关讨论中看到的多参数写法实际测试不生效,当前代码仅能实现带UV映射的可拖拽矩形,需要替换为贝塞尔曲线并保留UV映射功能。
解决方案
p5.js中bezierVertex()的UV参数需要为每个贝塞尔控制点单独指定u、v值,正确的2D格式如下:
bezierVertex(控制点2x, 控制点2y, 控制点2u, 控制点2v, 控制点3x, 控制点3y, 控制点3u, 控制点3v, 目标点x, 目标点y, 目标点u, 目标点v)
注意事项:
- 必须先调用
vertex()定义形状的起始顶点(需包含UV参数) - 确保已启用
textureMode(NORMAL),UV值保持在0-1的标准区间内 - 若使用3D场景,可在每个坐标参数后添加z值,格式为
x, y, z, u, v
修改后的完整代码
sketch.js
let initVariables = { mP: false, }; const mM = []; class Measurement { constructor(initVariables, _ref) { this.initVariables = initVariables; this.ref = _ref; this.pointActive = false; this.activePoint = createVector(0, 0); } checkPoints() { // 转换WEBGL坐标为屏幕坐标 const screenX = this.ref.x + width/2; const screenY = this.ref.y + height/2; if (dist(mouseX, mouseY, screenX, screenY) < 26) { this.pointActive = true; if (this.initVariables.mP) { this.ref.x = mouseX - width/2; this.ref.y = mouseY - height/2; } } else { this.pointActive = false; } } drawMeasurement() { if (this.pointActive) { noStroke(); fill(0, 20); circle(this.ref.x, this.ref.y, 26); } stroke(255, 0, 0); fill(255, 0, 0); strokeWeight(2); circle(this.ref.x, this.ref.y, 5); noFill(); } } let img; function preload() { img = loadImage('https://t3.ftcdn.net/jpg/02/77/30/98/360_F_277309825_h8RvZkoyBGPDocMtippdfe3497xTrOXO.jpg'); } function setup() { createCanvas(600, 500, WEBGL); // 定义贝塞尔形状的控制点(含UV):起始点+4组贝塞尔控制点 const measurements = [ // 起始点 { x: -150, y: -100, u: 0, v: 0 }, // 第一组贝塞尔控制点(控制点2、控制点3、目标点) { x: -50, y: -150, u: 0.2, v: 0 }, { x: 50, y: -150, u: 0.8, v: 0 }, { x: 150, y: -100, u: 1, v: 0 }, // 第二组贝塞尔控制点 { x: 200, y: 0, u: 1, v: 0.3 }, { x: 200, y: 100, u: 1, v: 0.7 }, { x: 150, y: 150, u: 1, v: 1 }, // 第三组贝塞尔控制点 { x: 50, y: 200, u: 0.8, v: 1 }, { x: -50, y: 200, u: 0.2, v: 1 }, { x: -150, y: 150, u: 0, v: 1 }, // 第四组贝塞尔控制点(闭合形状) { x: -200, y: 100, u: 0, v: 0.7 }, { x: -200, y: 0, u: 0, v: 0.3 }, { x: -150, y: -100, u: 0, v: 0 }, ]; for(let measurement of measurements){ const m = new Measurement(initVariables, measurement); mM.push(m); } } function draw() { background(237, 239, 237); textureMode(NORMAL); texture(img); stroke(0); strokeWeight(2); beginShape(); // 起始顶点 vertex(mM[0].ref.x, mM[0].ref.y, mM[0].ref.u, mM[0].ref.v); // 绘制贝塞尔曲线段,每3个控制点为一组 for(let i = 1; i < mM.length; i += 3){ const ctrl2 = mM[i]; const ctrl3 = mM[i+1]; const target = mM[i+2]; bezierVertex( ctrl2.ref.x, ctrl2.ref.y, ctrl2.ref.u, ctrl2.ref.v, ctrl3.ref.x, ctrl3.ref.y, ctrl3.ref.u, ctrl3.ref.v, target.ref.x, target.ref.y, target.ref.u, target.ref.v ); } endShape(); // 绘制并检测可拖拽控制点 for (let dimension of mM) { dimension.drawMeasurement(); dimension.checkPoints(); } } mousePressed = () => { initVariables.mP = true; }; mouseReleased = () => { initVariables.mP = false; };
style.css
html, body { margin: 0; padding: 0; } canvas { display: block; }
index.html
<!DOCTYPE html> <html lang="en"> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.js"></script> <link rel="stylesheet" type="text/css" href="style.css"> <meta charset="utf-8" /> </head> <body> <main></main> <script src="sketch.js"></script> </body> </html>
代码说明
- 重构了控制点数据结构,将贝塞尔曲线的每组控制点(控制点2、控制点3、目标点)与对应的UV值绑定
- 在
draw()函数中,先通过vertex()定义起始点,再循环调用bezierVertex()绘制完整的贝塞尔形状,每个控制点都传入对应的u、v参数 - 优化了坐标转换逻辑,统一使用
width/2和height/2计算屏幕坐标,提升代码通用性
内容的提问来源于stack exchange,提问作者mrconcerned
相关产品推荐
相关产品推荐

