diff --git a/resources.qrc b/resources.qrc index 79ed3608..12c324c5 100644 --- a/resources.qrc +++ b/resources.qrc @@ -10,8 +10,8 @@ shaders/default.frag shaders/default.core.vert shaders/default.core.frag - shaders/pbr.vert - shaders/pbr.frag + shaders/pbr-qt.frag + shaders/pbr-joey.frag ACKNOWLEDGEMENTS.html AUTHORS CONTRIBUTORS diff --git a/shaders/default.vert b/shaders/default.vert index a711eda0..dae66df8 100644 --- a/shaders/default.vert +++ b/shaders/default.vert @@ -10,15 +10,18 @@ varying vec3 vertColor; varying vec2 vertTexCoord; varying float vertMetalness; varying float vertRoughness; -varying vec3 vertView; -uniform mat4 projMatrix; -uniform mat4 mvMatrix; -uniform mat3 normalMatrix; -void main() +varying vec3 cameraPos; +uniform mat4 projectionMatrix; +uniform mat4 modelMatrix; +uniform mat4 viewMatrix; +void main() { - vert = vertex.xyz; - vertNormal = normalMatrix * normal; + vert = (modelMatrix * vertex).xyz; + vertNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz); vertColor = color; vertTexCoord = texCoord; - gl_Position = projMatrix * mvMatrix * vertex; + vertMetalness = metalness; + vertRoughness = roughness; + cameraPos = vec3(0, 0, -2.1); + gl_Position = projectionMatrix * viewMatrix * vec4(vert, 1.0); } \ No newline at end of file diff --git a/shaders/pbr-joey.frag b/shaders/pbr-joey.frag new file mode 100644 index 00000000..03fa0df5 --- /dev/null +++ b/shaders/pbr-joey.frag @@ -0,0 +1,154 @@ +// Copy and modified from Joey's code +// https://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/6.pbr/1.1.lighting/1.1.pbr.fs + +const float PI = 3.14159265359; + +// ---------------------------------------------------------------------------- +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float nom = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return nom / max(denom, 0.001); // prevent divide by zero for roughness=0.0 and NdotH=1.0 +} +// ---------------------------------------------------------------------------- +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return nom / denom; +} +// ---------------------------------------------------------------------------- +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} +// ---------------------------------------------------------------------------- +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +varying highp vec3 vert; +varying highp vec3 vertNormal; +varying highp vec3 vertColor; +varying highp vec2 vertTexCoord; +varying highp float vertMetalness; +varying highp float vertRoughness; +varying highp vec3 cameraPos; +uniform highp vec3 lightPos; +uniform highp sampler2D textureId; +uniform highp int textureEnabled; + +const int MAX_LIGHTS = 8; +struct Light { + vec3 position; + vec3 color; + float intensity; +}; +int lightCount; +Light lights[MAX_LIGHTS]; + +void main() +{ + const highp float vertAmbientOcclusion = 1.0; + + vec3 albedo = vertColor; + if (textureEnabled == 1) { + albedo = texture2D(textureId, vertTexCoord).rgb; + } + albedo = pow(albedo, vec3(2.2)); + + lightCount = 3; + float roughness = vertRoughness; + float metalness = vertMetalness; + + // Key light + lights[0].position = vec3(5.0, 5.0, 5.0); + lights[0].color = vec3(150.0, 150.0, 150.0); + lights[0].intensity = 0.8; + + // Fill light + lights[1].position = vec3(-5.0, 5.0, 5.0); + lights[1].color = vec3(150.0, 150.0, 150.0); + lights[1].intensity = 0.4; + + // Rim light + lights[2].position = vec3(0.0, -2.5, -5.0); + lights[2].color = vec3(150.0, 150.0, 150.0); + lights[2].intensity = 0.2; + + vec3 N = normalize(vertNormal); + vec3 V = normalize(cameraPos - vert); + + // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 + // of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow) + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metalness); + + // reflectance equation + vec3 Lo = vec3(0.0); + for (int i = 0; i < lightCount; ++i) + { + // calculate per-light radiance + vec3 L = normalize(lights[i].position - vert); + vec3 H = normalize(V + L); + float distance = length(lights[i].position - vert); + float attenuation = 1.0 / (distance * distance); + vec3 radiance = lights[i].color * attenuation; + + // Cook-Torrance BRDF + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = nominator / max(denominator, 0.001); // prevent divide by zero for NdotV=0.0 or NdotL=0.0 + + // kS is equal to Fresnel + vec3 kS = F; + // for energy conservation, the diffuse and specular light can't + // be above 1.0 (unless the surface emits light); to preserve this + // relationship the diffuse component (kD) should equal 1.0 - kS. + vec3 kD = vec3(1.0) - kS; + // multiply kD by the inverse metalness such that only non-metals + // have diffuse lighting, or a linear blend if partly metal (pure metals + // have no diffuse light). + kD *= 1.0 - metalness; + + // scale light by NdotL + float NdotL = max(dot(N, L), 0.0); + + // add to outgoing radiance Lo + Lo += lights[i].intensity * (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again + } + + // ambient lighting (note that the next IBL tutorial will replace + // this ambient lighting with environment lighting). + vec3 ambient = vec3(0.03) * albedo * vertAmbientOcclusion; + + vec3 color = ambient + Lo; + + // HDR tonemapping + color = color / (color + vec3(1.0)); + // gamma correct + color = pow(color, vec3(1.0/2.2)); + + gl_FragColor = vec4(color, 1.0); +} \ No newline at end of file diff --git a/shaders/pbr.frag b/shaders/pbr-qt.frag similarity index 92% rename from shaders/pbr.frag rename to shaders/pbr-qt.frag index 3309648a..22f603d1 100644 --- a/shaders/pbr.frag +++ b/shaders/pbr-qt.frag @@ -62,7 +62,7 @@ varying highp vec3 vertColor; varying highp vec2 vertTexCoord; varying highp float vertMetalness; varying highp float vertRoughness; -varying highp vec3 vertView; +varying highp vec3 cameraPos; uniform highp vec3 lightPos; uniform highp sampler2D textureId; uniform highp int textureEnabled; @@ -272,11 +272,6 @@ vec4 metalRoughFunction(const in vec4 baseColor, void main() { - highp vec3 color = vertColor; - if (textureEnabled == 1) { - color = texture2D(textureId, vertTexCoord).rgb; - } - // FIXME: don't hard code here exposure = 0.0; gamma = 2.2; @@ -286,35 +281,46 @@ void main() // https://doc-snapshots.qt.io/qt5-5.12/qt3d-pbr-materials-lights-qml.html lightCount = 3; + // Key light lights[0].type = TYPE_POINT; - lights[0].position = vec3(0.0, 5.0, 0.0); - lights[0].color = vec3(1.0, 1.0, 1.0); + lights[0].position = vec3(5.0, 5.0, 5.0); + lights[0].color = vec3(0.588, 0.588, 0.588); lights[0].intensity = 5.0; - lights[0].constantAttenuation = 1.0; + lights[0].constantAttenuation = 0.0; lights[0].linearAttenuation = 0.0; - lights[0].quadraticAttenuation = 0.0025; + lights[0].quadraticAttenuation = 0.0; + // Fill light lights[1].type = TYPE_POINT; - lights[1].position = vec3(5.0, 0.0, 0.0); - lights[1].color = vec3(1.0, 1.0, 1.0); - lights[1].intensity = 0.8; - lights[1].constantAttenuation = 1.0; + lights[1].position = vec3(-5.0, 5.0, 5.0); + lights[1].color = vec3(0.588, 0.588, 0.588); + lights[1].intensity = 3.0; + lights[1].constantAttenuation = 0.0; lights[1].linearAttenuation = 0.0; - lights[1].quadraticAttenuation = 0.0025; + lights[1].quadraticAttenuation = 0.0; + // Rim light lights[2].type = TYPE_POINT; - lights[2].position = vec3(0.0, -5.0, 0.0); - lights[2].color = vec3(1.0, 1.0, 1.0); - lights[2].intensity = 0.15; - lights[2].constantAttenuation = 1.0; + lights[2].position = vec3(0.0, -5.0, -5.0); + lights[2].color = vec3(0.588, 0.588, 0.588); + lights[2].intensity = 2.5; + lights[2].constantAttenuation = 0.0; lights[2].linearAttenuation = 0.0; - lights[2].quadraticAttenuation = 0.0025; + lights[2].quadraticAttenuation = 0.0; + + highp vec3 color = vertColor; + if (textureEnabled == 1) { + color = texture2D(textureId, vertTexCoord).rgb; + } + color = pow(color, vec3(gamma)); + + float roughness = min(0.99, vertRoughness); gl_FragColor = metalRoughFunction(vec4(color, 1.0), vertMetalness, - vertRoughness, + roughness, vertAmbientOcclusion, vert, - vertView, + normalize(cameraPos - vert), vertNormal); } diff --git a/shaders/pbr.vert b/shaders/pbr.vert deleted file mode 100644 index bbfe78fe..00000000 --- a/shaders/pbr.vert +++ /dev/null @@ -1,27 +0,0 @@ -attribute vec4 vertex; -attribute vec3 normal; -attribute vec3 color; -attribute vec2 texCoord; -attribute float metalness; -attribute float roughness; -varying vec3 vert; -varying vec3 vertNormal; -varying vec3 vertColor; -varying vec2 vertTexCoord; -varying float vertMetalness; -varying float vertRoughness; -varying vec3 vertView; -uniform mat4 projMatrix; -uniform mat4 mvMatrix; -uniform mat3 normalMatrix; -void main() -{ - vert = vertex.xyz; - vertNormal = normalize((projMatrix * mvMatrix * vec4(normal, 1.0)).xyz); - vertColor = color; - vertTexCoord = texCoord; - vertMetalness = metalness; - vertRoughness = roughness; - vertView = (projMatrix * mvMatrix * vec4(0, 0, 0, 1.0)).xyz; - gl_Position = projMatrix * mvMatrix * vertex; -} \ No newline at end of file diff --git a/src/modelshaderprogram.cpp b/src/modelshaderprogram.cpp index b2ed2062..cf29a264 100644 --- a/src/modelshaderprogram.cpp +++ b/src/modelshaderprogram.cpp @@ -23,11 +23,10 @@ ModelShaderProgram::ModelShaderProgram(bool usePBR) this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.core.vert")); this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.core.frag")); } else { + this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.vert")); if (usePBR) { - this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/pbr.vert")); - this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/pbr.frag")); + this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/pbr-qt.frag")); } else { - this->addShaderFromSourceCode(QOpenGLShader::Vertex, loadShaderSource(":/shaders/default.vert")); this->addShaderFromSourceCode(QOpenGLShader::Fragment, loadShaderSource(":/shaders/default.frag")); } } @@ -40,27 +39,27 @@ ModelShaderProgram::ModelShaderProgram(bool usePBR) this->link(); this->bind(); - m_projMatrixLoc = this->uniformLocation("projMatrix"); - m_mvMatrixLoc = this->uniformLocation("mvMatrix"); - m_normalMatrixLoc = this->uniformLocation("normalMatrix"); + m_projectionMatrixLoc = this->uniformLocation("projectionMatrix"); + m_modelMatrixLoc = this->uniformLocation("modelMatrix"); + m_viewMatrixLoc = this->uniformLocation("viewMatrix"); m_lightPosLoc = this->uniformLocation("lightPos"); m_textureIdLoc = this->uniformLocation("textureId"); m_textureEnabledLoc = this->uniformLocation("textureEnabled"); } -int ModelShaderProgram::projMatrixLoc() +int ModelShaderProgram::projectionMatrixLoc() { - return m_projMatrixLoc; + return m_projectionMatrixLoc; } -int ModelShaderProgram::mvMatrixLoc() +int ModelShaderProgram::modelMatrixLoc() { - return m_mvMatrixLoc; + return m_modelMatrixLoc; } -int ModelShaderProgram::normalMatrixLoc() +int ModelShaderProgram::viewMatrixLoc() { - return m_normalMatrixLoc; + return m_viewMatrixLoc; } int ModelShaderProgram::lightPosLoc() diff --git a/src/modelshaderprogram.h b/src/modelshaderprogram.h index eb7da3f8..1cfb20ca 100644 --- a/src/modelshaderprogram.h +++ b/src/modelshaderprogram.h @@ -6,18 +6,18 @@ class ModelShaderProgram : public QOpenGLShaderProgram { public: - ModelShaderProgram(bool usePBR=false); - int projMatrixLoc(); - int mvMatrixLoc(); - int normalMatrixLoc(); + ModelShaderProgram(bool usePBR=true); + int projectionMatrixLoc(); + int modelMatrixLoc(); + int viewMatrixLoc(); int lightPosLoc(); int textureIdLoc(); int textureEnabledLoc(); static const QString &loadShaderSource(const QString &name); private: - int m_projMatrixLoc; - int m_mvMatrixLoc; - int m_normalMatrixLoc; + int m_projectionMatrixLoc; + int m_modelMatrixLoc; + int m_viewMatrixLoc; int m_lightPosLoc; int m_textureIdLoc; int m_textureEnabledLoc; diff --git a/src/modelwidget.cpp b/src/modelwidget.cpp index a6e40621..1d69c658 100644 --- a/src/modelwidget.cpp +++ b/src/modelwidget.cpp @@ -156,12 +156,10 @@ void ModelWidget::paintGL() m_world.rotate(m_yRot / 16.0f, 0, 1, 0); m_world.rotate(m_zRot / 16.0f, 0, 0, 1); - m_program->bind(); - m_program->setUniformValue(m_program->projMatrixLoc(), m_proj); - m_program->setUniformValue(m_program->mvMatrixLoc(), m_camera * m_world); - QMatrix3x3 normalMatrix = m_world.normalMatrix(); - m_program->setUniformValue(m_program->normalMatrixLoc(), normalMatrix); + m_program->setUniformValue(m_program->projectionMatrixLoc(), m_projection); + m_program->setUniformValue(m_program->modelMatrixLoc(), m_world); + m_program->setUniformValue(m_program->viewMatrixLoc(), m_camera); m_program->setUniformValue(m_program->textureEnabledLoc(), 0); m_meshBinder.paint(m_program); @@ -171,8 +169,8 @@ void ModelWidget::paintGL() void ModelWidget::resizeGL(int w, int h) { - m_proj.setToIdentity(); - m_proj.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f); + m_projection.setToIdentity(); + m_projection.perspective(45.0f, GLfloat(w) / h, 0.01f, 100.0f); } void ModelWidget::toggleWireframe() diff --git a/src/modelwidget.h b/src/modelwidget.h index 5daf126f..2b43ff07 100644 --- a/src/modelwidget.h +++ b/src/modelwidget.h @@ -70,7 +70,7 @@ private: private: QPoint m_lastPos; ModelMeshBinder m_meshBinder; - QMatrix4x4 m_proj; + QMatrix4x4 m_projection; QMatrix4x4 m_camera; QMatrix4x4 m_world; static bool m_transparent; diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll index 36a54529..b4ebb2e1 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib index 38a1871c..baca58a3 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x64/meshlite_ffi.dll.lib differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll index 530591cd..9ad41c33 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll differ diff --git a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib index b211e81f..9431d288 100644 Binary files a/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib and b/thirdparty/meshlite/meshlite_unstable_vc14_x86/meshlite_ffi.dll.lib differ