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()))
);