blob: ce489472cc3ebd1013b574b9b78ad9f71e271b0f [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "lib/escher/impl/model_moment_shadow_map_lighting_pass.h"
#include "lib/escher/impl/model_data.h"
namespace {
constexpr char kFragmentShaderSourceCode[] = R"GLSL(
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec2 inUV;
layout(location = 1) in vec4 shadowPos;
layout(set = 0, binding = 0) uniform PerModel {
vec2 frag_coord_to_uv_multiplier;
float time;
vec3 ambient_light_intensity;
vec3 direct_light_intensity;
vec2 shadow_map_uv_multiplier;
};
layout(set = 0, binding = 1) uniform sampler2D shadow_map_tex;
layout(set = 1, binding = 0) uniform PerObject {
mat4 camera_transform;
mat4 light_transform;
vec4 color;
};
layout(set = 1, binding = 1) uniform sampler2D material_tex;
layout(location = 0) out vec4 outColor;
// Solve x for Bx = z, where B is symmetric and positive semi-definite.
// https://en.wikipedia.org/wiki/Cholesky_decomposition
// https://en.wikipedia.org/wiki/Triangular_matrix
vec3 solveLinear(mat3 B, vec3 z) {
// Compute the lower triangular matrix L.
float D1 = B[0][0];
float invD1 = 1. / B[0][0];
float L21 = B[1][0] * invD1;
float L31 = B[2][0] * invD1;
float D2 = B[1][1] - L21 * L21 * D1;
float invD2 = 1. / D2;
float L32 = (B[2][1] - L31 * L21 * D1) * invD2;
float D3 = B[2][2] - L31 * L31 * D1 - L32 * L32 * D2;
float invD3 = 1. / D3;
// Solve (D * L.T * x) with forward substitution.
vec3 y;
y[0] = z[0];
y[1] = z[1] - L21 * y[0];
y[2] = z[2] - L31 * y[0] - L32 * y[1];
// Scale y to get (L.T * x).
y *= vec3(invD1, invD2, invD3);
// Solve x with backward substitution.
vec3 x;
x[2] = y[2];
x[1] = y[1] - L32 * x[2];
x[0] = y[0] - L21 * x[1] - L31 * x[2];
return x;
}
float computeVisibility(vec4 moments, float fragLightDist) {
// The magic numbers come from paper
// http://cg.cs.uni-bonn.de/aigaion2root/attachments/MomentShadowMapping.pdf
// The alpha (kMomentBias here) from paper is 3e-5, but that will cause a lot
// shadow acne for us.
const float kMomentBias = 4e-4;
const float kDepthBias = 1e-6;
vec4 b = mix(moments, vec4(.5, .5, .5, .5), kMomentBias);
mat3 B = mat3(
1.0, b.x, b.y,
b.x, b.y, b.z,
b.y, b.z, b.w);
float zf = fragLightDist - kDepthBias;
vec3 z = vec3(1., zf, zf * zf);
vec3 c = solveLinear(B, z);
float sqrtDelta = sqrt(max(0., c.y * c.y - 4. * c.z * c.x));
float d1 = (-c.y - sqrtDelta) / (2. * c.z);
float d2 = (-c.y + sqrtDelta) / (2. * c.z);
if (d2 < d1) {
float tmp = d1;
d1 = d2;
d2 = tmp;
}
if (zf <= d1) {
return 1.;
} else if (zf <= d2) {
return 1. - (zf * d2 - b.x * (zf + d2) + b.y) / ((d2 - d1) * (zf - d1));
} else {
return (d1 * d2 - b.x * (d1 + d2) + b.y) / ((zf - d1) * (zf - d2));
}
}
// The magic weights come from paper
// http://cg.cs.uni-bonn.de/aigaion2root/attachments/MomentShadowMapping.pdf
vec4 decode(vec4 y) {
const mat4 kWeight = mat4(
0.2227744146, 0.1549679261, 0.1451988946, 0.163127443,
0.0771972861, 0.1394629426, 0.2120202157, 0.2591432266,
0.7926986636, 0.7963415838, 0.7258694464, 0.6539092497,
0.0319417555, -0.1722823173, -0.2758014811, -0.3376131734);
const vec4 kBias = vec4(0.035955884801, 0., 0., 0.);
return kWeight * (y - kBias);
}
void main() {
vec4 shadowUV = shadowPos / shadowPos.w;
float fragLightDist = shadowUV.z;
vec4 shadowMapSample = texture(shadow_map_tex, shadowUV.xy);
vec4 moments = decode(shadowMapSample);
float visibility = computeVisibility(moments, fragLightDist);
visibility = clamp(visibility, 0., 1.);
vec3 light = ambient_light_intensity + visibility * direct_light_intensity;
outColor = vec4(light, 1.) * color * texture(material_tex, inUV);
}
)GLSL";
} // namespace
namespace escher {
namespace impl {
ModelMomentShadowMapLightingPass::ModelMomentShadowMapLightingPass(
ResourceRecycler* recycler, ModelDataPtr model_data,
vk::Format color_format, vk::Format depth_format, uint32_t sample_count)
: ModelShadowMapLightingPass(recycler, std::move(model_data), color_format,
depth_format, sample_count) {}
std::string ModelMomentShadowMapLightingPass::GetFragmentShaderSourceCode(
const ModelPipelineSpec& spec) {
return kFragmentShaderSourceCode;
}
} // namespace impl
} // namespace escher