Introducing the sl files and the blurring stages

This commit is contained in:
samcake 2016-01-02 11:27:50 -04:00
parent 8dba66fb14
commit 52f3c7e2cb
24 changed files with 579 additions and 787 deletions

View file

@ -12,7 +12,7 @@
Script.include("cookies.js");
var MENU = "Developer>Render>Debug Deferred Buffer";
var ACTIONS = ["Off", "Diffuse", "Alpha", "Specular", "Roughness", "Normal", "Depth", "Lighting", "AmbientOcclusion", "Custom"];
var ACTIONS = ["Off", "Diffuse", "AmbientOcclusion", "Specular", "Roughness", "Normal", "Depth", "Lighting", "PyramidDepth", "OcclusionRaw", "OcclusionBlurred", "Custom"];
var SETTINGS_KEY = "EngineDebugScript.DebugMode";
Number.prototype.clamp = function(min, max) {
@ -52,6 +52,7 @@ var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D);
var resizing = false;
var previousMode = Settings.getValue(SETTINGS_KEY, -1);
previousMode = 1;
Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]);
Render.deferredDebugMode = previousMode;
Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size
@ -98,6 +99,11 @@ panel.newSlider("Tone Mapping Exposure", -10, 10,
function() { return Render.tone.exposure; },
function (value) { return (value); });
panel.newSlider("Ambient Occlusion Radius", 0.0, 2.0,
function (value) { Render.ambientOcclusion.radius = value; },
function() { return Render.ambientOcclusion.radius; },
function (value) { return (value); });
var tickTackPeriod = 500;
function updateCounters() {

View file

@ -12,6 +12,9 @@
#include <glm/gtc/random.hpp>
#include <algorithm> //min max and more
#include <PathUtils.h>
#include <SharedUtil.h>
#include <gpu/Context.h>
@ -24,19 +27,16 @@
#include "ViewFrustum.h"
#include "GeometryCache.h"
#include "ambient_occlusion_vert.h"
#include "ambient_occlusion_frag.h"
#include "gaussian_blur_vertical_vert.h"
#include "gaussian_blur_horizontal_vert.h"
#include "gaussian_blur_frag.h"
#include "occlusion_blend_frag.h"
#include "ssao_makePyramid_frag.h"
#include "ssao_makeOcclusion_frag.h"
#include "ssao_makeHorizontalBlur_frag.h"
#include "ssao_makeVerticalBlur_frag.h"
const int AmbientOcclusionEffect_ParamsSlot = 0;
const int AmbientOcclusionEffect_DeferredTransformSlot = 1;
const int AmbientOcclusionEffect_DepthMapSlot = 0;
const int AmbientOcclusionEffect_PyramidMapSlot = 0;
const int AmbientOcclusionEffect_OcclusionMapSlot = 0;
AmbientOcclusionEffect::AmbientOcclusionEffect() {
Parameters parameters;
@ -45,44 +45,8 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() {
const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() {
if (!_pyramidPipeline) {
const char AO_frag[] = R"SCRIBE(#version 410 core
//
// Created by Sam Gateau on 12/23/2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
struct AmbientOcclusionParams {
vec4 _clipInfo;
mat4 _projection;
vec4 _radius_s0_s1_s2;
};
uniform ambientOcclusionParamsBuffer {
AmbientOcclusionParams params;
};
float evalZeyeFromZdb(float depth) {
return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z);
}
// the depth texture
uniform sampler2D depthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
float Zdb = texture(depthMap, varTexCoord0).x;
float Zeye = -evalZeyeFromZdb(Zdb);
outFragColor = vec4(Zeye, 0.0, 0.0, 1.0);
}
)SCRIBE";
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(AO_frag));
auto ps = gpu::Shader::createPixel(std::string(ssao_makePyramid_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
@ -108,210 +72,8 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() {
const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() {
if (!_occlusionPipeline) {
const char AO_frag[] = R"SCRIBE(#version 410 core
//
// Created by Sam Gateau on 12/23/2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
const int NUM_SAMPLES = 11;
const int NUM_SPIRAL_TURNS= 7;
const int LOG_MAX_OFFSET = 3;
const int MAX_MIP_LEVEL = 5;
// the depth texture
uniform sampler2D pyramidMap;
float getZeye(vec2 texcoord) {
return -texture(pyramidMap, texcoord, 0).x;
}
struct AmbientOcclusionParams {
vec4 _clipInfo;
mat4 _projection;
vec4 _radius_s0_s1_s2;
};
uniform ambientOcclusionParamsBuffer {
AmbientOcclusionParams params;
};
float getProjScale() {
return 500.0; // this should be viewportHeight * Proj[1][1] / 2.0
}
float getRadius() {
return params._radius_s0_s1_s2.x;
}
vec3 evalEyePositionFromZeye(float Zeye, vec2 texcoord) {
// compute the view space position using the depth
// basically manually pick the proj matrix components to do the inverse
float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * params._projection[2][0] - params._projection[3][0]) / params._projection[0][0];
float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * params._projection[2][1] - params._projection[3][1]) / params._projection[1][1];
return vec3(Xe, Ye, Zeye);
}
vec3 evalEyePosition(vec2 texcoord) {
float Zeye = getZeye(texcoord);
return evalEyePositionFromZeye(Zeye, texcoord);
}
vec3 evalEyeNormal(vec3 C) {
return -normalize(cross(dFdy(C), dFdx(C)));
}
/** Used for packing Z into the GB channels */
float CSZToKey(float z) {
return clamp(z * (1.0 / params._clipInfo.z), 0.0, 1.0);
}
/** Used for packing Z into the GB channels */
void packKey(float key, out vec2 p) {
// Round to the nearest 1/256.0
float temp = floor(key * 256.0);
// Integer part
p.x = temp * (1.0 / 256.0);
// Fractional part
p.y = key * 256.0 - temp;
}
vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){
// Radius relative to ssR
float alpha = float(sampleNumber + 0.5) * (1.0 / NUM_SAMPLES);
float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle;
ssR = alpha;
return vec2(cos(angle), sin(angle));
}
in vec2 varTexCoord0;
vec3 getOffsetPosition(ivec2 ssC, vec2 unitOffset, float ssR) {
// Derivation:
// mipLevel = floor(log(ssR / MAX_OFFSET));
int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
ivec2 ssP = ivec2(ssR * unitOffset) + ssC;
vec3 P;
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
// Manually clamp to the texture size because texelFetch bypasses the texture unit
ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), textureSize(pyramidMap, mipLevel) - ivec2(1));
P.z = -texelFetch(pyramidMap, mipP, mipLevel).r;
// Offset to pixel center
//P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / textureSize(pyramidMap, 0);
P = evalEyePositionFromZeye(P.z, tapUV);
return P;
}
float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) {
// Offset on the unit disk, spun for this pixel
float ssR;
vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
ssR *= ssDiskRadius;
// The occluding point in camera space
vec3 Q = getOffsetPosition(ssC, unitOffset, ssR);
vec3 v = Q - C;
float vv = dot(v, v);
float vn = dot(v, n_C);
const float bias = 0.01;
const float epsilon = 0.01;
const float radius2 = 0.5 * 0.5;
// A: From the HPG12 paper
// Note large epsilon to avoid overdarkening within cracks
// return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6;
// B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended]
float f = max(radius2 - vv, 0.0); return f * f * f * max((vn - bias) / (epsilon + vv), 0.0);
// C: Medium contrast (which looks better at high radii), no division. Note that the
// contribution still falls off with radius^2, but we've adjusted the rate in a way that is
// more computationally efficient and happens to be aesthetically pleasing.
// return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0);
// D: Low contrast, no division operation
// return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0);
}
out vec4 outFragColor;
vec3 debugValue(float f, float scale) {
if (f < 0.0) {
return vec3((scale + f) / scale, 0.0, 0.0);
} else {
return vec3(0.0, (scale - f) / scale, 0.0);
}
}
void main(void) {
// Pixel being shaded
ivec2 ssC = ivec2(gl_FragCoord.xy);
vec3 Cp = evalEyePosition(varTexCoord0);
// packKey(CSZToKey(Cp.z), bilateralKey);
// Hash function used in the HPG12 AlchemyAO paper
float randomPatternRotationAngle = (3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10;
vec3 Cn = evalEyeNormal(Cp);
// Choose the screen-space sample radius
// proportional to the projected area of the sphere
float ssDiskRadius = -getProjScale() * getRadius() / Cp.z;
float sum = 0.0;
for (int i = 0; i < NUM_SAMPLES; ++i) {
sum += sampleAO(ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle);
}
float intensityDivR6 = 1.0;
float A = max(0.0, 1.0 - sum * intensityDivR6 * (5.0 / NUM_SAMPLES));
// Bilateral box-filter over a quad for free, respecting depth edges
// (the difference that this makes is subtle)
if (abs(dFdx(Cp.z)) < 0.02) {
A -= dFdx(A) * ((ssC.x & 1) - 0.5);
}
if (abs(dFdy(Cp.z)) < 0.02) {
A -= dFdy(A) * ((ssC.y & 1) - 0.5);
}
//outFragColor = vec4(debugValue(Cn.y, 10), A);
outFragColor = vec4(debugValue(Cn.y, 1), A);
}
)SCRIBE";
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(AO_frag));
auto ps = gpu::Shader::createPixel(std::string(ssao_makeOcclusion_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
@ -327,8 +89,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() {
// Stencil test all the ao passes for objects pixels only, not the background
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
// state->setColorWriteMask(false, false, false, true);
state->setColorWriteMask(true, true, true, true);
state->setColorWriteMask(true, true, true, false);
// Good to go add the brand new pipeline
_occlusionPipeline = gpu::Pipeline::create(program, state);
@ -336,77 +97,148 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() {
return _occlusionPipeline;
}
const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() {
if (!_vBlurPipeline) {
auto vs = gpu::Shader::createVertex(std::string(gaussian_blur_vertical_vert));
auto ps = gpu::Shader::createPixel(std::string(gaussian_blur_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Link the horizontal blur FBO to texture
/* _vBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width(), DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height()));
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _vBlurBuffer->getWidth();
auto height = _vBlurBuffer->getHeight();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_vBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
*/
// Good to go add the brand new pipeline
_vBlurPipeline = gpu::Pipeline::create(program, state);
}
return _vBlurPipeline;
}
const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() {
if (!_hBlurPipeline) {
auto vs = gpu::Shader::createVertex(std::string(gaussian_blur_horizontal_vert));
auto ps = gpu::Shader::createPixel(std::string(gaussian_blur_frag));
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(ssao_makeHorizontalBlur_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), AmbientOcclusionEffect_DeferredTransformSlot));
slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
/* // Link the horizontal blur FBO to texture
_hBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
DependencyManager::get<FramebufferCache>()->getFrameBufferSize().width(), DependencyManager::get<FramebufferCache>()->getFrameBufferSize().height()));
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _hBlurBuffer->getWidth();
auto height = _hBlurBuffer->getHeight();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_hBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
*/
// Stencil test all the ao passes for objects pixels only, not the background
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
state->setColorWriteMask(true, true, true, false);
// Good to go add the brand new pipeline
_hBlurPipeline = gpu::Pipeline::create(program, state);
}
return _hBlurPipeline;
}
const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() {
if (!_vBlurPipeline) {
auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS();
auto ps = gpu::Shader::createPixel(std::string(ssao_makeVerticalBlur_frag));
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
gpu::Shader::BindingSet slotBindings;
slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot));
slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), AmbientOcclusionEffect_DeferredTransformSlot));
slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot));
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
// Stencil test all the ao passes for objects pixels only, not the background
state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP));
// Vertical blur write just the final result Occlusion value in the alpha channel
state->setColorWriteMask(false, false, false, true);
// Good to go add the brand new pipeline
_vBlurPipeline = gpu::Pipeline::create(program, state);
const char blur_frag[] = R"SCRIBE(#version 410 core
//
// Created by Sam Gateau on 12/31/2015
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
struct AmbientOcclusionParams {
vec4 _clipInfo;
mat4 _projection;
vec4 _radius_s0_s1_s2;
};
uniform ambientOcclusionParamsBuffer {
AmbientOcclusionParams params;
};
float evalZeyeFromZdb(float depth) {
return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z);
}
// the depth texture
uniform sampler2D depthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
ivec2 ssC = ivec2(gl_FragCoord.xy);
vec4 temp = texelFetch(source, ssC, 0);
keyPassThrough = temp.KEY_COMPONENTS;
float key = unpackKey(keyPassThrough);
VALUE_TYPE sum = temp.VALUE_COMPONENTS;
if (key == 1.0) {
// Sky pixel (if you aren't using depth keying, disable this test)
result = sum;
return;
}
// Base weight for depth falloff. Increase this for more blurriness,
// decrease it for better edge discrimination
float BASE = gaussian[0];
float totalWeight = BASE;
sum *= totalWeight;
for (int r = -R; r <= R; ++r) {
// We already handled the zero case above. This loop should be unrolled and the static branch optimized out,
// so the IF statement has no runtime cost
if (r != 0) {
temp = texelFetch(source, ssC + axis * (r * SCALE), 0);
float tapKey = unpackKey(temp.KEY_COMPONENTS);
VALUE_TYPE value = temp.VALUE_COMPONENTS;
// spatial domain: offset gaussian tap
float weight = 0.3 + gaussian[abs(r)];
// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
weight *= max(0.0, 1.0
- (EDGE_SHARPNESS * 2000.0) * abs(tapKey - key)
);
sum += value * weight;
totalWeight += weight;
}
}
const float epsilon = 0.0001;
result = sum / (totalWeight + epsilon);
)SCRIBE";
}
return _vBlurPipeline;
}
void AmbientOcclusionEffect::setClipInfo(float nearZ, float farZ) {
_parametersBuffer.edit<Parameters>()._clipInfo = glm::vec4(nearZ*farZ, farZ -nearZ, -farZ, 0.0f);
}
void AmbientOcclusionEffect::setRadius(float radius) {
radius = std::max(0.01f, radius);
_parametersBuffer.edit<Parameters>()._radius_radius2_InvRadius6_s2 = glm::vec4(radius, radius * radius, 1.0f / pow(radius, 6.0f), 0.0f);
}
void AmbientOcclusionEffect::updateDeferredTransformBuffer(const render::RenderContextPointer& renderContext) {
// Allocate the parameters buffer used by all the deferred shaders
if (!_deferredTransformBuffer[0]._buffer) {
@ -515,7 +347,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
auto framebufferCache = DependencyManager::get<FramebufferCache>();
auto depthBuffer = framebufferCache->getPrimaryDepthTexture();
auto pyramidFBO = framebufferCache->getDepthPyramidFramebuffer();
auto occlusionFBO = framebufferCache->getDeferredFramebufferDepthColor();
auto occlusionFBO = framebufferCache->getOcclusionFramebuffer();
auto occlusionBlurredFBO = framebufferCache->getOcclusionBlurredFramebuffer();
auto occlusionFinalFBO = framebufferCache->getDeferredFramebufferDepthColor();
QSize framebufferSize = framebufferCache->getFrameBufferSize();
float sMin = args->_viewport.x / (float)framebufferSize.width();
@ -536,7 +370,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
auto pyramidPipeline = getPyramidPipeline();
auto occlusionPipeline = getOcclusionPipeline();
auto firstHBlurPipeline = getHBlurPipeline();
auto lastVBlurPipeline = getVBlurPipeline();
gpu::doInBatch(args->_context, [=](gpu::Batch& batch) {
batch.enableStereo(false);
@ -557,11 +393,12 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
// Pyramid pass
batch.setFramebuffer(pyramidFBO);
batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(args->_viewFrustum->getFarClip(), 0.0f, 0.0f, 0.0f));
batch.setPipeline(pyramidPipeline);
batch.setResourceTexture(AmbientOcclusionEffect_DepthMapSlot, depthBuffer);
batch.draw(gpu::TRIANGLE_STRIP, 4);
//
// Make pyramid mips
batch.setFramebuffer(occlusionFBO);
batch.generateTextureMips(pyramidFBO->getRenderBuffer(0));
@ -570,5 +407,16 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
batch.setResourceTexture(AmbientOcclusionEffect_PyramidMapSlot, pyramidFBO->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
// Blur 1 pass
batch.setFramebuffer(occlusionBlurredFBO);
batch.setPipeline(firstHBlurPipeline);
batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionFBO->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
// Blur 2 pass
batch.setFramebuffer(occlusionFinalFBO);
batch.setPipeline(lastVBlurPipeline);
batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionBlurredFBO->getRenderBuffer(0));
batch.draw(gpu::TRIANGLE_STRIP, 4);
});
}

