├── README.md ├── RenderMonkey ├── DFAA-RenderMonkey-Project.zip └── README.md ├── bin ├── README.md ├── dfaa-fullscreen.exe └── dfaa-windowed.exe ├── generate-uv-ids.h ├── ps1.hlsl └── ps2.hlsl /README.md: -------------------------------------------------------------------------------- 1 | ## DFAA Antialiasing Algorithm 2 | 3 | This repository contains two HLSL SM3.0 shaders that provide the most basic 4 | implementation of the DFAA algorithm. More info could be found in the 5 | [blog post ](http://alexpolt.github.io/dfaa.html). 6 | 7 | In the bin directory there is a DirectX9 standalone demo. But advise you 8 | to better use the provided RenderMonkey project (in the RenderMonkey folder). 9 | There you will find knobs on the right to have better control over DFAA. 10 | 11 | --- 12 | 13 | ## License 14 | 15 | The code in this repository is Public-domain software. 16 | 17 | ![Pubic domain software](http://alexpolt.github.io/images/public_domain_mark.png) 18 | 19 | -------------------------------------------------------------------------------- /RenderMonkey/DFAA-RenderMonkey-Project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexpolt/DFAA/918bcb3c5206bccddade46a6a8d3abd66c76789c/RenderMonkey/DFAA-RenderMonkey-Project.zip -------------------------------------------------------------------------------- /RenderMonkey/README.md: -------------------------------------------------------------------------------- 1 | ##RenderMonkey project 2 | 3 | This is a RenderMonkey project that demonstrates the DFAA Antialiasing Algorithm. 4 | I've copy-pasted an adaptation of FXAA from . 5 | By checking checkboxes on the right you can clearly see the difference between the two methods. 6 | 7 | On the right of the RenderMonkey UI you have **artist variables**: 8 | 9 | * uv01colors - high contrast coloring using provided 01 uv's 10 | * rotate - auto rotate the model 11 | * DFAA 12 | * FXAA (DFAA has priority if both are on) 13 | 14 | 15 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | ##DFAA demo windows application 2 | 3 | It's a 32-bit demo of DFAA written for DirectX9, no dependencies. 4 | 5 | Controls are: 6 | 7 | * Space - pause animation 8 | * A - toggle DFAA (on by default) 9 | * Z - zoom to see the DFAA at work 10 | * ESC - exit 11 | 12 | -------------------------------------------------------------------------------- /bin/dfaa-fullscreen.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexpolt/DFAA/918bcb3c5206bccddade46a6a8d3abd66c76789c/bin/dfaa-fullscreen.exe -------------------------------------------------------------------------------- /bin/dfaa-windowed.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexpolt/DFAA/918bcb3c5206bccddade46a6a8d3abd66c76789c/bin/dfaa-windowed.exe -------------------------------------------------------------------------------- /generate-uv-ids.h: -------------------------------------------------------------------------------- 1 | /* 2 | The code to generate a buffer with 0,1,2 mapping for index geometry, 3 | that can be used in a shader to index into: 4 | float2 uv01[] = {{0,0},{1,0},{0,1}} 5 | Delete the returned buffer with a delete[] operator. 6 | 7 | Parameters: 8 | index_buffer: index buffer 9 | index_count: number of indices ( number of tries * 3 ) 10 | vertex_count (optional): size of the generated buffer (by default looks up the largest index and uses it) 11 | 12 | Alexandr Poltavsky 13 | */ 14 | 15 | template 16 | R* generate_uv_ids( T* index_buffer, int index_count, int vertex_count = 0 ) { 17 | 18 | //check preconditions 19 | if( ! index_buffer ) return nullptr; 20 | if( ! index_count ) return nullptr; 21 | if( ! vertex_count ) { 22 | //look up the largest index to use it as the generated buffer size 23 | for( int i = 0; i < index_count; i++ ) 24 | vertex_count = vertex_count < index_buffer[i] ? index_buffer[i] : vertex_count; 25 | ++vertex_count; 26 | } 27 | 28 | #define mask(a,b,c) ((a) << 4) | ((b) << 2) | (c) 29 | typedef unsigned char byte; 30 | 31 | //a helper data structure for generating proper vertex ids 32 | struct mask_to_values { 33 | byte mask; 34 | byte value[3]; 35 | }; 36 | 37 | mask_to_values mv[] = { 38 | 39 | { mask(3,3,3), {0,1,2} }, 40 | { mask(3,3,0), {1,2,0} }, 41 | { mask(3,3,1), {0,2,1} }, 42 | { mask(3,3,2), {0,1,2} }, 43 | 44 | { mask(3,0,3), {1,0,2} }, 45 | { mask(3,0,1), {2,0,1} }, 46 | { mask(3,0,2), {1,0,2} }, 47 | 48 | { mask(3,1,3), {0,1,2} }, 49 | { mask(3,1,0), {2,1,0} }, 50 | { mask(3,1,2), {0,1,2} }, 51 | 52 | { mask(3,2,3), {0,2,1} }, 53 | { mask(3,2,0), {1,2,0} }, 54 | { mask(3,2,1), {0,2,1} }, 55 | 56 | { mask(0,3,3), {0,1,2} }, 57 | { mask(0,3,1), {0,2,1} }, 58 | { mask(0,3,2), {0,1,2} }, 59 | { mask(0,1,3), {0,1,2} }, 60 | { mask(0,1,2), {3,3,3} }, 61 | { mask(0,2,3), {0,2,1} }, 62 | { mask(0,2,1), {3,3,3} }, 63 | 64 | { mask(1,3,3), {1,2,0} }, 65 | { mask(1,3,0), {1,2,0} }, 66 | { mask(1,3,2), {1,0,2} }, 67 | { mask(1,0,3), {1,0,2} }, 68 | { mask(1,0,2), {3,3,3} }, 69 | { mask(1,2,3), {1,2,0} }, 70 | { mask(1,2,0), {3,3,3} }, 71 | 72 | { mask(2,3,3), {2,1,0} }, 73 | { mask(2,3,0), {2,1,0} }, 74 | { mask(2,3,1), {2,0,1} }, 75 | { mask(2,0,3), {2,0,1} }, 76 | { mask(2,0,1), {3,3,3} }, 77 | { mask(2,1,3), {2,1,0} }, 78 | { mask(2,1,0), {3,3,3} }, 79 | 80 | }; 81 | 82 | //get the memory for the resulting vid buffer, delete with delete[] 83 | R* vids = new R[ vertex_count ]; 84 | 85 | //init it with 3 (3 means vacant) 86 | for( int i = 0; i < vertex_count; i++ ) vids[ i ] = 3; 87 | 88 | int bad_tri = 0; //number of tris for which no good 0,1,2 mapping exists 89 | 90 | //loop through the index buffer 91 | for( int idx = 0; idx < index_count; idx+=3 ) { 92 | 93 | byte m = mask( vids[ index_buffer[idx+0] ], 94 | vids[ index_buffer[idx+1] ], 95 | vids[ index_buffer[idx+2] ] ); //helper mask for searching in mv array 96 | 97 | //loop through the mv array and find the mapping 98 | int i = 0; const int mv_size = sizeof(mv)/sizeof(mv[0]); 99 | for(; i < mv_size; i++ ) { 100 | 101 | if( m == mv[i].mask ) { 102 | if( 3 == mv[i].value[0] ) break; //proper vids are already in place, early out 103 | //assign 104 | vids[ index_buffer[idx+0] ] = mv[i].value[0]; 105 | vids[ index_buffer[idx+1] ] = mv[i].value[1]; 106 | vids[ index_buffer[idx+2] ] = mv[i].value[2]; 107 | break; 108 | } 109 | } 110 | 111 | if( i == mv_size) //no mask was found, means we've got a tri with no proper mapping, gets zeros 112 | bad_tri++; //print it to get the number of tris with wrong vertex ids, no or partial AA on them 113 | 114 | } 115 | 116 | //clean up any remaining 3s 117 | for( int i = 0; i < vertex_count; i++ ) vids[i] = vids[i] == 3 ? 0 : vids[i]; 118 | 119 | return vids; 120 | } 121 | 122 | -------------------------------------------------------------------------------- /ps1.hlsl: -------------------------------------------------------------------------------- 1 | /* 2 | The most basic implementation of the DFAA Antialiasing Algorithm. 3 | More info is in the blog post http://alexpolt.github.io/dfaa.html 4 | 5 | Alexandr Poltavsky 6 | */ 7 | 8 | //forward declaration 9 | float DFAA1( float2 uv01 ); 10 | 11 | static const float pi2 = 2*3.1415926; 12 | 13 | /* first pass pixel shader, uv01 should be with noperspective modifier */ 14 | 15 | float4 main( noperspective float2 uv01: TEXCOORD0 ) : COLOR0 16 | { 17 | float dfaa = DFAA1( uv01 ); 18 | 19 | //returns some default color and packed dfaa in alpha 20 | return float4( uv01, 1-uv01.x-uv01.y, dfaa ); 21 | } 22 | 23 | 24 | //Implementation of the DFAA algorithm, first pass 25 | //should be fed with a [0,0],[1,0],[0,1] UV 26 | //returns one byte with packed direction and coverage 27 | 28 | static float rad = 0.5; //rad - radius of sampling, 0.5 means half-pixel 29 | static float steps = 3; //(steps+1)^2 - total number subsamples for coverage computation 30 | 31 | float DFAA1( float2 uv01 ) { 32 | 33 | float2 uvdx = ddx( uv01 ); 34 | float2 uvdy = ddy( uv01 ); 35 | 36 | float area=0; 37 | 38 | //compute non-coverage 39 | 40 | for(float y=0; y<=steps; y++) { 41 | for(float x=0; x<=steps; x++) { 42 | float2 dxdy = float2( 2*rad*x/steps - rad, 2*rad*y/steps - rad ); 43 | float2 edge = uv01 + uvdx * dxdy.x + uvdy * dxdy.y; 44 | // if we are out of the triangle - increase by one the non-coverage 45 | if( edge.x < 0 || edge.y < 0 || ( edge.x + edge.y ) > 1 ) area++; 46 | } 47 | } 48 | 49 | //get actual coverage 50 | area = 1 - area / pow(steps+1, 2); 51 | 52 | //the next big step is to compute the direction of sampling 53 | //we get the inverse matrix and compute the edge vectors of the polygon 54 | //then we break the polygon into three independent parts by medians 55 | //and get the right edge, then rotate it by 90 degrees 56 | 57 | float2 dir; 58 | float2 uvdx2 = normalize( uvdx ), uvdy2 = normalize( uvdy ); 59 | 60 | //matrix inverse 61 | float det = 1 / ( uvdx2.x * uvdy2.y - uvdx2.y * uvdy2.x ); 62 | float2 xydu = det * float2( uvdy2.y, -uvdx2.y ); 63 | float2 xydv = det * float2( -uvdy2.x, uvdx2.x ); 64 | 65 | //choosing the edge using triangle medians 66 | float2 z = float2( xydv - xydu ); 67 | if( uv01.y > uv01.x && (uv01.y+2*uv01.x) < 1 ) dir = float2( -xydv.y, xydv.x ); 68 | else if( uv01.x > uv01.y && (uv01.x+2*uv01.y) < 1 ) dir = float2( xydu.y, -xydu.x ); 69 | else dir = float2( z.y, -z.x ); 70 | 71 | //so that we don't have to worry about winding of uv's 72 | dir = cross( float3(xydu, 0), float3(xydv, 0) ).z > 0 ? dir : -dir; 73 | 74 | //encode direction as an angle 75 | float dir_atan = atan2( dir.y, dir.x ); 76 | float dir_angle = ( dir_atan < 0 ? dir_atan + pi2 : dir_atan ) / pi2; 77 | 78 | //pack into one byte 79 | float dfaa = ( floor(15*dir_angle)*16 + 15*area ) / 255; 80 | 81 | return dfaa; 82 | 83 | } 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /ps2.hlsl: -------------------------------------------------------------------------------- 1 | /* 2 | The most basic implementation of the DFAA Antialiasing Algorithm. 3 | More info is in the blog post http://alexpolt.github.io/dfaa.html 4 | 5 | Alexandr Poltavsky 6 | */ 7 | 8 | //forward declare 9 | float3 DFAA2( float2 screenxy ); 10 | 11 | //pixel to display and packed DFAA in alpha 12 | sampler tex0: register(s0); 13 | 14 | static const float pi2 = 2 * 3.1415926; 15 | 16 | 17 | /* full screen pixel shader */ 18 | float4 main( float2 screenxy : TEXCOORD0 ) : COLOR0 { 19 | 20 | return float4( DFAA2( screenxy ), 1 ); 21 | 22 | } 23 | 24 | //defines used by DFAA 25 | #define dfaa_screen_texture tex0 26 | #define dfaa_tex2D tex2D 27 | 28 | //DFAA full screen pass: performs fetch, unpack and lerp 29 | float3 DFAA2( float2 screenxy ) { 30 | 31 | float4 color0 = dfaa_tex2D( dfaa_screen_texture, screenxy ); 32 | float dfaa_packed = color0.a; 33 | 34 | //unpack 35 | float dir_angle = floor(255*dfaa_packed/16)/15; 36 | float2 dir = float2( cos( dir_angle * pi2 ), sin( dir_angle * pi2 ) ); 37 | float area = frac(255*dfaa_packed/16)*16/15; 38 | 39 | float2 sdx = ddx( screenxy ); 40 | float2 sdy = ddy( screenxy ); 41 | 42 | float4 color1 = color0; 43 | 44 | //only do fetch if necessary (edge pixels) 45 | if( area > 0 && area < 1 ) 46 | color1 = dfaa_tex2D( dfaa_screen_texture, screenxy + sdx*dir.x + sdy*dir.y ); 47 | 48 | return lerp( color1, color0, area ).rgb; 49 | } 50 | 51 | 52 | --------------------------------------------------------------------------------