You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Curtains.js平面添加圆角且保留缩放扭曲效果?

Curtains.js平面添加圆角且保留原有交互效果的解决方案

问题背景

需要为Curtains.js实现的平面添加圆角样式,同时保留已有的Y轴缩放、纹理扭曲及滚动视差效果。尝试用Three.js自定义圆角几何体替换原平面后,脚本运行失败,所有原有效果均无法正常工作。

问题原因

Curtains.js的Plane对象内部封装了与自身默认几何体绑定的uniform变量、attribute属性以及渲染逻辑,直接替换为Three.js自定义几何体后,会打破这些内部关联,导致原有缩放、视差等控制逻辑无法作用于新几何体。

可行解决方案

方案一:着色器裁剪实现圆角(推荐,无需修改几何体)

通过片元着色器对平面进行像素级裁剪,不需要改动原有几何体结构,完全兼容现有效果。

  1. 修改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,
        },
    },
});
  1. 修改片元着色器:添加圆角裁剪逻辑
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);
    }
`;
  1. 窗口 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对象。

  1. 生成符合要求的圆角几何体:补充法线计算,并确保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;
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 05:23:08