View file

@ -24,6 +24,9 @@ public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
typedef render::Job::Model<AmbientOcclusionEffect> JobModel;
void setRadius(float radius);
float getRadius() const { return _parametersBuffer.get<Parameters>()._radius_radius2_InvRadius6_s2.x; }
private:
void setClipInfo(float nearZ, float farZ);
@ -33,7 +36,7 @@ private:
public:
glm::vec4 _clipInfo;
glm::mat4 _projection;
glm::vec4 _radius_s0_s1_s2{ 0.5, 0.0, 0.0, 0.0 };
glm::vec4 _radius_radius2_InvRadius6_s2{ 0.5, 0.5 * 0.5, 1.0 / (0.25 * 0.25 * 0.25), 0.0 };
Parameters() {}
};
@ -56,8 +59,8 @@ private:
const gpu::PipelinePointer& getPyramidPipeline();
const gpu::PipelinePointer& getOcclusionPipeline();
const gpu::PipelinePointer& getHBlurPipeline();
const gpu::PipelinePointer& getVBlurPipeline();
const gpu::PipelinePointer& getHBlurPipeline(); // first
const gpu::PipelinePointer& getVBlurPipeline(); // second
gpu::PipelinePointer _pyramidPipeline;
gpu::PipelinePointer _occlusionPipeline;

