Compare commits

..

2 Commits

13 changed files with 321 additions and 131 deletions

View File

@ -893,21 +893,56 @@ bool angleLargeThanPi(vec2 a, vec2 b)
/************************************************************************************/
void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness)
void drawLine(in float d, inout uint styleIndex, out vec4 elementColor, out vec2 metallicRoughness)
{
elementColor = vec4(1);
metallicRoughness = vec2(0.8);
// switch(int(elementData[styleIndex+3]))
switch (0)
metallicRoughness = vec2(0.8);
uint headUint = floatBitsToUint(style[styleIndex+1]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a*100)%10)
//switch (2)
{
/// Plain
case 0: {
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 2])).rgb, 1);
metallicRoughness = vec2(unpackUnorm4x8(floatBitsToUint(style[styleIndex + 1])).rg);
metallicRoughness = head.rg;
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(style[styleIndex+2])).rgb, 1);
break;
}
/// RadialGradient
case 1: {
elementColor = vec4(mix(vec3(0), vec3(1), d), 1);
metallicRoughness = vec2(0, 0.8);
uint size = headUint%(1<<15);
bool gradual = (headUint&(1<<15))!=0;
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);
for(uint i = 0; i < size; i++)
{
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);
vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg;
if (d <= level)
{
if(gradual)
{
float a = (d-lastLevel)/(level-lastLevel);
elementColor = mix(lastColor, currentColor, a);
metallicRoughness = mix(lastMetallicRoughness, currentMetallicRoughness, a);
}
else
{
elementColor = currentColor;
metallicRoughness = currentMetallicRoughness;
}
break;
}
lastMetallicRoughness = currentMetallicRoughness;
lastColor = currentColor;
lastLevel = level;
}
break;
}
case 2: {
@ -987,8 +1022,9 @@ void main()
else // Stroke
{
float minDistance = 1e38;
// float lineType = elementData[styleIndex+4];
float lineType = 2;
vec4 styleHead = unpackUnorm4x8(floatBitsToUint(style[styleIndex+1]));
float lineType = floor(styleHead.b*10);
//float lineType = 2;
int debugBegin = 0;
for (uint pathIndex = 0; pathIndex < pathSize; pathIndex++)
{

View File

@ -898,44 +898,76 @@ void drawLine(in float d, in uint styleIndex, out vec4 elementColor, out vec2 me
{
elementColor = vec4(1);
metallicRoughness = vec2(0.8);
//switch(int(elementData[styleIndex+3]))
switch(0)
{
case 0:
{
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+2])).rgb,1);
metallicRoughness = vec2(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+1])).rg);
break;
}
case 1:
{
elementColor = vec4(mix(vec3(0), vec3(1), d), 1);
metallicRoughness = vec2(0,0.8);
break;
}
case 2:
{
float levels[] = {0.25,0.5,0.75};
vec3 colors[] = {vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(1,1,0)};
int i = 0;
while(i<3)
{
if(d<levels[i])
{
elementColor = vec4(colors[i], 1);
metallicRoughness = vec2(0,0.8);
break;
}
i++;
}
if(i==3)
{
elementColor = vec4(colors[i], 1);
metallicRoughness = vec2(0,0.8);
}
break;
}
}
uint headUint = floatBitsToUint(elementData[styleIndex+1]);
vec4 head = unpackUnorm4x8(headUint);
switch (int(head.a*100)%10)
//switch (2)
{
/// Plain
case 0: {
metallicRoughness = head.rg;
elementColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+2])).rgb, 1);
break;
}
/// RadialGradient
case 1: {
uint size = headUint%(1<<15);
bool gradual = (headUint&(1<<15))!=0;
uint lastData = floatBitsToUint(elementData[styleIndex+2+0*2]);
float lastLevel = 0;
vec2 lastMetallicRoughness = unpackUnorm4x8(lastData).rg;
vec4 lastColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+0*2])).rgb, 1);
for(uint i = 0; i < size; i++)
{
uint data = floatBitsToUint(elementData[styleIndex+2+i*2]);
float level = unpackUnorm2x16(data).y;
vec4 currentColor = vec4(unpackUnorm4x8(floatBitsToUint(elementData[styleIndex+3+i*2])).rgb, 1);
vec2 currentMetallicRoughness = unpackUnorm4x8(data).rg;
if (d <= level)
{
if(gradual)
{
float a = (d-lastLevel)/(level-lastLevel);
elementColor = mix(lastColor, currentColor, a);
metallicRoughness = mix(lastMetallicRoughness, currentMetallicRoughness, a);
}
else
{
elementColor = currentColor;
metallicRoughness = currentMetallicRoughness;
}
break;
}
lastMetallicRoughness = currentMetallicRoughness;
lastColor = currentColor;
lastLevel = level;
}
break;
}
case 2: {
float levels[] = {0.25, 0.5, 0.75};
vec3 colors[] = {vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1), vec3(1, 1, 0)};
int i = 0;
while (i < 3)
{
if (d < levels[i])
{
elementColor = vec4(colors[i], 1);
metallicRoughness = vec2(0, 0.8);
break;
}
i++;
}
if (i == 3)
{
elementColor = vec4(colors[i], 1);
metallicRoughness = vec2(0, 0.8);
}
break;
}
}
}
bool drawElement(uint elementIndex, vec2 localUV, out vec3 color, out vec2 metallicRoughness, inout vec3 debugBVH = vec3(0))

