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

请求编写适配Qt SceneGraph的PainterPath影线画笔GLSL着色器程序

Qt SceneGraph 影线画笔风格着色器实现方案

我来帮你搞定适配Qt SceneGraph的影线材质着色器,下面是完整的实现代码和说明,完美对接你已经完成三角化的几何节点:

1. 补全BrushMaterial结构体

首先把你未完成的BrushMaterial结构体补全,包含影线的核心配置参数:

struct BrushMaterial {
    ~BrushMaterial() { if (texture) texture->deleteLater(); }

    QColor color;          // 影线颜色
    qreal lineWidth;       // 影线宽度(像素)
    qreal lineSpacing;     // 影线间距(像素)
    qreal angle;           // 影线角度(弧度,比如M_PI/4对应45度)
    QSGTexture *texture = nullptr; // 可选:复杂图案用预生成纹理

    // 必须实现的相等判断,用于Qt SceneGraph的材质缓存
    bool operator==(const BrushMaterial &other) const {
        return color == other.color &&
               qFuzzyCompare(lineWidth, other.lineWidth) &&
               qFuzzyCompare(lineSpacing, other.lineSpacing) &&
               qFuzzyCompare(angle, other.angle) &&
               texture == other.texture;
    }
};

2. 顶点着色器(hatch_vert.glsl)

负责传递顶点位置和计算屏幕空间坐标,为片段着色器的影线计算做准备:

attribute highp vec4 aVertex;
attribute highp vec2 aTexCoord;

uniform highp mat4 qt_Matrix;

varying highp vec2 vScreenPos;

void main() {
    gl_Position = qt_Matrix * aVertex;
    // 计算裁剪空间下的坐标,用于后续影线旋转计算
    vScreenPos = (qt_Matrix * aVertex).xy;
}

3. 片段着色器(hatch_frag.glsl)

这是实现影线效果的核心,通过坐标旋转和距离计算动态生成影线:

uniform lowp vec4 color;
uniform highp float lineWidth;
uniform highp float lineSpacing;
uniform highp float angle;

varying highp vec2 vScreenPos;

void main() {
    // 旋转坐标系统,匹配设置的影线角度
    highp float cosAngle = cos(angle);
    highp float sinAngle = sin(angle);
    highp vec2 rotatedPos = vec2(
        vScreenPos.x * cosAngle - vScreenPos.y * sinAngle,
        vScreenPos.x * sinAngle + vScreenPos.y * cosAngle
    );

    // 计算当前点到最近影线的距离
    highp float distanceToLine = mod(rotatedPos.x, lineSpacing);
    distanceToLine = abs(distanceToLine - lineSpacing / 2.0);

    // 生成影线透明度:在影线宽度内不透明,否则完全透明
    lowp float alpha = step(distanceToLine, lineWidth / 2.0);

    // 输出最终颜色
    gl_FragColor = color * alpha;
}

4. QSGSimpleMaterialShader子类实现

实现Qt SceneGraph要求的着色器子类,负责绑定uniform和管理材质状态:

class BrushMaterialShader : public QSGSimpleMaterialShader<BrushMaterial> {
    QSG_DECLARE_SIMPLE_SHADER(BrushMaterialShader, BrushMaterial)

public:
    BrushMaterialShader() {
        // 加载外部着色器文件,注意修改路径为你的实际资源路径
        setShaderSourceFile(QOpenGLShader::Vertex, ":/shaders/hatch_vert.glsl");
        setShaderSourceFile(QOpenGLShader::Fragment, ":/shaders/hatch_frag.glsl");
    }

    // 声明顶点属性
    const QList<QByteArray> attributes() const override {
        return { "aVertex" };
    }

    // 更新材质状态到着色器
    void updateState(const BrushMaterial *mat, const BrushMaterial *) override {
        program()->setUniformValue("color", mat->color);
        program()->setUniformValue("lineWidth", mat->lineWidth);
        program()->setUniformValue("lineSpacing", mat->lineSpacing);
        program()->setUniformValue("angle", mat->angle);
    }
};

5. PathNode中集成使用示例

在你的PathNode类里,把三角化后的几何和影线材质绑定:

void PathNode::updatePath(const QPainterPath &path) {
    // 你的三角化逻辑(保留你已有的代码)
    QTriangulator triangulator;
    triangulator.setPath(path);
    QTriangulator::Result result = triangulator.triangulate();

    // 创建QSGGeometry并填充顶点数据
    QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), result.vertices.size());
    QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
    for (int i = 0; i < result.vertices.size(); ++i) {
        const QPointF &p = result.vertices[i];
        vertices[i].x = p.x();
        vertices[i].y = p.y();
    }
    geometry->setIndexData(result.indices.constData(), result.indices.size());
    setGeometry(geometry);
    setFlag(OwnsGeometry, true);

    // 创建并配置影线材质
    QSGSimpleMaterial<BrushMaterial> *material = BrushMaterialShader::createMaterial();
    BrushMaterial *mat = material->state();
    mat->color = QColor(30, 30, 30);    // 深灰色影线
    mat->lineWidth = 1.5;              // 1.5像素线宽
    mat->lineSpacing = 8.0;            // 8像素线间距
    mat->angle = M_PI / 3;             // 60度角
    setMaterial(material);
    setFlag(OwnsMaterial, true);
}

关键注意点

  • 这个实现是动态计算影线,不需要预生成纹理,性能足够大部分2D场景使用
  • 如果需要交叉影线、虚线等复杂效果,可以修改片段着色器的距离计算逻辑
  • 若你的节点存在缩放/变换,可将vScreenPos替换为局部坐标,避免影线随缩放变形
  • 务必确保BrushMaterial的operator==正确实现,否则会导致Qt SceneGraph材质缓存失效,引发性能问题

内容的提问来源于stack exchange,提问作者Alexey Korolev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:46:50