View file

@ -32,7 +32,9 @@ enum Slots {
Specular,
Depth,
Lighting,
Pyramid
Pyramid,
OcclusionRaw,
OcclusionBlurred
};
static const std::string DEEFAULT_DIFFUSE_SHADER {
@ -40,7 +42,7 @@ static const std::string DEEFAULT_DIFFUSE_SHADER {
" return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
" }"
};
static const std::string DEEFAULT_ALPHA_SHADER {
static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(diffuseMap, uv).a), 1.0);"
" }"
@ -70,12 +72,23 @@ static const std::string DEEFAULT_LIGHTING_SHADER {
" return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);"
" }"
};
static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER {
static const std::string DEFAULT_PYRAMID_DEPTH_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(1.0 - texture(pyramidMap, uv).x * 0.01), 1.0);"
//" return vec4(vec3(1.0 - textureLod(pyramidMap, uv, 3).x * 0.01), 1.0);"
" }"
};
static const std::string DEFAULT_OCCLUSION_RAW_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(occlusionRawMap, uv).x), 1.0);"
" }"
};
static const std::string DEFAULT_OCCLUSION_BLURRED_SHADER {
"vec4 getFragmentColor() {"
" return vec4(vec3(texture(occlusionBlurredMap, uv).x), 1.0);"
" }"
};
static const std::string DEEFAULT_CUSTOM_SHADER {
"vec4 getFragmentColor() {"
" return vec4(1.0, 0.0, 0.0, 1.0);"
@ -106,8 +119,8 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string cus
switch (mode) {
case DiffuseMode:
return DEEFAULT_DIFFUSE_SHADER;
case AlphaMode:
return DEEFAULT_ALPHA_SHADER;
case AmbientOcclusionMode:
return DEFAULT_AMBIENT_OCCLUSION_SHADER;
case SpecularMode:
return DEEFAULT_SPECULAR_SHADER;
case RoughnessMode:
@ -118,8 +131,12 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string cus
return DEEFAULT_DEPTH_SHADER;
case LightingMode:
return DEEFAULT_LIGHTING_SHADER;
case AmbientOcclusionMode:
return DEFAULT_AMBIENT_OCCLUSION_SHADER;
case PyramidDepthMode:
return DEFAULT_PYRAMID_DEPTH_SHADER;
case OcclusionRawMode:
return DEFAULT_OCCLUSION_RAW_SHADER;
case OcclusionBlurredMode:
return DEFAULT_OCCLUSION_BLURRED_SHADER;
case CustomMode:
return getFileContent(customFile, DEEFAULT_CUSTOM_SHADER);
}
@ -168,6 +185,8 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::st
slotBindings.insert(gpu::Shader::Binding("depthMap", Depth));
slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting));
slotBindings.insert(gpu::Shader::Binding("pyramidMap", Pyramid));
slotBindings.insert(gpu::Shader::Binding("occlusionRawMap", OcclusionRaw));
slotBindings.insert(gpu::Shader::Binding("occlusionBlurredMap", OcclusionBlurred));
gpu::Shader::makeProgram(*program, slotBindings);
auto pipeline = gpu::Pipeline::create(program, std::make_shared<gpu::State>());
@ -216,6 +235,8 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren
batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture());
batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture());
batch.setResourceTexture(Pyramid, framebufferCache->getDepthPyramidTexture());
batch.setResourceTexture(OcclusionRaw, framebufferCache->getOcclusionTexture());
batch.setResourceTexture(OcclusionBlurred, framebufferCache->getOcclusionBlurredTexture());
const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f);
const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y);