View File

@ -392,12 +392,13 @@ GLuint Renderer::Model::loadPainting(std::string path)
vector<std::shared_ptr<ElementStyle>> style = {
std::make_shared<ElementStyleFillDemo>(),
std::make_shared<ElementStyleStrokeDemo>(0.02)
std::make_shared<ElementStyleStrokeDemo>(0.02),
std::make_shared<ElementStyleStrokeRadialGradientDemo>(0.02)
};
vector<std::shared_ptr<Element>> element = {
std::make_shared<Element>(Element{ contour[0], style[0]}),
std::make_shared<Element>(Element{ contour[1], style[1]}),
std::make_shared<Element>(Element{ contour[1], style[2]}),
std::make_shared<Element>(Element{ contour[2], style[0]}),
};
Painting painting;

View File

@ -18,5 +18,23 @@ Renderer::ElementStyleStrokeDemo::ElementStyleStrokeDemo(float width)
std::vector<BaseStyle> Renderer::ElementStyleStrokeDemo::toBaseStyles() const
{
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(width, StrokeType::kLeftSide, StrokeEndType::kRound, std::make_shared<StrokePlain>(QColor(0, 0, 255), 0, 0.8))) };
std::make_shared<MaterialStyleStroke>(width, StrokeType::kBothSides, StrokeEndType::kRound, std::make_shared<StrokePlain>(QColor(0, 0, 255), 0, 0.8))) };
}
Renderer::ElementStyleStrokeRadialGradientDemo::ElementStyleStrokeRadialGradientDemo(float width)
: width(width)
{
}
std::vector<BaseStyle> Renderer::ElementStyleStrokeRadialGradientDemo::toBaseStyles() const
{
std::map<float, Material> 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)}},
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(width, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}

View File

@ -31,4 +31,14 @@ namespace Renderer
protected:
float width;
};
class ElementStyleStrokeRadialGradientDemo : public ElementStyle
{
public:
ElementStyleStrokeRadialGradientDemo(float width);
virtual std::vector<BaseStyle> toBaseStyles() const override;
protected:
float width;
};
}

View File

