From bf04c70f509799a687c51a51fcf1c05247533294 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 6 Jan 2018 00:51:05 +0300 Subject: [PATCH] Set SMAA antialiasing --- assets/shader1fragment.txt | 2 +- assets/smaa/blend_fragment.txt | 422 ++++++++++++++++++++++++++ assets/smaa/blend_vertex.txt | 74 +++++ assets/smaa/edge_fragment.txt | 105 +++++++ assets/smaa/edge_vertex.txt | 67 ++++ assets/smaa/neighborhood_fragment.txt | 102 +++++++ assets/smaa/neighborhood_vertex.txt | 65 ++++ assets/smaa/smaa_area.raw | Bin 0 -> 179200 bytes assets/smaa/smaa_search.raw | Bin 0 -> 2178 bytes game/main_code.cpp | 214 ++++++++++--- 10 files changed, 1015 insertions(+), 36 deletions(-) create mode 100755 assets/smaa/blend_fragment.txt create mode 100755 assets/smaa/blend_vertex.txt create mode 100755 assets/smaa/edge_fragment.txt create mode 100755 assets/smaa/edge_vertex.txt create mode 100755 assets/smaa/neighborhood_fragment.txt create mode 100755 assets/smaa/neighborhood_vertex.txt create mode 100755 assets/smaa/smaa_area.raw create mode 100755 assets/smaa/smaa_search.raw diff --git a/assets/shader1fragment.txt b/assets/shader1fragment.txt index 523790e..f8d773f 100755 --- a/assets/shader1fragment.txt +++ b/assets/shader1fragment.txt @@ -7,7 +7,7 @@ void main() { vec4 color = texture2D(Texture,texCoord).rgba; - gl_FragColor = vec4(color.rgb, color.a * Transparency); + gl_FragColor = vec4(color.rgb, 1.0); diff --git a/assets/smaa/blend_fragment.txt b/assets/smaa/blend_fragment.txt new file mode 100755 index 0000000..b832128 --- /dev/null +++ b/assets/smaa/blend_fragment.txt @@ -0,0 +1,422 @@ +#version 410 compatibility +#define SMAA_PIXEL_SIZE vec2(1.0 / 512.0, 1.0 / 512.0) + + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + + + + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +/** + * These functions allows to perform diagonal pattern searches. + */ +float SMAASearchDiag1(sampler2D edgesTex, vec2 texcoord, vec2 dir, float c) { + texcoord += dir * SMAA_PIXEL_SIZE; + vec2 e = vec2(0.0, 0.0); + float i; + for (i = 0.0; i < float(SMAA_MAX_SEARCH_STEPS_DIAG); i++) { + e.rg = textureLod(edgesTex, texcoord, 0.0).rg; + + //SMAA_FLATTEN + if (dot(e, vec2(1.0, 1.0)) < 1.9) break; + + texcoord += dir * SMAA_PIXEL_SIZE; + } + return i + float(e.g > 0.9) * c; +} + +float SMAASearchDiag2(sampler2D edgesTex, vec2 texcoord, vec2 dir, float c) { + texcoord += dir * SMAA_PIXEL_SIZE; + vec2 e = vec2(0.0, 0.0); + float i; + for (i = 0.0; i < float(SMAA_MAX_SEARCH_STEPS_DIAG); i++) { + e.g = textureLod(edgesTex, texcoord, 0.0).g; + e.r = textureLodOffset(edgesTex, texcoord, 0.0, ivec2(1, 0)).r; + + //SMAA_FLATTEN + if (dot(e, vec2(1.0, 1.0)) < 1.9) break; + + texcoord += dir * SMAA_PIXEL_SIZE; + } + return i + float(e.g > 0.9) * c; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +vec2 SMAAAreaDiag(sampler2D areaTex, vec2 dist, vec2 e, float offset) { + vec2 texcoord = float(SMAA_AREATEX_MAX_DISTANCE_DIAG) * e + dist; + + // We do a scale and bias for mapping to texel space: + texcoord = SMAA_AREATEX_PIXEL_SIZE * texcoord + (0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + + return textureLod(areaTex, texcoord, 0.0).rg; +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +vec2 SMAACalculateDiagWeights(sampler2D edgesTex, sampler2D areaTex, vec2 texcoord, vec2 e, ivec4 subsampleIndices) { + vec2 weights = vec2(0.0, 0.0); + + vec2 d; + d.x = e.r > 0.0? SMAASearchDiag1(edgesTex, texcoord, vec2(-1.0, 1.0), 1.0) : 0.0; + d.y = SMAASearchDiag1(edgesTex, texcoord, vec2(1.0, -1.0), 0.0); + + //SMAA_BRANCH + if (d.r + d.g > 2.0) { // d.r + d.g + 1 > 3 + vec4 coords = fma(vec4(-d.r, d.r, d.g, -d.g), SMAA_PIXEL_SIZE.xyxy, texcoord.xyxy); + + vec4 c; + c.x = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-1, 0)).g; + c.y = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2( 0, 0)).r; + c.z = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, 0)).g; + c.w = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, -1)).r; + vec2 e = 2.0 * c.xz + c.yw; + float t = float(SMAA_MAX_SEARCH_STEPS_DIAG) - 1.0; + e *= step(d.rg, vec2(t, t)); + + weights += SMAAAreaDiag(areaTex, d, e, float(subsampleIndices.z)); + } + + d.x = SMAASearchDiag2(edgesTex, texcoord, vec2(-1.0, -1.0), 0.0); + float right = textureLodOffset(edgesTex, texcoord, 0.0, ivec2(1, 0)).r; + d.y = right > 0.0? SMAASearchDiag2(edgesTex, texcoord, vec2(1.0, 1.0), 1.0) : 0.0; + + //SMAA_BRANCH + if (d.r + d.g > 2.0) { // d.r + d.g + 1 > 3 + vec4 coords = fma(vec4(-d.r, -d.r, d.g, d.g), SMAA_PIXEL_SIZE.xyxy, texcoord.xyxy); + + vec4 c; + c.x = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-1, 0)).g; + c.y = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2( 0, -1)).r; + c.zw = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1, 0)).gr; + vec2 e = 2.0 * c.xz + c.yw; + float t = float(SMAA_MAX_SEARCH_STEPS_DIAG) - 1.0; + e *= step(d.rg, vec2(t, t)); + + weights += SMAAAreaDiag(areaTex, d, e, float(subsampleIndices.w)).gr; + } + + return weights; +} + + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(sampler2D searchTex, vec2 e, float bias, float scale) { + // Not required if searchTex accesses are set to point: + // vec2 SEARCH_TEX_PIXEL_SIZE = 1.0 / vec2(66.0, 33.0); + // e = vec2(bias, 0.0) + 0.5 * SEARCH_TEX_PIXEL_SIZE + + // e * vec2(scale, 1.0) * vec2(64.0, 32.0) * SEARCH_TEX_PIXEL_SIZE; + e.r = bias + e.r * scale; + e.g = -e.g; + return 255.0 * textureLod(searchTex, e, 0.0).r; +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + vec2 e = vec2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = textureLod(edgesTex, texcoord, 0.0).rg; + texcoord -= vec2(2.0, 0.0) * SMAA_PIXEL_SIZE; + } + + // We correct the previous (-0.25, -0.125) offset we applied: + texcoord.x += 0.25 * SMAA_PIXEL_SIZE.x; + + // The searches are bias by 1, so adjust the coords accordingly: + texcoord.x += SMAA_PIXEL_SIZE.x; + + // Disambiguate the length added by the last step: + texcoord.x += 2.0 * SMAA_PIXEL_SIZE.x; // Undo last step + texcoord.x -= SMAA_PIXEL_SIZE.x * SMAASearchLength(searchTex, e, 0.0, 0.5); + + return texcoord.x; +} + +float SMAASearchXRight(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { + vec2 e = vec2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = textureLod(edgesTex, texcoord, 0.0).rg; + texcoord += vec2(2.0, 0.0) * SMAA_PIXEL_SIZE; + } + + texcoord.x -= 0.25 * SMAA_PIXEL_SIZE.x; + texcoord.x -= SMAA_PIXEL_SIZE.x; + texcoord.x -= 2.0 * SMAA_PIXEL_SIZE.x; + texcoord.x += SMAA_PIXEL_SIZE.x * SMAASearchLength(searchTex, e, 0.5, 0.5); + return texcoord.x; +} + +float SMAASearchYUp(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { + vec2 e = vec2(1.0, 0.0); + while (texcoord.y > end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = textureLod(edgesTex, texcoord, 0.0).rg; + texcoord -= vec2(0.0, 2.0) * SMAA_PIXEL_SIZE; + } + + texcoord.y += 0.25 * SMAA_PIXEL_SIZE.y; + texcoord.y += SMAA_PIXEL_SIZE.y; + texcoord.y += 2.0 * SMAA_PIXEL_SIZE.y; + texcoord.y -= SMAA_PIXEL_SIZE.y * SMAASearchLength(searchTex, e.gr, 0.0, 0.5); + return texcoord.y; +} + +float SMAASearchYDown(sampler2D edgesTex, sampler2D searchTex, vec2 texcoord, float end) { + vec2 e = vec2(1.0, 0.0); + while (texcoord.y < end && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = textureLod(edgesTex, texcoord, 0.0).rg; + texcoord += vec2(0.0, 2.0) * SMAA_PIXEL_SIZE; + } + + texcoord.y -= 0.25 * SMAA_PIXEL_SIZE.y; + texcoord.y -= SMAA_PIXEL_SIZE.y; + texcoord.y -= 2.0 * SMAA_PIXEL_SIZE.y; + texcoord.y += SMAA_PIXEL_SIZE.y * SMAASearchLength(searchTex, e.gr, 0.5, 0.5); + return texcoord.y; +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +vec2 SMAAArea(sampler2D areaTex, vec2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + vec2 texcoord = float(SMAA_AREATEX_MAX_DISTANCE) * round(4.0 * vec2(e1, e2)) + dist; + + // We do a scale and bias for mapping to texel space: + texcoord = SMAA_AREATEX_PIXEL_SIZE * texcoord + (0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + + return textureLod(areaTex, texcoord, 0.0).rg; + +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(sampler2D edgesTex, inout vec2 weights, vec2 texcoord, vec2 d) { + #if SMAA_CORNER_ROUNDING < 100 || SMAA_FORCE_CORNER_DETECTION == 1 + vec4 coords = fma(vec4(d.x, 0.0, d.y, 0.0), + SMAA_PIXEL_SIZE.xyxy, texcoord.xyxy); + vec2 e; + e.r = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(0.0, 1.0)).r; + bool left = abs(d.x) < abs(d.y); + e.g = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(0.0, -2.0)).r; + if (left) weights *= clamp(float(SMAA_CORNER_ROUNDING) / 100.0 + 1.0 - e, 0.0, 1.0); + + e.r = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2(1.0, 1.0)).r; + e.g = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2(1.0, -2.0)).r; + if (!left) weights *= clamp(float(SMAA_CORNER_ROUNDING) / 100.0 + 1.0 - e, 0.0, 1.0); + #endif +} + +void SMAADetectVerticalCornerPattern(sampler2D edgesTex, inout vec2 weights, vec2 texcoord, vec2 d) { + #if SMAA_CORNER_ROUNDING < 100 || SMAA_FORCE_CORNER_DETECTION == 1 + vec4 coords = fma(vec4(0.0, d.x, 0.0, d.y), + SMAA_PIXEL_SIZE.xyxy, texcoord.xyxy); + vec2 e; + e.r = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2( 1.0, 0.0)).g; + bool left = abs(d.x) < abs(d.y); + e.g = textureLodOffset(edgesTex, coords.xy, 0.0, ivec2(-2.0, 0.0)).g; + if (left) weights *= clamp(float(SMAA_CORNER_ROUNDING) / 100.0 + 1.0 - e, 0.0, 1.0); + + e.r = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2( 1.0, 1.0)).g; + e.g = textureLodOffset(edgesTex, coords.zw, 0.0, ivec2(-2.0, 1.0)).g; + if (!left) weights *= clamp(float(SMAA_CORNER_ROUNDING) / 100.0 + 1.0 - e, 0.0, 1.0); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +vec4 SMAABlendingWeightCalculationPS(vec2 texcoord, + vec2 pixcoord, + vec4 offset[3], + sampler2D edgesTex, + sampler2D areaTex, + sampler2D searchTex, + ivec4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + vec4 weights = vec4(0.0, 0.0, 0.0, 0.0); + + vec2 e = texture(edgesTex, texcoord).rg; + + //SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if SMAA_MAX_SEARCH_STEPS_DIAG > 0 || SMAA_FORCE_DIAGONAL_DETECTION == 1 + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(edgesTex, areaTex, texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + //SMAA_BRANCH + if (dot(weights.rg, vec2(1.0, 1.0)) == 0.0) { + #endif + + vec2 d; + + // Find the distance to the left: + vec2 coords; + coords.x = SMAASearchXLeft(edgesTex, searchTex, offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_PIXEL_SIZE.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = textureLod(edgesTex, coords, 0.0).r; + + // Find the distance to the right: + coords.x = SMAASearchXRight(edgesTex, searchTex, offset[0].zw, offset[2].y); + d.y = coords.x; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = d / SMAA_PIXEL_SIZE.x - pixcoord.x; + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + vec2 sqrt_d = sqrt(abs(d)); + + // Fetch the right crossing edges: + float e2 = textureLodOffset(edgesTex, coords, 0.0, ivec2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(areaTex, sqrt_d, e1, e2, float(subsampleIndices.y)); + + // Fix corners: + SMAADetectHorizontalCornerPattern(edgesTex, weights.rg, texcoord, d); + + #if SMAA_MAX_SEARCH_STEPS_DIAG > 0 || SMAA_FORCE_DIAGONAL_DETECTION == 1 + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + //SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + vec2 d; + + // Find the distance to the top: + vec2 coords; + coords.y = SMAASearchYUp(edgesTex, searchTex, offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_PIXEL_SIZE.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = textureLod(edgesTex, coords, 0.0).g; + + // Find the distance to the bottom: + coords.y = SMAASearchYDown(edgesTex, searchTex, offset[1].zw, offset[2].w); + d.y = coords.y; + + // We want the distances to be in pixel units: + d = d / SMAA_PIXEL_SIZE.y - pixcoord.y; + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + vec2 sqrt_d = sqrt(abs(d)); + + // Fetch the bottom crossing edges: + float e2 = textureLodOffset(edgesTex, coords, 0.0, ivec2(0, 1)).g; + + // Get the area for this direction: + weights.ba = SMAAArea(areaTex, sqrt_d, e1, e2, float(subsampleIndices.x)); + + // Fix corners: + SMAADetectVerticalCornerPattern(edgesTex, weights.ba, texcoord, d); + + //return vec4(weights.ba, 0.0, 1.0); + } + + return weights; + + +} + + + +/* ------------- Header is over -------------- */ + + +uniform sampler2D edge_tex; +uniform sampler2D area_tex; +uniform sampler2D search_tex; +in vec2 texcoord; +in vec2 pixcoord; +in vec4 offset[3]; +in vec4 dummy2; +void main() +{ + gl_FragColor = SMAABlendingWeightCalculationPS(texcoord, pixcoord, offset, edge_tex, area_tex, search_tex, ivec4(0)); + +} \ No newline at end of file diff --git a/assets/smaa/blend_vertex.txt b/assets/smaa/blend_vertex.txt new file mode 100755 index 0000000..7746603 --- /dev/null +++ b/assets/smaa/blend_vertex.txt @@ -0,0 +1,74 @@ +#version 410 compatibility +#define SMAA_PIXEL_SIZE vec2(1.0 / 512.0, 1.0 / 512.0) + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(vec4 position, + out vec4 svPosition, + inout vec2 texcoord, + out vec2 pixcoord, + out vec4 offset[3]) { + svPosition = position; + + pixcoord = texcoord / SMAA_PIXEL_SIZE; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4(-0.25, -0.125, 1.25, -0.125); + offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4(-0.125, -0.25, -0.125, 1.25); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = vec4(offset[0].xz, offset[1].yw) + + vec4(-2.0, 2.0, -2.0, 2.0) * + SMAA_PIXEL_SIZE.xxyy * float(SMAA_MAX_SEARCH_STEPS); +} + +/* ------------- Header is over -------------- */ + +out vec2 texcoord; +out vec2 pixcoord; +out vec4 offset[3]; +out vec4 dummy2; + +attribute vec3 vPosition; +attribute vec2 vTexCoord; +uniform mat4 ProjectionMatrix; + + +void main() +{ + texcoord = vTexCoord; + vec4 dummy1 = vec4(0); + SMAABlendingWeightCalculationVS(dummy1, dummy2, texcoord, pixcoord, offset); + gl_Position = ProjectionMatrix * vec4(vPosition.xyz, 1.0); +} diff --git a/assets/smaa/edge_fragment.txt b/assets/smaa/edge_fragment.txt new file mode 100755 index 0000000..3d2de9a --- /dev/null +++ b/assets/smaa/edge_fragment.txt @@ -0,0 +1,105 @@ +#version 410 compatibility + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +vec4 SMAAColorEdgeDetectionPS(vec2 texcoord, + vec4 offset[3], + sampler2D colorTex + ) { + vec2 threshold = vec2(SMAA_THRESHOLD, SMAA_THRESHOLD); + + // Calculate color deltas: + vec4 delta; + vec3 C = texture(colorTex, texcoord).rgb; + + vec3 Cleft = texture(colorTex, offset[0].xy).rgb; + vec3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + vec3 Ctop = texture(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + vec2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, vec2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + vec3 Cright = texture(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + vec3 Cbottom = texture(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float maxDelta = max(max(max(delta.x, delta.y), delta.z), delta.w); + + // Calculate left-left and top-top deltas: + vec3 Cleftleft = texture(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + vec3 Ctoptop = texture(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(max(maxDelta, delta.z), delta.w); + + // Local contrast adaptation in action: + edges.xy *= step(0.5 * maxDelta, delta.xy); + + return vec4(edges, 0.0, 0.0); + +} + + +/* ------------- Header is over -------------- */ + +uniform sampler2D albedo_tex; +in vec2 texcoord; +in vec4 offset[3]; +in vec4 dummy2; +void main() +{ + gl_FragColor = SMAAColorEdgeDetectionPS(texcoord, offset, albedo_tex); +} \ No newline at end of file diff --git a/assets/smaa/edge_vertex.txt b/assets/smaa/edge_vertex.txt new file mode 100755 index 0000000..f7edf16 --- /dev/null +++ b/assets/smaa/edge_vertex.txt @@ -0,0 +1,67 @@ +#version 410 compatibility +#define SMAA_PIXEL_SIZE vec2(1.0/512.0, 1.0/512.0) + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(vec4 position, + out vec4 svPosition, + inout vec2 texcoord, + out vec4 offset[3]) { + svPosition = position; + + offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4(-1.0, 0.0, 0.0, -1.0); + offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4( 1.0, 0.0, 0.0, 1.0); + offset[2] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4(-2.0, 0.0, 0.0, -2.0); +} + +/* ------------- Header is over -------------- */ + +out vec2 texcoord; +out vec4 offset[3]; +out vec4 dummy2; + + + +attribute vec3 vPosition; +attribute vec2 vTexCoord; +uniform mat4 ProjectionMatrix; + + +void main() +{ + texcoord = vTexCoord; + vec4 dummy1 = vec4(0); + SMAAEdgeDetectionVS(dummy1, dummy2, texcoord, offset); + gl_Position = ProjectionMatrix * vec4(vPosition.xyz, 1.0); +} \ No newline at end of file diff --git a/assets/smaa/neighborhood_fragment.txt b/assets/smaa/neighborhood_fragment.txt new file mode 100755 index 0000000..32470b0 --- /dev/null +++ b/assets/smaa/neighborhood_fragment.txt @@ -0,0 +1,102 @@ +#version 410 compatibility +#define SMAA_PIXEL_SIZE vec2(1.0 / 512.0, 1.0 / 512.0) + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + +vec4 SMAANeighborhoodBlendingPS(vec2 texcoord, + vec4 offset[2], + sampler2D colorTex, + sampler2D blendTex) { + // Fetch the blending weights for current pixel: + vec4 a; + a.xz = texture(blendTex, texcoord).xz; + a.y = texture(blendTex, offset[1].zw).g; + a.w = texture(blendTex, offset[1].xy).a; + + // Is there any blending weight with a value greater than 0.0? + //SMAA_BRANCH + if (dot(a, vec4(1.0, 1.0, 1.0, 1.0)) < 1e-5) + return textureLod(colorTex, texcoord, 0.0); + else { + vec4 color = vec4(0.0, 0.0, 0.0, 0.0); + + // Up to 4 lines can be crossing a pixel (one through each edge). We + // favor blending by choosing the line with the maximum weight for each + // direction: + vec2 offset; + offset.x = a.a > a.b? a.a : -a.b; // left vs. right + offset.y = a.g > a.r? a.g : -a.r; // top vs. bottom + + // Then we go in the direction that has the maximum weight: + if (abs(offset.x) > abs(offset.y)) // horizontal vs. vertical + offset.y = 0.0; + else + offset.x = 0.0; + + #if SMAA_REPROJECTION == 1 + // Fetch the opposite color and lerp by hand: + vec4 C = textureLod(colorTex, texcoord, 0.0); + texcoord += sign(offset) * SMAA_PIXEL_SIZE; + vec4 Cop = textureLod(colorTex, texcoord, 0.0); + float s = abs(offset.x) > abs(offset.y)? abs(offset.x) : abs(offset.y); + + // Unpack the velocity values: + C.a *= C.a; + Cop.a *= Cop.a; + + // Lerp the colors: + vec4 Caa = mix(C, Cop, s); + + // Unpack velocity and return the resulting value: + Caa.a = sqrt(Caa.a); + return Caa; + #else + // Fetch the opposite color and lerp by hand: + vec4 C = textureLod(colorTex, texcoord, 0.0); + texcoord += sign(offset) * SMAA_PIXEL_SIZE; + vec4 Cop = textureLod(colorTex, texcoord, 0.0); + float s = abs(offset.x) > abs(offset.y)? abs(offset.x) : abs(offset.y); + return mix(C, Cop, s); + #endif + } +} + +/* ------------- Header is over -------------- */ + +uniform sampler2D albedo_tex; +uniform sampler2D blend_tex; +in vec2 texcoord; +in vec4 offset[2]; +in vec4 dummy2; +void main() +{ + gl_FragColor = SMAANeighborhoodBlendingPS(texcoord, offset, albedo_tex, blend_tex); +} \ No newline at end of file diff --git a/assets/smaa/neighborhood_vertex.txt b/assets/smaa/neighborhood_vertex.txt new file mode 100755 index 0000000..7df526e --- /dev/null +++ b/assets/smaa/neighborhood_vertex.txt @@ -0,0 +1,65 @@ +#version 410 compatibility +#define SMAA_PIXEL_SIZE vec2(1.0 / 512.0, 1.0 / 512.0) + +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 + + +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + + +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 + + +#ifndef SMAA_AREATEX_MAX_DISTANCE +#define SMAA_AREATEX_MAX_DISTANCE 16 +#endif +#ifndef SMAA_AREATEX_MAX_DISTANCE_DIAG +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#endif +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / vec2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) + + +/* --- Define section is over ---- */ + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(vec4 position, + out vec4 svPosition, + inout vec2 texcoord, + out vec4 offset[2]) { + svPosition = position; + + offset[0] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4(-1.0, 0.0, 0.0, -1.0); + offset[1] = texcoord.xyxy + SMAA_PIXEL_SIZE.xyxy * vec4( 1.0, 0.0, 0.0, 1.0); +} + +/* ------------- Header is over -------------- */ + + +out vec2 texcoord; +out vec4 offset[2]; +out vec4 dummy2; + +attribute vec3 vPosition; +attribute vec2 vTexCoord; +uniform mat4 ProjectionMatrix; + + +void main() +{ + texcoord = vTexCoord; + vec4 dummy1 = vec4(0); + SMAANeighborhoodBlendingVS(dummy1, dummy2, texcoord, offset); + gl_Position = ProjectionMatrix * vec4(vPosition.xyz, 1.0); +} \ No newline at end of file diff --git a/assets/smaa/smaa_area.raw b/assets/smaa/smaa_area.raw new file mode 100755 index 0000000000000000000000000000000000000000..f5e8575f3315269fb3a5728274fec80f84b5683f GIT binary patch literal 179200 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 literal 0 HcmV?d00001 diff --git a/game/main_code.cpp b/game/main_code.cpp index 3b10050..e01258a 100644 --- a/game/main_code.cpp +++ b/game/main_code.cpp @@ -12,6 +12,16 @@ TMyApplication* Application; +#define AREATEX_WIDTH 160 +#define AREATEX_HEIGHT 560 +#define SEARCHTEX_WIDTH 66 +#define SEARCHTEX_HEIGHT 33 + + +GLuint area_tex; +GLuint search_tex; + + void TMyApplication::InnerInit() { @@ -38,13 +48,20 @@ void TMyApplication::InnerInit() ResourceManager->ShaderManager.AddShader("DefaultShader", "shader1vertex.txt", "shader1fragment.txt"); ResourceManager->ShaderManager.AddShader("FrameShader", "frameshader_vertex.txt", "frameshader_fragment.txt"); ResourceManager->ShaderManager.AddShader("ColorShader", "color_vertex.txt", "color_fragment.txt"); - ResourceManager->ShaderManager.AddShader("SSAA_4X", "SSAA_4X.vertex", "SSAA_4X.frag"); + + + ResourceManager->ShaderManager.AddShader("SmaaEdge", "smaa/edge_vertex.txt", "smaa/edge_fragment.txt"); + ResourceManager->ShaderManager.AddShader("SmaaBlend", "smaa/blend_vertex.txt", "smaa/blend_fragment.txt"); + ResourceManager->ShaderManager.AddShader("SmaaNeighborhood", "smaa/neighborhood_vertex.txt", "smaa/neighborhood_fragment.txt"); + Renderer->PushShader("DefaultShader"); ResourceManager->TexList.AddTexture("console_bkg.bmp"); - ResourceManager->FrameManager.AddFrameRenderBuffer("LevelBuffer", 512, 512); - + ResourceManager->FrameManager.AddFrameRenderBuffer("AlbedoBuffer", 512, 512); + ResourceManager->FrameManager.AddFrameRenderBuffer("EdgeBuffer", 512, 512); + ResourceManager->FrameManager.AddFrameRenderBuffer("BlendBuffer", 512, 512); + pair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(Vector3f(0, 0, 0)); pair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(Vector3f(0, 512, 0)); pair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(Vector3f(512, 512, 0)); @@ -53,13 +70,24 @@ void TMyApplication::InnerInit() pair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(Vector3f(512, 0, 0)); + //Choose colored or black and white -- Vladislav Khorev vladislav.khorev@fishrungames.com +#if 1 pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(1, 0, 0, 1)); pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(1, 0, 0, 1)); pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 1, 0, 1)); pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 0, 1, 1)); pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 0, 1, 1)); pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 1, 0, 1)); +#else + + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(1, 1, 1, 1)); + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(1, 1, 1, 1)); + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(1, 1, 1, 1)); + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 0, 0, 1)); + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 0, 0, 1)); + pair.second.Data.Vec4CoordArr[CONST_STRING_COLOR_ATTRIB].push_back(Vector4f(0, 0, 0, 1)); +#endif pair.first.ShaderName = "ColorShader"; pair.second.RefreshBuffer(); @@ -87,11 +115,64 @@ void TMyApplication::InnerInit() Renderer->SetOrthoProjection(); Renderer->SetFullScreenViewport(); + + glDisable(GL_BLEND); //Don't forget to disable blending or whole thing will not work!!! -- Vladislav Khorev vladislav.khorev@fishrungames.com Inited = true; + + //This is stuff from original https://github.com/scrawl/smaa-opengl + //We must have these textures to be used for pattern matching or something -- Vladislav Khorev vladislav.khorev@fishrungames.com + + unsigned char* buffer = 0; + FILE* f = 0; + + buffer = new unsigned char[1024 * 1024]; + f = fopen((ST::PathToResources + "smaa/smaa_area.raw").c_str(), "rb"); //rb stands for "read binary file" + + if (!f) + { + std::cerr << "Couldn't open smaa_area.raw.\n"; + } + + fread(buffer, AREATEX_WIDTH * AREATEX_HEIGHT * 2, 1, f); + fclose(f); + + f = 0; + + glGenTextures(1, &area_tex); + glBindTexture(GL_TEXTURE_2D, area_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG8, (GLsizei)AREATEX_WIDTH, (GLsizei)AREATEX_HEIGHT, 0, GL_RG, GL_UNSIGNED_BYTE, buffer); + + f = fopen((ST::PathToResources + "smaa/smaa_search.raw").c_str(), "rb"); + + if (!f) + { + std::cerr << "Couldn't open smaa_search.raw.\n"; + } + + fread(buffer, SEARCHTEX_WIDTH * SEARCHTEX_HEIGHT, 1, f); + fclose(f); + + f = 0; + + glGenTextures(1, &search_tex); + glBindTexture(GL_TEXTURE_2D, search_tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, (GLsizei)SEARCHTEX_WIDTH, (GLsizei)SEARCHTEX_HEIGHT, 0, GL_RED, GL_UNSIGNED_BYTE, buffer); + + delete[] buffer; + + } void TMyApplication::InnerDeinit() @@ -103,6 +184,9 @@ void TMyApplication::InnerDeinit() *Console<<"APP DEINIT\n"; } + glDeleteTextures(1, &search_tex); + glDeleteTextures(1, &area_tex); + } void TMyApplication::InnerOnTapDown(Vector2f p) @@ -133,14 +217,17 @@ void TMyApplication::OnFling(Vector2f v) void TMyApplication::InnerDraw() { - Renderer->SwitchToFrameBuffer("LevelBuffer"); + //Render the frame + Renderer->SwitchToFrameBuffer("AlbedoBuffer"); Renderer->SetProjectionMatrix(512.f, 512.f); - glClearColor(0.0f, 0.0f, 1.0f, 1.0f); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); CheckGlError(""); { + //Render here all objects that you want to have -- Vladislav Khorev vladislav.khorev@fishrungames.com TRenderParamsSetter params(pair.first); Renderer->DrawTriangleList(pair.second); } @@ -148,47 +235,104 @@ void TMyApplication::InnerDraw() CheckGlError(""); - Renderer->SwitchToScreen(); - Renderer->SetFullScreenViewport(); + //------------------------- + //First pass - edge detection + + Renderer->PushShader("SmaaEdge"); - const float cos30 = sqrt(3) / 2; - const float sin30 = 0.5f; - const float sampleRadiusX = 0.75 / 512; - const float sampleRadiusY = 0.75 / 512; + Renderer->SwitchToFrameBuffer("EdgeBuffer"); + Renderer->SetProjectionMatrix(512.f, 512.f); - Matrix2f rotate30Matrix; - rotate30Matrix(0, 0) = cos30; - rotate30Matrix(0, 1) = sin30; - rotate30Matrix(1, 0) = -sin30; - rotate30Matrix(1, 1) = cos30; + RenderUniform1i("albedo_tex", 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("AlbedoBuffer")); - Renderer->PushShader("SSAA_4X"); - - - std::array sampleVector = { - Vector2f(sampleRadiusX, sampleRadiusY), - Vector2f(-sampleRadiusX, sampleRadiusY), - Vector2f(-sampleRadiusX, -sampleRadiusY), - Vector2f(sampleRadiusX, -sampleRadiusY) - }; - - for (size_t i = 0; i < 4; i++) - { - sampleVector[i] = rotate30Matrix * sampleVector[i]; - - RenderUniform2fv("samplesOffset[" + boost::lexical_cast(i) + "]", &sampleVector[i][0]); - } - - - glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("LevelBuffer")); Renderer->DrawTriangleList(rect.second); Renderer->PopShader(); CheckGlError(""); + + //------------------------- + //Second pass - blend plus pattern matching + + Renderer->PushShader("SmaaBlend"); + + Renderer->SwitchToFrameBuffer("BlendBuffer"); + Renderer->SetProjectionMatrix(512.f, 512.f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + RenderUniform1i("edge_tex", 0); + RenderUniform1i("area_tex", 1); + RenderUniform1i("search_tex", 2); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("EdgeBuffer")); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, area_tex); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, search_tex); + + Renderer->DrawTriangleList(rect.second); + + Renderer->PopShader(); + + CheckGlError(""); + +#if 1 + //------------------------- + //Last pass - neightborhood + + Renderer->PushShader("SmaaNeighborhood"); + + Renderer->SwitchToScreen(); + Renderer->SetFullScreenViewport(); + Renderer->SetProjectionMatrix(512.f, 512.f); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + RenderUniform1i("albedo_tex", 0); + RenderUniform1i("blend_tex", 1); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("BlendBuffer")); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("AlbedoBuffer")); + Renderer->DrawTriangleList(rect.second); + + Renderer->PopShader(); + + CheckGlError(""); +#else + + + //... or just render intermediate buffers to see what is going on + Renderer->PushShader("DefaultShader"); + Renderer->SwitchToScreen(); + Renderer->SetFullScreenViewport(); + Renderer->SetProjectionMatrix(512.f, 512.f); + + + glActiveTexture(GL_TEXTURE0); + + //Choose any to render for debug: + //glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("AlbedoBuffer")); + //glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("EdgeBuffer")); + glBindTexture(GL_TEXTURE_2D, ResourceManager->FrameManager.GetFrameTexture("BlendBuffer")); + + Renderer->DrawTriangleList(rect.second); + + Renderer->PopShader(); + + CheckGlError(""); +#endif + }