View file

@ -27,13 +27,15 @@ public:
private:
enum Modes : uint8_t {
DiffuseMode = 0,
AlphaMode,
AmbientOcclusionMode,
SpecularMode,
RoughnessMode,
NormalMode,
DepthMode,
LightingMode,
AmbientOcclusionMode,
PyramidDepthMode,
OcclusionRawMode,
OcclusionBlurredMode,
CustomMode // Needs to stay last
};

View file

@ -47,6 +47,10 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) {
_lightingFramebuffer.reset();
_depthPyramidFramebuffer.reset();
_depthPyramidTexture.reset();
_occlusionFramebuffer.reset();
_occlusionTexture.reset();
_occlusionBlurredFramebuffer.reset();
_occlusionBlurredTexture.reset();
}
}
@ -105,6 +109,18 @@ void FramebufferCache::createPrimaryFramebuffer() {
_depthPyramidFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
_depthPyramidFramebuffer->setRenderBuffer(0, _depthPyramidTexture);
_depthPyramidFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
_occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB), width, height, defaultSampler));
_occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
_occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture);
_occlusionFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
_occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB), width, height, defaultSampler));
_occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
_occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture);
_occlusionBlurredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
}
@ -220,4 +236,32 @@ gpu::TexturePointer FramebufferCache::getDepthPyramidTexture() {
createPrimaryFramebuffer();
}
return _depthPyramidTexture;
}
gpu::FramebufferPointer FramebufferCache::getOcclusionFramebuffer() {
if (!_occlusionFramebuffer) {
createPrimaryFramebuffer();
}
return _occlusionFramebuffer;
}
gpu::TexturePointer FramebufferCache::getOcclusionTexture() {
if (!_occlusionTexture) {
createPrimaryFramebuffer();
}
return _occlusionTexture;
}
gpu::FramebufferPointer FramebufferCache::getOcclusionBlurredFramebuffer() {
if (!_occlusionBlurredFramebuffer) {
createPrimaryFramebuffer();
}
return _occlusionBlurredFramebuffer;
}
gpu::TexturePointer FramebufferCache::getOcclusionBlurredTexture() {
if (!_occlusionBlurredTexture) {
createPrimaryFramebuffer();
}
return _occlusionBlurredTexture;
}

View file

@ -43,6 +43,12 @@ public:
gpu::FramebufferPointer getDepthPyramidFramebuffer();
gpu::TexturePointer getDepthPyramidTexture();
gpu::FramebufferPointer getOcclusionFramebuffer();
gpu::TexturePointer getOcclusionTexture();
gpu::FramebufferPointer getOcclusionBlurredFramebuffer();
gpu::TexturePointer getOcclusionBlurredTexture();
gpu::TexturePointer getLightingTexture();
gpu::FramebufferPointer getLightingFramebuffer();
@ -87,6 +93,13 @@ private:
gpu::FramebufferPointer _depthPyramidFramebuffer;
gpu::TexturePointer _depthPyramidTexture;
gpu::FramebufferPointer _occlusionFramebuffer;
gpu::TexturePointer _occlusionTexture;
gpu::FramebufferPointer _occlusionBlurredFramebuffer;
gpu::TexturePointer _occlusionBlurredTexture;
QSize _frameBufferSize{ 100, 100 };
};

