SSAO experiment

This commit is contained in:
Robert Beckebans 2016-01-02 19:28:49 +01:00
parent 08c7b35fd1
commit e555e83119
13 changed files with 660 additions and 10 deletions

View file

@ -0,0 +1,390 @@
/**
\file AmbientOcclusion_AO.pix
\author Morgan McGuire and Michael Mara, NVIDIA Research
Reference implementation of the Scalable Ambient Obscurance (AmbientOcclusion) screen-space ambient obscurance algorithm.
The optimized algorithmic structure of AmbientOcclusion was published in McGuire, Mara, and Luebke, Scalable Ambient Obscurance,
<i>HPG</i> 2012, and was developed at NVIDIA with support from Louis Bavoil.
The mathematical ideas of AlchemyAO were first described in McGuire, Osman, Bukowski, and Hennessy, The
Alchemy Screen-Space Ambient Obscurance Algorithm, <i>HPG</i> 2011 and were developed at
Vicarious Visions.
DX11 HLSL port by Leonardo Zide of Treyarch
<hr>
Open Source under the "BSD" license: http://www.opensource.org/licenses/bsd-license.php
Copyright (c) 2011-2012, NVIDIA
Copyright (c) 2016 Robert Beckebans ( id Tech 4.x integration )
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "renderprogs/global.inc"
#define DIFFERENT_DEPTH_RESOLUTIONS 0
#define USE_DEPTH_PEEL 0
#define CS_Z_PACKED_TOGETHER 0
#define TEMPORALLY_VARY_SAMPLES 0
#define HIGH_QUALITY 1
// Total number of direct samples to take at each pixel
#define NUM_SAMPLES 11
// This is the number of turns around the circle that the spiral pattern makes. This should be prime to prevent
// taps from lining up. This particular choice was tuned for NUM_SAMPLES == 9
#define NUM_SPIRAL_TURNS 7
// If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower
// miplevel to maintain reasonable spatial locality in the cache
// If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing.
// If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively
#define LOG_MAX_OFFSET (3)
// This must be less than or equal to the MAX_MIP_LEVEL defined in SAmbientOcclusion.cpp
#define MAX_MIP_LEVEL (5)
const float DOOM_TO_METERS = 0.0254; // doom to meters
const float METERS_TO_DOOM = ( 1.0 / DOOM_TO_METERS ); // meters to doom
/** Used for preventing AO computation on the sky (at infinite depth) and defining the CS Z to bilateral depth key scaling.
This need not match the real far plane but should not be much more than it.*/
const float FAR_PLANE_Z = -4000.0;// * METERS_TO_DOOM;
/** World-space AO radius in scene units (r). e.g., 1.0m */
const float radius = 1.0 * METERS_TO_DOOM;
const float radius2 = radius * radius;
const float invRadius2 = 1.0 / radius2;
/** Bias to avoid AO in smooth corners, e.g., 0.01m */
const float bias = 0.01 * METERS_TO_DOOM;
/** intensity / radius^6 */
const float intensity = 0.2;
const float intensityDivR6 = intensity / ( radius* radius* radius* radius* radius* radius );
const float projScale = 300.0;// * METERS_TO_DOOM;
//#expect NUM_SAMPLES "Integer number of samples to take at each pixels"
//#expect NUM_SPIRAL_TURNS "Integer number of turns around the circle that the spiral pattern makes. The G3D::AmbientOcclusion class provides a discrepancy-minimizing value of NUM_SPIRAL_TURNS for eac value of NUM_SAMPLES."
//#expect DIFFERENT_DEPTH_RESOLUTIONS "1 if the peeled depth buffer is at a different resolution than the primary depth buffer"
//#expect USE_DEPTH_PEEL "1 to enable, 0 to disable"
//#expect CS_Z_PACKED_TOGETHER "1 to enable, 0 to disable"
//#expect TEMPORALLY_VARY_SAMPLES "1 to enable, 0 to disable"
// *INDENT-OFF*
uniform sampler2D samp0 : register( s0 ); // view color
uniform sampler2D samp1 : register( s1 ); // view depth
#define CS_Z_buffer samp1
struct PS_IN
{
float2 texcoord0 : TEXCOORD0_centroid;
};
struct PS_OUT
{
float4 color : COLOR;
};
// *INDENT-ON*
/** Returns a unit vector and a screen-space radius for the tap on a unit disk
(the caller should scale by the actual disk radius) */
float2 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 float2( cos( angle ), sin( angle ) );
}
/** Used for packing Z into the GB channels */
float CSZToKey( float z )
{
return clamp( z * ( 1.0 / FAR_PLANE_Z ), 0.0, 1.0 );
}
/** Reconstructs screen-space unit normal from screen-space position */
float3 reconstructCSFaceNormal( float3 C )
{
return normalize( cross( dFdy( C ), dFdx( C ) ) );
}
float3 reconstructNonUnitCSFaceNormal( float3 C )
{
float3 n = cross( dFdy( C ), dFdx( C ) );
//n.z = sqrt( abs( dot( n.xy, n.xy ) - 0.25 ) );
return n;
}
/** Read the camera-space position of the point at screen-space pixel ssP */
float3 getPosition( float2 ssP, sampler2D cszBuffer )
{
float4 P;
P.z = tex2D( cszBuffer, ssP ).r;
P.xy = float2( ssP ); //+ float2( 0.5 ) );
P.w = 1.0;
// offset to pixel center
//P = reconstructCSPosition( float2( ssP ) + float2( 0.5 ), P.z, projInfo );
float4 csP;
csP.x = dot4( P, rpProjectionMatrixX );
csP.y = dot4( P, rpProjectionMatrixY );
csP.z = dot4( P, rpProjectionMatrixZ );
csP.w = dot4( P, rpProjectionMatrixW );
csP.xyz /= csP.w;
return csP.xyz;
}
/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1.
Use cszBufferScale if reading from the peeled depth buffer, which has been scaled by (1 / invCszBufferScale) from the original */
float3 getOffsetPosition( ivec2 issC, vec2 unitOffset, float ssR, sampler2D cszBuffer, float invCszBufferScale )
{
// Derivation:
// mipLevel = floor(log(ssR / MAX_OFFSET));
#ifdef GL_EXT_gpu_shader5
int mipLevel = clamp( findMSB( int( ssR ) ) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL );
#else
int mipLevel = clamp( int( floor( log2( ssR ) ) ) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL );
#endif
ivec2 ssP = ivec2( ssR * unitOffset ) + issC;
float4 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( CS_Z_buffer, mipLevel ) - ivec2( 1 ) );
float2 ossC = float2( ssP ) * rpScreenCorrectionFactor.xy;
P.z = texture( cszBuffer, ossC, 0.0 ).r;
//P.z = texture( cszBuffer, mipP, 0.0 ).r;
//P.z = texture( cszBuffer, mipP, mipLevel ).r;
// Offset to pixel center
//P = reconstructCSPosition( ( vec2( ssP ) + vec2( 0.5 ) ) * invCszBufferScale, P.z, projInfo );
P.xy = ossC; //+ float2( 0.5 ) ) * invCszBufferScale;
P.w = 1.0;
float4 csP;
csP.x = dot4( P, rpProjectionMatrixX );
csP.y = dot4( P, rpProjectionMatrixY );
csP.z = dot4( P, rpProjectionMatrixZ );
csP.w = dot4( P, rpProjectionMatrixW );
csP.xyz /= csP.w;
return csP.xyz;
}
float fallOffFunction( float vv, float vn, float epsilon )
{
// A: From the HPG12 paper
// Note large epsilon to avoid overdarkening within cracks
// Assumes the desired result is intensity/radius^6 in main()
// 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]
#if HIGH_QUALITY
// Epsilon inside the sqrt for rsqrt operation
float f = max( 1.0 - vv * invRadius2, 0.0 );
return f * max( ( vn - bias ) * rsqrt( epsilon + vv ), 0.0 );
#else
// Avoid the square root from above.
// Assumes the desired result is intensity/radius^6 in main()
float f = max( radius2 - vv, 0.0 );
return f * f * f * max( ( vn - bias ) / ( epsilon + vv ), 0.0 );
#endif
// 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. Assumes
// division by radius^6 in main()
// 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);
}
/** Compute the occlusion due to sample point \a Q about camera-space point \a C with unit normal \a n_C */
float aoValueFromPositionsAndNormal( float3 C, float3 n_C, float3 Q )
{
float3 v = Q - C;
float vv = dot( v, v );
float vn = dot( v, n_C );
const float epsilon = 0.001;
// Without the angular adjustment term, surfaces seen head on have less AO
return fallOffFunction( vv, vn, epsilon ) * lerp( 1.0, max( 0.0, 1.5 * n_C.z ), 0.35 );
}
/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling radius \a ssDiskRadius
Note that units of H() in the HPG12 paper are meters, not
unitless. The whole falloff/sampling function is therefore
unitless. In this implementation, we factor out (9 / radius).
Four versions of the falloff function are implemented below
When sampling from the peeled depth buffer, make sure ssDiskRadius has been premultiplied by cszBufferScale
*/
float sampleAO( ivec2 issC, in float3 C, in float3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle, in sampler2D cszBuffer, in float invCszBufferScale )
{
// Offset on the unit disk, spun for this pixel
float ssR;
float2 unitOffset = tapLocation( tapIndex, randomPatternRotationAngle, ssR );
// Ensure that the taps are at least 1 pixel away
ssR = max( 0.75, ssR * ssDiskRadius );
#if (CS_Z_PACKED_TOGETHER != 0)
vec3 Q0, Q1;
getOffsetPositions( ssC, unitOffset, ssR, cszBuffer, Q0, Q1 );
return max( aoValueFromPositionsAndNormal( C, n_C, Q0 ), aoValueFromPositionsAndNormal( C, n_C, Q1 ) );
#else
// The occluding point in camera space
vec3 Q = getOffsetPosition( issC, unitOffset, ssR, cszBuffer, invCszBufferScale );
return aoValueFromPositionsAndNormal( C, n_C, Q );
#endif
}
const float MIN_RADIUS = 3.0; // pixels
#define visibility result.color.r
#define bilateralKey result.color.g
void main( PS_IN fragment, out PS_OUT result )
{
result.color = float4( 0.0, 0.0, 0.0, 1.0 );
#if 1
if( fragment.texcoord0.x < 0.5 )
{
discard;
}
#endif
//float3 normal = tex2D( samp0, fragment.texcoord0 ).rgb * 2.0 - 1.0;
//if( length( normal ) < 0.9 )
//{
//discard;
//}
// Pixel being shaded
float2 ssC = fragment.texcoord0;
ivec2 issC = ivec2( ssC.x * rpScreenCorrectionFactor.z, ssC.y * rpScreenCorrectionFactor.w );
// World space point being shaded
vec3 C = getPosition( ssC, CS_Z_buffer );
//float z = length( C - rpGlobalEyePos.xyz ) * 10.0;
bilateralKey = CSZToKey( C.z );
visibility = 0.0;
#if 1
//vec3 n_C = texelFetch( normal_buffer, ivec2( gl_FragCoord.xy ), 0 ).xyz;
//n_C = normalize( n_C * normal_readMultiplyFirst.xyz + normal_readAddSecond.xyz );
float3 n_C = tex2D( samp0, fragment.texcoord0 ).rgb * 2.0 - 1.0;
//n_C = -n_C;
#else
// Reconstruct normals from positions.
float3 n_C = reconstructCSFaceNormal( C );
// Since n_C is computed from the cross product of cmaera-space edge vectors from points at adjacent pixels, its magnitude will be proportional to the square of distance from the camera
if( dot( n_C, n_C ) > ( square( C.z * C.z * 0.00006 ) ) ) // if the threshold # is too big you will see black dots where we used a bad normal at edges, too small -> white
{
// The normals from depth should be very small values before normalization,
// except at depth discontinuities, where they will be large and lead
// to 1-pixel false occlusions because they are not reliable
visibility = 1.0;
return;
}
else
{
n_C = normalize( -n_C );
}
#endif
// Hash function used in the HPG12 AlchemyAO paper
float randomPatternRotationAngle = ( ( ( 3 * issC.x ) ^ ( issC.y + issC.x * issC.y ) )
#if TEMPORALLY_VARY_SAMPLES
+ g3d_SceneTime
#endif
) * 10;
// Choose the screen-space sample radius
// proportional to the projected area of the sphere
float ssDiskRadius = -projScale * radius / C.z;
#if 0
if( ssDiskRadius <= MIN_RADIUS )
{
// There is no way to compute AO at this radius
visibility = 1.0;
return;
}
#endif
#if USE_DEPTH_PEEL == 1
#if DIFFERENT_DEPTH_RESOLUTIONS == 1
float unpeeledToPeeledScale = 1.0 / peeledToUnpeeledScale;
#endif
#endif
float sum = 0.0;
for( int i = 0; i < NUM_SAMPLES; ++i )
{
sum += sampleAO( issC, C, n_C, ssDiskRadius, i, randomPatternRotationAngle, CS_Z_buffer, 1 );
}
#if HIGH_QUALITY
float A = pow( max( 0.0, 1.0 - sqrt( sum * ( 3.0 / NUM_SAMPLES ) ) ), intensity );
#else
float A = max( 0.0, 1.0 - sum * intensityDivR6 * ( 5.0 / NUM_SAMPLES ) );
// Anti-tone map to reduce contrast and drag dark region farther
// (x^0.2 + 1.2 * x^4)/2.2
A = ( pow( A, 0.2 ) + 1.2 * A * A * A * A ) / 2.2;
#endif
// Visualize random spin distribution
//A = mod(randomPatternRotationAngle / (2 * 3.141592653589), 1.0);
// Fade in as the radius reaches 2 pixels
visibility = lerp( 1.0, A, saturate( ssDiskRadius - MIN_RADIUS ) );
//result.color = float4( visibility, bilateralKey, 0.0, 1.0 );
//result.color = float4( bilateralKey, bilateralKey, bilateralKey, 1.0 );
result.color = float4( visibility, visibility, visibility, 1.0 );
//result.color = float4( n_C * 0.5 + 0.5, 1.0 );
//result.color = texture( samp0, fragment.texcoord0 ).rgba;
// derive clip space from the depth buffer and screen position
// float windowZ = tex2D( samp1, fragment.texcoord0 ).x;
// float3 ndc = float3( fragment.texcoord0 * 2.0 - 1.0, windowZ * 2.0 - 1.0 );
// float clipW = -rpProjectionMatrixZ.w / ( -rpProjectionMatrixZ.z - ndc.z );
// float4 clip = float4( ndc * clipW, clipW );
}

