From a3813bda8dac4915d2ba59de8efef8051406f554 Mon Sep 17 00:00:00 2001 From: Frederic Bouvier Date: Mon, 25 Oct 2010 22:37:05 +0200 Subject: [PATCH] Urban shader using Quadtree Displacement Mapping --- Effects/urban.eff | 31 ++++++++++-- Shaders/urban.frag | 116 +++++++++++++++++++++++++++++++++------------ 2 files changed, 114 insertions(+), 33 deletions(-) diff --git a/Effects/urban.eff b/Effects/urban.eff index f8eecd141..d73d5b2ea 100644 --- a/Effects/urban.eff +++ b/Effects/urban.eff @@ -7,6 +7,7 @@ 0.008 0.75 0.59 0.05 /sim/rendering/quality-level + 10 15 @@ -68,12 +69,24 @@ texture[2]/filter texture[2]/wrap-s texture[2]/wrap-t - - texture[2]/internal-format - + texture[2]/internal-format 2 + texture[2]/image + nearest-mipmap-nearest + texture[2]/wrap-s + texture[2]/wrap-t + texture[2]/internal-format + + average + average + average + min + + + + 3 noise @@ -102,10 +115,15 @@ sampler-2d 1 + + QDMTex + sampler-2d + 2 + NoiseTex sampler-3d - 2 + 3 depth_factor @@ -132,6 +150,11 @@ float snow-level + + max_lod_level + float + max-lod-level + diff --git a/Shaders/urban.frag b/Shaders/urban.frag index 7e207cf44..f61faa35a 100644 --- a/Shaders/urban.frag +++ b/Shaders/urban.frag @@ -2,9 +2,15 @@ // Licence: GPL v2 // Author: Frederic Bouvier. // Adapted from the paper by F. Policarpo et al. : Real-time Relief Mapping on Arbitrary Polygonal Surfaces +// Adapted from the paper and sources by M. Drobot in GPU Pro : Quadtree Displacement Mapping with Height Blending #version 120 +#define TEXTURE_MIP_LEVELS 10 +#define TEXTURE_PIX_COUNT 1024 //pow(2,TEXTURE_MIP_LEVELS) +#define BINARY_SEARCH_COUNT 10 +#define BILINEAR_SMOOTH_FACTOR 2.0 + varying vec4 rawpos; varying vec4 ecPosition; varying vec3 VNormal; @@ -16,6 +22,7 @@ varying vec4 constantColor; uniform sampler3D NoiseTex; uniform sampler2D BaseTex; uniform sampler2D NormalTex; +uniform sampler2D QDMTex; uniform float depth_factor; uniform float tile_size; uniform float quality_level; // From /sim/rendering/quality-level @@ -23,52 +30,103 @@ uniform float snowlevel; // From /sim/rendering/snow-level-m uniform vec3 night_color; const float scale = 1.0; -int linear_search_steps = 10; +int GlobalIterationCount = 0; +int gIterationCap = 64; -float ray_intersect(sampler2D reliefMap, vec2 dp, vec2 ds) +void QDM(inout vec3 p, inout vec3 v) { - float size = 1.0 / float(linear_search_steps); - float depth = 0.0; - float best_depth = 1.0; + const int MAX_LEVEL = TEXTURE_MIP_LEVELS; + const float NODE_COUNT = TEXTURE_PIX_COUNT; + const float TEXEL_SPAN_HALF = 1.0 / NODE_COUNT / 2.0; - for(int i = 0; i < linear_search_steps - 1; ++i) + float fDeltaNC = TEXEL_SPAN_HALF * depth_factor; + + vec3 p2 = p; + float level = MAX_LEVEL; + vec2 dirSign = (sign(v.xy) + 1.0) * 0.5; + GlobalIterationCount = 0; + float d = 0; + + while (level >= 0 && GlobalIterationCount < gIterationCap) { - depth += size; - float t = step(0.95, texture2D(reliefMap, dp + ds * depth).a); - if(best_depth > 0.996) - if(depth >= t) - best_depth = depth; - } - depth = best_depth; + vec4 uv = vec4(p2.xyz, level); + d = texture2DLod(QDMTex, uv.xy, uv.w).w; - const int binary_search_steps = 5; - - for(int i = 0; i < binary_search_steps; ++i) - { - size *= 0.5; - float t = step(0.95, texture2D(reliefMap, dp + ds * depth).a); - if(depth >= t) + if (d > p2.z) { - best_depth = depth; - depth -= 2.0 * size; + //predictive point of ray traversal + vec3 tmpP2 = p + v * d; + + //current node count + float nodeCount = pow(2.0, (MAX_LEVEL - level)); + //current and predictive node ID + vec4 nodeID = floor(vec4(p2.xy, tmpP2.xy)*nodeCount); + + //check if we are crossing the current cell + if (nodeID.x != nodeID.z || nodeID.y != nodeID.w) + { + //calculate distance to nearest bound + vec2 a = p2.xy - p.xy; + vec2 p3 = (nodeID.xy + dirSign) / nodeCount; + vec2 b = p3.xy - p.xy; + + vec2 dNC = (b.xy * p2.z) / a.xy; + //take the nearest cell + d = min(d,min(dNC.x, dNC.y))+fDeltaNC; + + level++; + + //use additional convergence speed-up + #ifdef USE_QDM_ASCEND_INTERVAL + if(frac(level*0.5) > EPSILON) + level++; + #elseif USE_QDM_ASCEND_CONST + level++; + #endif + } + p2 = p + v * d; } - depth += size; + level--; + GlobalIterationCount++; } - return(best_depth); + // + // Manual Bilinear filtering + // + float rayLength = length(p2.xy - p.xy) + fDeltaNC; + + float dA = p2.z * (rayLength - BILINEAR_SMOOTH_FACTOR * TEXEL_SPAN_HALF) / rayLength; + float dB = p2.z * (rayLength + BILINEAR_SMOOTH_FACTOR * TEXEL_SPAN_HALF) / rayLength; + + vec4 p2a = vec4(p + v * dA, 0); + vec4 p2b = vec4(p + v * dB, 0); + dA = texture2DLod(NormalTex, p2a.xy, p2a.w).w; + dB = texture2DLod(NormalTex, p2b.xy, p2b.w).w; + + dA = abs(p2a.z - dA); + dB = abs(p2b.z - dB); + + p2 = mix(p2a.xyz, p2b.xyz, dA / (dA + dB)); + + p = p2; +} + +float ray_intersect(vec2 dp, vec2 ds) +{ + vec3 p = vec3( dp, 0.0 ); + vec3 v = vec3( ds, 1.0 ); + QDM( p, v ); + return p.z; } void main (void) { - if ( quality_level >= 3.5 ) { - linear_search_steps = 20; - } vec3 ecPos3 = ecPosition.xyz / ecPosition.w; vec3 V = normalize(ecPos3); vec3 s = vec3(dot(V, VTangent), dot(V, VBinormal), dot(VNormal, -V)); vec2 ds = s.xy * depth_factor / s.z; vec2 dp = gl_TexCoord[0].st - ds; - float d = ray_intersect(NormalTex, dp, ds); + float d = ray_intersect(dp, ds); vec2 uv = dp + ds * d; vec3 N = texture2D(NormalTex, uv).xyz * 2.0 - 1.0; @@ -89,7 +147,7 @@ void main (void) vec3 sl = normalize( vec3( dot( l, VTangent ), dot( l, VBinormal ), dot( -l, VNormal ) ) ); ds = sl.xy * depth_factor / sl.z; dp -= ds * d; - float dl = ray_intersect(NormalTex, dp, ds); + float dl = ray_intersect(dp, ds); if ( dl < d - 0.05 ) shadow_factor = dot( constantColor.xyz, vec3( 1.0, 1.0, 1.0 ) ) * 0.25; }