View file

@ -158,12 +158,16 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
// TODO: turn on/off AO through menu item
setOcclusionStatus(renderContext->getOcclusionStatus());
if (_occlusionJobIndex >= 0) {
_jobs[_occlusionJobIndex].edit<AmbientOcclusionEffect>().setRadius(renderContext->getAmbientOcclusion().radius);
}
setAntialiasingStatus(renderContext->getFxaaStatus());
setToneMappingExposure(renderContext->getTone().exposure);
setToneMappingToneCurve(renderContext->getTone().toneCurve);
renderContext->getArgs()->_context->syncCache();
for (auto job : _jobs) {

View file

@ -42,7 +42,7 @@ QString RenderScripting::Tone::getCurve() const {
render::RenderContext RenderScriptingInterface::getRenderContext() {
render::RenderContext::ItemsConfig items{ *_opaque, *_transparent, *_overlay3D };
return render::RenderContext{ items, *_tone, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode };
return render::RenderContext{ items, *_tone, *_ambientOcclusion, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode };
}
void RenderScriptingInterface::setItemCounts(const render::RenderContext::ItemsConfig& items) {

View file

@ -65,6 +65,14 @@ namespace RenderScripting {
void setCurve(const QString& curve);
};
using TonePointer = std::unique_ptr<Tone>;
class AmbientOcclusion : public QObject, public render::RenderContext::AmbientOcclusion {
Q_OBJECT
public:
Q_PROPERTY(float radius MEMBER radius)
};
using AmbientOcclusionPointer = std::unique_ptr<AmbientOcclusion>;
};
class RenderScriptingInterface : public QObject, public Dependency {
@ -77,7 +85,8 @@ class RenderScriptingInterface : public QObject, public Dependency {
Q_PROPERTY(RenderScripting::ItemCounter* overlay3D READ getOverlay3D)
Q_PROPERTY(RenderScripting::Tone* tone READ getTone)
Q_PROPERTY(RenderScripting::AmbientOcclusion* ambientOcclusion READ getAmbientOcclusion)
Q_PROPERTY(int displayItemStatus MEMBER _drawStatus)
Q_PROPERTY(bool displayHitEffect MEMBER _drawHitEffect)
@ -96,12 +105,15 @@ protected:
RenderScripting::ItemCounter* getOverlay3D() const { return _overlay3D.get(); }
RenderScripting::Tone* getTone() const { return _tone.get(); }
RenderScripting::AmbientOcclusion* getAmbientOcclusion() const { return _ambientOcclusion.get(); }
RenderScripting::ItemStatePointer _opaque = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}};
RenderScripting::ItemStatePointer _transparent = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}};
RenderScripting::ItemCounterPointer _overlay3D = RenderScripting::ItemCounterPointer{new RenderScripting::ItemCounter{}};
RenderScripting::TonePointer _tone = RenderScripting::TonePointer{ new RenderScripting::Tone{} };
RenderScripting::AmbientOcclusionPointer _ambientOcclusion = RenderScripting::AmbientOcclusionPointer{ new RenderScripting::AmbientOcclusion{} };
// Options
int _drawStatus = 0;

View file

