如何为Curtains.js平面添加圆角且保留缩放扭曲效果?
Curtains.js平面添加圆角且保留原有交互效果的解决方案
问题背景
需要为Curtains.js实现的平面添加圆角样式,同时保留已有的Y轴缩放、纹理扭曲及滚动视差效果。尝试用Three.js自定义圆角几何体替换原平面后,脚本运行失败,所有原有效果均无法正常工作。
问题原因
Curtains.js的Plane对象内部封装了与自身默认几何体绑定的uniform变量、attribute属性以及渲染逻辑,直接替换为Three.js自定义几何体后,会打破这些内部关联,导致原有缩放、视差等控制逻辑无法作用于新几何体。
可行解决方案
方案一:着色器裁剪实现圆角(推荐,无需修改几何体)
通过片元着色器对平面进行像素级裁剪,不需要改动原有几何体结构,完全兼容现有效果。
- 修改Plane初始化配置:添加控制圆角半径和平面宽高比的uniform变量
// 在创建Plane时添加uniforms const plane = new Plane(curtains, planeElements[i], { vertexShader: vs, fragmentShader: fs, uniforms: { // 圆角半径(相对于平面短边的比例,0-0.5之间) cornerRadius: { name: "uCornerRadius", type: "1f", value: 0.1, // 可根据需求调整 }, // 平面宽高比 planeAspectRatio: { name: "uPlaneAspectRatio", type: "1f", value: planeElements[i].clientWidth / planeElements[i].clientHeight, }, }, });
- 修改片元着色器:添加圆角裁剪逻辑
const fs = ` precision mediump float; varying vec3 vVertexPosition; varying vec2 vTextureCoord; varying vec3 vVertexNormal; uniform sampler2D planeTexture; uniform float uCornerRadius; uniform float uPlaneAspectRatio; void main() { // 将顶点坐标归一化到[-0.5, 0.5]范围 vec2 pos = vVertexPosition.xy; // 适配宽高比,确保圆角在不同比例下保持圆形 pos.x *= uPlaneAspectRatio; // 计算当前像素到四个角的距离 vec2 roundedPos = abs(pos) - vec2(0.5 - uCornerRadius); float dist = length(max(roundedPos, 0.0)) + min(max(roundedPos.x, roundedPos.y), 0.0); // 裁剪圆角外的像素 if(dist > uCornerRadius) { discard; } // 保留原有纹理渲染逻辑 gl_FragColor = texture2D(planeTexture, vTextureCoord); } `;
- 窗口 resize 时更新宽高比:在
handlePlanes的onAfterResize回调中更新宽高比uniform
plane.onAfterResize(() => { applyPlanesParallax(index); // 更新宽高比 plane.uniforms.planeAspectRatio.value = planeElements[index].clientWidth / planeElements[index].clientHeight; });
方案二:适配Curtains.js的自定义圆角几何体
如果必须使用自定义几何体,需要确保几何体的attribute与Curtains.js的预期完全匹配,包括aVertexPosition、aTextureCoord、aVertexNormal,并正确传递给Plane对象。
- 生成符合要求的圆角几何体:补充法线计算,并确保attribute名称正确
function createRoundedPlaneGeometry(width, height, radius, smoothness) { const w = width; const h = height; const r = radius; const s = smoothness; const wi = w / 2 - r; const hi = h / 2 - r; const w2 = w / 2; const h2 = h / 2; const ul = r / w; const ur = (w - r) / w; const vl = r / h; const vh = (h - r) / h; let positions = [ -wi, -h2, 0, wi, -h2, 0, wi, h2, 0, -wi, -h2, 0, wi, h2, 0, -wi, h2, 0, -w2, -hi, 0, -wi, -hi, 0, -wi, hi, 0, -w2, -hi, 0, -wi, hi, 0, -w2, hi, 0, wi, -hi, 0, w2, -hi, 0, w2, hi, 0, wi, -hi, 0, w2, hi, 0, wi, hi, 0 ]; let uvs = [ ul, 0, ur, 0, ur, 1, ul, 0, ur, 1, ul, 1, 0, vl, ul, vl, ul, vh, 0, vl, ul, vh, 0, vh, ur, vl, 1, vl, 1, vh, ur, vl, 1, vh, ur, vh ]; // 生成法线(平面法线都指向Z轴正方向) let normals = []; // 先为矩形部分添加法线 for(let i = 0; i < positions.length / 3; i++) { normals.push(0, 0, 1); } let phia = 0; let phib, xc, yc, uc, vc; for (let i = 0; i < s * 4; i++) { phib = Math.PI * 2 * (i + 1) / (4 * s); xc = i < s || i >= 3 * s ? wi : -wi; yc = i < 2 * s ? hi : -hi; positions.push(xc, yc, 0, xc + r * Math.cos(phia), yc + r * Math.sin(phia), 0, xc + r * Math.cos(phib), yc + r * Math.sin(phib), 0); // 为圆角部分添加法线 normals.push(0, 0, 1); normals.push(0, 0, 1); normals.push(0, 0, 1); uc = i < s || i >= 3 * s ? ur : ul; vc = i < 2 * s ? vh : vl; uvs.push(uc, vc, uc + ul * Math.cos(phia), vc + vl * Math.sin(phia), uc + ul * Math.cos(phib), vc + vl * Math.sin(phib)); phia = phib; } const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3)); geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); geometry.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(normals), 3)); return geometry; }
- 在Plane初始化时传入自定义几何体:
// 在创建Plane循环中 const planeGeometry = createRoundedPlaneGeometry(16, 9, 2, 18); const plane = new Plane(curtains, planeElements[i], { vertexShader: vs, fragmentShader: fs, geometry: planeGeometry, // 传入自定义几何体 });
验证效果
两种方案均能在保留原有Y轴缩放、纹理扭曲及滚动视差效果的前提下,实现平面圆角样式。方案一无需修改几何体,实现成本更低,兼容性更好;方案二更适合需要复杂几何体的场景。
内容的提问来源于stack exchange,提问作者Michel dev
相关产品推荐
相关产品推荐

