mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-16 12:20:12 +02:00
279 lines
7.6 KiB
Text
279 lines
7.6 KiB
Text
<@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);
|
|
}
|