Some experiments similar to Obra Dinn and fixed old SSAO

This commit is contained in:
Robert Beckebans 2024-07-30 22:01:27 +02:00
parent cebba26908
commit a3a52c97fe
9 changed files with 611 additions and 37 deletions

View file

@ -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() )

View file

@ -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 );
}

View file

@ -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,

View file

@ -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 },

View file

@ -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

View file

@ -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 );

View file

@ -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 );
}

View file

@ -43,6 +43,7 @@ struct VS_IN
struct VS_OUT {
float4 position : SV_Position;
float2 texcoord0 : TEXCOORD0_centroid;
float3 texcoord1 : TEXCOORD1_centroid;
};
// *INDENT-ON*

View file

@ -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