@ -1,4 +1,5 @@
#include "MaterialStyleStroke.h"
#include <QDebug>
using namespace Renderer;
@ -7,6 +8,11 @@ Renderer::StrokePlain::StrokePlain(QColor color, float metallic, float roughness
{
}
Renderer::StrokePlain::StrokePlain(const Material& material)
: material(material)
{
}
MaterialStrokeType Renderer::StrokePlain::type() const
{
return MaterialStrokeType::kPlain;
@ -24,8 +30,43 @@ bool Renderer::StrokePlain::operator==(const MaterialStroke& m) const
&& material == static_cast<const StrokePlain&>(m).material;
}
Renderer::StrokeRadialGradient::StrokeRadialGradient(const std::map<float, Material>& materialMap, bool gradual)
: materialMap(materialMap), gradual(gradual)
{
}
MaterialStrokeType Renderer::StrokeRadialGradient::type() const
{
return MaterialStrokeType::kRadialGradient;
}
std::vector<GLfloat> Renderer::StrokeRadialGradient::encoded() const
{
GLuint head = materialMap.size() + (gradual ? (1 << 15) : 0);
std::vector<GLfloat> result = { glm::uintBitsToFloat(head) };
for (auto& pair : materialMap)
{
auto [color, metallicRoughness] = pair.second.toVec();
GLuint i = glm::packUnorm4x8(glm::vec4(metallicRoughness, 0, 0));
glm::vec2 v = glm::unpackUnorm2x16(i);
v.y = pair.first;
result.push_back(glm::uintBitsToFloat(glm::packUnorm2x16(v)));
result.push_back(glm::uintBitsToFloat(glm::packUnorm4x8(color)));
qDebug() << pair.first;
}
return result;
}
bool Renderer::StrokeRadialGradient::operator==(const MaterialStroke& m) const
{
return type() == m.type()
&& gradual == static_cast<const StrokeRadialGradient&>(m).gradual
&& materialMap == static_cast<const StrokeRadialGradient&>(m).materialMap;
}
Renderer::MaterialStyleStroke::MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke)
: width(width), strokeType(strokeType), endType(endType), materialStroke(materialStroke)
: halfWidth(width/2), strokeType(strokeType), endType(endType), materialStroke(materialStroke)
{
}
@ -36,11 +77,11 @@ MaterialStyleType Renderer::MaterialStyleStroke::type() const
std::vector<GLfloat> Renderer::MaterialStyleStroke::encoded() const
{
std::vector<GLfloat> v = { width };
std::vector<GLfloat> v = { halfWidth };
auto encoded = materialStroke->encoded();
glm::vec4 head = glm::unpackUnorm4x8(glm::floatBitsToUint(encoded[0]));
head.b = (float)strokeType / 10. + (float)endType / 100.;
head.a = 1; /// Ô¤Áô
head.a = 0.6 /*ʹµÃpackºóСÓÚ0*/ + (float)materialStroke->type() / 100.;
encoded[0] = glm::uintBitsToFloat(glm::packUnorm4x8(head));
v.insert(v.end(), encoded.begin(), encoded.end());
return v;
@ -51,9 +92,9 @@ bool Renderer::MaterialStyleStroke::operator==(const MaterialStyle& m) const
return type() == m.type() && *materialStroke == *static_cast<const MaterialStyleStroke&>(m).materialStroke;
}
float Renderer::MaterialStyleStroke::getWidth() const
float Renderer::MaterialStyleStroke::getHalfWidth() const
{
return width;
return halfWidth;
}

View File

