2019-12-22 12:07:42 +00:00
|
|
|
#version 330
|
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
|
|
|
**
|
|
|
|
** This file is part of the Qt3D module of the Qt Toolkit.
|
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:BSD$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** BSD License Usage
|
|
|
|
** Alternatively, you may use this file under the terms of the BSD license
|
|
|
|
** as follows:
|
|
|
|
**
|
|
|
|
** "Redistribution and use in source and binary forms, with or without
|
|
|
|
** modification, are permitted provided that the following conditions are
|
|
|
|
** met:
|
|
|
|
** * Redistributions of source code must retain the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer.
|
|
|
|
** * Redistributions in binary form must reproduce the above copyright
|
|
|
|
** notice, this list of conditions and the following disclaimer in
|
|
|
|
** the documentation and/or other materials provided with the
|
|
|
|
** distribution.
|
|
|
|
** * Neither the name of The Qt Company Ltd nor the names of its
|
|
|
|
** contributors may be used to endorse or promote products derived
|
|
|
|
** from this software without specific prior written permission.
|
|
|
|
**
|
|
|
|
**
|
|
|
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
// Please note that, this file "pbr.frag" is copied and slightly modified from the Qt3D's pbr shader "metalrough.inc.frag"
|
|
|
|
// https://github.com/qt/qt3d/blob/5.11/src/extras/shaders/gl3/metalrough.inc.frag
|
|
|
|
|
|
|
|
// Exposure correction
|
2020-03-18 14:17:09 +00:00
|
|
|
float exposure;
|
2019-12-22 12:07:42 +00:00
|
|
|
// Gamma correction
|
2020-03-18 14:17:09 +00:00
|
|
|
float gamma;
|
|
|
|
|
|
|
|
in vec3 vert;
|
|
|
|
in vec3 vertRaw;
|
|
|
|
in vec3 vertNormal;
|
|
|
|
in vec3 vertColor;
|
|
|
|
in vec2 vertTexCoord;
|
|
|
|
in float vertMetalness;
|
|
|
|
in float vertRoughness;
|
|
|
|
in vec3 cameraPos;
|
|
|
|
in vec3 firstLightPos;
|
|
|
|
in vec3 secondLightPos;
|
|
|
|
in vec3 thirdLightPos;
|
|
|
|
in float vertAlpha;
|
|
|
|
out vec4 fragColor;
|
|
|
|
uniform sampler2D textureId;
|
|
|
|
uniform int textureEnabled;
|
|
|
|
uniform sampler2D normalMapId;
|
|
|
|
uniform int normalMapEnabled;
|
|
|
|
uniform sampler2D metalnessRoughnessAmbientOcclusionMapId;
|
|
|
|
uniform int metalnessMapEnabled;
|
|
|
|
uniform int roughnessMapEnabled;
|
|
|
|
uniform int ambientOcclusionMapEnabled;
|
|
|
|
uniform int mousePickEnabled;
|
|
|
|
uniform vec3 mousePickTargetPosition;
|
|
|
|
uniform float mousePickRadius;
|
|
|
|
uniform samplerCube environmentIrradianceMapId;
|
|
|
|
uniform int environmentIrradianceMapEnabled;
|
|
|
|
uniform samplerCube environmentSpecularMapId;
|
|
|
|
uniform int environmentSpecularMapEnabled;
|
2020-04-02 10:39:57 +00:00
|
|
|
uniform int toonShadingEnabled;
|
|
|
|
uniform int renderPurpose;
|
|
|
|
uniform int toonEdgeEnabled;
|
|
|
|
uniform float screenWidth;
|
|
|
|
uniform float screenHeight;
|
|
|
|
uniform sampler2D toonNormalMapId;
|
|
|
|
uniform sampler2D toonDepthMapId;
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
const int MAX_LIGHTS = 8;
|
|
|
|
const int TYPE_POINT = 0;
|
|
|
|
const int TYPE_DIRECTIONAL = 1;
|
|
|
|
const int TYPE_SPOT = 2;
|
|
|
|
struct Light {
|
|
|
|
int type;
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 position;
|
|
|
|
vec3 color;
|
|
|
|
float intensity;
|
|
|
|
vec3 direction;
|
|
|
|
float constantAttenuation;
|
|
|
|
float linearAttenuation;
|
|
|
|
float quadraticAttenuation;
|
|
|
|
float cutOffAngle;
|
2019-12-22 12:07:42 +00:00
|
|
|
};
|
|
|
|
int lightCount;
|
|
|
|
Light lights[MAX_LIGHTS];
|
|
|
|
|
2020-04-02 10:39:57 +00:00
|
|
|
float sobel_v[9];
|
|
|
|
float sobel_h[9];
|
|
|
|
|
|
|
|
float normalEdgeSobel()
|
|
|
|
{
|
|
|
|
sobel_v[0] = -1.0;
|
|
|
|
sobel_v[1] = 0.0;
|
|
|
|
sobel_v[2] = 1.0;
|
|
|
|
sobel_v[3] = -2.0;
|
|
|
|
sobel_v[4] = 0.0;
|
|
|
|
sobel_v[5] = 2.0;
|
|
|
|
sobel_v[6] = -1.0;
|
|
|
|
sobel_v[7] = 0.0;
|
|
|
|
sobel_v[8] = 1.0;
|
|
|
|
|
|
|
|
sobel_h[0] = -1.0;
|
|
|
|
sobel_h[1] = -2.0;
|
|
|
|
sobel_h[2] = -1.0;
|
|
|
|
sobel_h[3] = 0.0;
|
|
|
|
sobel_h[4] = 0.0;
|
|
|
|
sobel_h[5] = 0.0;
|
|
|
|
sobel_h[6] = 1.0;
|
|
|
|
sobel_h[7] = 2.0;
|
|
|
|
sobel_h[8] = 1.0;
|
|
|
|
|
|
|
|
// vec2 coord = gl_TexCoord[0].st;
|
|
|
|
vec2 coord = vec2(gl_FragCoord.x / screenWidth, 1.0 - gl_FragCoord.y / screenHeight);
|
|
|
|
//float len = length(coord);
|
|
|
|
|
|
|
|
float sx = 1.0 / screenWidth;
|
|
|
|
float sy = 1.0 / screenHeight;
|
|
|
|
float n[9];
|
|
|
|
vec3 ref = vec3(1.0, 1.0, 1.0);
|
|
|
|
|
|
|
|
n[0] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y - sy)).rgb, ref);
|
|
|
|
n[1] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y - sy)).rgb, ref);
|
|
|
|
n[2] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y - sy)).rgb, ref);
|
|
|
|
n[3] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y)).rgb, ref);
|
|
|
|
n[4] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y)).rgb, ref);
|
|
|
|
n[5] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y)).rgb, ref);
|
|
|
|
n[6] = dot(texture(toonNormalMapId, vec2(coord.x - sx, coord.y + sy)).rgb, ref);
|
|
|
|
n[7] = dot(texture(toonNormalMapId, vec2(coord.x, coord.y + sy)).rgb, ref);
|
|
|
|
n[8] = dot(texture(toonNormalMapId, vec2(coord.x + sx, coord.y + sy)).rgb, ref);
|
|
|
|
|
|
|
|
float v, h;
|
|
|
|
|
|
|
|
v = 0.0;
|
|
|
|
h = 0.0;
|
|
|
|
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
|
|
v += sobel_v[i] * n[i];
|
|
|
|
h += sobel_h[i] * n[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
float enhanceFactor = 1.0;
|
|
|
|
|
|
|
|
float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
|
|
|
|
return smoothstep(0.0, 1.0, r);
|
|
|
|
}
|
|
|
|
|
|
|
|
float depthEdgeSobel()
|
|
|
|
{
|
|
|
|
sobel_v[0] = -1;
|
|
|
|
sobel_v[1] = 0;
|
|
|
|
sobel_v[2] = 1;
|
|
|
|
sobel_v[3] = -2;
|
|
|
|
sobel_v[4] = 0;
|
|
|
|
sobel_v[5] = 2;
|
|
|
|
sobel_v[6] = -1;
|
|
|
|
sobel_v[7] = 0;
|
|
|
|
sobel_v[8] = 1;
|
|
|
|
|
|
|
|
sobel_h[0] = -1;
|
|
|
|
sobel_h[1] = -2;
|
|
|
|
sobel_h[2] = -1;
|
|
|
|
sobel_h[3] = 0;
|
|
|
|
sobel_h[4] = 0;
|
|
|
|
sobel_h[5] = 0;
|
|
|
|
sobel_h[6] = 1;
|
|
|
|
sobel_h[7] = 2;
|
|
|
|
sobel_h[8] = 1;
|
|
|
|
|
|
|
|
// vec2 coord = gl_TexCoord[0].st;
|
|
|
|
vec2 coord = vec2(gl_FragCoord.x / screenWidth, 1.0 - gl_FragCoord.y / screenHeight);
|
|
|
|
//float len = length(coord);
|
|
|
|
|
|
|
|
float sx = 1.0 / screenWidth;
|
|
|
|
float sy = 1.0 / screenHeight;
|
|
|
|
float n[9];
|
|
|
|
|
|
|
|
n[0] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y - sy)).r;
|
|
|
|
n[1] = texture(toonDepthMapId, vec2(coord.x, coord.y - sy)).r;
|
|
|
|
n[2] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y - sy)).r;
|
|
|
|
n[3] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y)).r;
|
|
|
|
n[4] = texture(toonDepthMapId, vec2(coord.x, coord.y)).r;
|
|
|
|
n[5] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y)).r;
|
|
|
|
n[6] = texture(toonDepthMapId, vec2(coord.x - sx, coord.y + sy)).r;
|
|
|
|
n[7] = texture(toonDepthMapId, vec2(coord.x, coord.y + sy)).r;
|
|
|
|
n[8] = texture(toonDepthMapId, vec2(coord.x + sx, coord.y + sy)).r;
|
|
|
|
|
|
|
|
float v, h;
|
|
|
|
|
|
|
|
v = 0.0;
|
|
|
|
h = 0.0;
|
|
|
|
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
|
|
v += sobel_v[i] * n[i];
|
|
|
|
h += sobel_h[i] * n[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
float enhanceFactor = 10.0;
|
|
|
|
|
|
|
|
float r = sqrt(v * v * enhanceFactor + h * h * enhanceFactor);
|
|
|
|
return smoothstep(0.0, 1.0, r);
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
int mipLevelCount(const in samplerCube cube)
|
|
|
|
{
|
|
|
|
int baseSize = textureSize(cube, 0).x;
|
|
|
|
int nMips = int(log2(float(baseSize > 0 ? baseSize : 1))) + 1;
|
|
|
|
return nMips;
|
|
|
|
}
|
|
|
|
|
|
|
|
float remapRoughness(const in float roughness)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// As per page 14 of
|
|
|
|
// http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
|
|
|
|
// we remap the roughness to give a more perceptually linear response
|
|
|
|
// of "bluriness" as a function of the roughness specified by the user.
|
|
|
|
// r = roughness^2
|
2020-03-18 14:17:09 +00:00
|
|
|
float maxSpecPower;
|
|
|
|
float minRoughness;
|
2019-12-22 12:07:42 +00:00
|
|
|
maxSpecPower = 999999.0;
|
|
|
|
minRoughness = sqrt(2.0 / (maxSpecPower + 2.0));
|
|
|
|
return max(roughness * roughness, minRoughness);
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float alphaToMipLevel(float alpha)
|
|
|
|
{
|
|
|
|
float specPower = 2.0 / (alpha * alpha) - 2.0;
|
|
|
|
|
|
|
|
// We use the mip level calculation from Lys' default power drop, which in
|
|
|
|
// turn is a slight modification of that used in Marmoset Toolbag. See
|
|
|
|
// https://docs.knaldtech.com/doku.php?id=specular_lys for details.
|
|
|
|
// For now we assume a max specular power of 999999 which gives
|
|
|
|
// maxGlossiness = 1.
|
|
|
|
const float k0 = 0.00098;
|
|
|
|
const float k1 = 0.9921;
|
|
|
|
float glossiness = (pow(2.0, -10.0 / sqrt(specPower)) - k0) / k1;
|
|
|
|
|
|
|
|
// TODO: Optimize by doing this on CPU and set as
|
|
|
|
// uniform int environmentSpecularMapIdMipLevels say (if present in shader).
|
|
|
|
// Lookup the number of mips in the specular envmap
|
|
|
|
int mipLevels = mipLevelCount(environmentSpecularMapId);
|
|
|
|
|
|
|
|
// Offset of smallest miplevel we should use (corresponds to specular
|
|
|
|
// power of 1). I.e. in the 32x32 sized mip.
|
|
|
|
const float mipOffset = 5.0;
|
|
|
|
|
|
|
|
// The final factor is really 1 - g / g_max but as mentioned above g_max
|
|
|
|
// is 1 by definition here so we can avoid the division. If we make the
|
|
|
|
// max specular power for the spec map configurable, this will need to
|
|
|
|
// be handled properly.
|
|
|
|
float mipLevel = (mipLevels - 1.0 - mipOffset) * (1.0 - glossiness);
|
|
|
|
return mipLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// Blinn-Phong approximation - see
|
|
|
|
// http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
|
2020-03-18 14:17:09 +00:00
|
|
|
float specPower = 2.0 / (alpha * alpha) - 2.0;
|
2019-12-22 12:07:42 +00:00
|
|
|
return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// Calculate the Fresnel effect value
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 f = color;
|
|
|
|
vec3 F = f + (1.0 - f) * pow(1.0 - cosineFactor, 5.0);
|
2019-12-22 12:07:42 +00:00
|
|
|
return clamp(F, f, vec3(1.0));
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float geometricModel(const in float lDotN,
|
|
|
|
const in float vDotN,
|
|
|
|
const in vec3 h)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// Implicit geometric model (equal to denominator in specular model).
|
|
|
|
// This currently assumes that there is no attenuation by geometric shadowing or
|
|
|
|
// masking according to the microfacet theory.
|
|
|
|
return lDotN * vDotN;
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 specularModel(const in vec3 F0,
|
|
|
|
const in float sDotH,
|
|
|
|
const in float sDotN,
|
|
|
|
const in float vDotN,
|
|
|
|
const in vec3 n,
|
|
|
|
const in vec3 h)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// Clamp sDotN and vDotN to small positive value to prevent the
|
|
|
|
// denominator in the reflection equation going to infinity. Balance this
|
|
|
|
// by using the clamped values in the geometric factor function to
|
|
|
|
// avoid ugly seams in the specular lighting.
|
2020-03-18 14:17:09 +00:00
|
|
|
float sDotNPrime = max(sDotN, 0.001);
|
|
|
|
float vDotNPrime = max(vDotN, 0.001);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 F = fresnelFactor(F0, sDotH);
|
|
|
|
float G = geometricModel(sDotNPrime, vDotNPrime, h);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 cSpec = F * G / (4.0 * sDotNPrime * vDotNPrime);
|
2019-12-22 12:07:42 +00:00
|
|
|
return clamp(cSpec, vec3(0.0), vec3(1.0));
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 pbrModel(const in int lightIndex,
|
|
|
|
const in vec3 wPosition,
|
|
|
|
const in vec3 wNormal,
|
|
|
|
const in vec3 wView,
|
|
|
|
const in vec3 baseColor,
|
|
|
|
const in float metalness,
|
|
|
|
const in float alpha,
|
|
|
|
const in float ambientOcclusion)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
// Calculate some useful quantities
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 n = wNormal;
|
|
|
|
vec3 s = vec3(0.0);
|
|
|
|
vec3 v = wView;
|
|
|
|
vec3 h = vec3(0.0);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float vDotN = dot(v, n);
|
|
|
|
float sDotN = 0.0;
|
|
|
|
float sDotH = 0.0;
|
|
|
|
float att = 1.0;
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
if (lights[lightIndex].type != TYPE_DIRECTIONAL) {
|
|
|
|
// Point and Spot lights
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 sUnnormalized = vec3(lights[lightIndex].position) - wPosition;
|
2019-12-22 12:07:42 +00:00
|
|
|
s = normalize(sUnnormalized);
|
|
|
|
|
|
|
|
// Calculate the attenuation factor
|
|
|
|
sDotN = dot(s, n);
|
|
|
|
if (sDotN > 0.0) {
|
|
|
|
if (lights[lightIndex].constantAttenuation != 0.0
|
|
|
|
|| lights[lightIndex].linearAttenuation != 0.0
|
|
|
|
|| lights[lightIndex].quadraticAttenuation != 0.0) {
|
2020-03-18 14:17:09 +00:00
|
|
|
float dist = length(sUnnormalized);
|
2019-12-22 12:07:42 +00:00
|
|
|
att = 1.0 / (lights[lightIndex].constantAttenuation +
|
|
|
|
lights[lightIndex].linearAttenuation * dist +
|
|
|
|
lights[lightIndex].quadraticAttenuation * dist * dist);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The light direction is in world space already
|
|
|
|
if (lights[lightIndex].type == TYPE_SPOT) {
|
|
|
|
// Check if fragment is inside or outside of the spot light cone
|
|
|
|
if (degrees(acos(dot(-s, lights[lightIndex].direction))) > lights[lightIndex].cutOffAngle)
|
|
|
|
sDotN = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Directional lights
|
|
|
|
// The light direction is in world space already
|
|
|
|
s = normalize(-lights[lightIndex].direction);
|
|
|
|
sDotN = dot(s, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
h = normalize(s + v);
|
|
|
|
sDotH = dot(s, h);
|
|
|
|
|
|
|
|
// Calculate diffuse component
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 diffuseColor = (1.0 - metalness) * baseColor * lights[lightIndex].color;
|
|
|
|
vec3 diffuse = diffuseColor * max(sDotN, 0.0) / 3.14159;
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Calculate specular component
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 dielectricColor = vec3(0.04);
|
|
|
|
vec3 F0 = mix(dielectricColor, baseColor, metalness);
|
|
|
|
vec3 specularFactor = vec3(0.0);
|
2019-12-22 12:07:42 +00:00
|
|
|
if (sDotN > 0.0) {
|
|
|
|
specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
|
|
|
|
specularFactor *= normalDistribution(n, h, alpha);
|
|
|
|
}
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 specularColor = lights[lightIndex].color;
|
|
|
|
vec3 specular = specularColor * specularFactor;
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Blend between diffuse and specular to conserver energy
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 color = att * lights[lightIndex].intensity * (specular + diffuse * (vec3(1.0) - specular));
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Reduce by ambient occlusion amount
|
|
|
|
color *= ambientOcclusion;
|
|
|
|
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 pbrIblModel(const in vec3 wNormal,
|
|
|
|
const in vec3 wView,
|
|
|
|
const in vec3 baseColor,
|
|
|
|
const in float metalness,
|
|
|
|
const in float alpha,
|
|
|
|
const in float ambientOcclusion)
|
|
|
|
{
|
|
|
|
// Calculate reflection direction of view vector about surface normal
|
|
|
|
// vector in world space. This is used in the fragment shader to sample
|
|
|
|
// from the environment textures for a light source. This is equivalent
|
|
|
|
// to the l vector for punctual light sources. Armed with this, calculate
|
|
|
|
// the usual factors needed
|
|
|
|
vec3 n = wNormal;
|
|
|
|
vec3 l = reflect(-wView, n);
|
|
|
|
vec3 v = wView;
|
|
|
|
vec3 h = normalize(l + v);
|
|
|
|
float vDotN = dot(v, n);
|
|
|
|
float lDotN = dot(l, n);
|
|
|
|
float lDotH = dot(l, h);
|
|
|
|
|
|
|
|
// Calculate diffuse component
|
|
|
|
vec3 diffuseColor = (1.0 - metalness) * baseColor;
|
|
|
|
vec3 diffuse = diffuseColor * texture(environmentIrradianceMapId, l).rgb;
|
|
|
|
|
|
|
|
// Calculate specular component
|
|
|
|
vec3 dielectricColor = vec3(0.04);
|
|
|
|
vec3 F0 = mix(dielectricColor, baseColor, metalness);
|
|
|
|
vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
|
|
|
|
|
|
|
|
float lod = alphaToMipLevel(alpha);
|
|
|
|
//#define DEBUG_SPECULAR_LODS
|
|
|
|
#ifdef DEBUG_SPECULAR_LODS
|
|
|
|
if (lod > 7.0)
|
|
|
|
return vec3(1.0, 0.0, 0.0);
|
|
|
|
else if (lod > 6.0)
|
|
|
|
return vec3(1.0, 0.333, 0.0);
|
|
|
|
else if (lod > 5.0)
|
|
|
|
return vec3(1.0, 1.0, 0.0);
|
|
|
|
else if (lod > 4.0)
|
|
|
|
return vec3(0.666, 1.0, 0.0);
|
|
|
|
else if (lod > 3.0)
|
|
|
|
return vec3(0.0, 1.0, 0.666);
|
|
|
|
else if (lod > 2.0)
|
|
|
|
return vec3(0.0, 0.666, 1.0);
|
|
|
|
else if (lod > 1.0)
|
|
|
|
return vec3(0.0, 0.0, 1.0);
|
|
|
|
else if (lod > 0.0)
|
|
|
|
return vec3(1.0, 0.0, 1.0);
|
|
|
|
#endif
|
|
|
|
vec3 specularSkyColor = textureLod(environmentSpecularMapId, l, lod).rgb;
|
|
|
|
vec3 specular = specularSkyColor * specularFactor;
|
|
|
|
|
|
|
|
// Blend between diffuse and specular to conserve energy
|
|
|
|
vec3 color = specular + diffuse * (vec3(1.0) - specularFactor);
|
|
|
|
|
|
|
|
// Reduce by ambient occlusion amount
|
|
|
|
color *= ambientOcclusion;
|
|
|
|
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
vec3 toneMap(const in vec3 c)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
return c / (c + vec3(1.0));
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 gammaCorrect(const in vec3 color)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
|
|
|
return pow(color, vec3(1.0 / gamma));
|
|
|
|
}
|
|
|
|
|
2020-03-29 10:06:45 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec4 metalRoughFunction(const in vec4 baseColor,
|
|
|
|
const in float metalness,
|
|
|
|
const in float roughness,
|
|
|
|
const in float ambientOcclusion,
|
|
|
|
const in vec3 worldPosition,
|
|
|
|
const in vec3 worldView,
|
|
|
|
const in vec3 worldNormal)
|
2019-12-22 12:07:42 +00:00
|
|
|
{
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 cLinear = vec3(0.0);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Remap roughness for a perceptually more linear correspondence
|
2020-03-18 14:17:09 +00:00
|
|
|
float alpha = remapRoughness(roughness);
|
|
|
|
|
2020-04-02 10:39:57 +00:00
|
|
|
if (toonShadingEnabled != 1) {
|
2020-03-29 10:06:45 +00:00
|
|
|
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));
|
2020-04-02 10:39:57 +00:00
|
|
|
|
2020-04-08 10:09:16 +00:00
|
|
|
if (toonEdgeEnabled > 0) {
|
2020-04-02 10:39:57 +00:00
|
|
|
float depthEdge = depthEdgeSobel();
|
|
|
|
float normalEdge = normalEdgeSobel();
|
2020-10-17 01:10:08 +00:00
|
|
|
if (depthEdge >= 0.009 || normalEdge >= 0.7) {
|
2020-04-02 10:39:57 +00:00
|
|
|
cLinear = hsv2rgb(vec3(hsv.r, hsv.g, hsv.b * 0.02));
|
2020-04-08 10:09:16 +00:00
|
|
|
} else if (toonEdgeEnabled == 2) {
|
|
|
|
return vec4(0.0, 0.0, 0.0, 0.0);
|
2020-04-02 10:39:57 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-22 12:07:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply exposure correction
|
2020-11-17 14:31:51 +00:00
|
|
|
//cLinear *= pow(2.0, exposure);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Apply simple (Reinhard) tonemap transform to get into LDR range [0, 1]
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 cToneMapped = toneMap(cLinear);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Apply gamma correction prior to display
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 cGamma = gammaCorrect(cToneMapped);
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
return vec4(cGamma, baseColor.a);
|
|
|
|
}
|
|
|
|
|
2018-10-03 05:52:35 +00:00
|
|
|
void main()
|
|
|
|
{
|
2019-12-22 12:07:42 +00:00
|
|
|
// FIXME: don't hard code here
|
|
|
|
exposure = 1.0;
|
|
|
|
gamma = 2.2;
|
|
|
|
|
|
|
|
// Light settings:
|
|
|
|
// 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 = firstLightPos;
|
|
|
|
lights[0].color = vec3(1.0, 1.0, 1.0);
|
2020-11-17 14:31:51 +00:00
|
|
|
lights[0].intensity = 3.0;
|
2020-10-18 05:03:15 +00:00
|
|
|
lights[0].constantAttenuation = 1.0;
|
2019-12-22 12:07:42 +00:00
|
|
|
lights[0].linearAttenuation = 0.0;
|
2020-10-18 05:03:15 +00:00
|
|
|
lights[0].quadraticAttenuation = 0.0025;
|
2019-12-22 12:07:42 +00:00
|
|
|
|
|
|
|
// Fill light
|
|
|
|
lights[1].type = TYPE_POINT;
|
|
|
|
lights[1].position = secondLightPos;
|
|
|
|
lights[1].color = vec3(1.0, 1.0, 1.0);
|
2020-10-18 05:03:15 +00:00
|
|
|
lights[1].intensity = 0.1;
|
2019-12-22 12:07:42 +00:00
|
|
|
lights[1].constantAttenuation = 0.0;
|
|
|
|
lights[1].linearAttenuation = 0.0;
|
|
|
|
lights[1].quadraticAttenuation = 0.0;
|
|
|
|
|
|
|
|
// Rim light
|
|
|
|
lights[2].type = TYPE_POINT;
|
|
|
|
lights[2].position = thirdLightPos;
|
|
|
|
lights[2].color = vec3(1.0, 1.0, 1.0);
|
2020-10-18 05:03:15 +00:00
|
|
|
lights[2].intensity = 0.05;
|
2019-12-22 12:07:42 +00:00
|
|
|
lights[2].constantAttenuation = 0.0;
|
|
|
|
lights[2].linearAttenuation = 0.0;
|
|
|
|
lights[2].quadraticAttenuation = 0.0;
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 color = vertColor;
|
|
|
|
float alpha = vertAlpha;
|
2018-10-03 05:52:35 +00:00
|
|
|
if (textureEnabled == 1) {
|
2020-03-18 14:17:09 +00:00
|
|
|
vec4 textColor = texture(textureId, vertTexCoord);
|
2019-12-22 12:07:42 +00:00
|
|
|
color = textColor.rgb;
|
|
|
|
alpha = textColor.a;
|
|
|
|
}
|
|
|
|
if (mousePickEnabled == 1) {
|
2020-11-17 14:31:51 +00:00
|
|
|
float dist = distance(mousePickTargetPosition, vertRaw);
|
|
|
|
if (dist <= mousePickRadius && dist >= mousePickRadius * 0.9) {
|
2019-12-22 12:07:42 +00:00
|
|
|
color = color + vec3(0.99, 0.4, 0.13);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
color = pow(color, vec3(gamma));
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
vec3 normal = vertNormal;
|
2019-12-22 12:07:42 +00:00
|
|
|
if (normalMapEnabled == 1) {
|
|
|
|
normal = texture(normalMapId, vertTexCoord).rgb;
|
|
|
|
normal = normalize(normal * 2.0 - 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Red: Ambient Occlusion
|
|
|
|
// Green: Roughness
|
|
|
|
// Blue: Metallic
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float metalness = vertMetalness;
|
2019-12-22 12:07:42 +00:00
|
|
|
if (metalnessMapEnabled == 1) {
|
|
|
|
metalness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).b;
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float roughness = vertRoughness;
|
2019-12-22 12:07:42 +00:00
|
|
|
if (roughnessMapEnabled == 1) {
|
|
|
|
roughness = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).g;
|
|
|
|
}
|
|
|
|
|
2020-03-18 14:17:09 +00:00
|
|
|
float ambientOcclusion = 1.0;
|
2019-12-22 12:07:42 +00:00
|
|
|
if (ambientOcclusionMapEnabled == 1) {
|
|
|
|
ambientOcclusion = texture(metalnessRoughnessAmbientOcclusionMapId, vertTexCoord).r;
|
2018-10-03 05:52:35 +00:00
|
|
|
}
|
2019-12-22 12:07:42 +00:00
|
|
|
|
2020-10-18 05:03:15 +00:00
|
|
|
roughness = min(0.99, roughness);
|
2020-03-18 14:17:09 +00:00
|
|
|
if (environmentIrradianceMapEnabled != 1) {
|
|
|
|
metalness = min(0.99, metalness);
|
|
|
|
}
|
2020-04-02 10:39:57 +00:00
|
|
|
|
|
|
|
if (renderPurpose == 0) {
|
|
|
|
fragColor = metalRoughFunction(vec4(color, alpha),
|
|
|
|
metalness,
|
|
|
|
roughness,
|
|
|
|
ambientOcclusion,
|
|
|
|
vert,
|
|
|
|
normalize(cameraPos - vert),
|
|
|
|
normal);
|
|
|
|
} else if (renderPurpose == 1) {
|
|
|
|
fragColor = vec4(normal, 1.0);
|
|
|
|
} else if (renderPurpose == 2) {
|
|
|
|
fragColor = vec4(vec3(gl_FragCoord.w), 1.0);
|
|
|
|
}
|
2019-12-22 12:07:42 +00:00
|
|
|
}
|