diff --git a/ACKNOWLEDGEMENTS.html b/ACKNOWLEDGEMENTS.html index 39a2c273..0606f6d3 100644 --- a/ACKNOWLEDGEMENTS.html +++ b/ACKNOWLEDGEMENTS.html @@ -548,6 +548,7 @@ https://www.reddit.com/r/gamedev/comments/5iuf3h/i_am_writting_a_3d_monster_mode

Sam Hocevar

     https://gamedev.stackexchange.com/questions/98246/quaternion-slerp-and-lerp-implementation-with-overshoot
+    http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
 

David Rosen

diff --git a/shaders/default.core.frag b/shaders/default.core.frag index 41c6a8f6..29496534 100644 --- a/shaders/default.core.frag +++ b/shaders/default.core.frag @@ -86,6 +86,7 @@ uniform samplerCube environmentIrradianceMapId; uniform int environmentIrradianceMapEnabled; uniform samplerCube environmentSpecularMapId; uniform int environmentSpecularMapEnabled; +uniform int tongShadingEnabled; const int MAX_LIGHTS = 8; const int TYPE_POINT = 0; @@ -352,6 +353,25 @@ vec3 gammaCorrect(const in vec3 color) return pow(color, vec3(1.0 / gamma)); } +// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + vec4 metalRoughFunction(const in vec4 baseColor, const in float metalness, const in float roughness, @@ -365,24 +385,35 @@ vec4 metalRoughFunction(const in vec4 baseColor, // Remap roughness for a perceptually more linear correspondence float alpha = remapRoughness(roughness); - if (environmentIrradianceMapEnabled == 1) { - cLinear += pbrIblModel(worldNormal, - worldView, - baseColor.rgb, - metalness, - alpha, - ambientOcclusion); - } - - for (int i = 0; i < lightCount; ++i) { - cLinear += pbrModel(i, - worldPosition, - worldNormal, - worldView, - baseColor.rgb, - metalness, - alpha, - ambientOcclusion); + if (tongShadingEnabled != 1) { + if (environmentIrradianceMapEnabled == 1) { + cLinear += pbrIblModel(worldNormal, + worldView, + baseColor.rgb, + metalness, + alpha, + ambientOcclusion); + } + + for (int i = 0; i < lightCount; ++i) { + cLinear += pbrModel(i, + worldPosition, + worldNormal, + worldView, + baseColor.rgb, + metalness, + alpha, + ambientOcclusion); + } + } else { + float intensity; + intensity = dot(vec3(1.0, 1.0, 1.0), worldNormal); + + vec3 hsv = rgb2hsv(baseColor.rgb); + if (intensity > 0.966) + cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0)); + else + cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1)); } // Apply exposure correction diff --git a/shaders/default.frag b/shaders/default.frag index 6a1c225e..8714b995 100644 --- a/shaders/default.frag +++ b/shaders/default.frag @@ -80,6 +80,7 @@ uniform int ambientOcclusionMapEnabled; uniform int mousePickEnabled; uniform vec3 mousePickTargetPosition; uniform float mousePickRadius; +uniform int tongShadingEnabled; const int MAX_LIGHTS = 8; const int TYPE_POINT = 0; @@ -248,6 +249,25 @@ vec3 gammaCorrect(const in vec3 color) return pow(color, vec3(1.0 / gamma)); } +// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl +vec3 rgb2hsv(vec3 c) +{ + vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + float d = q.x - min(q.w, q.y); + float e = 1.0e-10; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +vec3 hsv2rgb(vec3 c) +{ + vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + vec4 metalRoughFunction(const in vec4 baseColor, const in float metalness, const in float roughness, @@ -260,16 +280,27 @@ vec4 metalRoughFunction(const in vec4 baseColor, // Remap roughness for a perceptually more linear correspondence float alpha = remapRoughness(roughness); - - for (int i = 0; i < lightCount; ++i) { - cLinear += pbrModel(i, - worldPosition, - worldNormal, - worldView, - baseColor.rgb, - metalness, - alpha, - ambientOcclusion); + + if (tongShadingEnabled != 1) { + for (int i = 0; i < lightCount; ++i) { + cLinear += pbrModel(i, + worldPosition, + worldNormal, + worldView, + baseColor.rgb, + metalness, + alpha, + ambientOcclusion); + } + } else { + float intensity; + intensity = dot(vec3(1.0, 1.0, 1.0), worldNormal); + + vec3 hsv = rgb2hsv(baseColor.rgb); + if (intensity > 0.966) + cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 2.0)); + else + cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.1)); } // Apply exposure correction diff --git a/src/modelshaderprogram.cpp b/src/modelshaderprogram.cpp index 07a8b082..9948ffda 100644 --- a/src/modelshaderprogram.cpp +++ b/src/modelshaderprogram.cpp @@ -58,6 +58,7 @@ ModelShaderProgram::ModelShaderProgram(bool isCoreProfile) m_mousePickEnabledLoc = this->uniformLocation("mousePickEnabled"); m_mousePickTargetPositionLoc = this->uniformLocation("mousePickTargetPosition"); m_mousePickRadiusLoc = this->uniformLocation("mousePickRadius"); + m_tongShadingEnabledLoc = this->uniformLocation("tongShadingEnabled"); if (m_isCoreProfile) { m_environmentIrradianceMapIdLoc = this->uniformLocation("environmentIrradianceMapId"); m_environmentIrradianceMapEnabledLoc = this->uniformLocation("environmentIrradianceMapEnabled"); @@ -166,3 +167,7 @@ int ModelShaderProgram::environmentSpecularMapEnabledLoc() return m_environmentSpecularMapEnabledLoc; } +int ModelShaderProgram::tongShadingEnabledLoc() +{ + return m_tongShadingEnabledLoc; +} \ No newline at end of file diff --git a/src/modelshaderprogram.h b/src/modelshaderprogram.h index c36c51c5..729dfa9f 100644 --- a/src/modelshaderprogram.h +++ b/src/modelshaderprogram.h @@ -27,6 +27,7 @@ public: int environmentIrradianceMapEnabledLoc(); int environmentSpecularMapIdLoc(); int environmentSpecularMapEnabledLoc(); + int tongShadingEnabledLoc(); bool isCoreProfile(); static const QString &loadShaderSource(const QString &name); private: @@ -51,6 +52,7 @@ private: int m_environmentIrradianceMapEnabledLoc = 0; int m_environmentSpecularMapIdLoc = 0; int m_environmentSpecularMapEnabledLoc = 0; + int m_tongShadingEnabledLoc = 0; }; #endif diff --git a/src/modelwidget.cpp b/src/modelwidget.cpp index afe4df33..ebce009e 100644 --- a/src/modelwidget.cpp +++ b/src/modelwidget.cpp @@ -7,6 +7,7 @@ #include #include "modelwidget.h" #include "util.h" +#include "preferences.h" // Modifed from http://doc.qt.io/qt-5/qtopengl-hellogl2-glwidget-cpp.html @@ -46,6 +47,13 @@ ModelWidget::ModelWidget(QWidget *parent) : } setContextMenuPolicy(Qt::CustomContextMenu); zoom(200); + + connect(&Preferences::instance(), &Preferences::tongShadingChanged, this, &ModelWidget::reRender); +} + +void ModelWidget::reRender() +{ + update(); } int ModelWidget::xRot() @@ -173,6 +181,7 @@ void ModelWidget::paintGL() m_world.rotate(m_zRot / 16.0f, 0, 0, 1); m_program->bind(); + m_program->setUniformValue(m_program->tongShadingEnabledLoc(), Preferences::instance().tongShading() ? 1 : 0); m_program->setUniformValue(m_program->projectionMatrixLoc(), m_projection); m_program->setUniformValue(m_program->modelMatrixLoc(), m_world); QMatrix3x3 normalMatrix = m_world.normalMatrix(); diff --git a/src/modelwidget.h b/src/modelwidget.h index 4f156447..12efe1c0 100644 --- a/src/modelwidget.h +++ b/src/modelwidget.h @@ -56,6 +56,7 @@ public slots: void zoom(float delta); void setMousePickTargetPositionInModelSpace(QVector3D position); void setMousePickRadius(float radius); + void reRender(); signals: void xRotationChanged(int angle); void yRotationChanged(int angle); diff --git a/src/preferences.cpp b/src/preferences.cpp index 25b42eec..90d66f94 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -15,6 +15,7 @@ void Preferences::loadDefault() m_componentCombineMode = CombineMode::Normal; m_partColor = Qt::white; m_flatShading = true; + m_tongShading = false; m_textureSize = 1024; } @@ -38,6 +39,13 @@ Preferences::Preferences() else m_flatShading = isTrueValueString(value); } + { + QString value = m_settings.value("tongShading").toString(); + if (value.isEmpty()) + m_tongShading = false; + else + m_tongShading = isTrueValueString(value); + } { QString value = m_settings.value("textureSize").toString(); if (!value.isEmpty()) @@ -60,6 +68,11 @@ bool Preferences::flatShading() const return m_flatShading; } +bool Preferences::tongShading() const +{ + return m_tongShading; +} + int Preferences::textureSize() const { return m_textureSize; @@ -92,6 +105,15 @@ void Preferences::setFlatShading(bool flatShading) emit flatShadingChanged(); } +void Preferences::setTongShading(bool tongShading) +{ + if (m_tongShading == tongShading) + return; + m_tongShading = tongShading; + m_settings.setValue("tongShading", tongShading ? "true" : "false"); + emit tongShadingChanged(); +} + void Preferences::setTextureSize(int textureSize) { if (m_textureSize == textureSize) @@ -118,5 +140,6 @@ void Preferences::reset() emit componentCombineModeChanged(); emit partColorChanged(); emit flatShadingChanged(); + emit tongShadingChanged(); emit textureSizeChanged(); } diff --git a/src/preferences.h b/src/preferences.h index bd010bc4..ad2b77db 100644 --- a/src/preferences.h +++ b/src/preferences.h @@ -14,6 +14,7 @@ public: CombineMode componentCombineMode() const; const QColor &partColor() const; bool flatShading() const; + bool tongShading() const; QSize documentWindowSize() const; void setDocumentWindowSize(const QSize&); int textureSize() const; @@ -21,17 +22,20 @@ signals: void componentCombineModeChanged(); void partColorChanged(); void flatShadingChanged(); + void tongShadingChanged(); void textureSizeChanged(); public slots: void setComponentCombineMode(CombineMode mode); void setPartColor(const QColor &color); void setFlatShading(bool flatShading); + void setTongShading(bool tongShading); void setTextureSize(int textureSize); void reset(); private: CombineMode m_componentCombineMode; QColor m_partColor; bool m_flatShading; + bool m_tongShading; QSettings m_settings; int m_textureSize; private: diff --git a/src/preferenceswidget.cpp b/src/preferenceswidget.cpp index e1c4720c..b8fd80f9 100644 --- a/src/preferenceswidget.cpp +++ b/src/preferenceswidget.cpp @@ -65,6 +65,12 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent) Preferences::instance().setFlatShading(flatShadingBox->isChecked()); }); + QCheckBox *tongShadingBox = new QCheckBox(); + Theme::initCheckbox(tongShadingBox); + connect(tongShadingBox, &QCheckBox::stateChanged, this, [=]() { + Preferences::instance().setTongShading(tongShadingBox->isChecked()); + }); + QComboBox *textureSizeSelectBox = new QComboBox; textureSizeSelectBox->addItem("512"); textureSizeSelectBox->addItem("1024"); @@ -78,12 +84,14 @@ PreferencesWidget::PreferencesWidget(const Document *document, QWidget *parent) formLayout->addRow(tr("Part color:"), colorLayout); formLayout->addRow(tr("Combine mode:"), combineModeSelectBox); formLayout->addRow(tr("Flat shading:"), flatShadingBox); + formLayout->addRow(tr("Tong shading:"), tongShadingBox); formLayout->addRow(tr("Texture size:"), textureSizeSelectBox); auto loadFromPreferences = [=]() { updatePickButtonColor(); combineModeSelectBox->setCurrentIndex((int)Preferences::instance().componentCombineMode()); flatShadingBox->setChecked(Preferences::instance().flatShading()); + tongShadingBox->setChecked(Preferences::instance().tongShading()); textureSizeSelectBox->setCurrentIndex( textureSizeSelectBox->findText(QString::number(Preferences::instance().textureSize())) );