@ -3,7 +3,7 @@
namespace Renderer
{
enum class MaterialStrokeType { kPlain, kLinearGradient, kRadialGradient};
enum class MaterialStrokeType { kPlain = 0, kRadialGradient = 1, kLinearGradient = 2 };
class MaterialStroke
{
@ -17,6 +17,7 @@ namespace Renderer
{
public:
StrokePlain(QColor color, float metallic, float roughness);
StrokePlain(const Material& material);
virtual MaterialStrokeType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual bool operator==(const MaterialStroke&) const override;
@ -27,12 +28,13 @@ namespace Renderer
class StrokeRadialGradient : public MaterialStroke
{
public:
StrokeRadialGradient(QColor color, float metallic, float roughness);
StrokeRadialGradient(const std::map<float, Material>& materialMap, bool gradual);
virtual MaterialStrokeType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual bool operator==(const MaterialStroke&) const override;
//std::map<
std::map<float, Material> materialMap;
bool gradual = true;
};
enum class StrokeType { kBothSides = 2, kLeftSide = 1, kRightSide = 0 };
@ -41,13 +43,13 @@ namespace Renderer
class MaterialStyleStroke : public MaterialStyle
{
public:
MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke);
MaterialStyleStroke(float width, StrokeType strokeType, StrokeEndType endType, std::shared_ptr<MaterialStroke> materialStroke);
virtual MaterialStyleType type() const override;
virtual std::vector<GLfloat> encoded() const override;
virtual bool operator==(const MaterialStyle&) const override;
float getWidth() const;
float getHalfWidth() const;
protected:
float width;
float halfWidth;
StrokeType strokeType;
StrokeEndType endType;
std::shared_ptr<MaterialStroke> materialStroke;

View File

@ -43,7 +43,7 @@ void Renderer::Painting::addElement(ElementWithTransform elementWithTransform)
{
qDebug() << "Build LineTree-------------------------------------------------------------------------------------------------------";
LineTree lineTree(maxLineCount);
lineTree.buildLineTree(*element.contour, std::static_pointer_cast<MaterialStyleStroke>(element.style)->getWidth());
lineTree.buildLineTree(*element.contour, std::static_pointer_cast<MaterialStyleStroke>(element.style)->getHalfWidth());
ContourBuffer elementBuffer;
std::vector<BvhTreeData> bvhLeaves = lineTree.getPointLineAndBvhTree(elementBuffer.pointBuffer, elementBuffer.lineBuffer);
BvhTree bvhTree;

View File

@ -20,7 +20,7 @@ Renderer::ElementRenderer::ElementRenderer(QOpenGLWidget* glWidget)
void Renderer::ElementRenderer::initialize()
{
initializeOpenGLFunctions();
shader = std::make_shared<QOpenGLShaderProgram>();
shader = std::make_unique<QOpenGLShaderProgram>();
if (!shader->addShaderFromSourceFile(QOpenGLShader::Compute, ":/Shaders/element.comp"))
qDebug() << "ERROR: " << shader->log();
if (!shader->link())
@ -37,30 +37,25 @@ std::vector<glm::vec2> generatePathBuffer(const QPainterPath& path)
switch (element.type)
{
case QPainterPath::MoveToElement:
qDebug() << "MoveToElement";
pathBuffer.push_back(glm::vec2(std::numeric_limits<float>::infinity()));
pathBuffer.push_back(glm::vec2(element.x, element.y));
break;
case QPainterPath::LineToElement:
qDebug() << "LineToElement";
{
glm::vec2 end = glm::vec2(element.x, element.y);
glm::vec2 mid = (pathBuffer.back() + end) / 2.f;
pathBuffer.push_back(mid);
pathBuffer.push_back(mid);
pathBuffer.push_back(end);
}
{
glm::vec2 end = glm::vec2(element.x, element.y);
glm::vec2 mid = (pathBuffer.back() + end) / 2.f;
pathBuffer.push_back(mid);
pathBuffer.push_back(mid);
pathBuffer.push_back(end);
break;
}
case QPainterPath::CurveToElement:
qDebug() << "CurveToElement";
pathBuffer.push_back(glm::vec2(element.x, element.y));
break;
case QPainterPath::CurveToDataElement:
qDebug() << "CurveToDataElement";
pathBuffer.push_back(glm::vec2(element.x, element.y));
break;
}
qDebug() << element;
}
return pathBuffer;
}
@ -95,11 +90,8 @@ QRectF calcBoundingRect(const QPainterPath& path, const std::vector<BaseStyle>&
BaseTransform transform(originTransform.appliedTransformStyle(*baseStyle.transform));
if (baseStyle.material->type() == MaterialStyleType::kStroke)
{
float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getWidth() / 2;
transform.bound.x -= halfWidth;
transform.bound.y -= halfWidth;
transform.bound.z += halfWidth;
transform.bound.w += halfWidth;
float halfWidth = std::static_pointer_cast<MaterialStyleStroke>(baseStyle.material)->getHalfWidth();
transform.bound += glm::vec4(glm::vec2(-halfWidth), glm::vec2(halfWidth));
}
glm::vec2 points[] = {
glm::vec2(transform.bound.x, transform.bound.y),

View File

@ -28,7 +28,7 @@ namespace Renderer
std::pair<QImage, QPointF> drawElement(const QPainterPath& path, const ElementStyle& style, float pixelRatio, bool releaseContext = true);
protected:
QOpenGLWidget* glWidget;
std::shared_ptr<QOpenGLShaderProgram> shader;
std::unique_ptr<QOpenGLShaderProgram> shader;
};
}

View File

@ -19,15 +19,9 @@
### 场景渲染Renderer
采用 PBR (金属度-粗糙度) 材质模型,场景中存在一个方向光,可拖动滑动条调整光源方向观察材质效果。
已实现BVH加速结构的建立
已实现单图元的拆分,即划分网格并生成快捷段和缠绕增量
采用 PBR (金属度-粗糙度) 材质模型,场景中存在一个方向光,可拖动滑动条调整光源方向观察材质效果
已实现由图元数据建立完整彩绘编码
已实现线的单侧描边
待完善图元样式的编解码
已实现面的纯色样式,线的纯色、径向渐变/分层、单侧等样式

View File

@ -2,25 +2,81 @@
#include <QGuiApplication>
#include <QtWidgets/QApplication>
#include "ElementRendererTest.h"
#include "Renderer/Painting/ElementStyle.h"
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace Renderer;
namespace UnitTest
{
TEST_CLASS(ElementRendererTest)
{
public:
TEST_METHOD(TestMethod1)
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
char arg[] = "";
char* argv[] = { arg };
int argc = 1;
QApplication a(argc, argv);
TestGLWidget w;
w.show();
a.exec();
}
};
TEST_CLASS(ElementRendererTest)
{
public:
TEST_METHOD(TestStrokePlain)
{
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);
Renderer::ElementStyleStrokeDemo style(2);
TestGLWidget w(style);
w.show();
a.exec();
}
TEST_METHOD(TestStrokeRadialGradient1)
{
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<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> 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)}},
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(4, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, false))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
TEST_METHOD(TestStrokeRadialGradient2)
{
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<Renderer::BaseStyle> toBaseStyles() const override
{
std::map<float, Material> materialMap = {
{0.50, Material{QColor(0,255,0)}},
{0.75, Material{QColor(0,0,255)}},
{1.00, Material{QColor(255,255,0)}},
};
return { BaseStyle(std::make_shared<TransformStyle>(),
std::make_shared<MaterialStyleStroke>(4, StrokeType::kLeftSide, StrokeEndType::kRound,
std::make_shared<StrokeRadialGradient>(materialMap, true))) };
}
} style;
TestGLWidget w(style);
w.show();
a.exec();
}
};
}