@ -1,279 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// ambient_occlusion.frag
// fragment shader
//
// Created by Niraj Venkat on 7/15/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
// Based on NVidia HBAO implementation in D3D11
// http://www.nvidia.co.uk/object/siggraph-2008-HBAO.html
in vec2 varTexcoord;
uniform sampler2D depthTexture;
uniform sampler2D normalTexture;
uniform float g_scale;
uniform float g_bias;
uniform float g_sample_rad;
uniform float g_intensity;
// the distance to the near clip plane
uniform float near;
// scale factor for depth: (far - near) / far
uniform float depthScale;
// offset for depth texture coordinates
uniform vec2 depthTexCoordOffset;
// scale for depth texture coordinates
uniform vec2 depthTexCoordScale;
// the resolution of the occlusion buffer
// and its inverse
uniform vec2 renderTargetRes;
uniform vec2 renderTargetResInv;
const float PI = 3.14159265;
const float AOStrength = 1.9;
// TODO: R (radius) should be exposed as a uniform parameter
const float R = 0.01;
const float R2 = 0.01*0.01;
const float NegInvR2 = - 1.0 / (0.01*0.01);
// can't use tan to initialize a const value
const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0);
const float MaxRadiusPixels = 50.0;
const int NumDirections = 6;
const int NumSamples = 4;
out vec4 outFragColor;
/**
* Gets the normal in view space from a normal texture.
* uv: the uv texture coordinates to look up in the texture at.
*/
vec3 GetViewNormalFromTexture(vec2 uv) {
// convert [0,1] -> [-1,1], note: since we're normalizing
// we don't need to do v*2 - 1.0, we can just do a v-0.5
return normalize(texture(normalTexture, uv).xyz - 0.5);
}
/**
* Gets the linearized depth in view space.
* d: the depth value [0-1], usually from a depth texture to convert.
*/
float ViewSpaceZFromDepth(float d){
return near / (d * depthScale - 1.0);
}
/**
* Converts a uv coordinate and depth value into a 3D view space coordinate.
* uv: the uv coordinates to convert
* z: the view space depth of the uv coordinate.
*/
vec3 UVToViewSpace(vec2 uv, float z){
return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z);
}
/**
* Converts a uv coordinate into a 3D view space coordinate.
* The depth of the uv coord is determined from the depth texture.
* uv: the uv coordinates to convert
*/
vec3 GetViewPos(vec2 uv) {
float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r);
return UVToViewSpace(uv, z);
}
float TanToSin(float x) {
return x * inversesqrt(x*x + 1.0);
}
float InvLength(vec2 V) {
return inversesqrt(dot(V, V));
}
float Tangent(vec3 V) {
return V.z * InvLength(V.xy);
}
float BiasedTangent(vec3 V) {
return V.z * InvLength(V.xy) + TanBias;
}
float Tangent(vec3 P, vec3 S) {
return -(P.z - S.z) * InvLength(S.xy - P.xy);
}
float Length2(vec3 V) {
return dot(V, V);
}
vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) {
vec3 V1 = Pr - P;
vec3 V2 = P - Pl;
return (Length2(V1) < Length2(V2)) ? V1 : V2;
}
vec2 SnapUVOffset(vec2 uv) {
return round(uv * renderTargetRes) * renderTargetResInv;
}
float Falloff(float d2) {
return d2 * NegInvR2 + 1.0f;
}
float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) {
float ao = 0;
// Offset the first coord with some noise
vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV);
deltaUV = SnapUVOffset(deltaUV);
// Calculate the tangent vector
vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv;
// Get the angle of the tangent vector from the viewspace axis
float tanH = BiasedTangent(T);
float sinH = TanToSin(tanH);
float tanS;
float d2;
vec3 S;
// Sample to find the maximum angle
for (float s = 1; s <= numSamples; ++s) {
uv += deltaUV;
S = GetViewPos(uv);
tanS = Tangent(P, S);
d2 = Length2(S - P);
// Is the sample within the radius and the angle greater?
if (d2 < R2 && tanS > tanH) {
float sinS = TanToSin(tanS);
// Apply falloff based on the distance
ao += Falloff(d2) * (sinS - sinH);
tanH = tanS;
sinH = sinS;
}
}
return ao;
}
vec2 RotateDirections(vec2 Dir, vec2 CosSin) {
return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y,
Dir.x*CosSin.y + Dir.y*CosSin.x);
}
void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand) {
// Avoid oversampling if numSteps is greater than the kernel radius in pixels
numSteps = min(NumSamples, rayRadiusPix);
// Divide by Ns+1 so that the farthest samples are not fully attenuated
float stepSizePix = rayRadiusPix / (numSteps + 1);
// Clamp numSteps if it is greater than the max kernel footprint
float maxNumSteps = MaxRadiusPixels / stepSizePix;
if (maxNumSteps < numSteps) {
// Use dithering to avoid AO discontinuities
numSteps = floor(maxNumSteps + rand);
numSteps = max(numSteps, 1);
stepSizePix = MaxRadiusPixels / numSteps;
}
// Step size in uv space
stepSizeUv = stepSizePix * renderTargetResInv;
}
float getRandom(vec2 uv) {
return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
void main(void) {
mat4 projMatrix = getTransformCamera()._projection;
float numDirections = NumDirections;
vec3 P, Pr, Pl, Pt, Pb;
P = GetViewPos(varTexcoord);
// Sample neighboring pixels
Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0));
Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0));
Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y));
Pb = GetViewPos(varTexcoord + vec2( 0,-renderTargetResInv.y));
// Calculate tangent basis vectors using the minimum difference
vec3 dPdu = MinDiff(P, Pr, Pl);
vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x);
// Get the random samples from the noise function
vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx));
// Calculate the projected size of the hemisphere
float w = P.z * projMatrix[2][3] + projMatrix[3][3];
vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv
float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x;
float ao = 1.0;
// Make sure the radius of the evaluated hemisphere is more than a pixel
if(rayRadiusPix > 1.0) {
ao = 0.0;
float numSteps;
vec2 stepSizeUV;
// Compute the number of steps
ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z);
float alpha = 2.0 * PI / numDirections;
// Calculate the horizon occlusion of each direction
for(float d = 0; d < numDirections; ++d) {
float theta = alpha * d;
// Apply noise to the direction
vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy);
vec2 deltaUV = dir * stepSizeUV;
// Sample the pixels along the direction
ao += HorizonOcclusion( deltaUV,
P,
dPdu,
dPdv,
random.z,
numSteps);
}
// Average the results and produce the final AO
ao = 1.0 - ao / numDirections * AOStrength;
}
outFragColor = vec4(vec3(ao), 1.0);
}

View file

@ -1,26 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// ambient_occlusion.vert
// vertex shader
//
// Created by Niraj Venkat on 7/15/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
out vec2 varTexcoord;
void main(void) {
varTexcoord = inTexCoord0.xy;
gl_Position = inPosition;
}

View file

@ -15,6 +15,8 @@
<@include DeferredBuffer.slh@>
uniform sampler2D pyramidMap;
uniform sampler2D occlusionRawMap;
uniform sampler2D occlusionBlurredMap;
in vec2 uv;
out vec4 outFragColor;

View file

@ -1,43 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// gaussian_blur.frag
// fragment shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
// the interpolated normal
//in vec4 interpolatedNormal;
in vec2 varTexcoord;
in vec2 varBlurTexcoords[14];
uniform sampler2D occlusionTexture;
out vec4 outFragColor;
void main(void) {
outFragColor = vec4(0.0);
outFragColor += texture(occlusionTexture, varBlurTexcoords[0])*0.0044299121055113265;
outFragColor += texture(occlusionTexture, varBlurTexcoords[1])*0.00895781211794;
outFragColor += texture(occlusionTexture, varBlurTexcoords[2])*0.0215963866053;
outFragColor += texture(occlusionTexture, varBlurTexcoords[3])*0.0443683338718;
outFragColor += texture(occlusionTexture, varBlurTexcoords[4])*0.0776744219933;
outFragColor += texture(occlusionTexture, varBlurTexcoords[5])*0.115876621105;
outFragColor += texture(occlusionTexture, varBlurTexcoords[6])*0.147308056121;
outFragColor += texture(occlusionTexture, varTexcoord)*0.159576912161;
outFragColor += texture(occlusionTexture, varBlurTexcoords[7])*0.147308056121;
outFragColor += texture(occlusionTexture, varBlurTexcoords[8])*0.115876621105;
outFragColor += texture(occlusionTexture, varBlurTexcoords[9])*0.0776744219933;
outFragColor += texture(occlusionTexture, varBlurTexcoords[10])*0.0443683338718;
outFragColor += texture(occlusionTexture, varBlurTexcoords[11])*0.0215963866053;
outFragColor += texture(occlusionTexture, varBlurTexcoords[12])*0.00895781211794;
outFragColor += texture(occlusionTexture, varBlurTexcoords[13])*0.0044299121055113265;
}

