Add GTAO shader support (#8688)

* Add GTAO options into the struct

* Add gtao mat and shader

* Set type specific parameters in ppm

* Fix inconsistent name

* Fix incorrect param type

* Add bent normal calculation

* Fix the gui

* Adjust precision

* Use mix

* Add some comments

* Refactoring

* Update comments

* Add comments for aoOptions

* Update

* Update

* Address the comments

* Split .mat into bent and non-bent

* Update

* Update

* Use sqrt on mobile

* Omit default case

* Commit beamsplitter generated changes

* Use built-in acosFast

* Remove unused function

* Fix the mismatched parameter type

* Some optimizations

* Commit beamsplitter results

* Update the comment

* Update default value

* Commit beamsplitter changes

* Extract common parameters out
This commit is contained in:
Doris Wu
2025-05-16 00:13:12 +00:00
committed by GitHub
parent 81008abe06
commit fe474a9d7e
14 changed files with 811 additions and 79 deletions
@@ -1801,6 +1801,22 @@ public class View {
* @see setAmbientOcclusionOptions()
*/
public static class AmbientOcclusionOptions {
public enum AmbientOcclusionType {
/**
* use Scalable Ambient Occlusion
*/
SAO,
/**
* use Ground Truth-Based Ambient Occlusion
*/
GTAO,
}
/**
* Type of ambient occlusion algorithm.
*/
@NonNull
public AmbientOcclusionOptions.AmbientOcclusionType aoType = AmbientOcclusionOptions.AmbientOcclusionType.SAO;
/**
* Ambient Occlusion radius in meters, between 0 and ~10.
*/
@@ -1810,7 +1826,8 @@ public class View {
*/
public float power = 1.0f;
/**
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
* Self-occlusion bias in meters. Use to avoid self-occlusion.
* Between 0 and a few mm. No effect when aoType set to GTAO
*/
public float bias = 0.0005f;
/**
@@ -1826,12 +1843,12 @@ public class View {
*/
public float bilateralThreshold = 0.05f;
/**
* affects # of samples used for AO.
* affects # of samples used for AO and params for filtering
*/
@NonNull
public QualityLevel quality = QualityLevel.LOW;
/**
* affects AO smoothness
* affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO.
*/
@NonNull
public QualityLevel lowPassFilter = QualityLevel.MEDIUM;
@@ -1849,7 +1866,7 @@ public class View {
*/
public boolean bentNormals = false;
/**
* min angle in radian to consider
* min angle in radian to consider. No effect when aoType set to GTAO.
*/
public float minHorizonAngleRad = 0.0f;
/**
@@ -1904,6 +1921,19 @@ public class View {
*/
public boolean ssctEnabled = false;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public int gtaoSampleSliceCount = 4;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public int gtaoSampleStepsPerSlice = 3;
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
public float gtaoThicknessHeuristic = 0.004f;
}
/**
+24
View File
@@ -262,6 +262,8 @@ set(MATERIAL_SRCS
src/materials/ssao/mipmapDepth.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/ssao/gtao.mat
src/materials/ssao/gtaoBentNormals.mat
src/materials/vsmMipmap.mat
)
@@ -461,6 +463,28 @@ add_custom_command(
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
+26 -4
View File
@@ -373,18 +373,30 @@ struct RenderQuality {
* @see setAmbientOcclusionOptions()
*/
struct AmbientOcclusionOptions {
enum class AmbientOcclusionType : uint8_t {
SAO, //!< use Scalable Ambient Occlusion
GTAO, //!< use Ground Truth-Based Ambient Occlusion
};
AmbientOcclusionType aoType = AmbientOcclusionType::SAO;//!< Type of ambient occlusion algorithm.
float radius = 0.3f; //!< Ambient Occlusion radius in meters, between 0 and ~10.
float power = 1.0f; //!< Controls ambient occlusion's contrast. Must be positive.
float bias = 0.0005f; //!< Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
/**
* Self-occlusion bias in meters. Use to avoid self-occlusion.
* Between 0 and a few mm. No effect when aoType set to GTAO
*/
float bias = 0.0005f;
float resolution = 0.5f;//!< How each dimension of the AO buffer is scaled. Must be either 0.5 or 1.0.
float intensity = 1.0f; //!< Strength of the Ambient Occlusion effect.
float bilateralThreshold = 0.05f; //!< depth distance that constitute an edge for filtering
QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO.
QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness
QualityLevel quality = QualityLevel::LOW; //!< affects # of samples used for AO and params for filtering
QualityLevel lowPassFilter = QualityLevel::MEDIUM; //!< affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO.
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
float minHorizonAngleRad = 0.0f; //!< min angle in radian to consider. No effect when aoType set to GTAO.
/**
* Screen Space Cone Tracing (SSCT) options
* Ambient shadows from dominant light
@@ -402,6 +414,16 @@ struct AmbientOcclusionOptions {
bool enabled = false; //!< enables or disables SSCT
};
Ssct ssct; // %codegen_skip_javascript% %codegen_java_flatten%
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
struct Gtao {
uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise.
uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias.
float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0
};
Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten%
};
/**
+90 -64
View File
@@ -100,7 +100,9 @@ using namespace backend;
static constexpr uint8_t kMaxBloomLevels = 12u;
static_assert(kMaxBloomLevels >= 3, "We require at least 3 bloom levels");
constexpr static float halton(unsigned int i, unsigned int const b) noexcept {
namespace {
constexpr float halton(unsigned int i, unsigned int const b) noexcept {
// skipping a bunch of entries makes the average of the sequence closer to 0.5
i += 409;
float f = 1.0f;
@@ -113,6 +115,19 @@ constexpr static float halton(unsigned int i, unsigned int const b) noexcept {
return r;
}
template <typename ValueType>
void setConstantParameter(FMaterial* const material, std::string_view const name,
ValueType value, bool& dirty) noexcept {
auto id = material->getSpecializationConstantId(name);
if (id.has_value()) {
if (material->setConstant(id.value(), value)) {
dirty = true;
}
}
}
} // anonymous
// ------------------------------------------------------------------------------------------------
PostProcessManager::PostProcessMaterial::PostProcessMaterial(StaticMaterialInfo const& info) noexcept
@@ -289,6 +304,8 @@ static const PostProcessManager::StaticMaterialInfo sMaterialList[] = {
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
{ "sao", MATERIAL(SAO) },
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
{ "gtao", MATERIAL(GTAO) },
{ "gtaoBentNormals", MATERIAL(GTAOBENTNORMALS) },
{ "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR),
{ {"arraySampler", false}, {"componentCount", 1} } },
{ "separableGaussianBlur1L", MATERIAL(SEPARABLEGAUSSIANBLUR),
@@ -846,12 +863,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
0.5f * cameraInfo.projection[0].x * desc.width,
0.5f * cameraInfo.projection[1].y * desc.height);
// Where the falloff function peaks
const float peak = 0.1f * options.radius;
const float intensity = (f::TAU * peak) * options.intensity;
// always square AO result, as it looks much better
const float power = options.power * 2.0f;
const auto invProjection = inverse(cameraInfo.projection);
const float inc = (1.0f / (sampleCount - 0.5f)) * spiralTurns * f::TAU;
@@ -862,40 +873,75 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
0.0, 0.0, 0.0, 1.0
}};
auto& material = computeBentNormals ?
getPostProcessMaterial("saoBentNormals") :
getPostProcessMaterial("sao");
FMaterial const* const ma = material.getMaterial(mEngine);
std::string_view materialName;
if (options.aoType ==
AmbientOcclusionOptions::AmbientOcclusionType::GTAO) {
materialName = computeBentNormals ? "gtaoBentNormals" : "gtao";
} else {
materialName = computeBentNormals ? "saoBentNormals" : "sao";
}
auto& material = getPostProcessMaterial(materialName);
FMaterial const * const ma = material.getMaterial(mEngine);
FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(ma);
mi->setParameter("depth", depth, {
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
// Set AO type specific material parameters
switch (options.aoType) {
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: {
// Where the falloff function peaks
const float peak = 0.1f * options.radius;
const float intensity = (f::TAU * peak) * options.intensity;
// always square AO result, as it looks much better
const float power = options.power * 2.0f;
mi->setParameter("minHorizonAngleSineSquared",
std::pow(std::sin(options.minHorizonAngleRad), 2.0f));
mi->setParameter("intensity", intensity / sampleCount);
mi->setParameter("power", power);
mi->setParameter("peak2", peak * peak);
mi->setParameter("bias", options.bias);
mi->setParameter("sampleCount",
float2{ sampleCount, 1.0f / (sampleCount - 0.5f) });
mi->setParameter("spiralTurns", spiralTurns);
mi->setParameter("angleIncCosSin", float2{ std::cos(inc), std::sin(inc) });
break;
}
case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: {
const auto sliceCount = static_cast<float>(options.gtao.sampleSliceCount);
mi->setParameter("stepsPerSlice",
static_cast<float>(options.gtao.sampleStepsPerSlice));
mi->setParameter("sliceCount",
float2{ sliceCount, 1.0f / sliceCount });
mi->setParameter("power", options.power);
mi->setParameter("radius", options.radius);
mi->setParameter("intensity", options.intensity);
mi->setParameter("thicknessHeuristic", options.gtao.thicknessHeuristic);
break;
}
}
// Set common material parameters
mi->setParameter("invRadiusSquared", 1.0f / (options.radius * options.radius));
mi->setParameter("depth", depth,
{ .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->setParameter("screenFromViewMatrix",
mat4f(screenFromClipMatrix * cameraInfo.projection));
mi->setParameter("resolution",
float4{ desc.width, desc.height, 1.0f / desc.width, 1.0f / desc.height });
mi->setParameter("invRadiusSquared",
1.0f / (options.radius * options.radius));
mi->setParameter("minHorizonAngleSineSquared",
std::pow(std::sin(options.minHorizonAngleRad), 2.0f));
mi->setParameter("projectionScale",
projectionScale);
mi->setParameter("projectionScaleRadius",
projectionScale * options.radius);
mi->setParameter("positionParams", float2{
invProjection[0][0], invProjection[1][1] } * 2.0f);
mi->setParameter("peak2", peak * peak);
mi->setParameter("bias", options.bias);
mi->setParameter("power", power);
mi->setParameter("intensity", intensity / sampleCount);
mi->setParameter("projectionScale", projectionScale);
mi->setParameter("projectionScaleRadius", projectionScale * options.radius);
mi->setParameter("positionParams",
float2{ invProjection[0][0], invProjection[1][1] } * 2.0f);
mi->setParameter("maxLevel", uint32_t(levelCount - 1));
mi->setParameter("sampleCount", float2{ sampleCount, 1.0f / (sampleCount - 0.5f) });
mi->setParameter("spiralTurns", spiralTurns);
mi->setParameter("angleIncCosSin", float2{ std::cos(inc), std::sin(inc) });
mi->setParameter("invFarPlane", 1.0f / -cameraInfo.zf);
mi->setParameter("ssctShadowDistance", options.ssct.shadowDistance);
mi->setParameter("ssctConeAngleTangeant", std::tan(options.ssct.lightConeRad * 0.5f));
mi->setParameter("ssctContactDistanceMaxInv", 1.0f / options.ssct.contactDistanceMax);
mi->setParameter("ssctConeAngleTangeant",
std::tan(options.ssct.lightConeRad * 0.5f));
mi->setParameter("ssctContactDistanceMaxInv",
1.0f / options.ssct.contactDistanceMax);
// light direction in view space
const mat4f view{ cameraInfo.getUserViewMatrix() };
const float3 l = normalize(
@@ -2632,25 +2678,15 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial(
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
bool dirty = false;
auto setConstantParameter =
[&dirty](FMaterial* const material, std::string_view const name, auto value) noexcept {
auto id = material->getSpecializationConstantId(name);
if (id.has_value()) {
if (material->setConstant(id.value(), value)) {
dirty = true;
}
}
};
setConstantParameter(ma, "upscaling", taaOptions.upscaling);
setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection);
setConstantParameter(ma, "filterHistory", taaOptions.filterHistory);
setConstantParameter(ma, "filterInput", taaOptions.filterInput);
setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg);
setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering);
setConstantParameter(ma, "boxType", int32_t(taaOptions.boxType));
setConstantParameter(ma, "boxClipping", int32_t(taaOptions.boxClipping));
setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma);
setConstantParameter(ma, "upscaling", taaOptions.upscaling, dirty);
setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection, dirty);
setConstantParameter(ma, "filterHistory", taaOptions.filterHistory, dirty);
setConstantParameter(ma, "filterInput", taaOptions.filterInput, dirty);
setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg, dirty);
setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering, dirty);
setConstantParameter(ma, "boxType", int32_t(taaOptions.boxType), dirty);
setConstantParameter(ma, "boxClipping", int32_t(taaOptions.boxClipping), dirty);
setConstantParameter(ma, "varianceGamma", taaOptions.varianceGamma, dirty);
if (dirty) {
ma->invalidate();
// TODO: call Material::compile(), we can't do that now because it works only
@@ -2665,18 +2701,8 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(
FMaterial* const ma = material.getMaterial(mEngine);
bool dirty = false;
auto setConstantParameter =
[&dirty](FMaterial* const material, std::string_view const name, auto value) noexcept {
auto id = material->getSpecializationConstantId(name);
if (id.has_value()) {
if (material->setConstant(id.value(), value)) {
dirty = true;
}
}
};
setConstantParameter(ma, "isOneDimensional", colorGrading->isOneDimensional());
setConstantParameter(ma, "isLDR", colorGrading->isLDR());
setConstantParameter(ma, "isOneDimensional", colorGrading->isOneDimensional(), dirty);
setConstantParameter(ma, "isLDR", colorGrading->isLDR(), dirty);
if (dirty) {
ma->invalidate();
+160
View File
@@ -0,0 +1,160 @@
material {
name : gtao,
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 : invFarPlane
},
{
type : int,
name : maxLevel
},
{
type : float,
name : projectionScale
},
{
type : float,
name : intensity
},
{
type : float2,
name : sliceCount
},
{
type : float,
name : stepsPerSlice
},
{
type : float,
name : radius
},
{
type : float,
name : invRadiusSquared
},
{
type : float,
name : projectionScaleRadius
},
{
type : float,
name : power
},
{
type : float,
name : thicknessHeuristic
},
{
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
}
],
variables : [
vertex
],
domain : postprocess,
depthWrite : false,
depthCulling : true
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
// we don't use use uvToRenderTargetUV() here to compensate for metal/vulkan texture
// coordinates differences because this is done in sampleDepth()
postProcess.vertex.xy = postProcess.normalizedUV;
}
}
fragment {
#define COMPUTE_BENT_NORMAL 0
#include "gtaoImpl.fs"
#include "ssctImpl.fs"
#include "../utils/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);
highp vec3 origin = computeViewSpacePositionFromDepth(uv, z, materialParams.positionParams);
vec3 normal = computeViewSpaceNormal(materialParams_depth, uv, depth, origin,
materialParams.resolution.zw,
materialParams.positionParams);
float occlusion = 0.0;
vec3 bentNormal; // will be discarded
if (materialParams.intensity > 0.0) {
groundTruthAmbientOcclusion(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);
aoVisibility = mix(1.0, aoVisibility, materialParams.intensity);
#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
postProcess.color.rgb = vec3(aoVisibility, pack(origin.z * materialParams.invFarPlane));
}
}
@@ -0,0 +1,176 @@
material {
name : gtao,
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 : invFarPlane
},
{
type : int,
name : maxLevel
},
{
type : float,
name : projectionScale
},
{
type : float,
name : intensity
},
{
type : float2,
name : sliceCount
},
{
type : float,
name : stepsPerSlice
},
{
type : float,
name : radius
},
{
type : float,
name : invRadiusSquared
},
{
type : float,
name : projectionScaleRadius
},
{
type : float,
name : power
},
{
type : float,
name : thicknessHeuristic
},
{
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) {
// we don't use use uvToRenderTargetUV() here to compensate for metal/vulkan texture
// coordinates differences because this is done in sampleDepth()
postProcess.vertex.xy = postProcess.normalizedUV;
}
}
fragment {
#define COMPUTE_BENT_NORMAL 1
#include "gtaoImpl.fs"
#include "ssctImpl.fs"
#include "../utils/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);
highp vec3 origin = computeViewSpacePositionFromDepth(uv, z, materialParams.positionParams);
vec3 normal = computeViewSpaceNormal(materialParams_depth, uv, depth, origin,
materialParams.resolution.zw,
materialParams.positionParams);
float occlusion = 0.0;
vec3 bentNormal = normal;
if (materialParams.intensity > 0.0) {
groundTruthAmbientOcclusion(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);
aoVisibility = mix(1.0, aoVisibility, materialParams.intensity);
#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.bnData = packBentNormal(bn);
postProcess.aoData = vec3(aoVisibility, pack(origin.z * materialParams.invFarPlane));
}
}
+153
View File
@@ -0,0 +1,153 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This is our implementation of GTAO -- it's not standalone because it uses materialParams
* directly. Therefore it must be included in *.mat file that has all these parameters.
* The main reason for using a separate file is to be able to have several version of the
* code with only minor changes.
*/
#include "../utils/geometry.fs"
#define rsqrt inversesqrt
#ifndef COMPUTE_BENT_NORMAL
#error COMPUTE_BENT_NORMAL must be set
#endif
const float kLog2LodRate = 3.0;
// Ambient Occlusion, largely inspired from:
// "Practical Real-Time Strategies for Accurate Indirect Occlusion" by Jimenez et al.
// https://github.com/GameTechDev/XeGTAO
// https://github.com/MaxwellGengYF/Unity-Ground-Truth-Ambient-Occlusion
highp vec3 getViewSpacePosition(vec2 uv, float level) {
highp float depth = sampleDepthLinear(materialParams_depth, uv, level);
return computeViewSpacePositionFromDepth(uv, depth, materialParams.positionParams);
}
float integrateArcCosWeight(float h, float n) {
float arc = -cos(2.0 * h - n) + cos(n) + 2.0 * h * sin(n);
return 0.25 * arc;
}
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 93
float spatialDirectionNoise(float2 uv) {
int2 position = int2(uv * materialParams.resolution.xy);
return (1.0/16.0) * (float(((position.x + position.y) & 3) << 2) + float(position.x & 3));
}
// https://blog.selfshadow.com/publications/s2016-shading-course/activision/s2016_pbs_activision_occlusion.pdf slide 93
float spatialOffsetsNoise(float2 uv) {
int2 position = int2(uv * materialParams.resolution.xy);
return 0.25 * float((position.y - position.x) & 3);
}
void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
highp vec2 uv, highp vec3 origin, vec3 normal) {
vec2 uvSamplePos = uv;
highp vec3 viewDir = normalize(-origin);
highp float ssRadius = -(materialParams.projectionScaleRadius / origin.z);
float noiseOffset = spatialOffsetsNoise(uv);
float noiseDirection = spatialDirectionNoise(uv);
float initialRayStep = fract(noiseOffset);
// The distance we want to move forward for each step
float stepRadius = ssRadius / (materialParams.stepsPerSlice + 1.0);
float visibility = 0.0;
for (float i = 0.0; i < materialParams.sliceCount.x; i += 1.0) {
float slice = (i + noiseDirection) * materialParams.sliceCount.y;
float phi = slice * PI;
float cosPhi = cos(phi);
float sinPhi = sin(phi);
vec2 omega = vec2(cosPhi, sinPhi);
// Calculate the direction of the current slice
vec3 direction = vec3(cosPhi, sinPhi, 0.0);
// Project direction onto the plane orthogonal to viewDir.
vec3 orthoDirection = normalize(direction - (dot(direction, viewDir)*viewDir));
// axis is orthogonal to direction and viewDir (basically the normal of the slice plane)
// Used to define projectedNormal
vec3 axis = cross(orthoDirection, viewDir);
// Project the normal onto the slice plane
vec3 projNormal = normal - axis * dot(normal, axis);
float signNorm = sign(dot(orthoDirection, projNormal));
float projNormalLength = length(projNormal);
float cosNorm = saturate(dot(projNormal, viewDir) / projNormalLength);
float n = signNorm * acosFast(cosNorm);
float horizonCos0 = -1.0;
float horizonCos1 = -1.0;
for (float j = 0.0; j < materialParams.stepsPerSlice; j += 1.0) {
// At least move 1 pixel forward in the screen-space
vec2 sampleOffset = max((j + initialRayStep)*stepRadius, 1.0 + j) * omega;
float sampleOffsetLength = length(sampleOffset);
float level = clamp(floor(log2(sampleOffsetLength)) - kLog2LodRate, 0.0, float(materialParams.maxLevel));
vec2 uvSampleOffset = sampleOffset * materialParams.resolution.zw;
vec2 sampleScreenPos0 = uv + uvSampleOffset;
vec2 sampleScreenPos1 = uv - uvSampleOffset;
// Sample the depth and use it to reconstruct the view space position
highp vec3 samplePos0 = getViewSpacePosition(sampleScreenPos0, level);
highp vec3 samplePos1 = getViewSpacePosition(sampleScreenPos1, level);
highp vec3 sampleDelta0 = (samplePos0 - origin);
highp vec3 sampleDelta1 = (samplePos1 - origin);
vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1));
vec2 invSampleDist = rsqrt(sqSampleDist);
// Use the view space radius to calculate the fallOff
vec2 fallOff = saturate(sqSampleDist.xy * materialParams.invRadiusSquared * 2.0);
// sample horizon cos
float shc0 = dot(sampleDelta0, viewDir) * invSampleDist.x;
float shc1 = dot(sampleDelta1, viewDir) * invSampleDist.y;
// If the new sample value is greater then the current one, update the value with some fallOff.
// Otherwise, apply thicknessHeuristic.
horizonCos0 = shc0 > horizonCos0 ? mix(shc0, horizonCos0, fallOff.x) : mix(horizonCos0, shc0, materialParams.thicknessHeuristic);
horizonCos1 = shc1 > horizonCos1 ? mix(shc1, horizonCos1, fallOff.y) : mix(horizonCos1, shc1, materialParams.thicknessHeuristic);
}
float h0 = -acosFast(horizonCos1);
float h1 = acosFast(horizonCos0);
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
#if COMPUTE_BENT_NORMAL
float angle = 0.5 * (h0 + h1);
bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle);
#endif
visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n));
}
obscurance = 1.0 - saturate(visibility * materialParams.sliceCount.y);
#if COMPUTE_BENT_NORMAL
bentNormal = normalize(bentNormal);
#endif
}
+57 -2
View File
@@ -504,6 +504,23 @@ std::ostream& operator<<(std::ostream& out, const RenderQuality& in) {
<< "}";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::AmbientOcclusionType* out) {
if (0 == compare(tokens[i], jsonChunk, "SAO")) { *out = AmbientOcclusionOptions::AmbientOcclusionType::SAO; }
else if (0 == compare(tokens[i], jsonChunk, "GTAO")) { *out = AmbientOcclusionOptions::AmbientOcclusionType::GTAO; }
else {
slog.w << "Invalid AmbientOcclusionOptions::AmbientOcclusionType: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
}
return i + 1;
}
std::ostream& operator<<(std::ostream& out, AmbientOcclusionOptions::AmbientOcclusionType in) {
switch (in) {
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: return out << "\"SAO\"";
case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: return out << "\"GTAO\"";
}
return out << "\"INVALID\"";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Ssct* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
@@ -557,13 +574,47 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Ssct&
<< "}";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Gtao* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "sampleSliceCount") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->sampleSliceCount);
} else if (compare(tok, jsonChunk, "sampleStepsPerSlice") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->sampleStepsPerSlice);
} else if (compare(tok, jsonChunk, "thicknessHeuristic") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->thicknessHeuristic);
} else {
slog.w << "Invalid Gtao key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
}
if (i < 0) {
slog.e << "Invalid Gtao value: '" << STR(tok, jsonChunk) << "'" << io::endl;
return i;
}
}
return i;
}
std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Gtao& in) {
return out << "{\n"
<< "\"sampleSliceCount\": " << int(in.sampleSliceCount) << ",\n"
<< "\"sampleStepsPerSlice\": " << int(in.sampleStepsPerSlice) << ",\n"
<< "\"thicknessHeuristic\": " << (in.thicknessHeuristic) << "\n"
<< "}";
}
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions* out) {
CHECK_TOKTYPE(tokens[i], JSMN_OBJECT);
int size = tokens[i++].size;
for (int j = 0; j < size; ++j) {
const jsmntok_t tok = tokens[i];
CHECK_KEY(tok);
if (compare(tok, jsonChunk, "radius") == 0) {
if (compare(tok, jsonChunk, "aoType") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->aoType);
} else if (compare(tok, jsonChunk, "radius") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->radius);
} else if (compare(tok, jsonChunk, "power") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->power);
@@ -589,6 +640,8 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusio
i = parse(tokens, i + 1, jsonChunk, &out->minHorizonAngleRad);
} else if (compare(tok, jsonChunk, "ssct") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->ssct);
} else if (compare(tok, jsonChunk, "gtao") == 0) {
i = parse(tokens, i + 1, jsonChunk, &out->gtao);
} else {
slog.w << "Invalid AmbientOcclusionOptions key: '" << STR(tok, jsonChunk) << "'" << io::endl;
i = parse(tokens, i + 1);
@@ -603,6 +656,7 @@ int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusio
std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) {
return out << "{\n"
<< "\"aoType\": " << (in.aoType) << ",\n"
<< "\"radius\": " << (in.radius) << ",\n"
<< "\"power\": " << (in.power) << ",\n"
<< "\"bias\": " << (in.bias) << ",\n"
@@ -615,7 +669,8 @@ std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) {
<< "\"enabled\": " << to_string(in.enabled) << ",\n"
<< "\"bentNormals\": " << to_string(in.bentNormals) << ",\n"
<< "\"minHorizonAngleRad\": " << (in.minHorizonAngleRad) << ",\n"
<< "\"ssct\": " << (in.ssct) << "\n"
<< "\"ssct\": " << (in.ssct) << ",\n"
<< "\"gtao\": " << (in.gtao) << "\n"
<< "}";
}
+6
View File
@@ -60,9 +60,15 @@ std::ostream& operator<<(std::ostream& out, const VignetteOptions& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, RenderQuality* out);
std::ostream& operator<<(std::ostream& out, const RenderQuality& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::AmbientOcclusionType* out);
std::ostream& operator<<(std::ostream& out, AmbientOcclusionOptions::AmbientOcclusionType in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Ssct* out);
std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Ssct& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions::Gtao* out);
std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions::Gtao& in);
int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions* out);
std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in);
+28 -1
View File
@@ -824,6 +824,12 @@ void ViewerGui::updateUserInterface() {
if (ImGui::CollapsingHeader("SSAO Options")) {
auto& ssao = mSettings.view.ssao;
ImGui::Checkbox("Enabled", &ssao.enabled);
int ambienOcclusionType = (int)ssao.aoType;
ImGui::Combo("AO Type", &ambienOcclusionType, "SAO\0GTAO\0\0");
ssao.aoType = (decltype(ssao.aoType))ambienOcclusionType;
int quality = (int) ssao.quality;
int lowpass = (int) ssao.lowPassFilter;
bool upsampling = ssao.upsampling != View::QualityLevel::LOW;
@@ -833,7 +839,28 @@ void ViewerGui::updateUserInterface() {
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);
ImGui::SliderFloat("Radius", &ssao.radius, 0.1f, 10.0f);
ImGui::SliderFloat("Power", &ssao.power, 1.0f, 8.0f);
switch (ssao.aoType) {
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: {
ImGui::SliderFloat("Min Horizon angle", &ssao.minHorizonAngleRad, 0.0f,
(float) M_PI_4);
break;
}
case AmbientOcclusionOptions::AmbientOcclusionType::GTAO: {
int sliceCount = ssao.gtao.sampleSliceCount;
int stepsPerSlice = ssao.gtao.sampleStepsPerSlice;
ImGui::SliderInt("Slice Count", &sliceCount, 1, 10);
ImGui::SliderInt("Steps Per Slice", &stepsPerSlice, 1, 4);
ssao.gtao.sampleSliceCount = static_cast<uint8_t>(sliceCount);
ssao.gtao.sampleStepsPerSlice = static_cast<uint8_t>(stepsPerSlice);
break;
}
}
ImGui::SliderFloat("Bilateral Threshold", &ssao.bilateralThreshold, 0.0f, 0.1f);
ImGui::Checkbox("Half resolution", &halfRes);
ssao.resolution = halfRes ? 0.5f : 1.0f;
+11
View File
@@ -108,8 +108,18 @@ Filament.loadGeneratedExtensions = function() {
return Object.assign(options, overrides);
};
Filament.View.prototype.setGtaoDefaults = function(overrides) {
const options = {
sampleSliceCount: 4,
sampleStepsPerSlice: 3,
thicknessHeuristic: 0.004,
};
return Object.assign(options, overrides);
};
Filament.View.prototype.setAmbientOcclusionOptionsDefaults = function(overrides) {
const options = {
aoType: Filament.View$AmbientOcclusionOptions$AmbientOcclusionType.SAO,
radius: 0.3,
power: 1.0,
bias: 0.0005,
@@ -123,6 +133,7 @@ Filament.loadGeneratedExtensions = function() {
bentNormals: false,
minHorizonAngleRad: 0.0,
// JavaScript binding for ssct is not yet supported, must use default value.
// JavaScript binding for gtao is not yet supported, must use default value.
};
return Object.assign(options, overrides);
};
+33 -4
View File
@@ -1516,6 +1516,11 @@ export interface View$RenderQuality {
hdrColorBuffer?: View$QualityLevel;
}
export enum View$AmbientOcclusionOptions$AmbientOcclusionType {
SAO, // use Scalable Ambient Occlusion
GTAO, // use Ground Truth-Based Ambient Occlusion
}
/**
* Screen Space Cone Tracing (SSCT) options
* Ambient shadows from dominant light
@@ -1563,11 +1568,33 @@ export interface View$AmbientOcclusionOptions$Ssct {
enabled?: boolean;
}
/**
* Ground Truth-base Ambient Occlusion (GTAO) options
*/
export interface View$AmbientOcclusionOptions$Gtao {
/**
* # of slices. Higher value makes less noise.
*/
sampleSliceCount?: number;
/**
* # of steps the radius is divided into for integration. Higher value makes less bias.
*/
sampleStepsPerSlice?: number;
/**
* thickness heuristic, should be closed to 0
*/
thicknessHeuristic?: number;
}
/**
* Options for screen space Ambient Occlusion (SSAO) and Screen Space Cone Tracing (SSCT)
* @see setAmbientOcclusionOptions()
*/
export interface View$AmbientOcclusionOptions {
/**
* Type of ambient occlusion algorithm.
*/
aoType?: View$AmbientOcclusionOptions$AmbientOcclusionType;
/**
* Ambient Occlusion radius in meters, between 0 and ~10.
*/
@@ -1577,7 +1604,8 @@ export interface View$AmbientOcclusionOptions {
*/
power?: number;
/**
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
* Self-occlusion bias in meters. Use to avoid self-occlusion.
* Between 0 and a few mm. No effect when aoType set to GTAO
*/
bias?: number;
/**
@@ -1593,11 +1621,11 @@ export interface View$AmbientOcclusionOptions {
*/
bilateralThreshold?: number;
/**
* affects # of samples used for AO.
* affects # of samples used for AO and params for filtering
*/
quality?: View$QualityLevel;
/**
* affects AO smoothness
* affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO.
*/
lowPassFilter?: View$QualityLevel;
/**
@@ -1613,10 +1641,11 @@ export interface View$AmbientOcclusionOptions {
*/
bentNormals?: boolean;
/**
* min angle in radian to consider
* min angle in radian to consider. No effect when aoType set to GTAO.
*/
minHorizonAngleRad?: number;
// JavaScript binding for ssct is not yet supported, must use default value.
// JavaScript binding for gtao is not yet supported, must use default value.
}
/**
+8
View File
@@ -95,7 +95,14 @@ value_object<View::AmbientOcclusionOptions::Ssct>("View$AmbientOcclusionOptions$
.field("enabled", &View::AmbientOcclusionOptions::Ssct::enabled)
;
value_object<View::AmbientOcclusionOptions::Gtao>("View$AmbientOcclusionOptions$Gtao")
.field("sampleSliceCount", &View::AmbientOcclusionOptions::Gtao::sampleSliceCount)
.field("sampleStepsPerSlice", &View::AmbientOcclusionOptions::Gtao::sampleStepsPerSlice)
.field("thicknessHeuristic", &View::AmbientOcclusionOptions::Gtao::thicknessHeuristic)
;
value_object<View::AmbientOcclusionOptions>("View$AmbientOcclusionOptions")
.field("aoType", &View::AmbientOcclusionOptions::aoType)
.field("radius", &View::AmbientOcclusionOptions::radius)
.field("power", &View::AmbientOcclusionOptions::power)
.field("bias", &View::AmbientOcclusionOptions::bias)
@@ -109,6 +116,7 @@ value_object<View::AmbientOcclusionOptions>("View$AmbientOcclusionOptions")
.field("bentNormals", &View::AmbientOcclusionOptions::bentNormals)
.field("minHorizonAngleRad", &View::AmbientOcclusionOptions::minHorizonAngleRad)
// JavaScript binding for ssct is not yet supported, must use default value.
// JavaScript binding for gtao is not yet supported, must use default value.
;
value_object<View::MultiSampleAntiAliasingOptions>("View$MultiSampleAntiAliasingOptions")
+5
View File
@@ -33,6 +33,11 @@ enum_<View::DepthOfFieldOptions::Filter>("View$DepthOfFieldOptions$Filter")
.value("MEDIAN", View::DepthOfFieldOptions::Filter::MEDIAN)
;
enum_<View::AmbientOcclusionOptions::AmbientOcclusionType>("View$AmbientOcclusionOptions$AmbientOcclusionType")
.value("SAO", View::AmbientOcclusionOptions::AmbientOcclusionType::SAO)
.value("GTAO", View::AmbientOcclusionOptions::AmbientOcclusionType::GTAO)
;
enum_<View::TemporalAntiAliasingOptions::BoxType>("View$TemporalAntiAliasingOptions$BoxType")
.value("AABB", View::TemporalAntiAliasingOptions::BoxType::AABB)
.value("VARIANCE", View::TemporalAntiAliasingOptions::BoxType::VARIANCE)