├── .gitattributes ├── ConeSphereOcclusionLUT.exe ├── ConeSphereOcclusionLUT.sln ├── ConeSphereOcclusionLUT.vcxproj ├── README.md ├── cone_sphere_occlusion_lut.tga └── main.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /ConeSphereOcclusionLUT.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knarkowicz/ConeSphereOcclusionLUT/cc329868d7b4fb689f5c4a0c9e917351f6d06757/ConeSphereOcclusionLUT.exe -------------------------------------------------------------------------------- /ConeSphereOcclusionLUT.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ConeSphereOcclusionLUT", "ConeSphereOcclusionLUT.vcxproj", "{59AA66D1-11A7-41D9-86D8-32ABB1308D1A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {59AA66D1-11A7-41D9-86D8-32ABB1308D1A}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {59AA66D1-11A7-41D9-86D8-32ABB1308D1A}.Debug|Win32.Build.0 = Debug|Win32 16 | {59AA66D1-11A7-41D9-86D8-32ABB1308D1A}.Release|Win32.ActiveCfg = Release|Win32 17 | {59AA66D1-11A7-41D9-86D8-32ABB1308D1A}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /ConeSphereOcclusionLUT.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {59AA66D1-11A7-41D9-86D8-32ABB1308D1A} 15 | ConeSphereOcclusionLUT 16 | 17 | 18 | 19 | Application 20 | true 21 | v120 22 | MultiByte 23 | 24 | 25 | Application 26 | false 27 | v120 28 | true 29 | MultiByte 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Level3 45 | Disabled 46 | true 47 | 48 | 49 | true 50 | 51 | 52 | 53 | 54 | Level3 55 | MaxSpeed 56 | true 57 | true 58 | true 59 | 60 | 61 | true 62 | true 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ConeSphereOcclusionLUT 2 | ======= 3 | 4 | ConeSphereOcclusionLUT generates a cone sphere occlusion LUT to be used with TLoU style capsule AO shadows. For details see page 26 of ["Lighting Technology Of "The Last Of Us""](http://miciwan.com/SIGGRAPH2013/Lighting%20Technology%20of%20The%20Last%20Of%20Us.pdf) talk by Michał Iwanicki . This slick technique was originally created for SPU, but it perfectly works with compute shaders. 5 | -------------------------------------------------------------------------------- /cone_sphere_occlusion_lut.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knarkowicz/ConeSphereOcclusionLUT/cc329868d7b4fb689f5c4a0c9e917351f6d06757/cone_sphere_occlusion_lut.tga -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | float const MATH_PI = 3.14159265359f; 8 | unsigned const SAMPLE_NUM = 10000; 9 | 10 | struct Vec3 11 | { 12 | float x; 13 | float y; 14 | float z; 15 | }; 16 | 17 | float dot( Vec3 a, Vec3 b ) 18 | { 19 | return a.x * b.x + a.y * b.y + a.z * b.z; 20 | } 21 | 22 | void SaveTGA( char const* path, unsigned width, unsigned height, void const* data ) 23 | { 24 | #pragma pack( push, 1 ) 25 | struct STGAHeader 26 | { 27 | char idlength; 28 | char colourmaptype; 29 | char datatypecode; 30 | short int colourmaporigin; 31 | short int colourmaplength; 32 | char colourmapdepth; 33 | short int x_origin; 34 | short int y_origin; 35 | short width; 36 | short height; 37 | char bitsperpixel; 38 | char imagedescriptor; 39 | }; 40 | #pragma pack( pop ) 41 | 42 | 43 | FILE* f = nullptr; 44 | fopen_s( &f, path, "wb" ); 45 | if ( !f ) 46 | { 47 | return; 48 | } 49 | 50 | STGAHeader header; 51 | memset( &header, 0, sizeof( header ) ); 52 | header.width = width; 53 | header.height = height; 54 | header.datatypecode = 2; 55 | header.bitsperpixel = 24; 56 | header.imagedescriptor = 1 << 5; 57 | fwrite( &header, sizeof( header ), 1, f ); 58 | 59 | fwrite( data, width * height * 3, 1, f ); 60 | 61 | fclose( f ); 62 | } 63 | 64 | Vec3 RandomConeRays[ SAMPLE_NUM ]; 65 | 66 | void GenerateRandomConeRays( float coneAngle ) 67 | { 68 | float const cosConeAngle = cosf( 0.5f * ( coneAngle * MATH_PI / 180.0f ) ); 69 | for ( unsigned iSample = 0; iSample < SAMPLE_NUM; ++iSample ) 70 | { 71 | float cosTheta = ( (float) rand() / RAND_MAX ) * ( 1.0f - cosConeAngle ) + cosConeAngle; 72 | float sinTheta = sqrtf( 1.0f - cosTheta * cosTheta ); 73 | float phi = MATH_PI * ( (float) rand() / RAND_MAX ) * 2.0f - 1.0f; 74 | float cosPhi = cosf( phi ); 75 | float sinPhi = sinf( phi ); 76 | 77 | RandomConeRays[ iSample ] = { sinTheta * cosPhi, sinTheta * sinPhi, cosTheta }; 78 | } 79 | } 80 | 81 | float Occlusion( float occluderAngleSin, float occluderToBeamAngleCos ) 82 | { 83 | float const occluderToBeamAngleSin = sqrtf( 1.0f - occluderToBeamAngleCos * occluderToBeamAngleCos ); 84 | float const occluderAngleCos = sqrtf( 1.0f - occluderAngleSin * occluderAngleSin ); 85 | Vec3 const occluderDir = { occluderToBeamAngleSin, 0.0f, occluderToBeamAngleCos }; 86 | 87 | unsigned hitNum = 0; 88 | for ( unsigned iSample = 0; iSample < SAMPLE_NUM; ++iSample ) 89 | { 90 | if ( dot( occluderDir, RandomConeRays[ iSample ] ) > occluderAngleCos ) 91 | { 92 | ++hitNum; 93 | } 94 | } 95 | 96 | return 1.0f - hitNum / (float) SAMPLE_NUM; 97 | } 98 | 99 | int main( int argc, char** argv ) 100 | { 101 | if ( argc < 4 ) 102 | { 103 | printf( "Incorrect params\n" ); 104 | printf( "Usage: ConeSphereOcclusionLUT.exe lut_width lut_height cone_angle_in_degress\n" ); 105 | printf( "ConeSphereOcclusionLUT.exe 128 64 30\n" ); 106 | return 1; 107 | } 108 | 109 | float coneAngle = 30.0f; 110 | unsigned lutWidth = 128; 111 | unsigned lutHeight = 64; 112 | sscanf_s( argv[ 1 ], "%u", &lutWidth ); 113 | sscanf_s( argv[ 2 ], "%u", &lutHeight ); 114 | sscanf_s( argv[ 3 ], "%f", &coneAngle ); 115 | 116 | uint8_t* lutData = new uint8_t[ lutWidth * lutHeight * 3 ]; 117 | GenerateRandomConeRays( coneAngle ); 118 | 119 | for ( unsigned iSinTheta = 0; iSinTheta < lutHeight; ++iSinTheta ) // angle subtended by the occluder [0;1] 120 | { 121 | float const sinTheta = iSinTheta / ( lutHeight - 1.0f ); 122 | 123 | for ( unsigned iCosPhi = 0; iCosPhi < lutWidth; ++iCosPhi ) // angle between the cone axis and the vector pointing towards the occluder (sphere) center [-1;1] 124 | { 125 | float const cosPhi = 2.0f * ( iCosPhi / ( lutWidth - 1.0f ) ) - 1.0f; 126 | float const sampleValue = Occlusion( sinTheta, cosPhi ); 127 | 128 | uint8_t sampleU8 = uint8_t( sampleValue * 255.0f + 0.5f ); 129 | lutData[ iCosPhi * 3 + iSinTheta * lutWidth * 3 + 0 ] = sampleU8; 130 | lutData[ iCosPhi * 3 + iSinTheta * lutWidth * 3 + 1 ] = sampleU8; 131 | lutData[ iCosPhi * 3 + iSinTheta * lutWidth * 3 + 2 ] = sampleU8; 132 | } 133 | } 134 | 135 | SaveTGA( "cone_sphere_occlusion_lut.tga", lutWidth, lutHeight, lutData ); 136 | delete[] lutData; 137 | 138 | return 0; 139 | } --------------------------------------------------------------------------------