View file

@ -0,0 +1,47 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "renderprogs/global.inc"
struct VS_IN
{
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
};
struct VS_OUT
{
float4 position : POSITION;
float2 texcoord0 : TEXCOORD0;
};
void main( VS_IN vertex, out VS_OUT result )
{
result.position = vertex.position;
result.texcoord0 = vertex.texcoord;
}

View file

@ -62,9 +62,18 @@ void main( PS_IN fragment, out PS_OUT result )
localNormal.z = sqrt( 1.0f - dot3( localNormal, localNormal ) );
float3 globalNormal;
#if 1
globalNormal.x = dot3( localNormal, fragment.texcoord2 );
globalNormal.y = dot3( localNormal, fragment.texcoord3 );
globalNormal.z = dot3( localNormal, fragment.texcoord4 );
#else
// only the normal in view space
globalNormal.x = fragment.texcoord2.z;
globalNormal.y = fragment.texcoord3.z;
globalNormal.z = fragment.texcoord4.z;
//globalNormal.z = fragment.texcoord4.z * -0.001; //sqrt( abs( dot( globalNormal.xy, globalNormal.xy ) - 0.25 ) );
globalNormal = normalize( globalNormal );
#endif
// RB: rpColor is white and only used to generate the _fa_ uniform array
result.color.rgb = ( globalNormal.xyz * 0.5 + 0.5 ) * fragment.color.rgb;// * rpColor;

