请求编写适配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
相关产品推荐
相关产品推荐