View file

@ -1,43 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// guassian_blur_horizontal.vert
// vertex shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
out vec2 varTexcoord;
out vec2 varBlurTexcoords[14];
void main(void) {
varTexcoord = inTexCoord0.xy;
gl_Position = inPosition;
varBlurTexcoords[0] = varTexcoord + vec2(-0.028, 0.0);
varBlurTexcoords[1] = varTexcoord + vec2(-0.024, 0.0);
varBlurTexcoords[2] = varTexcoord + vec2(-0.020, 0.0);
varBlurTexcoords[3] = varTexcoord + vec2(-0.016, 0.0);
varBlurTexcoords[4] = varTexcoord + vec2(-0.012, 0.0);
varBlurTexcoords[5] = varTexcoord + vec2(-0.008, 0.0);
varBlurTexcoords[6] = varTexcoord + vec2(-0.004, 0.0);
varBlurTexcoords[7] = varTexcoord + vec2(0.004, 0.0);
varBlurTexcoords[8] = varTexcoord + vec2(0.008, 0.0);
varBlurTexcoords[9] = varTexcoord + vec2(0.012, 0.0);
varBlurTexcoords[10] = varTexcoord + vec2(0.016, 0.0);
varBlurTexcoords[11] = varTexcoord + vec2(0.020, 0.0);
varBlurTexcoords[12] = varTexcoord + vec2(0.024, 0.0);
varBlurTexcoords[13] = varTexcoord + vec2(0.028, 0.0);
}

View file

@ -1,43 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// guassian_blur_vertical.vert
// vertex shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include gpu/Inputs.slh@>
<@include gpu/Transform.slh@>
<$declareStandardTransform()$>
out vec2 varTexcoord;
out vec2 varBlurTexcoords[14];
void main(void) {
varTexcoord = inTexCoord0.xy;
gl_Position = inPosition;
varBlurTexcoords[0] = varTexcoord + vec2(0.0, -0.028);
varBlurTexcoords[1] = varTexcoord + vec2(0.0, -0.024);
varBlurTexcoords[2] = varTexcoord + vec2(0.0, -0.020);
varBlurTexcoords[3] = varTexcoord + vec2(0.0, -0.016);
varBlurTexcoords[4] = varTexcoord + vec2(0.0, -0.012);
varBlurTexcoords[5] = varTexcoord + vec2(0.0, -0.008);
varBlurTexcoords[6] = varTexcoord + vec2(0.0, -0.004);
varBlurTexcoords[7] = varTexcoord + vec2(0.0, 0.004);
varBlurTexcoords[8] = varTexcoord + vec2(0.0, 0.008);
varBlurTexcoords[9] = varTexcoord + vec2(0.0, 0.012);
varBlurTexcoords[10] = varTexcoord + vec2(0.0, 0.016);
varBlurTexcoords[11] = varTexcoord + vec2(0.0, 0.020);
varBlurTexcoords[12] = varTexcoord + vec2(0.0, 0.024);
varBlurTexcoords[13] = varTexcoord + vec2(0.0, 0.028);
}

View file

@ -1,27 +0,0 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// occlusion_blend.frag
// fragment shader
//
// Created by Niraj Venkat on 7/20/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include DeferredBufferWrite.slh@>
in vec2 varTexcoord;
out vec4 outFragColor;
uniform sampler2D blurredOcclusionTexture;
void main(void) {
vec4 occlusionColor = texture(blurredOcclusionTexture, varTexcoord);
outFragColor = vec4(vec3(0.0), occlusionColor.r);
}

View file

@ -0,0 +1,58 @@
<!
// AmbientOcclusion.slh
// libraries/render-utils/src
//
// Created by Sam Gateau on 1/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
!>
<@if not SSAO_SLH@>
<@def SSAO_SLH@>
<@func declareAmbientOcclusion()@>
struct AmbientOcclusionParams {
vec4 _clipInfo;
mat4 _projection;
vec4 _radius_radius2_InvRadius6_s2;
};
uniform ambientOcclusionParamsBuffer {
AmbientOcclusionParams params;
};
float getProjScale() {
return 500.0; // this should be viewportHeight * Proj[1][1] / 2.0
}
float getRadius() {
return params._radius_radius2_InvRadius6_s2.x;
}
float getRadius2() {
return params._radius_radius2_InvRadius6_s2.y;
}
float getInvRadius6() {
return params._radius_radius2_InvRadius6_s2.z;
}
float evalZeyeFromZdb(float depth) {
return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z);
}
vec3 evalEyePositionFromZeye(float Zeye, vec2 texcoord) {
// compute the view space position using the depth
// basically manually pick the proj matrix components to do the inverse
float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * params._projection[2][0] - params._projection[3][0]) / params._projection[0][0];
float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * params._projection[2][1] - params._projection[3][1]) / params._projection[1][1];
return vec3(Xe, Ye, Zeye);
}
vec3 evalEyeNormal(vec3 C) {
return normalize(cross(dFdy(C), dFdx(C)));
}
<@endfunc@>
<@endif@>

View file

@ -0,0 +1,23 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 1/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ssao.slh@>
<$declareAmbientOcclusion()$>
// the depth texture
uniform sampler2D occlusionMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
outFragColor = texture(occlusionMap, varTexCoord0);
}

View file

