Implement SSAO bent normals
We now have an option for evaluating the bent normals from SSAO. This is used to improve specular AO. Currently these bent normals are not used for diffuse lighting because they're "flat" (face normals), which is too distracting. The bent normal buffer is stored in the SSAO texture, in a separate layer. This is the first post processing effect that uses 2D array in order to limit our sampler usage. This CL also changes how we handle SPECULAR_AO_BENT_NORMALS when material bent normals are not available: we're now using the "Cones" algorithm with the regular normal, instead of Lagarde's approximation.
This commit is contained in:
committed by
Mathias Agopian
parent
282b6f72c1
commit
4e9dcb45ea
@@ -5,6 +5,9 @@ A new header is inserted each time a *tag* is created.
|
||||
|
||||
## v1.11.3 (currently main branch)
|
||||
|
||||
- engine: Option to automatically compute bent normals from SSAO & apply to specular AO
|
||||
[⚠️ **Material breakage**].
|
||||
|
||||
## v1.11.2
|
||||
|
||||
- engine: New API: `ColorGrading::Builder::toneMapper(const ToneMapper*)`.
|
||||
|
||||
@@ -217,7 +217,8 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetAmbientOcclusionOptions(JNIEnv*, jclass,
|
||||
jlong nativeView, jfloat radius, jfloat bias, jfloat power, jfloat resolution, jfloat intensity,
|
||||
jfloat bilateralThreshold,
|
||||
jint quality, jint lowPassFilter, jint upsampling, jboolean enabled, jfloat minHorizonAngleRad) {
|
||||
jint quality, jint lowPassFilter, jint upsampling, jboolean enabled, jboolean bentNormals,
|
||||
jfloat minHorizonAngleRad) {
|
||||
View* view = (View*) nativeView;
|
||||
View::AmbientOcclusionOptions options = view->getAmbientOcclusionOptions();
|
||||
options.radius = radius;
|
||||
@@ -230,6 +231,7 @@ Java_com_google_android_filament_View_nSetAmbientOcclusionOptions(JNIEnv*, jclas
|
||||
options.lowPassFilter = (View::QualityLevel)lowPassFilter;
|
||||
options.upsampling = (View::QualityLevel)upsampling;
|
||||
options.enabled = (bool)enabled;
|
||||
options.bentNormals = (bool)bentNormals;
|
||||
options.minHorizonAngleRad = minHorizonAngleRad;
|
||||
view->setAmbientOcclusionOptions(options);
|
||||
}
|
||||
|
||||
@@ -208,6 +208,11 @@ public class View {
|
||||
*/
|
||||
public boolean enabled = false;
|
||||
|
||||
/**
|
||||
* enables bent normals computation from AO, and specular AO
|
||||
*/
|
||||
public boolean bentNormals = false;
|
||||
|
||||
/**
|
||||
* Minimal angle to consider in radian. This is used to reduce the creases that can
|
||||
* appear due to insufficiently tessellated geometry.
|
||||
@@ -1390,7 +1395,7 @@ public class View {
|
||||
nSetAmbientOcclusionOptions(getNativeObject(), options.radius, options.bias, options.power,
|
||||
options.resolution, options.intensity, options.bilateralThreshold,
|
||||
options.quality.ordinal(), options.lowPassFilter.ordinal(), options.upsampling.ordinal(),
|
||||
options.enabled, options.minHorizonAngleRad);
|
||||
options.enabled, options.bentNormals, options.minHorizonAngleRad);
|
||||
nSetSSCTOptions(getNativeObject(), options.ssctLightConeRad, options.ssctStartTraceDistance,
|
||||
options.ssctContactDistanceMax, options.ssctIntensity,
|
||||
options.ssctLightDirection[0], options.ssctLightDirection[1], options.ssctLightDirection[2],
|
||||
@@ -1567,7 +1572,7 @@ public class View {
|
||||
private static native boolean nIsFrontFaceWindingInverted(long nativeView);
|
||||
private static native void nSetAmbientOcclusion(long nativeView, int ordinal);
|
||||
private static native int nGetAmbientOcclusion(long nativeView);
|
||||
private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, float bilateralThreshold, int quality, int lowPassFilter, int upsampling, boolean enabled, float minHorizonAngleRad);
|
||||
private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, float bilateralThreshold, int quality, int lowPassFilter, int upsampling, boolean enabled, boolean bentNormals, float minHorizonAngleRad);
|
||||
private static native void nSetSSCTOptions(long nativeView, float ssctLightConeRad, float ssctStartTraceDistance, float ssctContactDistanceMax, float ssctIntensity, float v, float v1, float v2, float ssctDepthBias, float ssctDepthSlopeBias, int ssctSampleCount, int ssctRayCount, boolean ssctEnabled);
|
||||
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled, float highlight,
|
||||
boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold);
|
||||
|
||||
@@ -179,9 +179,11 @@ set(MATERIAL_SRCS
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomUpsample.mat
|
||||
src/materials/ssao/bilateralBlur.mat
|
||||
src/materials/ssao/bilateralBlurBentNormals.mat
|
||||
src/materials/ssao/mipmapDepth.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/ssao/sao.mat
|
||||
src/materials/ssao/saoBentNormals.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
@@ -322,12 +324,29 @@ add_custom_command(
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/saoBentNormals.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
DEPENDS src/materials/ssao/ssct.fs
|
||||
DEPENDS src/materials/ssao/depthUtils.fs
|
||||
DEPENDS src/materials/ssao/geometry.fs
|
||||
DEPENDS src/materials/ssao/saoImpl.fs
|
||||
DEPENDS src/materials/ssao/ssctImpl.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/bilateralBlurBentNormals.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen ${RESGEN_FLAGS} ${MATERIAL_BINS}
|
||||
|
||||
@@ -3026,7 +3026,7 @@ void OpenGLDriver::blit(TargetBufferFlags buffers,
|
||||
glFilterMode = GL_NEAREST;
|
||||
}
|
||||
|
||||
// note: for msaa RenderTargets withh non-msaa attachments, we copy from the msaa sidecar
|
||||
// note: for msaa RenderTargets with non-msaa attachments, we copy from the msaa sidecar
|
||||
// buffer -- this should produce the same output that if we copied from the resolved
|
||||
// texture. EXT_multisampled_render_to_texture seems to allow both behaviours, and this
|
||||
// is an emulation of that. We cannot use the resolved texture easily because it's not
|
||||
|
||||
@@ -288,6 +288,7 @@ public:
|
||||
QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness
|
||||
QualityLevel upsampling = QualityLevel::LOW; //!< affects AO buffer upsampling quality
|
||||
bool enabled = false; //!< enables or disables screen-space ambient occlusion
|
||||
bool bentNormals = false; //!< enables bent normals computation from AO, and specular AO
|
||||
float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider
|
||||
/**
|
||||
* Screen Space Cone Tracing (SSCT) options
|
||||
|
||||
@@ -201,6 +201,7 @@ struct MaterialInfo {
|
||||
|
||||
static const MaterialInfo sMaterialList[] = {
|
||||
{ "bilateralBlur", MATERIAL(BILATERALBLUR) },
|
||||
{ "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) },
|
||||
{ "blitHigh", MATERIAL(BLITHIGH) },
|
||||
{ "blitLow", MATERIAL(BLITLOW) },
|
||||
{ "blitMedium", MATERIAL(BLITMEDIUM) },
|
||||
@@ -221,6 +222,7 @@ static const MaterialInfo sMaterialList[] = {
|
||||
{ "fxaa", MATERIAL(FXAA) },
|
||||
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
|
||||
{ "sao", MATERIAL(SAO) },
|
||||
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
|
||||
{ "separableGaussianBlur", MATERIAL(SEPARABLEGAUSSIANBLUR) },
|
||||
{ "taa", MATERIAL(TAA) },
|
||||
{ "vsmMipmap", MATERIAL(VSMMIPMAP) },
|
||||
@@ -399,7 +401,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
// (see en.wikipedia.org/wiki/Gaussian_filter)
|
||||
// More intuitively, 2q is the width of the filter in pixels.
|
||||
BilateralPassConfig config = {
|
||||
.bilateralThreshold = options.bilateralThreshold
|
||||
.bentNormals = options.bentNormals,
|
||||
.bilateralThreshold = options.bilateralThreshold,
|
||||
};
|
||||
|
||||
float sampleCount{};
|
||||
@@ -452,8 +455,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
struct SSAOPassData {
|
||||
FrameGraphId<FrameGraphTexture> depth;
|
||||
FrameGraphId<FrameGraphTexture> ssao;
|
||||
FrameGraphId<FrameGraphTexture> ao;
|
||||
FrameGraphId<FrameGraphTexture> bn;
|
||||
};
|
||||
|
||||
const bool computeBentNormals = options.bentNormals;
|
||||
|
||||
const bool highQualityUpsampling =
|
||||
options.upsampling >= View::QualityLevel::HIGH && options.resolution < 1.0f;
|
||||
|
||||
@@ -467,29 +474,39 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
data.ssao = builder.createTexture("SSAO Buffer", {
|
||||
.width = desc.width,
|
||||
.height = desc.height,
|
||||
.depth = 1,
|
||||
.depth = computeBentNormals ? 2u : 1u,
|
||||
.type = Texture::Sampler::SAMPLER_2D_ARRAY,
|
||||
.format = (lowPassFilterEnabled || highQualityUpsampling) ? TextureFormat::RGB8 : TextureFormat::R8
|
||||
.format = (lowPassFilterEnabled || highQualityUpsampling || computeBentNormals) ?
|
||||
TextureFormat::RGB8 : TextureFormat::R8
|
||||
});
|
||||
|
||||
if (computeBentNormals) {
|
||||
data.ao = builder.createSubresource(data.ssao, "SSAO attachment", { .layer = 0 });
|
||||
data.bn = builder.createSubresource(data.ssao, "Bent Normals attachment", { .layer = 1 });
|
||||
data.ao = builder.write(data.ao, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.bn = builder.write(data.bn, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
} else {
|
||||
data.ao = data.ssao;
|
||||
data.ao = builder.write(data.ao, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
}
|
||||
|
||||
// Here we use the depth test to skip pixels at infinity (i.e. the skybox)
|
||||
// Note that we have to clear the SAO buffer because blended objects will end-up
|
||||
// reading into it even though they were not written in the depth buffer.
|
||||
// The bilateral filter in the blur pass will ignore pixels at infinity.
|
||||
|
||||
data.depth = builder.read(data.depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
data.ssao = builder.write(data.ssao, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
builder.declareRenderPass("SSAO Target", {
|
||||
.attachments = { .color = { data.ssao }, .depth = data.depth },
|
||||
.attachments = { .color = { data.ao, data.bn }, .depth = data.depth },
|
||||
.clearColor = { 1.0f },
|
||||
.clearFlags = TargetBufferFlags::COLOR
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
auto depth = resources.getTexture(data.depth);
|
||||
auto ssao = resources.getRenderPassInfo();
|
||||
auto const& desc = resources.getDescriptor(data.ssao);
|
||||
auto const& desc = resources.getDescriptor(data.depth);
|
||||
|
||||
// estimate of the size in pixel of a 1m tall/wide object viewed from 1m away (i.e. at z=-1)
|
||||
const float projectionScale = std::min(
|
||||
@@ -512,7 +529,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
0.0, 0.0, 0.0, 1.0
|
||||
}};
|
||||
|
||||
auto& material = getPostProcessMaterial("sao");
|
||||
auto& material = computeBentNormals ?
|
||||
getPostProcessMaterial("saoBentNormals") :
|
||||
getPostProcessMaterial("sao");
|
||||
|
||||
FMaterialInstance* const mi = material.getMaterialInstance();
|
||||
mi->setParameter("depth", depth, {
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
@@ -584,7 +604,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
config);
|
||||
|
||||
ssao = bilateralBlurPass(fg, ssao, { 0, config.scale }, cameraInfo.zf,
|
||||
highQualityUpsampling ? TextureFormat::RGB8 : TextureFormat::R8,
|
||||
(highQualityUpsampling || computeBentNormals) ? TextureFormat::RGB8 : TextureFormat::R8,
|
||||
config);
|
||||
}
|
||||
|
||||
@@ -601,6 +621,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(
|
||||
struct BlurPassData {
|
||||
FrameGraphId<FrameGraphTexture> input;
|
||||
FrameGraphId<FrameGraphTexture> blurred;
|
||||
FrameGraphId<FrameGraphTexture> ao;
|
||||
FrameGraphId<FrameGraphTexture> bn;
|
||||
};
|
||||
|
||||
auto& blurPass = fg.addPass<BlurPassData>("Separable Blur Pass",
|
||||
@@ -621,13 +643,24 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(
|
||||
assert_invariant(depth);
|
||||
depth = builder.read(depth, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
if (config.bentNormals) {
|
||||
data.ao = builder.createSubresource(data.blurred, "SSAO attachment", { .layer = 0 });
|
||||
data.bn = builder.createSubresource(data.blurred, "Bent Normals attachment", { .layer = 1 });
|
||||
data.ao = builder.write(data.ao, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
data.bn = builder.write(data.bn, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
} else {
|
||||
data.ao = data.blurred;
|
||||
data.ao = builder.write(data.ao, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
}
|
||||
|
||||
// Here we use the depth test to skip pixels at infinity (i.e. the skybox)
|
||||
// We need to clear the buffers because we are skipping pixels at infinity (skybox)
|
||||
data.blurred = builder.write(data.blurred, FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
|
||||
builder.declareRenderPass("Blurred target", {
|
||||
.attachments = { .color = { data.blurred }, .depth = depth },
|
||||
.attachments = { .color = { data.ao, data.bn }, .depth = depth },
|
||||
.clearColor = { 1.0f },
|
||||
.clearFlags = TargetBufferFlags::COLOR
|
||||
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1
|
||||
});
|
||||
},
|
||||
[=](FrameGraphResources const& resources,
|
||||
@@ -653,7 +686,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(
|
||||
uint32_t kGaussianCount = gaussianKernel(kGaussianSamples,
|
||||
config.kernelSize, config.standardDeviation);
|
||||
|
||||
auto& material = getPostProcessMaterial("bilateralBlur");
|
||||
auto& material = config.bentNormals ?
|
||||
getPostProcessMaterial("bilateralBlurBentNormals") :
|
||||
getPostProcessMaterial("bilateralBlur");
|
||||
FMaterialInstance* const mi = material.getMaterialInstance();
|
||||
mi->setParameter("ssao", ssao, { /* only reads level 0 */ });
|
||||
mi->setParameter("axis", axis / float2{desc.width, desc.height});
|
||||
|
||||
@@ -152,6 +152,7 @@ private:
|
||||
|
||||
struct BilateralPassConfig {
|
||||
uint8_t kernelSize = 11;
|
||||
bool bentNormals = false;
|
||||
float standardDeviation = 1.0f;
|
||||
float bilateralThreshold = 0.0625f;
|
||||
float scale = 1.0f;
|
||||
|
||||
@@ -682,10 +682,12 @@ void FView::prepareSSAO(Handle<HwTexture> ssao) const noexcept {
|
||||
SamplerMagFilter::LINEAR : SamplerMagFilter::NEAREST
|
||||
});
|
||||
|
||||
const float edgeDistance = 1.0 / 0.0625;// TODO: don't hardcode this
|
||||
const float edgeDistance = 1.0f / mAmbientOcclusionOptions.bilateralThreshold;
|
||||
auto& s = mPerViewUb.edit();
|
||||
s.aoSamplingQualityAndEdgeDistance =
|
||||
mAmbientOcclusionOptions.enabled && highQualitySampling ? edgeDistance : 0.0f;
|
||||
s.aoBentNormals =
|
||||
mAmbientOcclusionOptions.enabled && mAmbientOcclusionOptions.bentNormals ? 1.0f : 0.0f;
|
||||
}
|
||||
|
||||
void FView::prepareSSR(backend::Handle<backend::HwTexture> ssr, float refractionLodOffset) const noexcept {
|
||||
|
||||
@@ -54,8 +54,7 @@ fragment {
|
||||
vec3 data = textureLod(saoTexture, vec3(position, 0.0), 0.0).rgb;
|
||||
|
||||
// bilateral sample
|
||||
float bilateral = bilateralWeight(depth, unpack(data.gb));
|
||||
bilateral *= weight;
|
||||
float bilateral = weight * bilateralWeight(depth, unpack(data.gb));
|
||||
sum += data.r * bilateral;
|
||||
totalWeight += bilateral;
|
||||
}
|
||||
|
||||
135
filament/src/materials/ssao/bilateralBlurBentNormals.mat
Normal file
135
filament/src/materials/ssao/bilateralBlurBentNormals.mat
Normal file
@@ -0,0 +1,135 @@
|
||||
material {
|
||||
name : bilateralBlurBentNormals,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2dArray,
|
||||
name : ssao,
|
||||
precision: medium
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : axis,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : sampleCount
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : farPlaneOverEdgeDistance
|
||||
},
|
||||
{
|
||||
type : float[16],
|
||||
name : kernel
|
||||
}
|
||||
],
|
||||
outputs : [
|
||||
{
|
||||
name : aoData,
|
||||
target : color,
|
||||
type : float3
|
||||
},
|
||||
{
|
||||
name : bnData,
|
||||
target : color,
|
||||
type : float3
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : false
|
||||
}
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
postProcess.vertex.xy = postProcess.normalizedUV;
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
#include "ssaoUtils.fs"
|
||||
|
||||
void dummy(){}
|
||||
|
||||
float bilateralWeight(in highp float depth, in highp float sampleDepth) {
|
||||
float diff = (sampleDepth - depth) * materialParams.farPlaneOverEdgeDistance;
|
||||
return max(0.0, 1.0 - diff * diff);
|
||||
}
|
||||
|
||||
void tapAO(const highp sampler2DArray saoTexture, highp vec2 uv,
|
||||
out float ao, out highp float sampleDepth) {
|
||||
vec3 data = textureLod(saoTexture, vec3(uv, 0.0), 0.0).rgb;
|
||||
ao = data.r;
|
||||
sampleDepth = unpack(data.gb);
|
||||
}
|
||||
|
||||
void tapBN(const highp sampler2DArray saoTexture, highp vec2 uv,
|
||||
out vec3 bentNormal) {
|
||||
vec3 data = textureLod(saoTexture, vec3(uv, 1.0), 0.0).xyz;
|
||||
bentNormal = unpackBentNormal(data);
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy; // interpolated at pixel's center
|
||||
|
||||
float ao;
|
||||
highp float depth;
|
||||
highp float sampleDepth;
|
||||
vec3 bn;
|
||||
|
||||
vec3 data = textureLod(materialParams_ssao, vec3(uv, 0.0), 0.0).rgb;
|
||||
ao = data.r;
|
||||
depth = unpack(data.gb);
|
||||
|
||||
if (data.g * data.b == 1.0) {
|
||||
// This is the skybox, skip
|
||||
postProcess.aoData = data;
|
||||
postProcess.bnData = vec3(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
tapBN(materialParams_ssao, uv, bn);
|
||||
|
||||
// we handle the center pixel separately because it doesn't participate in
|
||||
// bilateral filtering
|
||||
float totalWeight = materialParams.kernel[0];
|
||||
float sumAO = ao * totalWeight;
|
||||
vec3 sumBN = bn * totalWeight;
|
||||
|
||||
vec2 offset = materialParams.axis;
|
||||
for (int i = 1; i < materialParams.sampleCount; i++) {
|
||||
float weight = materialParams.kernel[i];
|
||||
float bilateral;
|
||||
|
||||
tapAO(materialParams_ssao, uv + offset, ao, sampleDepth);
|
||||
bilateral = weight * bilateralWeight(depth, sampleDepth);
|
||||
totalWeight += bilateral;
|
||||
tapBN(materialParams_ssao, uv + offset, bn);
|
||||
sumAO += ao * bilateral;
|
||||
sumBN += bn * bilateral;
|
||||
|
||||
tapAO(materialParams_ssao, uv - offset, ao, sampleDepth);
|
||||
bilateral = weight * bilateralWeight(depth, sampleDepth);
|
||||
totalWeight += bilateral;
|
||||
tapBN(materialParams_ssao, uv - offset, bn);
|
||||
sumAO += ao * bilateral;
|
||||
sumBN += bn * bilateral;
|
||||
|
||||
offset += materialParams.axis;
|
||||
}
|
||||
|
||||
ao = sumAO * (1.0 / totalWeight);
|
||||
bn = sumBN * (1.0 / totalWeight);
|
||||
|
||||
// simple dithering helps a lot (assumes 8 bits target)
|
||||
// this is most useful with high quality/large blurs
|
||||
ao += ((random(gl_FragCoord.xy) - 0.5) / 255.0);
|
||||
|
||||
postProcess.aoData = vec3(ao, data.gb);
|
||||
postProcess.bnData = packBentNormal(bn);
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,9 @@ vertex {
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define COMPUTE_BENT_NORMAL 0
|
||||
|
||||
#include "saoImpl.fs"
|
||||
#include "ssctImpl.fs"
|
||||
#include "geometry.fs"
|
||||
@@ -155,9 +158,10 @@ fragment {
|
||||
materialParams.depthParams);
|
||||
|
||||
float occlusion = 0.0;
|
||||
vec3 bentNormal; // will be discarded
|
||||
|
||||
if (materialParams.intensity > 0.0) {
|
||||
occlusion = scalableAmbientObscurance(uv, origin, normal);
|
||||
scalableAmbientObscurance(occlusion, bentNormal, uv, origin, normal);
|
||||
}
|
||||
|
||||
if (materialParams.ssctIntensity > 0.0) {
|
||||
|
||||
197
filament/src/materials/ssao/saoBentNormals.mat
Normal file
197
filament/src/materials/ssao/saoBentNormals.mat
Normal file
@@ -0,0 +1,197 @@
|
||||
material {
|
||||
name : saoBentNormals,
|
||||
parameters : [
|
||||
{
|
||||
type : sampler2d,
|
||||
name : depth,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : mat4,
|
||||
name : screenFromViewMatrix
|
||||
},
|
||||
{
|
||||
type : float4,
|
||||
name : resolution,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : positionParams,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : depthParams,
|
||||
precision: high
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : invRadiusSquared
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : minHorizonAngleSineSquared
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : peak2
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : projectionScale
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : projectionScaleRadius
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : bias
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : power
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : intensity
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : spiralTurns
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : sampleCount
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : angleIncCosSin
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : invFarPlane
|
||||
},
|
||||
{
|
||||
type : int,
|
||||
name : maxLevel
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : reserved
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctShadowDistance
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctConeAngleTangeant
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctContactDistanceMaxInv
|
||||
},
|
||||
{
|
||||
type : float3,
|
||||
name : ssctVsLightDirection
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctIntensity
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : ssctDepthBias
|
||||
},
|
||||
{
|
||||
type : float2,
|
||||
name : ssctRayCount
|
||||
},
|
||||
{
|
||||
type : uint,
|
||||
name : ssctSampleCount
|
||||
}
|
||||
],
|
||||
outputs : [
|
||||
{
|
||||
name : aoData,
|
||||
target : color,
|
||||
type : float3
|
||||
},
|
||||
{
|
||||
name : bnData,
|
||||
target : color,
|
||||
type : float3
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
domain : postprocess,
|
||||
depthWrite : false,
|
||||
depthCulling : true
|
||||
}
|
||||
|
||||
vertex {
|
||||
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
|
||||
postProcess.vertex.xy = postProcess.normalizedUV;
|
||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// On metal/vulkan postProcess.normalizedUV has its origin at the left-top, but we need a
|
||||
// uniform coordinate space for SAO. So, we flip the y coordinate so we have bottom-left UV
|
||||
// coordinates across all backends.
|
||||
postProcess.vertex.y = 1.0 - postProcess.vertex.y;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
#define COMPUTE_BENT_NORMAL 1
|
||||
|
||||
#include "saoImpl.fs"
|
||||
#include "ssctImpl.fs"
|
||||
#include "geometry.fs"
|
||||
#include "ssaoUtils.fs"
|
||||
|
||||
void dummy(){}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
highp vec2 uv = variable_vertex.xy; // interpolated to pixel center
|
||||
|
||||
highp float depth = sampleDepth(materialParams_depth, uv, 0.0);
|
||||
highp float z = linearizeDepth(depth, materialParams.depthParams);
|
||||
highp vec3 origin = computeViewSpacePositionFromDepth(uv, z, materialParams.positionParams);
|
||||
|
||||
vec3 normal = computeViewSpaceNormal(materialParams_depth, uv, depth, origin,
|
||||
materialParams.resolution,
|
||||
materialParams.positionParams,
|
||||
materialParams.depthParams);
|
||||
|
||||
float occlusion = 0.0;
|
||||
vec3 bentNormal = normal;
|
||||
|
||||
if (materialParams.intensity > 0.0) {
|
||||
scalableAmbientObscurance(occlusion, bentNormal, uv, origin, normal);
|
||||
}
|
||||
|
||||
if (materialParams.ssctIntensity > 0.0) {
|
||||
occlusion = max(occlusion, dominantLightShadowing(uv, origin, normal));
|
||||
}
|
||||
|
||||
// occlusion to visibility
|
||||
float aoVisibility = pow(saturate(1.0 - occlusion), materialParams.power);
|
||||
|
||||
#if defined(TARGET_MOBILE)
|
||||
// this line is needed to workaround what seems to be a bug on qualcomm hardware
|
||||
aoVisibility += gl_FragCoord.x * MEDIUMP_FLT_MIN;
|
||||
#endif
|
||||
|
||||
// transform to world space (we're guaranteed the view matrix is a rigid body transform)
|
||||
vec3 bn = mat3(getWorldFromViewMatrix()) * bentNormal;
|
||||
|
||||
postProcess.aoData = vec3(aoVisibility, pack(origin.z * materialParams.invFarPlane));
|
||||
postProcess.bnData = packBentNormal(bn);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@
|
||||
#include "ssaoUtils.fs"
|
||||
#include "geometry.fs"
|
||||
|
||||
#ifndef COMPUTE_BENT_NORMAL
|
||||
#error "COMPUTE_BENT_NORMAL must be set"
|
||||
#endif
|
||||
|
||||
const float kLog2LodRate = 3.0;
|
||||
|
||||
// Ambient Occlusion, largely inspired from:
|
||||
@@ -52,7 +56,8 @@ vec3 tapLocationFast(float i, vec2 p, const float noise) {
|
||||
return vec3(p, radius * radius);
|
||||
}
|
||||
|
||||
void computeAmbientOcclusionSAO(inout float occlusion, float i, float ssDiskRadius,
|
||||
void computeAmbientOcclusionSAO(inout float occlusion, inout vec3 bentNormal,
|
||||
float i, float ssDiskRadius,
|
||||
const highp vec2 uv, const highp vec3 origin, const vec3 normal,
|
||||
const vec2 tapPosition, const float noise) {
|
||||
|
||||
@@ -82,10 +87,26 @@ void computeAmbientOcclusionSAO(inout float occlusion, float i, float ssDiskRadi
|
||||
// sin(beta) * |v|. So the test simplifies to vn^2 < vv * sin(epsilon)^2.
|
||||
w *= step(vv * materialParams.minHorizonAngleSineSquared, vn * vn);
|
||||
|
||||
occlusion += w * max(0.0, vn + origin.z * materialParams.bias) / (vv + materialParams.peak2);
|
||||
float sampleOcclusion = max(0.0, vn + (origin.z * materialParams.bias)) / (vv + materialParams.peak2);
|
||||
occlusion += w * sampleOcclusion;
|
||||
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
|
||||
// TODO: revisit how we choose to keep the normal or not
|
||||
// reject samples beyond the far plane
|
||||
if (occlusionDepth * materialParams.invFarPlane < 1.0) {
|
||||
float rr = 1.0 / materialParams.invRadiusSquared;
|
||||
float cc = vv - vn*vn;
|
||||
float s = sqrt(max(0.0, rr - cc));
|
||||
vec3 n = normalize(v + normal * (s - vn));// vn is negative
|
||||
bentNormal += n * (sampleOcclusion <= 0.0 ? 1.0 : 0.0);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
float scalableAmbientObscurance(highp vec2 uv, highp vec3 origin, vec3 normal) {
|
||||
void scalableAmbientObscurance(out float obscurance, out vec3 bentNormal,
|
||||
highp vec2 uv, highp vec3 origin, vec3 normal) {
|
||||
float noise = random(getFragCoord(materialParams.resolution));
|
||||
highp vec2 tapPosition = startPosition(noise);
|
||||
highp mat2 angleStep = tapAngleStep();
|
||||
@@ -94,10 +115,15 @@ float scalableAmbientObscurance(highp vec2 uv, highp vec3 origin, vec3 normal) {
|
||||
// proportional to the projected area of the sphere
|
||||
float ssDiskRadius = -(materialParams.projectionScaleRadius / origin.z);
|
||||
|
||||
float occlusion = 0.0;
|
||||
obscurance = 0.0;
|
||||
bentNormal = normal;
|
||||
for (float i = 0.0; i < materialParams.sampleCount.x; i += 1.0) {
|
||||
computeAmbientOcclusionSAO(occlusion, i, ssDiskRadius, uv, origin, normal, tapPosition, noise);
|
||||
computeAmbientOcclusionSAO(obscurance, bentNormal,
|
||||
i, ssDiskRadius, uv, origin, normal, tapPosition, noise);
|
||||
tapPosition = angleStep * tapPosition;
|
||||
}
|
||||
return sqrt(occlusion * materialParams.intensity);
|
||||
obscurance = sqrt(obscurance * materialParams.intensity);
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
bentNormal = normalize(bentNormal);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -50,5 +50,14 @@ highp float unpack(highp vec2 depth) {
|
||||
return (depth.x * (256.0 / 257.0) + depth.y * (1.0 / 257.0));
|
||||
}
|
||||
|
||||
vec3 packBentNormal(vec3 bn) {
|
||||
return bn * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
vec3 unpackBentNormal(vec3 bn) {
|
||||
return bn * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
|
||||
#endif // FILAMENT_MATERIALS_SSAO_UTILS
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 11;
|
||||
static constexpr size_t MATERIAL_VERSION = 12;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -115,7 +115,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
||||
uint32_t cascades;
|
||||
|
||||
float aoSamplingQualityAndEdgeDistance; // 0: bilinear, !0: bilateral edge distance
|
||||
float aoReserved1;
|
||||
float aoBentNormals; // 0: no AO bent normal, >0.0 AO bent normals
|
||||
float aoReserved2;
|
||||
float aoReserved3;
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
|
||||
|
||||
// SSAO sampling parameters
|
||||
.add("aoSamplingQualityAndEdgeDistance", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("aoReserved1", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("aoBentNormals", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("aoReserved2", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
.add("aoReserved3", 1, UniformInterfaceBlock::Type::FLOAT)
|
||||
|
||||
|
||||
@@ -428,6 +428,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk,
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->upsampling);
|
||||
} else if (compare(tok, jsonChunk, "enabled") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->enabled);
|
||||
} else if (compare(tok, jsonChunk, "bentNormals") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->bentNormals);
|
||||
} else if (compare(tok, jsonChunk, "minHorizonAngleRad") == 0) {
|
||||
i = parse(tokens, i + 1, jsonChunk, &out->minHorizonAngleRad);
|
||||
} else if (compare(tok, jsonChunk, "ssct") == 0) {
|
||||
@@ -1204,6 +1206,7 @@ static std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions
|
||||
<< "\"lowPassFilter\": " << (in.lowPassFilter) << ",\n"
|
||||
<< "\"upsampling\": " << (in.upsampling) << ",\n"
|
||||
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
|
||||
<< "\"bentNormals\": " << to_string(in.bentNormals) << ",\n"
|
||||
<< "\"minHorizonAngleRad\": " << (in.minHorizonAngleRad) << ",\n"
|
||||
<< "\"ssct\": " << (in.ssct) << "\n"
|
||||
<< "}";
|
||||
|
||||
@@ -700,6 +700,7 @@ void SimpleViewer::updateUserInterface() {
|
||||
|
||||
ImGui::SliderInt("Quality", &quality, 0, 3);
|
||||
ImGui::SliderInt("Low Pass", &lowpass, 0, 2);
|
||||
ImGui::Checkbox("Bent Normals", &ssao.bentNormals);
|
||||
ImGui::Checkbox("High quality upsampling", &upsampling);
|
||||
ImGui::SliderFloat("Min Horizon angle", &ssao.minHorizonAngleRad, 0.0f, (float)M_PI_4);
|
||||
|
||||
|
||||
@@ -620,6 +620,8 @@ static void gui(filament::Engine* engine, filament::View*) {
|
||||
ImGui::SliderFloat("Power", ¶ms.ssaoOptions.power, 0.0f, 4.0f);
|
||||
ImGui::SliderInt("Quality", &quality, 0, 3);
|
||||
ImGui::SliderInt("Low Pass", &lowpass, 0, 2);
|
||||
ImGui::SliderFloat("Bilateral Threshold", ¶ms.ssaoOptions.bilateralThreshold, 0.0f, 0.5f);
|
||||
ImGui::Checkbox("Bent Normals", ¶ms.ssaoOptions.bentNormals);
|
||||
ImGui::Checkbox("High quality upsampling", &upsampling);
|
||||
params.ssaoOptions.upsampling = upsampling ? View::QualityLevel::HIGH : View::QualityLevel::LOW;
|
||||
params.ssaoOptions.quality = (View::QualityLevel)quality;
|
||||
|
||||
@@ -241,6 +241,15 @@ static void setup(Engine* engine, View* view, Scene* scene) {
|
||||
Path path(g_pbrConfig.materialDir);
|
||||
std::string name(path.getName());
|
||||
|
||||
view->setAmbientOcclusionOptions({
|
||||
.radius = 0.01f,
|
||||
.bilateralThreshold = 0.005f,
|
||||
.quality = View::QualityLevel::ULTRA,
|
||||
.lowPassFilter = View::QualityLevel::MEDIUM,
|
||||
.upsampling = View::QualityLevel::HIGH,
|
||||
.enabled = true
|
||||
});
|
||||
|
||||
bool hasUV = false;
|
||||
for (auto& map: g_maps) {
|
||||
loadTexture(engine, path.concat(name + map.suffix + ".png"), &map.texture, map.sRGB);
|
||||
@@ -377,6 +386,7 @@ static void setup(Engine* engine, View* view, Scene* scene) {
|
||||
.targetApi(MaterialBuilder::TargetApi::ALL)
|
||||
#ifndef NDEBUG
|
||||
.optimization(MaterialBuilderBase::Optimization::NONE)
|
||||
.generateDebugInfo(true)
|
||||
#endif
|
||||
.material(shader.c_str())
|
||||
.multiBounceAmbientOcclusion(true)
|
||||
|
||||
@@ -16,9 +16,12 @@ float unpack(vec2 depth) {
|
||||
return (depth.x * (256.0 / 257.0) + depth.y * (1.0 / 257.0));
|
||||
}
|
||||
|
||||
float evaluateSSAO() {
|
||||
struct SSAOInterpolationCache {
|
||||
highp vec4 weights;
|
||||
};
|
||||
|
||||
float evaluateSSAO(const highp vec2 uv, out SSAOInterpolationCache cache) {
|
||||
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED)
|
||||
highp vec2 uv = uvToRenderTargetUV(getNormalizedViewportCoord().xy);
|
||||
|
||||
// Upscale the SSAO buffer in real-time, in high quality mode we use a custom bilinear
|
||||
// filter. This adds about 2.0ms @ 250MHz on Pixel 4.
|
||||
@@ -40,6 +43,14 @@ float evaluateSSAO() {
|
||||
vec4 dg = vec4(s01.g, s11.g, s10.g, s00.g);
|
||||
vec4 db = vec4(s01.b, s11.b, s10.b, s00.b);
|
||||
#endif
|
||||
// bilateral weights
|
||||
vec4 depths;
|
||||
depths.x = unpack(vec2(dg.x, db.x));
|
||||
depths.y = unpack(vec2(dg.y, db.y));
|
||||
depths.z = unpack(vec2(dg.z, db.z));
|
||||
depths.w = unpack(vec2(dg.w, db.w));
|
||||
depths *= -frameUniforms.cameraFar;
|
||||
|
||||
// bilinear weights
|
||||
vec2 f = fract(uv * size - 0.5);
|
||||
vec4 b;
|
||||
@@ -48,18 +59,12 @@ float evaluateSSAO() {
|
||||
b.z = f.x * (1.0 - f.y);
|
||||
b.w = (1.0 - f.x) * (1.0 - f.y);
|
||||
|
||||
// bilateral weights
|
||||
vec4 depths;
|
||||
depths.x = unpack(vec2(dg.x, db.x));
|
||||
depths.y = unpack(vec2(dg.y, db.y));
|
||||
depths.z = unpack(vec2(dg.z, db.z));
|
||||
depths.w = unpack(vec2(dg.w, db.w));
|
||||
depths *= -frameUniforms.cameraFar;
|
||||
highp mat4 m = getViewFromWorldMatrix();
|
||||
highp float d = dot(vec3(m[0].z, m[1].z, m[2].z), shading_position) + m[3].z;
|
||||
highp vec4 w = (vec4(d) - depths) * frameUniforms.aoSamplingQualityAndEdgeDistance;
|
||||
w = max(vec4(MEDIUMP_FLT_MIN), 1.0 - w * w) * b;
|
||||
return dot(ao, w) * (1.0 / (w.x + w.y + w.z + w.w));
|
||||
cache.weights = w / (w.x + w.y + w.z + w.w);
|
||||
return dot(ao, cache.weights);
|
||||
} else {
|
||||
return textureLod(light_ssao, vec3(uv, 0.0), 0.0).r;
|
||||
}
|
||||
@@ -74,7 +79,6 @@ float SpecularAO_Lagarde(float NoV, float visibility, float roughness) {
|
||||
return saturate(pow(NoV + visibility, exp2(-16.0 * roughness - 1.0)) - 1.0 + visibility);
|
||||
}
|
||||
|
||||
#if defined(MATERIAL_HAS_BENT_NORMAL)
|
||||
float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance) {
|
||||
// Oat and Sander 2007, "Ambient Aperture Lighting"
|
||||
// Approximation mentioned by Jimenez et al. 2016
|
||||
@@ -99,11 +103,9 @@ float sphericalCapsIntersection(float cosCap1, float cosCap2, float cosDistance)
|
||||
float area = sq(x) * (-2.0 * x + 3.0);
|
||||
return area * (1.0 - max(cosCap1, cosCap2));
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function could (should?) be implemented as a 3D LUT instead, but we need to save samplers
|
||||
float SpecularAO_Cones(float NoV, float visibility, float roughness) {
|
||||
#if defined(MATERIAL_HAS_BENT_NORMAL)
|
||||
float SpecularAO_Cones(vec3 bentNormal, float NoV, float visibility, float roughness) {
|
||||
// Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"
|
||||
|
||||
// aperture from ambient occlusion
|
||||
@@ -111,7 +113,7 @@ float SpecularAO_Cones(float NoV, float visibility, float roughness) {
|
||||
// aperture from roughness, log(10) / log(2) = 3.321928
|
||||
float cosAs = exp2(-3.321928 * sq(roughness));
|
||||
// angle betwen bent normal and reflection direction
|
||||
float cosB = dot(shading_bentNormal, shading_reflected);
|
||||
float cosB = dot(bentNormal, shading_reflected);
|
||||
|
||||
// Remove the 2 * PI term from the denominator, it cancels out the same term from
|
||||
// sphericalCapsIntersection()
|
||||
@@ -119,22 +121,66 @@ float SpecularAO_Cones(float NoV, float visibility, float roughness) {
|
||||
// Smoothly kill specular AO when entering the perceptual roughness range [0.1..0.3]
|
||||
// Without this, specular AO can remove all reflections, which looks bad on metals
|
||||
return mix(1.0, ao, smoothstep(0.01, 0.09, roughness));
|
||||
#else
|
||||
return SpecularAO_Lagarde(NoV, visibility, roughness);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a specular occlusion term from the ambient occlusion term.
|
||||
*/
|
||||
float computeSpecularAO(float NoV, float visibility, float roughness) {
|
||||
|
||||
vec3 unpackBentNormal(vec3 bn) {
|
||||
// this must match src/materials/ssao/ssaoUtils.fs
|
||||
return bn * 2.0 - 1.0;
|
||||
}
|
||||
|
||||
float computeSpecularAO(const highp vec2 uv, float NoV, float visibility, float roughness,
|
||||
const in SSAOInterpolationCache cache) {
|
||||
|
||||
float specularAO = 1.0;
|
||||
|
||||
// SSAO is not applied when blending is enabled
|
||||
#if defined(BLEND_MODE_OPAQUE) || defined(BLEND_MODE_MASKED)
|
||||
|
||||
#if SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_SIMPLE
|
||||
return SpecularAO_Lagarde(NoV, visibility, roughness);
|
||||
specularAO = SpecularAO_Lagarde(NoV, visibility, roughness);
|
||||
#elif SPECULAR_AMBIENT_OCCLUSION == SPECULAR_AO_BENT_NORMALS
|
||||
return SpecularAO_Cones(NoV, visibility, roughness);
|
||||
#else
|
||||
return 1.0;
|
||||
# if defined(MATERIAL_HAS_BENT_NORMAL)
|
||||
specularAO = SpecularAO_Cones(shading_bentNormal, NoV, visibility, roughness);
|
||||
# else
|
||||
specularAO = SpecularAO_Cones(shading_normal, NoV, visibility, roughness);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
if (frameUniforms.aoBentNormals > 0.0) {
|
||||
vec3 bn;
|
||||
if (frameUniforms.aoSamplingQualityAndEdgeDistance > 0.0) {
|
||||
#if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER)
|
||||
vec4 bnr = textureGather(light_ssao, vec3(uv, 1.0), 0);
|
||||
vec4 bng = textureGather(light_ssao, vec3(uv, 1.0), 1);
|
||||
vec4 bnb = textureGather(light_ssao, vec3(uv, 1.0), 2);
|
||||
#else
|
||||
vec3 s01 = textureLodOffset(light_ssao, vec3(uv, 1.0), 0.0, ivec2(0, 1)).rgb;
|
||||
vec3 s11 = textureLodOffset(light_ssao, vec3(uv, 1.0), 0.0, ivec2(1, 1)).rgb;
|
||||
vec3 s10 = textureLodOffset(light_ssao, vec3(uv, 1.0), 0.0, ivec2(1, 0)).rgb;
|
||||
vec3 s00 = textureLodOffset(light_ssao, vec3(uv, 1.0), 0.0, ivec2(0, 0)).rgb;
|
||||
vec4 bnr = vec4(s01.r, s11.r, s10.r, s00.r);
|
||||
vec4 bng = vec4(s01.g, s11.g, s10.g, s00.g);
|
||||
vec4 bnb = vec4(s01.b, s11.b, s10.b, s00.b);
|
||||
#endif
|
||||
bn.r = dot(bnr, cache.weights);
|
||||
bn.g = dot(bng, cache.weights);
|
||||
bn.b = dot(bnb, cache.weights);
|
||||
} else {
|
||||
bn = textureLod(light_ssao, vec3(uv, 1.0), 0.0).xyz;
|
||||
}
|
||||
bn = unpackBentNormal(bn);
|
||||
bn = normalize(bn);
|
||||
specularAO = min(specularAO, SpecularAO_Cones(bn, NoV, visibility, roughness));
|
||||
// For now we don't use the AO bent normal for the diffuse because the AO bent normal
|
||||
// is currently a face normal.
|
||||
}
|
||||
#endif
|
||||
|
||||
return specularAO;
|
||||
}
|
||||
|
||||
#if MULTI_BOUNCE_AMBIENT_OCCLUSION == 1
|
||||
|
||||
@@ -573,9 +573,13 @@ void combineDiffuseAndSpecular(
|
||||
}
|
||||
|
||||
void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout vec3 color) {
|
||||
float ssao = evaluateSSAO();
|
||||
SSAOInterpolationCache interpolationCache;
|
||||
|
||||
highp vec2 uv = uvToRenderTargetUV(getNormalizedViewportCoord().xy);
|
||||
float ssao = evaluateSSAO(uv, interpolationCache);
|
||||
float diffuseAO = min(material.ambientOcclusion, ssao);
|
||||
float specularAO = computeSpecularAO(shading_NoV, diffuseAO, pixel.roughness);
|
||||
float specularAO = computeSpecularAO(uv, shading_NoV, diffuseAO, pixel.roughness,
|
||||
interpolationCache);
|
||||
|
||||
// specular layer
|
||||
vec3 Fr;
|
||||
|
||||
@@ -365,7 +365,12 @@ value_object<filament::View::AmbientOcclusionOptions>("View$AmbientOcclusionOpti
|
||||
.field("resolution", &filament::View::AmbientOcclusionOptions::resolution)
|
||||
.field("intensity", &filament::View::AmbientOcclusionOptions::intensity)
|
||||
.field("bilateralThreshold", &filament::View::AmbientOcclusionOptions::bilateralThreshold)
|
||||
.field("quality", &filament::View::AmbientOcclusionOptions::quality);
|
||||
.field("quality", &filament::View::AmbientOcclusionOptions::quality)
|
||||
.field("lowPassFilter", &filament::View::AmbientOcclusionOptions::lowPassFilter)
|
||||
.field("upsampling", &filament::View::AmbientOcclusionOptions::upsampling)
|
||||
.field("enabled", &filament::View::AmbientOcclusionOptions::enabled)
|
||||
.field("bentNormals", &filament::View::AmbientOcclusionOptions::bentNormals)
|
||||
.field("minHorizonAngleRad", &filament::View::AmbientOcclusionOptions::minHorizonAngleRad);
|
||||
// TODO: ssct options
|
||||
|
||||
value_object<filament::View::DepthOfFieldOptions>("View$DepthOfFieldOptions")
|
||||
|
||||
Reference in New Issue
Block a user