From 3fe7bab96904d0904c10f366ab29384c17c64dcd Mon Sep 17 00:00:00 2001 From: wuyize Date: Wed, 15 Feb 2023 15:37:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E7=BA=BF=E7=9A=84=E5=B9=B3?= =?UTF-8?q?=E5=A4=B4=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../res/Shaders/element.comp | 109 ++++++++++---- .../src/Editor/util/PainterPathUtil.cpp | 14 +- .../src/Editor/util/PainterPathUtil.h | 4 +- .../Renderer/Painting/MaterialStyleStroke.h | 2 +- .../src/Renderer/Preview/ElementRenderer.cpp | 22 ++- README.md | 14 +- UnitTest/ElementRendererTest.cpp | 140 ++++++++++++++++-- UnitTest/ElementRendererTest.h | 8 +- UnitTest/UnitTest.vcxproj | 2 +- 9 files changed, 256 insertions(+), 59 deletions(-) diff --git a/ArchitectureColoredPainting/res/Shaders/element.comp b/ArchitectureColoredPainting/res/Shaders/element.comp index 72779c8..36f4220 100644 --- a/ArchitectureColoredPainting/res/Shaders/element.comp +++ b/ArchitectureColoredPainting/res/Shaders/element.comp @@ -896,39 +896,39 @@ bool angleLargeThanPi(vec2 a, vec2 b) void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness) { elementColor = vec4(1); - metallicRoughness = vec2(0.8); - uint headUint = floatBitsToUint(style[styleIndex+1]); + metallicRoughness = vec2(0.8); + uint headUint = floatBitsToUint(style[styleIndex + 1]); vec4 head = unpackUnorm4x8(headUint); - switch (int(head.a*100)%10) - //switch (2) + switch (int(head.a * 100) % 10) + // switch (2) { /// Plain case 0: { metallicRoughness = head.rg; - elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+2])).rgb, 1); + elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 2])).rgb, 1); break; } /// RadialGradient case 1: { - uint size = headUint%(1<<15); - bool gradual = (headUint&(1<<15))!=0; + uint size = headUint % (1 << 15); + bool gradual = (headUint & (1 << 15)) != 0; - uint lastData = floatBitsToUint(style[styleIndex+2+0*2]); + uint lastData = floatBitsToUint(style[styleIndex + 2 + 0 * 2]); float lastLevel = 0; vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg; - vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+3+0*2])).rgb, 1); + vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 3 + 0 * 2])).rgb, 1); - for(uint i = 0; i < size; i++) + for (uint i = 0; i < size; i++) { - uint data = floatBitsToUint(style[styleIndex+2+i*2]); + uint data = floatBitsToUint(style[styleIndex + 2 + i * 2]); float level = unpackUnorm2x16(data).y; - vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+3+i*2])).rgb, 1); + vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 3 + i * 2])).rgb, 1); vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg; if (d <= level) { - if(gradual) + if (gradual) { - float a = (d-lastLevel)/(level-lastLevel); + float a = (d - lastLevel) / (level - lastLevel); elementColor = mix(lastColor, currentColor, a); metallicRoughness = mix(lastMetallicRoughness, currentMetallicRoughness, a); } @@ -969,6 +969,35 @@ void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 } } +bool shouldFillBeginCap(vec2 localUV, bool onVeryBegin, int endType, vec2 p3, vec2 tangentBegin, vec2 tangentEndLast) +{ + vec2 normal; + if (onVeryBegin) + { + if (endType == 0) + return true; + else if (endType == 1) + normal = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); + } + else + { + vec2 normalLast = normalize(mat2(0, 1, -1, 0) * tangentEndLast); + vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (-tangentBegin)); + normal = normalLast + normalNow; + } + return angleLargeThanPi(normal, localUV - p3); +} + +bool shouldFillEndCap(vec2 localUV, int endType, vec2 p0, vec2 tangentEnd) +{ + vec2 normal; + if (endType == 0) + return true; + else if (endType == 1) + normal = normalize(mat2(0, 1, -1, 0) * tangentEnd); + return angleLargeThanPi(localUV - p0, normal); +} + void main() { uvec2 pixelLocation = gl_GlobalInvocationID.xy; @@ -1022,19 +1051,30 @@ void main() else // Stroke { float minDistance = 1e38; - vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex+1])); - float lineType = floor(styleHead.b*10); - //float lineType = 2; + vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])); + float lineType = floor(styleHead.b * 10); + // float lineType = 2; + int endType = int(round(styleHead.b * 100)) % 10; + //endType = 1; int debugBegin = 0; + bool onVeryBegin = false; + vec2 tangentEndLast; for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++) + // for (uint pathIndex = 0; pathIndex < 4; pathIndex++) { vec2 pTemp = path[pathIndex]; if (isinf(pTemp.x)) { // TODO: ¼ì²âÊÇ·ñ·â±Õ²¢´¦Àí + if (hitElement && distance(localUV, p3Last) <= strokeWidth) + { + hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); + } pBegin = path[++pathIndex]; p3Last = pBegin; + p2Last = pBegin; + onVeryBegin = true; continue; } mat4x2 p = mat4x2(p3Last, pTemp, path[++pathIndex], path[++pathIndex]); @@ -1043,22 +1083,24 @@ void main() if (d <= strokeWidth) { bool onBegin = distance(localUV, p[0]) <= strokeWidth && p3Last == p[0]; - bool fill = true; - if (onBegin) - { - vec2 normalLast = normalize(mat2(0, 1, -1, 0) * (p3Last - p2Last)); - vec2 normalNow = normalize(mat2(0, 1, -1, 0) * (p[1] - p[0])); - vec2 normal = normalLast + normalNow; - fill = angleLargeThanPi(normal, localUV - p[0]); - } - if (onBegin ? fill : d < minDistance) + vec2 tangentBegin; + vec2 tangentEnd; + if (p[0] != p[1]) + tangentBegin = normalize(p[0] - p[1]); + else + tangentBegin = normalize(p[0] - p[2]); + if (p[3] != p[2]) + tangentEnd = normalize(p[3] - p[2]); + else + tangentEnd = normalize(p[3] - p[1]); + + if (onBegin ? shouldFillBeginCap(localUV, onVeryBegin, endType, p[0], tangentBegin, p3Last - p2Last) + : d < minDistance) { minDistance = min(minDistance, d); bool reverse = p[3].y - p[0].y < 0.; - vec2 tangentBegin = normalize(p[0] - p[1]); - vec2 tangentEnd = normalize(p[3] - p[2]); if (tangentBegin.y == 0.) tangentBegin.y = reverse ? eps : -eps; if (tangentEnd.y == 0.) @@ -1066,19 +1108,26 @@ void main() int intTest = cubic_bezier_int_test2(localUV, p[0], p[1], p[2], p[3], reverse) + ray_int_test(localUV, p[0], tangentBegin, reverse) + ray_int_test(localUV, p[3], tangentEnd, reverse); - if (lineType == 2 || intTest % 2 == int(lineType)) + + if (lineType == 2 || (intTest % 2 == int(lineType))) { hitElement = true; - //elementColor = vec4(1, 1, 0, 1); + // elementColor = vec4(1, 1, 0, 1); vec2 metallicRoughness; drawLine(minDistance / strokeWidth, styleIndex, elementColor, metallicRoughness); } else if (p3Last == p[0]) hitElement = false; } + tangentEndLast = tangentEnd; } p3Last = p[3]; p2Last = p[2]; + onVeryBegin = false; + } + if (hitElement && distance(localUV, p3Last) <= strokeWidth) + { + hitElement = shouldFillEndCap(localUV, endType, p3Last, tangentEndLast); } } if (hitElement) diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp index 4569c12..8512b37 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.cpp @@ -7,7 +7,7 @@ using Renderer::Line; using std::vector; using std::shared_ptr; -vector > PainterPathUtil::transformToLines(QPainterPath& painterPath) { +vector> PainterPathUtil::transformToLines(QPainterPath& painterPath) { vector line; line.clear(); vector > lines; lines.clear(); QPointF startPoint(0, 0); @@ -48,16 +48,16 @@ QPainterPath PainterPathUtil::monotonization(QPainterPath& painterPath) { vector > lines = transformToLines(painterPath); vector> linePtrVector; Renderer::LineTree::monotonization(lines, linePtrVector); - for (auto linePtr : linePtrVector) { + Point lastPoint(std::numeric_limits::infinity(), std::numeric_limits::infinity()); + for (auto& linePtr : linePtrVector) { vector line = linePtr->toPointVector(); - if (line.size() == 2) { + if (line[0] != lastPoint) resPath.moveTo(line[0]); + if (line.size() == 2) resPath.lineTo(line[1]); - } - if (line.size() == 4) { - resPath.moveTo(line[0]); + else if (line.size() == 4) resPath.cubicTo(line[1], line[2], line[3]); - } + lastPoint = line.back(); } return resPath; } diff --git a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h index 0e045c4..d8e5da9 100644 --- a/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h +++ b/ArchitectureColoredPainting/src/Editor/util/PainterPathUtil.h @@ -3,12 +3,10 @@ #include #include "../Renderer/Painting/Line.h" -using Renderer::Point; - class PainterPathUtil { public: - static std::vector > transformToLines(QPainterPath& painterPath); + static std::vector > transformToLines(QPainterPath& painterPath); static QPainterPath monotonization(QPainterPath& painterPath); }; diff --git a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h index 20c2eeb..120fa3d 100644 --- a/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h +++ b/ArchitectureColoredPainting/src/Renderer/Painting/MaterialStyleStroke.h @@ -38,7 +38,7 @@ namespace Renderer }; enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 }; - enum class StrokeEndType { kRound = 0 }; + enum class StrokeEndType { kRound = 0, kFlat = 1 }; class MaterialStyleStroke : public MaterialStyle { diff --git a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp index ebb6ba0..4405bd7 100644 --- a/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp +++ b/ArchitectureColoredPainting/src/Renderer/Preview/ElementRenderer.cpp @@ -37,11 +37,15 @@ std::vector generatePathBuffer(const QPainterPath& path) switch (element.type) { case QPainterPath::MoveToElement: + qDebug() << "MoveToElement"; + qDebug() << element; pathBuffer.push_back(glm::vec2(std::numeric_limits::infinity())); pathBuffer.push_back(glm::vec2(element.x, element.y)); break; case QPainterPath::LineToElement: { + qDebug() << "LineToElement"; + qDebug() << element; glm::vec2 end = glm::vec2(element.x, element.y); glm::vec2 mid = (pathBuffer.back() + end) / 2.f; pathBuffer.push_back(mid); @@ -50,8 +54,24 @@ std::vector generatePathBuffer(const QPainterPath& path) break; } case QPainterPath::CurveToElement: - pathBuffer.push_back(glm::vec2(element.x, element.y)); + { + qDebug() << "CurveToElement"; + qDebug() << element; + glm::vec2 p1 = glm::vec2(element.x, element.y); + element = path.elementAt(++i); + qDebug() << element; + glm::vec2 p2 = glm::vec2(element.x, element.y); + element = path.elementAt(++i); + qDebug() << element; + glm::vec2 p3 = glm::vec2(element.x, element.y); + if (p3 != pathBuffer.back()) + { + pathBuffer.push_back(p1); + pathBuffer.push_back(p2); + pathBuffer.push_back(p3); + } break; + } case QPainterPath::CurveToDataElement: pathBuffer.push_back(glm::vec2(element.x, element.y)); break; diff --git a/README.md b/README.md index b5ef6ff..b578f67 100644 --- a/README.md +++ b/README.md @@ -23,5 +23,17 @@ 已实现由图元数æ®å»ºç«‹å®Œæ•´å½©ç»˜ç¼–ç  -已实现é¢çš„纯色样å¼ï¼Œçº¿çš„纯色ã€å¾„å‘æ¸å˜/分层ã€å•ä¾§ç­‰æ ·å¼ +#### 已实现的图元样å¼ï¼ˆMaterialStyle) + +##### é¢ï¼ˆMaterialStyleFill) + +纯色 + +##### 线(MaterialStyleStroke) + +åŒä¾§/左侧/å³ä¾§ + +圆头/平头 + +纯色/æ¸å˜/分层 diff --git a/UnitTest/ElementRendererTest.cpp b/UnitTest/ElementRendererTest.cpp index 6688750..f9c9538 100644 --- a/UnitTest/ElementRendererTest.cpp +++ b/UnitTest/ElementRendererTest.cpp @@ -9,7 +9,116 @@ using namespace Renderer; namespace UnitTest { - TEST_CLASS(ElementRendererTest) + TEST_CLASS(ElementRendererStokeTypeTest) + { + public: + TEST_METHOD(TestBothSidesRound) + { + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + char* argv[] = { (char*)"" }; + int argc = 1; + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style); + w.show(); + a.exec(); + } + TEST_METHOD(TestBothSidesFlat) + { + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + char* argv[] = { (char*)"" }; + int argc = 1; + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kFlat, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style); + w.show(); + a.exec(); + } + TEST_METHOD(TestLeftSideRound) + { + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + char* argv[] = { (char*)"" }; + int argc = 1; + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(60, StrokeType::kLeftSide, StrokeEndType::kRound, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style); + w.show(); + a.exec(); + } + TEST_METHOD(TestLeftSideFlat) + { + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); + char* argv[] = { (char*)"" }; + int argc = 1; + QApplication a(argc, argv); + class StyleStrokeRadialGradient : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} + }; + return { BaseStyle(std::make_shared(), + std::make_shared(60, StrokeType::kLeftSide, StrokeEndType::kFlat, + std::make_shared(materialMap, false))) }; + } + } style; + TestGLWidget w(style); + w.show(); + a.exec(); + } + }; + + TEST_CLASS(ElementRendererStokeMaterialTest) { public: TEST_METHOD(TestStrokePlain) @@ -20,7 +129,15 @@ namespace UnitTest char* argv[] = { (char*)"" }; int argc = 1; QApplication a(argc, argv); - Renderer::ElementStyleStrokeDemo style(2); + class StyleStrokePlain : public Renderer::ElementStyle + { + virtual std::vector toBaseStyles() const override + { + return { BaseStyle(std::make_shared(), + std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, + std::make_shared(QColor(255,255,255),1,1))) }; + } + } style; TestGLWidget w(style); w.show(); a.exec(); @@ -37,14 +154,13 @@ namespace UnitTest { virtual std::vector toBaseStyles() const override { - std::map materialMap = { - {0.25, Material{QColor(255,0,0)}}, - {0.50, Material{QColor(0,255,0)}}, - {0.75, Material{QColor(0,0,255)}}, - {1.00, Material{QColor(255,255,0)}}, + std::map materialMap = { + {0.20, Material{QColor(255,255,255)}}, + {0.60, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(4, StrokeType::kLeftSide, StrokeEndType::kRound, + std::make_shared(60, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared(materialMap, false))) }; } } style; @@ -65,12 +181,12 @@ namespace UnitTest virtual std::vector toBaseStyles() const override { std::map materialMap = { - {0.50, Material{QColor(0,255,0)}}, - {0.75, Material{QColor(0,0,255)}}, - {1.00, Material{QColor(255,255,0)}}, + {0.00, Material{QColor(255,255,255)}}, + {0.50, Material{QColor(165,176,207)}}, + {1.00, Material{QColor(58,64,151)}} }; return { BaseStyle(std::make_shared(), - std::make_shared(30, StrokeType::kLeftSide, StrokeEndType::kRound, + std::make_shared(30, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared(materialMap, true))) }; } } style; diff --git a/UnitTest/ElementRendererTest.h b/UnitTest/ElementRendererTest.h index a61fa93..c6d1651 100644 --- a/UnitTest/ElementRendererTest.h +++ b/UnitTest/ElementRendererTest.h @@ -4,6 +4,7 @@ #include #include "Renderer/Preview/ElementRenderer.h" #include "Editor/ThirdPartyLib/qquick/qquicksvgparser_p.h" +#include "Editor/util/PainterPathUtil.h" #include "Renderer/Painting/MaterialStyleStroke.h" #include "Renderer/Painting/ElementStyle.h" @@ -25,12 +26,13 @@ namespace UnitTest }; void paintGL() override { - glClearColor(1.0, 1.0, 1.0, 1.0); + glClearColor(219/255., 78/255., 32/255., 1.0); glClear(GL_COLOR_BUFFER_BIT); QPainterPath path; - QQuickSvgParser::parsePathDataFast("M100,100C-.5,100,0,100.5,0,0L40,.07C40,59.5,39.5,60,100,60Z", path); + QQuickSvgParser::parsePathDataFast("M292.82,107.78s0,0,0,0,0,3.59,0,7.62c0,3.85,0,5.78.06,6.43a19.94,19.94,0,0,0,2.87,7.58,15.85,15.85,0,0,0,6.61,6.23A14.75,14.75,0,0,0,310,137a11.69,11.69,0,0,0,7.59-2.92,11,11,0,0,0,3.2-6.84c.15-1.27.58-4.84-1.79-7.64a8.54,8.54,0,0,0-3.56-2.44c-1.32-.52-3.32-1.31-5.06-.33a5.41,5.41,0,0,0-2.14,3,3.48,3.48,0,0,0-.16,2.71c.78,1.86,3.36,2.14,3.47,2.15", path); + path = PainterPathUtil::monotonization(path); QTransform transform; - transform.scale(5, 5); + transform.scale(10, 10); path = transform.map(path); float pixelRatio = devicePixelRatioF(); auto [img, pos] = renderer.drawElement(path, style, pixelRatio, false); diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj index 2f62c21..8937397 100644 --- a/UnitTest/UnitTest.vcxproj +++ b/UnitTest/UnitTest.vcxproj @@ -69,7 +69,7 @@ FRAMELESSHELPER_WIDGETS_STATIC;_DEBUG;%(PreprocessorDefinitions) - $(SolutionDIr)ArchitectureColoredPainting\x64\Debug\*.obj;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperCore\debug\FramelessHelperCore.lib;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperWidgets\debug\FramelessHelperWidgets.lib;%(AdditionalDependencies) + $(SolutionDIr)ArchitectureColoredPainting\x64\Debug\*.obj;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperCore\debug\FramelessHelperCore.lib;$(SolutionDIr)FramelessHelper\qmake\FramelessHelperWidgets\debug\FramelessHelperWidgets.lib;opengl32.lib;%(AdditionalDependencies)