View File

@ -9,28 +9,36 @@
namespace UnitTest
{
class TestGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
private:
Renderer::ElementRenderer renderer;
public:
TestGLWidget(QWidget* parent = nullptr) : QOpenGLWidget(parent), renderer(this) {};
void initializeGL() override
{
initializeOpenGLFunctions();
renderer.initialize();
};
void paintGL() override
{
glClearColor(1.0, 1.0, 1.0, 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);
auto [img, pos] = renderer.drawElement(path, Renderer::ElementStyleStrokeDemo(2), 1, false);
QPainter painter(this);
painter.drawImage(pos, img);
};
void resizeGL(int w, int h) override {};
};
class TestGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
private:
Renderer::ElementRenderer renderer;
Renderer::ElementStyle& style;
public:
TestGLWidget(Renderer::ElementStyle& style, QWidget* parent = nullptr)
: QOpenGLWidget(parent), renderer(this), style(style) {};
void initializeGL() override
{
initializeOpenGLFunctions();
renderer.initialize();
};
void paintGL() override
{
glClearColor(1.0, 1.0, 1.0, 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);
/*QTransform transform;
transform.scale(10, 10);
transform.map(path);*/
float pixelRatio = 30;
auto [img, pos] = renderer.drawElement(path, style, pixelRatio, false);
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.drawImage(QRectF(QPointF(0, 0), img.size()/devicePixelRatioF()), img);
};
void resizeGL(int w, int h) override {};
};
}