View file

@ -135,6 +135,8 @@ void main( VS_IN vertex, out VS_OUT result ) {
//result.texcoord1.y = dot3( toEye, rpModelMatrixY );
//result.texcoord1.z = dot3( toEye, rpModelMatrixZ );
#if 1
// rotate into world space
result.texcoord2.x = dot3( tangent, rpModelMatrixX );
result.texcoord3.x = dot3( tangent, rpModelMatrixY );
result.texcoord4.x = dot3( tangent, rpModelMatrixZ );
@ -146,6 +148,21 @@ void main( VS_IN vertex, out VS_OUT result ) {
result.texcoord2.z = dot3( normal, rpModelMatrixX );
result.texcoord3.z = dot3( normal, rpModelMatrixY );
result.texcoord4.z = dot3( normal, rpModelMatrixZ );
#else
// rotate into view space
result.texcoord2.x = dot3( tangent, rpModelViewMatrixX );
result.texcoord3.x = dot3( tangent, rpModelViewMatrixY );
result.texcoord4.x = dot3( tangent, rpModelViewMatrixZ );
result.texcoord2.y = dot3( bitangent, rpModelViewMatrixX );
result.texcoord3.y = dot3( bitangent, rpModelViewMatrixY );
result.texcoord4.y = dot3( bitangent, rpModelViewMatrixZ );
result.texcoord2.z = dot3( normal, rpModelViewMatrixX );
result.texcoord3.z = dot3( normal, rpModelViewMatrixY );
result.texcoord4.z = dot3( normal, rpModelViewMatrixZ );
#endif
#if defined( USE_GPU_SKINNING )
// for joint transformation of the tangent space, we use color and

View file

@ -178,6 +178,8 @@ float rand( float2 co ) {
#define PI 3.14159265358979323846
#endif
#define square( x ) ( x * x )
#define DEG2RAD( a ) ( ( a ) * PI / 180.0f )
#define RAD2DEG( a ) ( ( a ) * 180.0f / PI )

View file

@ -1,5 +1,6 @@
astyle.exe -v --formatted --options=astyle-options.ini --exclude="libs" --recursive *.h
astyle.exe -v --formatted --options=astyle-options.ini --exclude="libs" --exclude="d3xp/gamesys/SysCvar.cpp" --exclude="d3xp/gamesys/Callbacks.cpp" --exclude="sys/win32/win_cpu.cpp" --exclude="sys/win32/win_main.cpp" --recursive *.cpp
astyle.exe -v -Q --options=astyle-options.ini ../base/renderprogs/postprocess.pixel
astyle.exe -v -Q --options=astyle-options.ini ../base/renderprogs/AmbientOcclusion_AO.pixel
pause

View file

@ -542,6 +542,23 @@ void R_SetupProjectionMatrix2( const viewDef_t* viewDef, const float zNear, cons
}
}
/*
=================
R_SetupUnprojection
create a matrix with similar functionality like gluUnproject, project from window space to world space
=================
*/
void R_SetupUnprojection( viewDef_t* viewDef )
{
R_MatrixFullInverse( viewDef->projectionMatrix, viewDef->unprojectionToCameraMatrix );
idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->unprojectionToCameraMatrix, viewDef->unprojectionToCameraRenderMatrix );
R_MatrixMultiply( viewDef->worldSpace.modelViewMatrix, viewDef->projectionMatrix, viewDef->unprojectionToWorldMatrix );
R_MatrixFullInverse( viewDef->unprojectionToWorldMatrix, viewDef->unprojectionToWorldMatrix );
idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->unprojectionToWorldMatrix, viewDef->unprojectionToWorldRenderMatrix );
}
void R_MatrixFullInverse( const float a[16], float r[16] )
{

View file

@ -63,6 +63,7 @@ void R_SetupViewMatrix( viewDef_t* viewDef );
void R_SetupProjectionMatrix( viewDef_t* viewDef );
// RB begin
void R_SetupUnprojection( viewDef_t* viewDef );
void R_SetupProjectionMatrix2( const viewDef_t* viewDef, const float zNear, const float zFar, float out[16] );
void R_MatrixFullInverse( const float in[16], float r[16] );
// RB end

View file

@ -147,6 +147,8 @@ void idRenderProgManager::Init()
{ BUILTIN_SMAA_EDGE_DETECTION, "SMAA_edge_detection", "", 0, false },
{ BUILTIN_SMAA_BLENDING_WEIGHT_CALCULATION, "SMAA_blending_weight_calc", "", 0, false },
{ BUILTIN_SMAA_NEIGHBORHOOD_BLENDING, "SMAA_final", "", 0, false },
{ BUILTIN_AMBIENT_OCCLUSION, "AmbientOcclusion_AO", "", 0, false },
// RB end
{ BUILTIN_STEREO_DEGHOST, "stereoDeGhost.vfp", 0, false },
{ BUILTIN_STEREO_WARP, "stereoWarp.vfp", 0, false },

View file

@ -453,6 +453,11 @@ public:
BindShader_Builtin( BUILTIN_SMAA_NEIGHBORHOOD_BLENDING );
}
void BindShader_AmbientOcclusion()
{
BindShader_Builtin( BUILTIN_AMBIENT_OCCLUSION );
}
#if 0
void BindShader_ZCullReconstruct()
{
@ -575,6 +580,8 @@ protected:
BUILTIN_SMAA_EDGE_DETECTION,
BUILTIN_SMAA_BLENDING_WEIGHT_CALCULATION,
BUILTIN_SMAA_NEIGHBORHOOD_BLENDING,
BUILTIN_AMBIENT_OCCLUSION,
// RB end
BUILTIN_STEREO_DEGHOST,
BUILTIN_STEREO_WARP,

View file

@ -1889,11 +1889,16 @@ static void RB_AmbientPass( const drawSurf_t* const* drawSurfs, int numDrawSurfs
SetVertexParm( RENDERPARM_LOCALLIGHTORIGIN, localLightDirection.ToFloatPtr() );
}
// RB: we want to store the normals in world space so we need the model -> world matrix
// RB: if we want to store the normals in world space so we need the model -> world matrix
idRenderMatrix modelMatrix;
idRenderMatrix::Transpose( *( idRenderMatrix* )drawSurf->space->modelMatrix, modelMatrix );
SetVertexParms( RENDERPARM_MODELMATRIX_X, modelMatrix[0], 4 );
// RB: if we want to store the normals in camera space so we need the model -> camera matrix
float modelViewMatrixTranspose[16];
R_MatrixTranspose( drawSurf->space->modelViewMatrix, modelViewMatrixTranspose );
SetVertexParms( RENDERPARM_MODELVIEWMATRIX_X, modelViewMatrixTranspose, 4 );
}
#if 0
@ -4495,6 +4500,142 @@ static void RB_Bloom( const viewDef_t* viewDef )
GL_State( GLS_DEFAULT );
GL_Cull( CT_FRONT_SIDED );
}
void RB_SSAO()
{
if( !backEnd.viewDef->viewEntitys )
{
// 3D views only
return;
}
if( r_useSSGI.GetInteger() <= 0 )
{
return;
}
// FIXME very expensive to enable this in subviews
if( backEnd.viewDef->isSubview )
{
return;
}
GL_CheckErrors();
#if 0
// clear the alpha buffer and draw only the hands + weapon into it so
// we can avoid blurring them
glClearColor( 0, 0, 0, 1 );
GL_State( GLS_COLORMASK | GLS_DEPTHMASK );
glClear( GL_COLOR_BUFFER_BIT );
GL_Color( 0, 0, 0, 0 );
GL_SelectTexture( 0 );
globalImages->blackImage->Bind();
backEnd.currentSpace = NULL;
drawSurf_t** drawSurfs = ( drawSurf_t** )&backEnd.viewDef->drawSurfs[0];
for( int surfNum = 0; surfNum < backEnd.viewDef->numDrawSurfs; surfNum++ )
{
const drawSurf_t* surf = drawSurfs[ surfNum ];
if( !surf->space->weaponDepthHack && !surf->space->skipMotionBlur && !surf->material->HasSubview() )
{
// Apply motion blur to this object
continue;
}
const idMaterial* shader = surf->material;
if( shader->Coverage() == MC_TRANSLUCENT )
{
// muzzle flash, etc
continue;
}
// set mvp matrix
if( surf->space != backEnd.currentSpace )
{
RB_SetMVP( surf->space->mvp );
backEnd.currentSpace = surf->space;
}
// this could just be a color, but we don't have a skinned color-only prog
if( surf->jointCache )
{
renderProgManager.BindShader_TextureVertexColorSkinned();
}
else
{
renderProgManager.BindShader_TextureVertexColor();
}
// draw it solid
RB_DrawElementsWithCounters( surf );
}
GL_State( GLS_DEPTHFUNC_ALWAYS );
// copy off the color buffer and the depth buffer for the motion blur prog
// we use the viewport dimensions for copying the buffers in case resolution scaling is enabled.
const idScreenRect& viewport = backEnd.viewDef->viewport;
globalImages->currentRenderImage->CopyFramebuffer( viewport.x1, viewport.y1, viewport.GetWidth(), viewport.GetHeight() );
// in stereo rendering, each eye needs to get a separate previous frame mvp
int mvpIndex = ( backEnd.viewDef->renderView.viewEyeBuffer == 1 ) ? 1 : 0;
// derive the matrix to go from current pixels to previous frame pixels
idRenderMatrix inverseMVP;
idRenderMatrix::Inverse( backEnd.viewDef->worldSpace.mvp, inverseMVP );
idRenderMatrix motionMatrix;
idRenderMatrix::Multiply( backEnd.prevMVP[mvpIndex], inverseMVP, motionMatrix );
backEnd.prevMVP[mvpIndex] = backEnd.viewDef->worldSpace.mvp;
RB_SetMVP( motionMatrix );
#endif
backEnd.currentSpace = &backEnd.viewDef->worldSpace;
RB_SetMVP( backEnd.viewDef->worldSpace.mvp );
//GL_State( GLS_DEPTHFUNC_ALWAYS );
//GL_Cull( CT_TWO_SIDED );
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS );
GL_Cull( CT_TWO_SIDED );
int screenWidth = renderSystem->GetWidth();
int screenHeight = renderSystem->GetHeight();
// set the window clipping
GL_Viewport( 0, 0, screenWidth, screenHeight );
GL_Scissor( 0, 0, screenWidth, screenHeight );
renderProgManager.BindShader_AmbientOcclusion();
float screenCorrectionParm[4];
screenCorrectionParm[0] = 1.0f / glConfig.nativeScreenWidth;
screenCorrectionParm[1] = 1.0f / glConfig.nativeScreenHeight;
screenCorrectionParm[2] = glConfig.nativeScreenWidth;
screenCorrectionParm[3] = glConfig.nativeScreenHeight;
SetFragmentParm( RENDERPARM_SCREENCORRECTIONFACTOR, screenCorrectionParm ); // rpScreenCorrectionFactor
// let the fragment program know how many samples we are going to use
idVec4 samples( ( float )( 1 << r_motionBlur.GetInteger() ) );
SetFragmentParm( RENDERPARM_OVERBRIGHT, samples.ToFloatPtr() );
// RB: set unprojection matrices so we can convert zbuffer values back to camera and world spaces
SetVertexParms( RENDERPARM_MODELMATRIX_X, backEnd.viewDef->unprojectionToWorldRenderMatrix[0], 4 );
SetVertexParms( RENDERPARM_PROJMATRIX_X, backEnd.viewDef->unprojectionToCameraRenderMatrix[0], 4 );
GL_SelectTexture( 0 );
globalImages->currentNormalsImage->Bind();
GL_SelectTexture( 1 );
globalImages->currentDepthImage->Bind();
RB_DrawElementsWithCounters( &backEnd.unitSquareSurface );
GL_CheckErrors();
}
// RB end
/*
@ -4620,6 +4761,17 @@ void RB_DrawViewInternal( const viewDef_t* viewDef, const int stereoEye )
//-------------------------------------------------
RB_DrawInteractions( viewDef );
//-------------------------------------------------
// capture the depth for the motion blur before rendering any post process surfaces that may contribute to the depth
//-------------------------------------------------
if( ( r_motionBlur.GetInteger() > 0 || r_useSSGI.GetBool() ) && !r_useHDR.GetBool() )
{
const idScreenRect& viewport = backEnd.viewDef->viewport;
globalImages->currentDepthImage->CopyDepthbuffer( viewport.x1, viewport.y1, viewport.GetWidth(), viewport.GetHeight() );
}
RB_SSAO();
//-------------------------------------------------
// now draw any non-light dependent shading passes
//-------------------------------------------------
@ -4647,15 +4799,6 @@ void RB_DrawViewInternal( const viewDef_t* viewDef, const int stereoEye )
//-------------------------------------------------
RB_FogAllLights();
//-------------------------------------------------
// capture the depth for the motion blur before rendering any post process surfaces that may contribute to the depth
//-------------------------------------------------
if( r_motionBlur.GetInteger() > 0 && !r_useHDR.GetBool() )
{
const idScreenRect& viewport = backEnd.viewDef->viewport;
globalImages->currentDepthImage->CopyDepthbuffer( viewport.x1, viewport.y1, viewport.GetWidth(), viewport.GetHeight() );
}
//-------------------------------------------------
// now draw any screen warping post-process effects using _currentRender
//-------------------------------------------------

View file

@ -472,6 +472,11 @@ void R_RenderView( viewDef_t* parms )
// portal-to-screen scissor calculations
R_SetupProjectionMatrix( tr.viewDef );
// RB: we need a unprojection matrix to calculate the vertex position based on the depth image value
// for some post process shaders
R_SetupUnprojection( tr.viewDef );
// RB end
// setup render matrices for faster culling
idRenderMatrix::Transpose( *( idRenderMatrix* )tr.viewDef->projectionMatrix, tr.viewDef->projectionRenderMatrix );
idRenderMatrix viewRenderMatrix;

View file

@ -419,6 +419,15 @@ struct viewDef_t
float projectionMatrix[16];
idRenderMatrix projectionRenderMatrix; // tech5 version of projectionMatrix
// RB begin
float unprojectionToCameraMatrix[16];
idRenderMatrix unprojectionToCameraRenderMatrix;
float unprojectionToWorldMatrix[16];
idRenderMatrix unprojectionToWorldRenderMatrix;
// RB end
viewEntity_t worldSpace;
idRenderWorldLocal* renderWorld;