@ -0,0 +1,162 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 1/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ssao.slh@>
<$declareAmbientOcclusion()$>
const int NUM_SAMPLES = 11;
const float INV_NUM_SAMPLES = 1.0 / float(NUM_SAMPLES);
const int NUM_SPIRAL_TURNS= 7;
const int LOG_MAX_OFFSET = 3;
const int MAX_MIP_LEVEL = 5;
// the depth pyramid texture
uniform sampler2D pyramidMap;
float getZeye(vec2 texcoord) {
return -texture(pyramidMap, texcoord, 0).x;
}
vec3 evalEyePosition(vec2 texcoord) {
float Zeye = getZeye(texcoord);
return evalEyePositionFromZeye(Zeye, texcoord);
}
in vec2 varTexCoord0;
out vec4 outFragColor;
/** Used for packing Z into the GB channels */
float CSZToKey(float z) {
return clamp(z * (1.0 / params._clipInfo.z), 0.0, 1.0);
}
/** Used for packing Z into the GB channels */
void packKey(float key, out vec2 p) {
// Round to the nearest 1/256.0
float temp = floor(key * 256.0);
// Integer part
p.x = temp * (1.0 / 256.0);
// Fractional part
p.y = key * 256.0 - temp;
}
vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){
// Radius relative to ssR
float alpha = float(sampleNumber + 0.5) * INV_NUM_SAMPLES;
float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle;
ssR = alpha;
return vec2(cos(angle), sin(angle));
}
vec3 getOffsetPosition(ivec2 ssC, vec2 unitOffset, float ssR) {
// Derivation:
// mipLevel = floor(log(ssR / MAX_OFFSET));
int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
ivec2 ssP = ivec2(ssR * unitOffset) + ssC;
vec3 P;
// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
// Manually clamp to the texture size because texelFetch bypasses the texture unit
ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), textureSize(pyramidMap, mipLevel) - ivec2(1));
P.z = -texelFetch(pyramidMap, mipP, mipLevel).r;
// Offset to pixel center
//P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
vec2 tapUV = (vec2(ssP) + vec2(0.5)) / textureSize(pyramidMap, 0);
P = evalEyePositionFromZeye(P.z, tapUV);
return P;
}
float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) {
// Offset on the unit disk, spun for this pixel
float ssR;
vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
ssR *= ssDiskRadius;
// The occluding point in camera space
vec3 Q = getOffsetPosition(ssC, unitOffset, ssR);
vec3 v = Q - C;
float vv = dot(v, v);
float vn = dot(v, n_C);
const float bias = 0.01;
const float epsilon = 0.01;
float radius2 = getRadius2();
// A: From the HPG12 paper
// Note large epsilon to avoid overdarkening within cracks
// return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6;
// B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended]
float f = max(radius2 - vv, 0.0); return f * f * f * max((vn - bias) / (epsilon + vv), 0.0);
// C: Medium contrast (which looks better at high radii), no division. Note that the
// contribution still falls off with radius^2, but we've adjusted the rate in a way that is
// more computationally efficient and happens to be aesthetically pleasing.
// return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0);
// D: Low contrast, no division operation
// return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0);
}
vec3 debugValue(float f, float scale) {
if (f < 0.0) {
return vec3((scale + f) / scale, 0.0, 0.0);
} else {
return vec3(0.0, (scale - f) / scale, 0.0);
}
}
void main(void) {
// Pixel being shaded
ivec2 ssC = ivec2(gl_FragCoord.xy);
vec3 Cp = evalEyePosition(varTexCoord0);
// packKey(CSZToKey(Cp.z), bilateralKey);
// Hash function used in the HPG12 AlchemyAO paper
float randomPatternRotationAngle = (3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10;
//float randomPatternRotationAngle = 0;
vec3 Cn = evalEyeNormal(Cp);
// Choose the screen-space sample radius
// proportional to the projected area of the sphere
float ssDiskRadius = -getProjScale() * getRadius() / Cp.z;
float sum = 0.0;
for (int i = 0; i < NUM_SAMPLES; ++i) {
sum += sampleAO(ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle);
}
float A = max(0.0, 1.0 - sum * getInvRadius6() * 5.0 * INV_NUM_SAMPLES);
// Bilateral box-filter over a quad for free, respecting depth edges
// (the difference that this makes is subtle)
if (abs(dFdx(Cp.z)) < 0.02) {
A -= dFdx(A) * ((ssC.x & 1) - 0.5);
}
if (abs(dFdy(Cp.z)) < 0.02) {
A -= dFdy(A) * ((ssC.y & 1) - 0.5);
}
outFragColor = vec4(A, debugValue(Cn.y, 10));
}

View file

@ -0,0 +1,25 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 1/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ssao.slh@>
<$declareAmbientOcclusion()$>
// the depth texture
uniform sampler2D depthMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
float Zdb = texture(depthMap, varTexCoord0).x;
float Zeye = -evalZeyeFromZdb(Zdb);
outFragColor = vec4(Zeye, 0.0, 0.0, 1.0);
}

View file

@ -0,0 +1,23 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// Created by Sam Gateau on 1/1/16.
// Copyright 2016 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
<@include ssao.slh@>
<$declareAmbientOcclusion()$>
// the depth texture
uniform sampler2D occlusionMap;
in vec2 varTexCoord0;
out vec4 outFragColor;
void main(void) {
outFragColor = vec4(0.0, 0.0, 0.0, texture(occlusionMap, varTexCoord0).x);
}

View file

@ -13,11 +13,11 @@
#include "DrawTask.h"
using namespace render;
RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode)
RenderContext::RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode)
: _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize },
_args{ nullptr },
_drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect },
_items{ items }, _tone{ tone } {}
_items{ items }, _tone{ tone }, _ambientOcclusion{ ao } {}
void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) {
_occlusionStatus = occlusion;

View file

@ -76,14 +76,20 @@ public:
int toneCurve = 1; // Means just Gamma 2.2 correction
float exposure = 0.0;
};
class AmbientOcclusion {
public:
float radius = 0.5; // radius in meters of the AO effect
};
RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode);
RenderContext() : RenderContext({}, {}, {}, {}, {}, {}) {};
RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode);
RenderContext() : RenderContext({}, {}, {}, {}, {}, {}, {}) {};
void setArgs(RenderArgs* args) { _args = args; }
inline RenderArgs* getArgs() { return _args; }
inline ItemsConfig& getItemsConfig() { return _items; }
inline Tone& getTone() { return _tone; }
inline AmbientOcclusion& getAmbientOcclusion() { return _ambientOcclusion; }
inline int getDrawStatus() { return _drawStatus; }
inline bool getDrawHitEffect() { return _drawHitEffect; }
inline bool getOcclusionStatus() { return _occlusionStatus; }
@ -105,6 +111,7 @@ protected:
ItemsConfig _items;
Tone _tone;
AmbientOcclusion _ambientOcclusion;
};
typedef std::shared_ptr<RenderContext> RenderContextPointer;