diff --git a/src/GLWindow.jl b/src/GLWindow.jl index fc26a3f..718a878 100755 --- a/src/GLWindow.jl +++ b/src/GLWindow.jl @@ -51,6 +51,8 @@ export screenbuffer export zeroposition export create_glcontext export renderlist - +export add_oit_fxaa_postprocessing! +export destroy! +export robj_from_camera end diff --git a/src/callbacks.jl b/src/callbacks.jl index 41090da..dd9a81c 100644 --- a/src/callbacks.jl +++ b/src/callbacks.jl @@ -74,7 +74,7 @@ returns `Signal{Vector{Compat.UTF8String}}`, which are absolute file paths """ function dropped_files(window, s::Signal{Vector{Compat.UTF8String}}=Signal(Compat.UTF8String[])) GLFW.SetDropCallback(window, (window, files) -> begin - push!(s, map(utf8, files)) + push!(s, map(Compat.String, files)) end) s end @@ -147,7 +147,10 @@ Takes a screen and registers a list of callback functions. Returns a dict{Symbol, Signal}(name_of_callback => signal) """ function register_callbacks(window::GLFW.Window, callbacks::Vector{Function}) - Dict{Symbol, Any}([Symbol(last(split(string(f),"."))) => f(window) for f in callbacks]) + tmp = map(callbacks) do f + (Symbol(last(split(string(f),"."))), f(window)) + end + Dict{Symbol, Any}(tmp) end function register_callbacks(window::Screen, callbacks::Vector{Function}) register_callbacks(window.nativewindow, callbacks) diff --git a/src/core.jl b/src/core.jl index ef5b1fe..e8cc350 100644 --- a/src/core.jl +++ b/src/core.jl @@ -16,10 +16,17 @@ function Base.show(io::IO, m::Screen) end end -GeometryTypes.isinside{T}(x::Screen, position::Vec{2, T}) = - !any(screen->isinside(screen.area.value, position...), x.children) && isinside(x.area.value, position...) - -GeometryTypes.isinside(screen::Screen, point) = isinside(screen.area.value, point...) +""" +mouse position is in coorditnates relative to `screen` +""" +function GeometryTypes.isinside(screen::Screen, mpos) + isinside(zeroposition(value(screen.area)), mpos...) || return false + for s in screen.children + # if inside any children, it's not inside screen + isinside(value(s.area), mpos...) && return false + end + true +end """ Args: `screens_mpos` -> Tuple{Vector{Screen}, Vec{2,T}} diff --git a/src/events.jl b/src/events.jl index 7c912d2..0909e2e 100644 --- a/src/events.jl +++ b/src/events.jl @@ -102,15 +102,17 @@ begin global push_selectionqueries! const selection_data = Array(SelectionID{UInt16}, 1, 1) -const old_mouse_position = Array(Vec{2, Float64}, 1) +const old_mouse_position = Ref{Vec{2, Float64}}() function push_selectionqueries!(screen) - mouse_position = value(mouseposition(screen)) + mouse_position = value(mouseposition(screen)) selection_signal = mouse2id(screen) - window_size = widths(screen) - buff = framebuffer(screen).objectid + window_size = widths(screen) + framebuffer = screen.renderpasses[1].target # first renderpass + # fourth buffer is the id picking buffer. [1] because framebuffer[4] == (Texture, Attachment) + buff = framebuffer[4][1] if old_mouse_position[] != mouse_position - glReadBuffer(GL_COLOR_ATTACHMENT1) + glReadBuffer(GL_COLOR_ATTACHMENT3) x,y = Vec{2, Int}(map(floor, mouse_position)) w,h = window_size if x > 0 && y > 0 && x <= w && y <= h diff --git a/src/fxaa.vert b/src/fullscreen.vert similarity index 100% rename from src/fxaa.vert rename to src/fullscreen.vert diff --git a/src/fxaa.frag b/src/fxaa.frag new file mode 100644 index 0000000..1153989 --- /dev/null +++ b/src/fxaa.frag @@ -0,0 +1,1049 @@ +{{GLSL_VERSION}} + +#define FXAA_PC 1 +#define FXAA_GLSL_130 1 +#define FXAA_QUALITY__PRESET 39 +#define FXAA_GREEN_AS_LUMA 1 +#define FXAA_GATHER4_ALPHA 0 + +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 +#endif +/*==========================================================================*/ +#ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif +#endif +/*--------------------------------------------------------------------------*/ +#ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif +#endif + + +/*============================================================================ + FXAA QUALITY - TUNING KNOBS +------------------------------------------------------------------------------ +NOTE the other tuning knobs are now in the shader function inputs! +============================================================================*/ +#ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 +#endif + + +/*============================================================================ + + FXAA QUALITY - PRESETS + +============================================================================*/ + +/*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#endif + +/*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +/*============================================================================ + FXAA QUALITY - EXTREME QUALITY +============================================================================*/ +#if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + + + +/*============================================================================ + + API PORTING + +============================================================================*/ +#if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D +#else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 half3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ +#if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif +#endif +/*--------------------------------------------------------------------------*/ + +/*--------------------------------------------------------------------------*/ +#if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) +#endif + + +/*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION +============================================================================*/ +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + + + + +/*============================================================================ + + FXAA3 QUALITY - PC + +============================================================================*/ +#if (FXAA_PC == 1) +/*--------------------------------------------------------------------------*/ +FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex fxaaConsole360TexExpBiasNegOne, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex fxaaConsole360TexExpBiasNegTwo, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat fxaaConsoleEdgeSharpness, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat fxaaConsoleEdgeThreshold, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaConsoleEdgeThresholdMin, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 fxaaConsole360ConstDir +) { +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} +/*==========================================================================*/ +#endif + + + + +//---------------------------------------------------------------------------------- +// File: es3-kepler/FXAA/assets/shaders/FXAA_Extreme_Quality.frag +// SDK Version: v2.11 +// Email: gameworks@nvidia.com +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//---------------------------------------------------------------------------------- + +precision highp float; + +uniform sampler2D color_texture; +uniform vec2 RCPFrame; +in vec2 frag_uv; + +out vec4 fragment_color; + +void main(void) +{ + fragment_color = FxaaPixelShader(frag_uv, + FxaaFloat4(0.0f, 0.0f, 0.0f, 0.0f), // FxaaFloat4 fxaaConsolePosPos, + color_texture, // FxaaTex tex, + color_texture, // FxaaTex fxaaConsole360TexExpBiasNegOne, + color_texture, // FxaaTex fxaaConsole360TexExpBiasNegTwo, + RCPFrame, // FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4(0.0f, 0.0f, 0.0f, 0.0f), // FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4(0.0f, 0.0f, 0.0f, 0.0f), // FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4(0.0f, 0.0f, 0.0f, 0.0f), // FxaaFloat4 fxaaConsole360RcpFrameOpt2, + 0.75f, // FxaaFloat fxaaQualitySubpix, + 0.166f, // FxaaFloat fxaaQualityEdgeThreshold, + 0.0833f, // FxaaFloat fxaaQualityEdgeThresholdMin, + 0.0f, // FxaaFloat fxaaConsoleEdgeSharpness, + 0.0f, // FxaaFloat fxaaConsoleEdgeThreshold, + 0.0f, // FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4(0.0f, 0.0f, 0.0f, 0.0f) // FxaaFloat fxaaConsole360ConstDir, + ); + // fragment_color = texture(color_texture, frag_uv); +} diff --git a/src/fxaa_combine.frag b/src/fxaa_combine.frag deleted file mode 100644 index e951e98..0000000 --- a/src/fxaa_combine.frag +++ /dev/null @@ -1,11 +0,0 @@ -{{GLSL_VERSION}} - -in vec2 frag_uv; - -uniform sampler2D color_texture; - -out vec4 fragment_color; - -void main(){ - fragment_color = texture(color_texture, frag_uv); -} diff --git a/src/oit_combine.frag b/src/oit_combine.frag new file mode 100644 index 0000000..fa7cde3 --- /dev/null +++ b/src/oit_combine.frag @@ -0,0 +1,44 @@ +{{GLSL_VERSION}} + +in vec2 frag_uv; + +uniform sampler2D sum_color_tex; +uniform sampler2D sum_weight_tex; +uniform sampler2D opaque_color_tex; + +layout(location=0) out vec4 fragment_color; + +vec3 linear_tone_mapping(vec3 color, float gamma) +{ + color = clamp(color, 0., 1.); + color = pow(color, vec3(1. / gamma)); + return color; +} + +vec4 resolve_color( + sampler2D sum_color_tex, + sampler2D sum_weight_tex, + sampler2D opaque_color, + vec2 frag_uv + ){ + float transmittance = texture(sum_weight_tex, frag_uv).r; + vec4 color = texture(opaque_color, frag_uv).rgba; + vec4 sum_color = texture(sum_color_tex, frag_uv); + vec3 average_color = sum_color.rgb / max(sum_color.a, 0.00001); + return vec4( + average_color * (1 - transmittance) + (color.rgb*color.a) * transmittance, + transmittance + ); +} + +void main(void) +{ + // resolve transparency + vec4 opaque = resolve_color(sum_color_tex, sum_weight_tex, opaque_color_tex, frag_uv); + + // do tonemapping + //opaque = linear_tone_mapping(opaque, 2.0); // linear color output + fragment_color.rgb = opaque.rgb; // gamma 2.0 color output + // save luma in alpha for FXAA + fragment_color.a = dot(opaque.rgb, vec3(0.299, 0.587, 0.114)); // compute luma +} diff --git a/src/render.jl b/src/render.jl index 5be80a4..c84ca76 100644 --- a/src/render.jl +++ b/src/render.jl @@ -1,4 +1,8 @@ + +""" +Clears everything in a window +""" function clear_all!(window) wh = widths(window) glViewport(0,0, wh...) @@ -10,79 +14,166 @@ function clear_all!(window) glClear(GL_COLOR_BUFFER_BIT) end +""" +Renders a `RenderPass` +""" +function GLAbstraction.render(rp::RenderPass) + bind(rp.target) + # bind the framebuffer that is targeted and set it as draw buffer + drawbuffers(rp.target) + # render the pass + render(rp.pass) +end + +function opaque_setup() + glDisable(GL_CULL_FACE) + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDisable(GL_BLEND) +end + +function oit_setup() + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_FALSE) + glDepthFunc(GL_LEQUAL) + zero_clear = Float32[0,0,0,0] + one_clear = Float32[1,1,1,1] + glClearBufferfv(GL_COLOR, 1, zero_clear) + glClearBufferfv(GL_COLOR, 2, one_clear) + glEnable(GL_BLEND) + glBlendEquation(GL_FUNC_ADD) + glBlendFunci(1, GL_ONE, GL_ONE) + glBlendFunci(2, GL_ZERO, GL_ONE_MINUS_SRC_COLOR) +end + + + + """ Renders a single frame of a `window` """ function render_frame(window) - fb = framebuffer(window) + isopen(window) || return wh = widths(window) + opaque_pass, tansp_pass, color_pass, fxaa_pass = window.renderpasses + + ot_fb = opaque_pass.target # opaque and transparent share the same framebuffer + bind(ot_fb) + resize!(ot_fb, wh) + glDisable(GL_SCISSOR_TEST) + glViewport(0,0, wh...) + drawbuffers(ot_fb, [1,4]) + glClearBufferfv(GL_DEPTH, 0, Float32[1]) + glClearBufferfv(GL_COLOR, 3, Float32[0,0,0,0]) # clear the hit detection buffer - resize!(fb, wh) - prepare(fb) + # render the pass + opaque_setup() + + render_opaque(window) + + glDisable(GL_SCISSOR_TEST) glViewport(0,0, wh...) - render(window) + drawbuffers(ot_fb, [2,3,4]) + oit_setup() + render_transparent(window) + + # while rendering windows, scissor test is on and viewport will be changed + # to the children windows... So we need to revert this + glDisable(GL_SCISSOR_TEST) + glViewport(0,0, wh...) #Read all the selection queries push_selectionqueries!(window) - display(fb, window) + # resolve colors + resize!(color_pass.target, wh) + render(color_pass) - swapbuffers(window) + # do anti aliasing and write to final color framebuffer + render(fxaa_pass) + + # swap buffers and poll GLFW events + GLFW.PollEvents() yield() + #GLFW.WaitEvents() + #@threadcall((:glfwWaitEvents, GLFW.lib), Void, ()) + Reactive.run_timer() + Reactive.run_till_now() + #Reactive.run_till_now() # execute secondary cycled events! + swapbuffers(window) + nothing end -function pollwindow(window) - while isopen(window) - GLFW.PollEvents() - yield() - sleep(1/60) - end -end + + +# nanoseconds between each frame +const _ns_per_frame = 1e9 / 60 + """ Blocking renderloop """ function renderloop(window::Screen) while isopen(window) + nw = time_ns() render_frame(window) - GLFW.PollEvents() + # if we've gone too fast, sleep for the remaining time + ns_diff = time_ns() - nw + while ns_diff < _ns_per_frame + ns_diff = time_ns() - nw + sleep(0.001) # sleep the minimal amount possible + end end - empty!(window) - yield() - GLFW.DestroyWindow(nativewindow(window)) + destroy!(window) end -function prepare(fb::GLFramebuffer) - glDisable(GL_SCISSOR_TEST) - glBindFramebuffer(GL_FRAMEBUFFER, fb.id) - glDrawBuffers(2, [GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1]) -end -function display(fb::GLFramebuffer, window) - glDisable(GL_SCISSOR_TEST) - glBindFramebuffer(GL_FRAMEBUFFER, 0) - glViewport(0,0, widths(window)...) - render(fb.postprocess) +function render_transparent(x::Screen, parent::Screen=x, context=x.area.value) + if isopen(x) && !ishidden(x) + sa = value(x.area) + sa = SimpleRectangle(context.x+sa.x, context.y+sa.y, sa.w, sa.h) # bring back to absolute values + pa = context + sa_pa = intersect(pa, sa) # intersection with parent + if sa_pa != SimpleRectangle{Int}(0,0,0,0) # if it is in the parent area + glEnable(GL_SCISSOR_TEST) + glScissor(sa_pa) + glViewport(sa) + c = Float32[red(x.color), green(x.color), blue(x.color), alpha(x.color)] + glClearBufferfv(GL_DEPTH, 0, Float32[1]) + glClearBufferfv(GL_COLOR, 0, c) + for i in x.transparent + elem = x.renderlist[i] + elem[:is_transparent_pass] = Cint(true) + render(elem) + end + for window in x.children + render_transparent(window, x, sa) + end + end + end end -function GLAbstraction.render(x::Screen, parent::Screen=x, context=x.area.value) + +function render_opaque(x::Screen, parent::Screen=x, context=x.area.value) if isopen(x) && !ishidden(x) sa = value(x.area) sa = SimpleRectangle(context.x+sa.x, context.y+sa.y, sa.w, sa.h) # bring back to absolute values pa = context sa_pa = intersect(pa, sa) # intersection with parent - if sa_pa != SimpleRectangle{Int}(0,0,0,0) # if it is in the parent area + if ( + sa_pa != SimpleRectangle{Int}(0,0,0,0) && # if it is in the parent area + (sa_pa.w > 0 && sa_pa.h > 0) + ) glEnable(GL_SCISSOR_TEST) glScissor(sa_pa) glViewport(sa) - colorbits = GL_DEPTH_BUFFER_BIT - if alpha(x.color) > 0 - glClearColor(red(x.color), green(x.color), blue(x.color), alpha(x.color)) - colorbits = colorbits | GL_COLOR_BUFFER_BIT + c = Float32[red(x.color), green(x.color), blue(x.color), alpha(x.color)] + glClearBufferfv(GL_DEPTH, 0, Float32[1]) + glClearBufferfv(GL_COLOR, 0, c) + for elem in x.renderlist + elem[:is_transparent_pass] = Cint(false) + render(elem) end - glClear(colorbits) - - render(x.renderlist) for window in x.children - render(window, x, sa) + render_opaque(window, x, sa) end end end diff --git a/src/screen.jl b/src/screen.jl index 2110f9c..e9d3af1 100644 --- a/src/screen.jl +++ b/src/screen.jl @@ -13,7 +13,7 @@ function openglerrorcallback( | | OpenGL Error! | source: $(GLENUM(source).name) :: type: $(GLENUM(typ).name) - | $(ascii(bytestring(message, length))) + | $(ascii(Compat.String(message, length))) |________________________________________________________________ """ output = typ == GL_DEBUG_TYPE_ERROR ? error : info @@ -27,6 +27,7 @@ global const _openglerrorcallback = cfunction( ) + """ Screen constructor cnstructing a new screen from a parant screen. """ @@ -35,51 +36,33 @@ function Screen( name = gensym(parent.name), area = parent.area, children::Vector{Screen} = Screen[], - inputs::Dict{Symbol, Any} = parent.inputs, - renderlist::Tuple = (), + inputs::Dict{Symbol, Any} = copy(parent.inputs), + renderlist::Vector = RenderObject[], hidden::Bool = parent.hidden, glcontext::GLContext = parent.glcontext, + cameras = Dict{Symbol, Any}(), position = Vec3f0(2), lookat = Vec3f0(0), color = RGBA{Float32}(1,1,1,1) ) + screen = Screen(name, + area, parent, children, inputs, + renderlist, hidden, color, + cameras, glcontext + ) pintersect = const_lift(x->intersect(zeroposition(value(parent.area)), x), area) relative_mousepos = const_lift(inputs[:mouseposition]) do mpos Point{2, Float64}(mpos[1]-value(pintersect).x, mpos[2]-value(pintersect).y) end #checks if mouse is inside screen and not inside any children - - insidescreen = droprepeats(const_lift(relative_mousepos) do mpos - for screen in children - # if inside any children, it's not inside screen - isinside(value(screen.area), mpos...) && return false - end - (mpos[1] < 0 || mpos[2] < 0) && return false - mpos[1] > value(pintersect).w && return false - mpos[2] > value(pintersect).h && return false - true - end) - # creates signals for the camera, which are only active if mouse is inside screen - camera_input = merge(inputs, Dict( - :mouseposition => filterwhen(insidescreen, Vec(0.0, 0.0), relative_mousepos), - :scroll => filterwhen(insidescreen, 0.0, inputs[:scroll]), - :window_area => area - )) - new_input = merge(inputs, Dict( + insidescreen = droprepeats(const_lift(isinside, screen, relative_mousepos)) + merge!(screen.inputs, Dict( :mouseinside => insidescreen, :mouseposition => relative_mousepos, - :scroll => inputs[:scroll], :window_area => area )) # creates cameras for the sceen with the new inputs - ocamera = OrthographicPixelCamera(camera_input) - pcamera = PerspectiveCamera(camera_input, position, lookat) - screen = Screen(name, - area, parent, children, new_input, - renderlist, hidden, color, - Dict{Symbol, Any}(:perspective=>pcamera, :orthographic_pixel=>ocamera), - glcontext - ) + push!(parent.children, screen) screen end @@ -199,7 +182,7 @@ function create_glcontext( GLFW.WindowHint(wh...) end - @osx_only begin + @static if is_apple() if debugging warn("OpenGL debug message callback not available on osx") debugging = false @@ -207,7 +190,7 @@ function create_glcontext( end GLFW.WindowHint(GLFW.OPENGL_DEBUG_CONTEXT, Cint(debugging)) - window = GLFW.CreateWindow(resolution..., utf8(name)) + window = GLFW.CreateWindow(resolution..., Compat.String(name)) GLFW.MakeContextCurrent(window) debugging && glDebugMessageCallbackARB(_openglerrorcallback, C_NULL) @@ -267,19 +250,17 @@ function Screen(name = "GLWindow"; signal_dict[:mouse2id] = Signal(SelectionID{Int}(-1, -1)) # TODO: free when context is freed. We don't have a good abstraction of a gl context yet, though # (It could be shared, so it does not map directly to one screen) - preserve(map(signal_dict[:window_open]) do open - if !open - GLAbstraction.empty_shader_cache!() - end - nothing - end) - screen = Screen(Symbol(name), window_area, Screen[], signal_dict, - (), false, color, + RenderObject[], false, color, Dict{Symbol, Any}(), - GLContext(window, GLFramebuffer(framebuffer_size)) + GLContext(window) ) + screen.inputs[:mouseinside] = droprepeats( + const_lift(isinside, screen, screen.inputs[:mouseposition]) + ) + GLFW.SwapInterval(0) # deactivating vsync seems to make everything quite a bit smoother + Reactive.stop() screen end @@ -288,8 +269,9 @@ Function that creates a screenshot from `window` and saves it to `path`. You can choose the channel of the framebuffer, which is usually: `color`, `depth` and `objectid` """ -screenshot(window; path="screenshot.png", channel=:color) = - save(path, screenbuffer(window, channel), true) +function screenshot(window; path="screenshot.png", channel=:color) + save(path, screenbuffer(window, channel), true) +end """ Returns the contents of the framebuffer of `window` as a Julia Array. @@ -297,11 +279,20 @@ You can choose the channel of the framebuffer, which is usually: `color`, `depth` and `objectid` """ function screenbuffer(window, channel=:color) - fb = framebuffer(window) - channels = fieldnames(fb)[2:end] - if channel in channels - img = gpu_data(getfield(fb, channel))[window.area.value] + if channel == :color + glBindFramebuffer(GL_FRAMEBUFFER, 0) + a = window.area.value + img = Array(BGR{U8}, a.w, a.h) + glReadPixels(a.x-1, a.y-1, a.w, a.h, GL_BGR, GL_UNSIGNED_BYTE, img) return rotl90(img) + elseif channel == :id + opaque_pass, tansp_pass, color_pass, fxaa_pass = window.renderpasses + id_buff = gpu_data(opaque_pass.target.attachments[4][1]) + maxi = maximum(id_buff) + return map(id_buff) do id + x = id ./ maxi + RGB{U8}(x[1], 0, x[2]) + end end error("Channel $channel does not exist. Only these channels are available: $channels") end @@ -310,8 +301,7 @@ end widths(s::Screen) = widths(value(s.area)) ishidden(s::Screen) = s.hidden -framebuffer(s::Screen) = s.glcontext.framebuffer -nativewindow(s::Screen) = s.glcontext.window +nativewindow(s::Screen) = s.glcontext.context """ Check if a Screen is opened. @@ -320,11 +310,18 @@ function Base.isopen(window::Screen) isopen(nativewindow(window)) end function Base.isopen(window::GLFW.Window) + window.handle == C_NULL && return false !GLFW.WindowShouldClose(window) end """ Swap the framebuffers on the Screen. """ +function makecontextcurrent(window::Screen) + GLFW.MakeContextCurrent(nativewindow(window)) +end +""" +Swap the framebuffers on the Screen. +""" function swapbuffers(window::Screen) swapbuffers(nativewindow(window)) end @@ -349,12 +346,70 @@ end Empties the content of the renderlist """ function Base.empty!(s::Screen) - s.renderlist = () + empty!(s.renderlist) + empty!(s.opaque) + empty!(s.transparent) + for c in s.children + empty!(c) + end + empty!(s.children) + nothing end """ returns a copy of the renderlist """ function GLAbstraction.renderlist(s::Screen) - vcat(s.renderlist...) + copy(s.renderlist) +end +function destroy!(screen::Screen) + empty!(screen) + nw = nativewindow(screen) + if nw.handle != C_NULL + GLFW.DestroyWindow(nw) + nw.handle = C_NULL + end +end +get_id(x::Integer) = x +get_id(x::RenderObject) = x.id +function delete_robj!(list, robj) + for (i, id) in enumerate(list) + if get_id(id) == robj.id + splice!(list, i) + return true, i + end + end + false, 0 +end +function delete_index(list, index) + newlist = Int[] + for (i, ref_i) in enumerate(list) + if ref_i < i + push!(newlist, ref_i) + elseif ref_i > index + push!(newlist, ref_i-1) + else + continue # skipp == + end + end + newlist +end +function Base.delete!(screen::Screen, robj::RenderObject) + deleted, i = delete_robj!(screen.renderlist, robj) + if deleted + for (k,v) in screen.camera2robj + delete_robj!(v, robj) + end + screen.transparent = delete_index(screen.transparent, i) + screen.opaque = delete_index(screen.opaque, i) + end + deleted +end + +function GLAbstraction.robj_from_camera(window, camera) + robj_list = get(window.camera2robj, camera, ()) + isempty(robj_list) && return RenderObject[] + return filter(window.renderlist) do robj + robj.id in robj_list + end end diff --git a/src/types.jl b/src/types.jl index 4272a1f..2efaed1 100644 --- a/src/types.jl +++ b/src/types.jl @@ -1,73 +1,239 @@ +immutable GLContext + context +end -type GLFramebuffer - id ::GLuint - color ::Texture{RGBA{UFixed8}, 2} - objectid ::Texture{Vec{2, GLushort}, 2} - depth ::Texture{Float32, 2} - postprocess::RenderObject +type FrameBuffer + id::GLuint + attachments::Vector + color_attachments::Vector{Int} # indices to color attachments + context::GLContext +end +Base.length(fb::FrameBuffer) = length(fb.attachments) +Base.start(fb::FrameBuffer) = 1 +Base.next(fb::FrameBuffer, state::Integer) = fb[state], state+1 +Base.done(fb::FrameBuffer, state::Integer) = length(fb) < state + +# used to bind GL_FRAMEBUFFER to 0 +function Base.bind(fb::FrameBuffer, id) + glBindFramebuffer(GL_FRAMEBUFFER, id) +end +function Base.bind(fb::FrameBuffer) + bind(fb, fb.id) +end +function drawbuffers(fb::FrameBuffer, bufferset=fb.color_attachments) + if !isempty(bufferset) + n = length(fb.attachments) + buffers = GLenum[i in bufferset ? fb.attachments[i][2] : GL_NONE for i=1:n] + glDrawBuffers(n, buffers) + end + nothing +end +function Base.getindex(fb::FrameBuffer, i::Integer) + fb.attachments[i] +end +function Base.setindex!(fb::FrameBuffer, attachment::Tuple{Texture, GLenum}, i::Integer) + bind(fb) + fb.attachments[i] = attachment + attach_framebuffer(attachment...) + bind(fb, 0) +end +function Base.size(fb::FrameBuffer) + if isempty(fb.attachments) + # no attachments implies, that the context holds the attachments and size + return size(fb.context) + else + size(first(fb)[1]) # it's guaranteed, that they all have the same size + end +end +function attach_framebuffer{T}(t::Texture{T, 2}, attachmentpoint) + glFramebufferTexture2D(GL_FRAMEBUFFER, + attachmentpoint, + t.texturetype, t.id, 0 + ) +end +""" +Creates a framebuffer from `targets`, which is a `Vector` of tuples containing +a texture with it's attachment point, e.g. `(Texture, GL_COLOR_ATTACHMENT0)` +`Context` is the context the texture is created from. +""" +function FrameBuffer(targets::Vector, context) + color_attachments = Int[] + fb = FrameBuffer( + glGenFramebuffers(), + targets, color_attachments, context + ) + bind(fb) + i = 1 + for (target, attachmentpoint) in targets + attach_framebuffer(target, attachmentpoint) + if attachmentpoint != GL_DEPTH_ATTACHMENT + push!(color_attachments, i) + end + i+=1 + end + bind(fb, 0) + fb +end +function Base.resize!(fb::FrameBuffer, window_size) + ws = tuple(window_size...) + if ws!=size(fb) && all(x->x>0, window_size) + @static if false# hacky workaround for linux driver bug (more specificall Intel!?) + fb.id = glGenFramebuffers() + bind(fb) + for (i, (attachment, attachmentpoint)) in enumerate(fb) + new_attachment = similar(attachment) + attach_framebuffer(new_attachment, attachmentpoint) + fb.attachments[i] = (attachment, attachmentpoint) + end + bind(fb, 0) + else + for (attachment, _) in fb + resize_nocopy!(attachment, ws) + end + end + end + nothing end -Base.size(fb::GLFramebuffer) = size(fb.color) # it's guaranteed, that they all have the same size -loadshader(name) = load(joinpath(dirname(@__FILE__), name)) -function draw_fullscreen(vao_id) - glBindVertexArray(vao_id) + + +immutable RenderPass + pass # any X that has render(::X) defined + target::FrameBuffer +end + + +immutable FullscreenPreRender end +@compat function (::FullscreenPreRender)() + glDisable(GL_DEPTH_TEST) + glDisable(GL_STENCIL_TEST) + glDisable(GL_CULL_FACE) + glDisable(GL_BLEND) +end +immutable FullScreenPostRender + vao_id::GLuint +end +@compat function (fs::FullScreenPostRender)() + glBindVertexArray(fs.vao_id) glDrawArrays(GL_TRIANGLES, 0, 3) glBindVertexArray(0) end +immutable OITPreRender end + +# TODO implement this correctly according to the paper +@compat function (::OITPreRender)() + glDisable(GL_DEPTH_TEST) + glDisable(GL_STENCIL_TEST) + glDisable(GL_CULL_FACE) + glDisable(GL_BLEND) +end + +immutable OpaquePreRender end +@compat function (::OpaquePreRender)() + glEnable(GL_DEPTH_TEST) + glDepthMask(GL_TRUE) + glDisable(GL_BLEND) + glEnable(GL_ALPHA_TEST) + glAlphaFunc(GL_EQUAL, 1.0f0) +end + + +loadshader(name) = joinpath(dirname(@__FILE__), name) + +rcpframe(x) = 1f0./Vec2f0(x[1], x[2]) """ Creates a postprocessing render object. -This will transfer the pixels from the color texture of the Framebuffer +This will transfer the pixels from the color texture of the FrameBuffer to the screen and while at it, it can do some postprocessing (not doing it right now): E.g fxaa anti aliasing, color correction etc. """ -function postprocess(color::Texture, framebuffer_size) +function FXAAProcess(color_texture, framebuffer_size) shader = LazyShader( - loadshader("fxaa.vert"), - loadshader("fxaa_combine.frag") + loadshader("fullscreen.vert"), + loadshader("fxaa.frag") ) - data = Dict{Symbol, Any}( - :color_texture => color + data = Dict( + :RCPFrame => map(rcpframe, framebuffer_size), + :color_texture => color_texture ) - robj = RenderObject(data, shader, EmptyPrerender(), nothing) - robj.postrenderfunction = () -> draw_fullscreen(robj.vertexarray.id) + robj = RenderObject(data, shader, FullscreenPreRender(), nothing) + robj.postrenderfunction = FullScreenPostRender(robj.vertexarray.id) robj end -function attach_framebuffer{T}(t::Texture{T, 2}, attachment) - glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, t.id, 0) +""" +Order independant Transparancy resolve pass +as described in this blog post/paper: +(OIT)[http://casual-effects.blogspot.de/2014/03/weighted-blended-order-independent.html] +""" +function OITResolve(sum_color, sum_weight, opaque_color) + shader = LazyShader( + loadshader("fullscreen.vert"), + loadshader("oit_combine.frag") + ) + data = Dict{Symbol, Any}( + :sum_color_tex => sum_color, + :sum_weight_tex => sum_weight, + :opaque_color_tex => opaque_color, + ) + robj = RenderObject(data, shader, OITPreRender(), nothing) + robj.postrenderfunction = FullScreenPostRender(robj.vertexarray.id) + robj end -function GLFramebuffer(fb_size) - render_framebuffer = glGenFramebuffers() - glBindFramebuffer(GL_FRAMEBUFFER, render_framebuffer) - buffersize = tuple(value(fb_size)...) - color_buffer = Texture(RGBA{UFixed8}, buffersize, minfilter=:nearest, x_repeat=:clamp_to_edge) +""" +Sets up renderpasses for `window` +""" +function add_oit_fxaa_postprocessing!(window) + buffersize_s = window.inputs[:framebuffer_size] + buffersize = tuple(value(buffersize_s)...) + context = window.glcontext + # render target for hit detection objectid_buffer = Texture(Vec{2, GLushort}, buffersize, minfilter=:nearest, x_repeat=:clamp_to_edge) - depth_buffer = Texture(Float32, buffersize, - internalformat = GL_DEPTH_COMPONENT32F, - format = GL_DEPTH_COMPONENT, + + # order independant transparency (OIT) render targets + sum_color = Texture(RGBA{Float16}, buffersize, minfilter=:nearest, x_repeat=:clamp_to_edge) + sum_weight = Texture(U8, buffersize, minfilter=:nearest, x_repeat=:clamp_to_edge) + + # opaque render targets for color combination and postprocessing + opaque_color = Texture(RGBA{U8}, buffersize, minfilter=:nearest, x_repeat=:clamp_to_edge) + depth_buffer = Texture(Float32, buffersize, + internalformat=GL_DEPTH_COMPONENT32F, + format=GL_DEPTH_COMPONENT, minfilter=:nearest, x_repeat=:clamp_to_edge ) - attach_framebuffer(color_buffer, GL_COLOR_ATTACHMENT0) - attach_framebuffer(objectid_buffer, GL_COLOR_ATTACHMENT1) - attach_framebuffer(depth_buffer, GL_DEPTH_ATTACHMENT) + # final tonemapped color stage + # must use linear interpolation for FXAA sampling and supply luma in alpha + tonemapped_luma = Texture(RGBA{U8}, buffersize, minfilter=:linear, x_repeat=:clamp_to_edge) - p = postprocess(color_buffer, fb_size) - fb = GLFramebuffer(render_framebuffer, color_buffer, objectid_buffer, depth_buffer, p) - glBindFramebuffer(GL_FRAMEBUFFER, 0) - fb -end -function Base.resize!(fb::GLFramebuffer, window_size) - ws = tuple(window_size...) - if ws!=size(fb) && all(x->x>0, window_size) - buffersize = tuple(window_size...) - resize_nocopy!(fb.color, buffersize) - resize_nocopy!(fb.objectid, buffersize) - resize_nocopy!(fb.depth, buffersize) - end + opaque_transparent_fb = FrameBuffer([ + (opaque_color, GL_COLOR_ATTACHMENT0), + (sum_color, GL_COLOR_ATTACHMENT1), + (sum_weight, GL_COLOR_ATTACHMENT2), + (objectid_buffer, GL_COLOR_ATTACHMENT3), + (depth_buffer, GL_DEPTH_ATTACHMENT), + ], context) + + + color_resolve_fb = FrameBuffer([ + (tonemapped_luma, GL_COLOR_ATTACHMENT0), + ], context) + + # window target... Could also be offscreen + final_target = FrameBuffer(0, Tuple{Texture, GLenum}[], GLenum[], context) + + opaque_pass = RenderPass(window, opaque_transparent_fb) + transparent_pass = RenderPass(window, opaque_transparent_fb) + colore_resolve_pass = RenderPass(OITResolve( + sum_color, sum_weight, opaque_color + ), color_resolve_fb) + fxaa_pass = RenderPass(FXAAProcess(tonemapped_luma, buffersize_s), final_target) + # update renderpasses + resize!(window.renderpasses, 4) + window.renderpasses[:] = [opaque_pass, transparent_pass, colore_resolve_pass, fxaa_pass] nothing end @@ -84,24 +250,18 @@ immutable MonitorProperties end function MonitorProperties(monitor::Monitor) - name = GLFW.GetMonitorName(monitor) - isprimary = GLFW.GetPrimaryMonitor() == monitor - position = Vec{2, Int}(GLFW.GetMonitorPos(monitor)...) + name = GLFW.GetMonitorName(monitor) + isprimary = GLFW.GetPrimaryMonitor() == monitor + position = Vec{2, Int}(GLFW.GetMonitorPos(monitor)...) physicalsize = Vec{2, Int}(GLFW.GetMonitorPhysicalSize(monitor)...) - videomode = GLFW.GetVideoMode(monitor) + videomode = GLFW.GetVideoMode(monitor) - dpi = Vec(videomode.width * 25.4, videomode.height * 25.4) ./ Vec{2, Float64}(physicalsize) + dpi = Vec(videomode.width * 25.4, videomode.height * 25.4) ./ Vec{2, Float64}(physicalsize) videomode_supported = GLFW.GetVideoModes(monitor) MonitorProperties(name, isprimary, position, physicalsize, videomode, videomode_supported, dpi, monitor) end -immutable GLContext - window::GLFW.Window - framebuffer::GLFramebuffer -end - - type Screen name ::Symbol @@ -109,7 +269,7 @@ type Screen parent ::Screen children ::Vector{Screen} inputs ::Dict{Symbol, Any} - renderlist ::Tuple # a tuple of specialized renderlists + renderlist ::Vector hidden ::Bool color ::RGBA{Float32} @@ -117,6 +277,11 @@ type Screen cameras ::Dict{Symbol, Any} glcontext ::GLContext + renderpasses::Vector{RenderPass} + opaque ::Vector{Int} + transparent ::Vector{Int} + camera2robj ::Dict{Symbol, Vector{Int}} + function Screen( name ::Symbol, @@ -124,17 +289,18 @@ type Screen parent ::Screen, children ::Vector{Screen}, inputs ::Dict{Symbol, Any}, - renderlist ::Tuple, + renderlist ::Vector, hidden ::Bool, color ::Colorant, cameras ::Dict{Symbol, Any}, context ::GLContext ) - new( + w = new( name, area, parent, children, inputs, renderlist, hidden, RGBA{Float32}(color), cameras, - context + context, RenderPass[], Int[], Int[], + Dict{Symbol, Vector{Int}}() ) end @@ -143,18 +309,20 @@ type Screen area ::Signal{SimpleRectangle{Int}}, children ::Vector{Screen}, inputs ::Dict{Symbol, Any}, - renderlist ::Tuple, + renderlist ::Vector, hidden ::Bool, color ::Colorant, cameras ::Dict{Symbol, Any}, context ::GLContext ) + parent = new() - new( + w = new( name, area, parent, children, inputs, renderlist, hidden, RGBA{Float32}(color), cameras, - context + context, RenderPass[], Int[], Int[], + Dict{Symbol, Vector{Int}}() ) end end