diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index 47be4bed..fb2b27a5 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -1528,6 +1528,43 @@ void idRenderBackend::GetCurrentBindingLayout( int type ) desc[1].bindings[1].resourceHandle = commonPasses.m_LinearWrapSampler; } } + else if( type == BINDING_LAYOUT_POST_PROCESS_FINAL2 ) + { + if( desc[0].bindings.empty() ) + { + desc[0].bindings = + { + nvrhi::BindingSetItem::ConstantBuffer( 0, paramCb, range ), + nvrhi::BindingSetItem::Texture_SRV( 0, ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID() ), + nvrhi::BindingSetItem::Texture_SRV( 1, ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID() ), + nvrhi::BindingSetItem::Texture_SRV( 2, ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID() ), + nvrhi::BindingSetItem::Texture_SRV( 3, ( nvrhi::ITexture* )GetImageAt( 3 )->GetTextureID() ) + }; + } + else + { + desc[0].bindings[0].resourceHandle = paramCb; + desc[0].bindings[0].range = range; + desc[0].bindings[1].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 0 )->GetTextureID(); + desc[0].bindings[2].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 1 )->GetTextureID(); + desc[0].bindings[3].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 2 )->GetTextureID(); + desc[0].bindings[4].resourceHandle = ( nvrhi::ITexture* )GetImageAt( 3 )->GetTextureID(); + } + + if( desc[1].bindings.empty() ) + { + desc[1].bindings = + { + nvrhi::BindingSetItem::Sampler( 0, commonPasses.m_LinearClampSampler ), + nvrhi::BindingSetItem::Sampler( 1, commonPasses.m_LinearWrapSampler ) + }; + } + else + { + desc[1].bindings[0].resourceHandle = commonPasses.m_LinearClampSampler; + desc[1].bindings[1].resourceHandle = commonPasses.m_LinearWrapSampler; + } + } else if( type == BINDING_LAYOUT_NORMAL_CUBE ) { if( desc[0].bindings.empty() ) diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 854a6c44..d2ddc89b 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -6259,6 +6259,34 @@ void idRenderBackend::PostProcess( const void* data ) GL_SelectTexture( 1 ); globalImages->blueNoiseImage256->Bind(); + GL_SelectTexture( 2 ); + globalImages->gbufferNormalsRoughnessImage->Bind(); + + GL_SelectTexture( 3 ); + if( r_useHierarchicalDepthBuffer.GetBool() ) + { + commandList->clearTextureFloat( globalImages->hierarchicalZbufferImage->GetTextureHandle(), nvrhi::AllSubresources, nvrhi::Color( 1.f ) ); + + // build hierarchical depth buffer + renderLog.OpenBlock( "Render_HiZ" ); + + commonPasses.BlitTexture( + commandList, + globalFramebuffers.csDepthFBO[0]->GetApiObject(), + globalImages->currentDepthImage->GetTextureHandle(), + &bindingCache ); + + hiZGenPass->Dispatch( commandList, MAX_HIERARCHICAL_ZBUFFERS ); + + renderLog.CloseBlock(); + + globalImages->hierarchicalZbufferImage->Bind(); + } + else + { + globalImages->currentDepthImage->Bind(); + } + float jitterTexScale[4] = {}; if( r_renderMode.GetInteger() == RENDERMODE_C64 || r_renderMode.GetInteger() == RENDERMODE_C64_HIGHRES ) @@ -6308,6 +6336,73 @@ void idRenderBackend::PostProcess( const void* data ) SetFragmentParm( RENDERPARM_JITTERTEXOFFSET, jitterTexOffset ); // rpJitterTexOffset +#if 1 + //SetVertexParms( RENDERPARM_MODELMATRIX_X, viewDef->unprojectionToCameraRenderMatrix[0], 4 ); + SetVertexParms( RENDERPARM_MODELMATRIX_X, viewDef->unprojectionToWorldRenderMatrix[0], 4 ); + + //R_MatrixMultiply( viewDef->worldSpace.modelViewMatrix, viewDef->projectionMatrix, viewDef->unprojectionToWorldMatrix ); + ///R_MatrixFullInverse( viewDef->unprojectionToWorldMatrix, viewDef->unprojectionToWorldMatrix ); + + //idRenderMatrix::Transpose( *( idRenderMatrix* )viewDef->unprojectionToWorldMatrix, viewDef->unprojectionToWorldRenderMatrix ); +#else + idRenderMatrix matClipToView; + idRenderMatrix::Inverse( *( idRenderMatrix* ) viewDef->projectionMatrix, matClipToView ); + + SetVertexParms( RENDERPARM_MODELMATRIX_X, matClipToView[0], 4 ); +#endif + + { + // transform by the camera rotation + const idVec3& origin = viewDef->renderView.vieworg; + const idMat3& axis = viewDef->renderView.viewaxis; + + float viewerMatrix[16]; + viewerMatrix[0 * 4 + 0] = axis[0][0]; + viewerMatrix[1 * 4 + 0] = axis[0][1]; + viewerMatrix[2 * 4 + 0] = axis[0][2]; + viewerMatrix[3 * 4 + 0] = - origin[0] * axis[0][0] - origin[1] * axis[0][1] - origin[2] * axis[0][2]; + + viewerMatrix[0 * 4 + 1] = axis[1][0]; + viewerMatrix[1 * 4 + 1] = axis[1][1]; + viewerMatrix[2 * 4 + 1] = axis[1][2]; + viewerMatrix[3 * 4 + 1] = - origin[0] * axis[1][0] - origin[1] * axis[1][1] - origin[2] * axis[1][2]; + + viewerMatrix[0 * 4 + 2] = axis[2][0]; + viewerMatrix[1 * 4 + 2] = axis[2][1]; + viewerMatrix[2 * 4 + 2] = axis[2][2]; + viewerMatrix[3 * 4 + 2] = - origin[0] * axis[2][0] - origin[1] * axis[2][1] - origin[2] * axis[2][2]; + + viewerMatrix[0 * 4 + 3] = 0.0f; + viewerMatrix[1 * 4 + 3] = 0.0f; + viewerMatrix[2 * 4 + 3] = 0.0f; + viewerMatrix[3 * 4 + 3] = 1.0f; + + float mvp[16]; + float modelViewdMatrix[16]; + float unprojectionToWorldMatrix[16]; + + static float s_flipMatrix[16] = + { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 + }; + + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + R_MatrixMultiply( viewerMatrix, s_flipMatrix, modelViewdMatrix ); + + R_MatrixMultiply( modelViewdMatrix, viewDef->projectionMatrix, mvp ); + R_MatrixFullInverse( mvp, unprojectionToWorldMatrix ); + + idRenderMatrix unprojectionToWorldRenderMatrix; + idRenderMatrix::Transpose( *( idRenderMatrix* )unprojectionToWorldMatrix, unprojectionToWorldRenderMatrix ); + //SetVertexParms( RENDERPARM_MODELMATRIX_X, unprojectionToWorldRenderMatrix[0], 4 ); + } + // Draw DrawElementsWithCounters( &unitSquareSurface ); } diff --git a/neo/renderer/RenderCommon.h b/neo/renderer/RenderCommon.h index 42bdc23e..93b4432e 100644 --- a/neo/renderer/RenderCommon.h +++ b/neo/renderer/RenderCommon.h @@ -822,6 +822,7 @@ enum bindingLayoutType_t // NO GPU SKINNING ANYMORE BINDING_LAYOUT_POST_PROCESS_INGAME, BINDING_LAYOUT_POST_PROCESS_FINAL, + BINDING_LAYOUT_POST_PROCESS_FINAL2, BINDING_LAYOUT_BLIT, BINDING_LAYOUT_DRAW_AO, diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 7a7cc931..11463598 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -327,8 +327,8 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) auto pp3DBindingLayout = nvrhi::BindingLayoutDesc() .setVisibility( nvrhi::ShaderType::All ) .addItem( renderParmLayoutItem ) - .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // current render - .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ) // normal map + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // HDR _currentRender + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ) // normal map .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 2 ) ); // mask bindingLayouts[BINDING_LAYOUT_POST_PROCESS_INGAME] = { device->createBindingLayout( pp3DBindingLayout ), samplerOneBindingLayout }; @@ -336,11 +336,21 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) auto ppFxBindingLayout = nvrhi::BindingLayoutDesc() .setVisibility( nvrhi::ShaderType::All ) .addItem( renderParmLayoutItem ) - .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) - .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ); + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // LDR _currentRender + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ); // _blueNoise bindingLayouts[BINDING_LAYOUT_POST_PROCESS_FINAL] = { device->createBindingLayout( ppFxBindingLayout ), samplerTwoBindingLayout }; + auto ppFxBindingLayout2 = nvrhi::BindingLayoutDesc() + .setVisibility( nvrhi::ShaderType::All ) + .addItem( renderParmLayoutItem ) + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // LDR _currentRender + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 1 ) ) // _blueNoise + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 2 ) ) // _currentNormals + .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 3 ) ); // _currentDepth + + bindingLayouts[BINDING_LAYOUT_POST_PROCESS_FINAL2] = { device->createBindingLayout( ppFxBindingLayout2 ), samplerTwoBindingLayout }; + auto normalCubeBindingLayoutDesc = nvrhi::BindingLayoutDesc() .setVisibility( nvrhi::ShaderType::Pixel ) .addItem( nvrhi::BindingLayoutItem::Texture_SRV( 0 ) ) // cube map @@ -522,7 +532,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) { BUILTIN_WOBBLESKY, "builtin/legacy/wobblesky", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_DEFAULT }, { BUILTIN_POSTPROCESS, "builtin/post/postprocess", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_C64, "builtin/post/retro_c64", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, - { BUILTIN_POSTPROCESS_RETRO_CPC, "builtin/post/retro_cpc", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, + { BUILTIN_POSTPROCESS_RETRO_CPC, "builtin/post/retro_cpc", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL2 }, { BUILTIN_POSTPROCESS_RETRO_GENESIS, "builtin/post/retro_genesis", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_POSTPROCESS_RETRO_PSX, "builtin/post/retro_ps1", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, { BUILTIN_CRT_MATTIAS, "builtin/post/crt_mattias", "", {}, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT, BINDING_LAYOUT_POST_PROCESS_FINAL }, diff --git a/neo/shaders/builtin/SSAO/AmbientOcclusion_AO.ps.hlsl b/neo/shaders/builtin/SSAO/AmbientOcclusion_AO.ps.hlsl index 597802b8..f3c8e3d9 100644 --- a/neo/shaders/builtin/SSAO/AmbientOcclusion_AO.ps.hlsl +++ b/neo/shaders/builtin/SSAO/AmbientOcclusion_AO.ps.hlsl @@ -160,18 +160,21 @@ float3 reconstructNonUnitCSFaceNormal( float3 C ) return n; } -float3 reconstructCSPosition( float2 S, float z ) +float3 reconstructCSPosition( float2 S, float depth ) { - float4 P; - P.z = z;// * 2.0 - 1.0; - P.xy = ( S * rpWindowCoord.xy ) * 2.0 - 1.0; - P.w = 1.0; + // derive clip space from the depth buffer and screen position + float2 uv = S * rpWindowCoord.xy; + float3 ndc = float3( uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth ); + float clipW = -rpProjectionMatrixZ.w / ( -rpProjectionMatrixZ.z - ndc.z ); + float4 clip = float4( ndc * clipW, clipW ); + + // camera space position float4 csP; - csP.x = dot4( P, rpModelMatrixX ); - csP.y = dot4( P, rpModelMatrixY ); - csP.z = dot4( P, rpModelMatrixZ ); - csP.w = dot4( P, rpModelMatrixW ); + csP.x = dot4( rpModelMatrixX, clip ); + csP.y = dot4( rpModelMatrixY, clip ); + csP.z = dot4( rpModelMatrixZ, clip ); + csP.w = dot4( rpModelMatrixW, clip ); csP.xyz /= csP.w; @@ -388,7 +391,7 @@ void main( PS_IN fragment, out PS_OUT result ) } #endif -#if 1 +#if 0 float randomPatternRotationAngle = BlueNoise( float2( ssP.xy ), 10.0 ) * 10.0; //float randomPatternRotationAngle = InterleavedGradientNoise( ssP.xy ) * 10.0; #else diff --git a/neo/shaders/builtin/lighting/interaction.vs.hlsl b/neo/shaders/builtin/lighting/interaction.vs.hlsl index ff580fe4..9a3184af 100644 --- a/neo/shaders/builtin/lighting/interaction.vs.hlsl +++ b/neo/shaders/builtin/lighting/interaction.vs.hlsl @@ -153,6 +153,8 @@ void main( VS_IN vertex, out VS_OUT result ) result.texcoord1.x = dot4( vertex.texcoord.xy, rpBumpMatrixS ); result.texcoord1.y = dot4( vertex.texcoord.xy, rpBumpMatrixT ); + //result.texcoord1.xy = psxAffineTexMapping( result.texcoord1.xy, ) + //# texture 2 has one texgen result.texcoord2 = defaultTexCoord; result.texcoord2.x = dot4( modelPosition, rpLightFalloffS ); diff --git a/neo/shaders/builtin/post/retro_cpc.ps.hlsl b/neo/shaders/builtin/post/retro_cpc.ps.hlsl index 30940f40..366a77f3 100644 --- a/neo/shaders/builtin/post/retro_cpc.ps.hlsl +++ b/neo/shaders/builtin/post/retro_cpc.ps.hlsl @@ -33,9 +33,11 @@ If you have questions concerning this license or the applicable additional terms // *INDENT-OFF* Texture2D t_BaseColor : register( t0 VK_DESCRIPTOR_SET( 0 ) ); Texture2D t_BlueNoise : register( t1 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_Normals : register( t2 VK_DESCRIPTOR_SET( 0 ) ); +Texture2D t_Depth : register( t3 VK_DESCRIPTOR_SET( 0 ) ); -SamplerState samp0 : register(s0 VK_DESCRIPTOR_SET( 1 ) ); -SamplerState samp1 : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 +SamplerState s_LinearClamp : register(s0 VK_DESCRIPTOR_SET( 1 ) ); +SamplerState s_LinearWrap : register(s1 VK_DESCRIPTOR_SET( 1 ) ); // blue noise 256 struct PS_IN { @@ -53,17 +55,6 @@ struct PS_OUT #define RESOLUTION_DIVISOR 4.0 #define NUM_COLORS 32 // original 27 -/* -float LinearTweak1( float c ) -{ - return ( c <= 0.04045 ) ? c / 12.92 : pow( ( c + 0.055 ) / 1.055, 1.4 ); -} - -float3 LinearTweak3( float3 c ) -{ - return float3( Linear1( c.r ), Linear1( c.g ), Linear1( c.b ) ); -} -*/ float3 Average( float3 pal[NUM_COLORS] ) { @@ -119,7 +110,210 @@ float3 LinearSearch( float3 c, float3 pal[NUM_COLORS] ) #define RGB(r, g, b) float3(float(r)/255.0, float(g)/255.0, float(b)/255.0) +// http://www.cse.cuhk.edu.hk/~ttwong/papers/spheremap/spheremap.html +float3 healpix( float3 p ) +{ + float a = atan2( p.z, p.x ) * 0.63662; + float h = 3.0 * abs( p.y ); + float h2 = 0.75 * p.y; + float2 uv = float2( a + h2, a - h2 ); + h2 = sqrt( 3.0 - h ); + float a2 = h2 * frac( a ); + uv = lerp( uv, float2( -h2 + a2, a2 ), step( 2.0, h ) ); +#if 1 + return float3( uv, 1.0 ); +#else + float3 col = spheretex( uv ); + col.x = a * 0.5; + + return HSVToRGB( float3( col.x, 0.8, col.z ) ); +#endif +} + +// can be either view space or world space depending on rpModelMatrix +float3 ReconstructPosition( float2 S, float depth ) +{ + // derive clip space from the depth buffer and screen position + float2 uv = S * rpWindowCoord.xy; + float3 ndc = float3( uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0, depth ); + float clipW = -rpProjectionMatrixZ.w / ( -rpProjectionMatrixZ.z - ndc.z ); + + float4 clip = float4( ndc * clipW, clipW ); + + // camera space position + float4 csP; + csP.x = dot4( rpModelMatrixX, clip ); + csP.y = dot4( rpModelMatrixY, clip ); + csP.z = dot4( rpModelMatrixZ, clip ); + csP.w = dot4( rpModelMatrixW, clip ); + + csP.xyz /= csP.w; + + return csP.xyz; +} + +float3 GetPosition( int2 ssP ) +{ + float depth = texelFetch( t_Depth, ssP, 0 ).r; + + // offset to pixel center + float3 P = ReconstructPosition( float2( ssP ) + _float2( 0.5 ), depth ); + + return P; +} + +float BlueNoise( float2 n, float x ) +{ + float noise = t_BlueNoise.Sample( s_LinearWrap, n.xy * rpJitterTexOffset.xy ).r; + + noise = frac( noise + c_goldenRatioConjugate * rpJitterTexOffset.z * x ); + + noise = RemapNoiseTriErp( noise ); + noise = noise * 2.0 - 0.5; + + return noise; +} + +// 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) +#define MIN_MIP_LEVEL 0 + +#define USE_MIPMAPS 1 + +static const float radius = 1.0 * METERS_TO_DOOM; +static const float radius2 = radius * radius; +static const float invRadius2 = 1.0 / radius2; + +/** Bias to avoid AO in smooth corners, e.g., 0.01m */ +static const float bias = 0.01 * METERS_TO_DOOM; + +/** intensity / radius^6 */ +static const float intensity = 0.6; +static const float intensityDivR6 = intensity / ( radius* radius* radius* radius* radius* radius ); + +/** The height in pixels of a 1m object if viewed from 1m away. + You can compute it from your projection matrix. The actual value is just + a scale factor on radius; you can simply hardcode this to a constant (~500) + and make your radius value unitless (...but resolution dependent.) */ +static const float projScale = 500.0; + +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] + // + // 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 ); +} + +float aoValueFromPositionsAndNormal( float3 C, float3 n_C, float3 Q ) +{ + float3 v = Q - C; + //v = normalize( v ); + 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 ); +} + +void computeMipInfo( float ssR, int2 ssP, out int mipLevel, out int2 mipP ) +{ + // Derivation: + // mipLevel = floor(log(ssR / MAX_OFFSET)); +#ifdef GL_EXT_gpu_shader5 + mipLevel = clamp( findMSB( int( ssR ) ) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL ); +#else + mipLevel = clamp( int( floor( log2( ssR ) ) ) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL ); +#endif + + // 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 + + // used in newer radiosity + //mipP = ssP >> mipLevel; + + mipP = clamp( ssP >> mipLevel, _int2( 0 ), textureSize( t_Depth, mipLevel ) - _int2( 1 ) ); +} + +float3 getOffsetPosition( int2 issC, float2 unitOffset, float ssR, float invCszBufferScale ) +{ + int2 ssP = int2( ssR * unitOffset ) + issC; + + float3 P; + + int mipLevel; + int2 mipP; + computeMipInfo( ssR, ssP, mipLevel, mipP ); + +#if USE_MIPMAPS + // RB: this is the key for fast ambient occlusion - use a hierarchical depth buffer + // for more information see McGuire12SAO.pdf - Scalable Ambient Obscurance + // http://graphics.cs.williams.edu/papers/SAOHPG12/ + P.z = t_Depth.Load( int3( mipP, mipLevel ) ).r; +#else + P.z = t_Depth.Load( int3( mipP, 0 ) ).r; +#endif + + // Offset to pixel center + P = ReconstructPosition( float2( ssP ) + _float2( 0.5 ), P.z ); + + return P; +} + +float2 tapLocation( int sampleNumber, float spinAngle, out float ssR ) +{ + // Radius relative to ssR + float alpha = ( float( sampleNumber ) + 0.5 ) * ( 1.0 / float( NUM_SAMPLES ) ); + float angle = alpha * ( float( NUM_SPIRAL_TURNS ) * 6.28 ) + spinAngle; + + ssR = alpha; + + return float2( cos( angle ), sin( angle ) ); +} + +float sampleAO( int2 issC, float3 C, float3 n_C, float ssDiskRadius, int tapIndex, float randomPatternRotationAngle, 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 ); + + // The occluding point in camera space + float3 Q = getOffsetPosition( issC, unitOffset, ssR, invCszBufferScale ); + + return aoValueFromPositionsAndNormal( C, n_C, Q ); +} + +float3 ditherRGB( float2 fragPos, float3 quantDeviation ) +{ + float2 uvDither = fragPos / ( RESOLUTION_DIVISOR / rpJitterTexScale.x ); + float dither = DitherArray8x8( uvDither ) - 0.5; + + return float3( dither, dither, dither ) * quantDeviation * rpJitterTexScale.y; +} void main( PS_IN fragment, out PS_OUT result ) { @@ -155,10 +349,18 @@ void main( PS_IN fragment, out PS_OUT result ) RGB( 255, 255, 128 ), // pastel yellow RGB( 255, 255, 255 ), // bright white -#if 1 +#if 0 RGB( 16, 16, 16 ), // black RGB( 0, 28, 28 ), // dark cyan RGB( 112, 164, 178 ) * 1.3, // cyan + +#else + // https://lospec.com/palette-list/2bit-demichrome + RGB( 33, 30, 32 ), + RGB( 85, 85, 104 ), + RGB( 160, 160, 139 ), + RGB( 233, 239, 236 ), + #endif }; @@ -266,8 +468,74 @@ void main( PS_IN fragment, out PS_OUT result ) RGB( 120, 120, 120 ), }; +#elif 1 + + // NES Advanced + // https://lospec.com/palette-list/nes-advanced + + const float3 palette[NUM_COLORS] = // 55 + { + RGB( 0, 0, 0 ), + RGB( 38, 35, 47 ), + RGB( 49, 64, 71 ), + RGB( 89, 109, 98 ), + RGB( 146, 156, 116 ), + RGB( 200, 197, 163 ), + RGB( 252, 252, 252 ), + RGB( 27, 55, 127 ), + RGB( 20, 122, 191 ), + RGB( 64, 175, 221 ), + RGB( 178, 219, 244 ), + RGB( 24, 22, 103 ), + RGB( 59, 44, 150 ), + RGB( 112, 106, 225 ), + RGB( 143, 149, 238 ), + RGB( 68, 10, 65 ), + RGB( 129, 37, 147 ), + RGB( 204, 75, 185 ), + RGB( 236, 153, 219 ), + RGB( 63, 0, 17 ), + RGB( 179, 28, 53 ), + RGB( 239, 32, 100 ), + RGB( 242, 98, 130 ), + RGB( 150, 8, 17 ), + RGB( 232, 24, 19 ), + RGB( 167, 93, 105 ), + RGB( 236, 158, 164 ), + RGB( 86, 13, 4 ), + RGB( 196, 54, 17 ), + RGB( 226, 106, 18 ), + RGB( 240, 175, 102 ), + RGB( 42, 26, 20 ), + RGB( 93, 52, 42 ), + RGB( 166, 110, 70 ), + RGB( 223, 156, 110 ), + RGB( 142, 78, 17 ), + RGB( 216, 149, 17 ), + RGB( 234, 209, 30 ), + RGB( 245, 235, 107 ), + RGB( 47, 84, 28 ), + RGB( 90, 131, 27 ), + RGB( 162, 187, 30 ), + RGB( 198, 223, 107 ), + RGB( 15, 69, 15 ), + RGB( 0, 139, 18 ), + RGB( 11, 203, 18 ), + RGB( 62, 243, 63 ), + RGB( 17, 81, 83 ), + RGB( 12, 133, 99 ), + RGB( 4, 191, 121 ), + RGB( 106, 230, 170 ), + RGB( 38, 39, 38 ), + RGB( 81, 79, 76 ), + RGB( 136, 126, 131 ), + RGB( 179, 170, 192 ), + }; + #elif 0 + // Atari STE + // https://lospec.com/palette-list/astron-ste32 const float3 palette[NUM_COLORS] = // 32 { RGB( 0, 0, 0 ), @@ -306,16 +574,31 @@ void main( PS_IN fragment, out PS_OUT result ) #elif 0 - // Gameboy - const float3 palette[NUM_COLORS] = // 4 + // Sega Genesis Evangelion + // https://lospec.com/palette-list/sega-genesis-evangelion + + const float3 palette[NUM_COLORS] = // 17 { - RGB( 27, 42, 9 ), - RGB( 14, 69, 11 ), - RGB( 73, 107, 34 ), - RGB( 154, 158, 63 ), + RGB( 207, 201, 179 ), + RGB( 163, 180, 158 ), + RGB( 100, 166, 174 ), + RGB( 101, 112, 141 ), + RGB( 52, 54, 36 ), + RGB( 37, 34, 70 ), + RGB( 39, 28, 21 ), + RGB( 20, 14, 11 ), + RGB( 0, 0, 0 ), + RGB( 202, 168, 87 ), + RGB( 190, 136, 51 ), + RGB( 171, 85, 92 ), + RGB( 186, 47, 74 ), + RGB( 131, 25, 97 ), + RGB( 102, 52, 143 ), + RGB( 203, 216, 246 ), + RGB( 140, 197, 79 ), }; -#else +#elif 0 // https://lospec.com/palette-list/existential-demo const float3 palette[NUM_COLORS] = // 8 @@ -330,6 +613,28 @@ void main( PS_IN fragment, out PS_OUT result ) RGB( 46, 43, 18 ), }; +#elif 0 + + // Gameboy + const float3 palette[NUM_COLORS] = // 4 + { + RGB( 27, 42, 9 ), + RGB( 14, 69, 11 ), + RGB( 73, 107, 34 ), + RGB( 154, 158, 63 ), + }; + +#else + + // https://lospec.com/palette-list/2bit-demichrome + const float3 palette[NUM_COLORS] = // 4 + { + RGB( 33, 30, 32 ), + RGB( 85, 85, 104 ), + RGB( 160, 160, 139 ), + RGB( 233, 239, 236 ), + }; + #endif float2 uv = ( fragment.texcoord0 ); @@ -339,7 +644,7 @@ void main( PS_IN fragment, out PS_OUT result ) float3 quantDeviation = Deviation( palette ); // get pixellated base color - float3 color = t_BaseColor.Sample( samp0, uvPixelated * rpWindowCoord.xy ).rgb; + float3 color = t_BaseColor.Sample( s_LinearClamp, uvPixelated * rpWindowCoord.xy ).rgb; float2 uvDither = uvPixelated; //if( rpJitterTexScale.x > 1.0 ) @@ -380,6 +685,12 @@ void main( PS_IN fragment, out PS_OUT result ) { color = _float3( uv.x ); color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + + color.rgb += float3( dither, dither, dither ) * quantDeviation * rpJitterTexScale.y; + color = LinearSearch( color, palette ); + + result.color = float4( color, 1.0 ); + return; } #endif @@ -389,5 +700,114 @@ void main( PS_IN fragment, out PS_OUT result ) // find closest color match from CPC color palette color = LinearSearch( color.rgb, palette ); +#if 0 + // similar to Obra Dinn + + // triplanar mapping based on reconstructed depth buffer + + int2 ssP = int2( fragment.position.xy ); + float3 C = GetPosition( ssP ); + float3 n_C = t_Normals.Sample( s_LinearClamp, uv ).rgb; + //float3 n_C = normalize( ( 2.0 * t_Normals.Sample( s_LinearClamp, uv ).rgb ) - 1.0 ); + + //result.color = float4( n_C * 0.5 + 0.5, 1.0 ); + //return; + +#if 0 + // SSAO to test the ReconstructPosition function + float vis = 1.0; + + //float randomPatternRotationAngle = BlueNoise( float2( ssP.xy ), 10.0 ) * 10.0; + float randomPatternRotationAngle = InterleavedGradientNoise( ssP.xy ) * 10.0; + + float ssDiskRadius = -projScale * radius / C.z; + + float sum = 0.0; + for( int i = 0; i < NUM_SAMPLES; ++i ) + { + sum += sampleAO( ssP, C, n_C, ssDiskRadius, i, randomPatternRotationAngle, 1.0 ); + } + + float A = pow( max( 0.0, 1.0 - sqrt( sum * ( 3.0 / float( NUM_SAMPLES ) ) ) ), intensity ); + + const float minRadius = 3.0; + vis = lerp( 1.0, A, saturate( ssDiskRadius - minRadius ) ); + result.color = float4( _float3( vis ), 1.0 ); + return; +#endif + + // triplanar UVs + float3 worldPos = C; + + float2 uvX = worldPos.zy; // x facing plane + float2 uvY = worldPos.xz; // y facing plane + float2 uvZ = worldPos.xy; // z facing plane + + float2 suvX = round( float2( uvX.x, uvX.y ) ); + float2 suvY = round( float2( uvY.x, uvY.y ) ); + float2 suvZ = round( float2( uvZ.x, uvZ.y ) ); + + // offset UVs to prevent obvious mirroring + //uvY += 0.33; + //uvZ += 0.67; + + float3 worldNormal = n_C; + + float3 triblend = saturate( pow( worldNormal, 4.0 ) ); + triblend /= max( dot( triblend, float3( 1, 1, 1 ) ), 0.0001 ); + + float3 axisSign = sign( n_C ); + + //uvX.x *= axisSign.x; + //uvY.x *= axisSign.y; + //uvZ.x *= -axisSign.z; + + //result.color = float4( suv.xy, 0.0, 1.0 ); + //return; + + // FIXME get pixellated base color + //color = t_BaseColor.Sample( s_LinearClamp, uvPixelated * rpWindowCoord.xy ).rgb; + color = t_BaseColor.Sample( s_LinearClamp, uv ).rgb; + + float3 colX = ditherRGB( uvX, quantDeviation ); + float3 colY = ditherRGB( uvY, quantDeviation ); + float3 colZ = ditherRGB( uvZ, quantDeviation ); + + float3 dither3D = colX * triblend.x + colY * triblend.y + colZ * triblend.z; + color.rgb += dither3D; + + // find closest color match from CPC color palette + color = LinearSearch( color.rgb, palette ); + +#if 0 + colX = _float3( DitherArray8x8( suvX ) - 0.5 ); + colY = _float3( DitherArray8x8( suvY ) - 0.5 ); + colZ = _float3( DitherArray8x8( suvZ ) - 0.5 ); + + dither3D = colX * triblend.x + colY * triblend.y + colZ * triblend.z; + color.rgb = dither3D; +#endif + +#if 0 + colX = float3( uvZ * 0.5 + 0.5, 0.0 ); + colY = _float3( 0 ); + colZ = _float3( 0 ); + + dither3D = colX * triblend.x + colY * triblend.y + colZ * triblend.z; + color.rgb = dither3D; +#endif + +#if 0 + color = _float3( uvZ.x * 0.5 + 0.5 ); + color = dither3D; + color = floor( color * NUM_COLORS ) * ( 1.0 / ( NUM_COLORS - 1.0 ) ); + color.rgb += dither3D; + //color.rgb += float3( dither, dither, dither ) * quantDeviation * rpJitterTexScale.y; + color = LinearSearch( color, palette ); +#endif + +#endif // cubic mapping + + //color.rgb = float3( suv.xy * 0.5 + 0.5, 1.0 ); result.color = float4( color, 1.0 ); } diff --git a/neo/shaders/builtin/post/retro_cpc.vs.hlsl b/neo/shaders/builtin/post/retro_cpc.vs.hlsl index 75519fe8..890b2a72 100644 --- a/neo/shaders/builtin/post/retro_cpc.vs.hlsl +++ b/neo/shaders/builtin/post/retro_cpc.vs.hlsl @@ -43,6 +43,7 @@ struct VS_IN struct VS_OUT { float4 position : SV_Position; float2 texcoord0 : TEXCOORD0_centroid; + float3 texcoord1 : TEXCOORD1_centroid; }; // *INDENT-ON* diff --git a/neo/shaders/global_inc.hlsl b/neo/shaders/global_inc.hlsl index 28a437b0..646e1455 100644 --- a/neo/shaders/global_inc.hlsl +++ b/neo/shaders/global_inc.hlsl @@ -519,6 +519,11 @@ static float3 psxVertexJitter( float4 spos ) return spos.xyz; } +static float2 psxAffineTexMapping( float2 uv, float4 spos ) +{ + return uv; +} + #define BRANCH #define IFANY