USING: arrays bunny.model bunny.cel-shaded continuations destructors kernel math multiline opengl opengl.shaders opengl.framebuffers opengl.gl opengl.capabilities sequences ui.gadgets combinators accessors ; IN: bunny.outlined STRING: outlined-pass1-fragment-shader-main-source varying vec3 normal; vec4 cel_light(); void main() { gl_FragData[0] = cel_light(); gl_FragData[1] = vec4(normal, 1); } ; STRING: outlined-pass2-vertex-shader-source varying vec2 coord; void main() { gl_Position = ftransform(); coord = (gl_Vertex * vec4(0.5) + vec4(0.5)).xy; } ; STRING: outlined-pass2-fragment-shader-source uniform sampler2D colormap, normalmap, depthmap; uniform vec4 line_color; varying vec2 coord; const float DEPTH_RATIO_THRESHOLD = 1.001, SAMPLE_SPREAD = 1.0/512.0; float depth_sample(vec2 c) { return texture2D(depthmap, c).x; } bool are_depths_border(vec3 depths) { return any(lessThan(depths, vec3(1.0/DEPTH_RATIO_THRESHOLD))) || any(greaterThan(depths, vec3(DEPTH_RATIO_THRESHOLD))); } vec3 normal_sample(vec2 c) { return texture2D(normalmap, c).xyz; } float min6(float a, float b, float c, float d, float e, float f) { return min(min(min(min(min(a, b), c), d), e), f); } float border_factor(vec2 c) { vec2 coord1 = c + vec2(-SAMPLE_SPREAD, -SAMPLE_SPREAD), coord2 = c + vec2( SAMPLE_SPREAD, -SAMPLE_SPREAD), coord3 = c + vec2(-SAMPLE_SPREAD, SAMPLE_SPREAD), coord4 = c + vec2( SAMPLE_SPREAD, SAMPLE_SPREAD); vec3 normal1 = normal_sample(coord1), normal2 = normal_sample(coord2), normal3 = normal_sample(coord3), normal4 = normal_sample(coord4); if (dot(normal1, normal1) < 0.5 && dot(normal2, normal2) < 0.5 && dot(normal3, normal3) < 0.5 && dot(normal4, normal4) < 0.5) { return 0.0; } else { vec4 depths = vec4(depth_sample(coord1), depth_sample(coord2), depth_sample(coord3), depth_sample(coord4)); vec3 ratios1 = depths.xxx/depths.yzw, ratios2 = depths.yyz/depths.zww; if (are_depths_border(ratios1) || are_depths_border(ratios2)) { return 1.0; } else { float normal_border = 1.0 - min6( dot(normal1, normal2), dot(normal1, normal3), dot(normal1, normal4), dot(normal2, normal3), dot(normal2, normal4), dot(normal3, normal4) ); return normal_border; } } } void main() { gl_FragColor = mix(texture2D(colormap, coord), line_color, border_factor(coord)); } ; TUPLE: bunny-outlined gadget pass1-program pass2-program color-texture normal-texture depth-texture framebuffer framebuffer-dim ; : outlining-supported? ( -- ? ) "2.0" { "GL_ARB_shading_objects" "GL_ARB_draw_buffers" "GL_ARB_multitexture" } has-gl-version-or-extensions? { "GL_EXT_framebuffer_object" "GL_ARB_texture_float" } has-gl-extensions? and ; : pass1-program ( -- program ) vertex-shader-source check-gl-shader cel-shaded-fragment-shader-lib-source check-gl-shader outlined-pass1-fragment-shader-main-source check-gl-shader 3array check-gl-program ; : pass2-program ( -- program ) outlined-pass2-vertex-shader-source outlined-pass2-fragment-shader-source ; : ( gadget -- draw ) outlining-supported? [ pass1-program pass2-program { (>>gadget) (>>pass1-program) (>>pass2-program) } bunny-outlined construct ] [ drop f ] if ; : (framebuffer-texture) ( dim iformat xformat -- texture ) swapd >r >r >r GL_TEXTURE0 glActiveTexture gen-texture GL_TEXTURE_2D over glBindTexture GL_TEXTURE_2D GL_TEXTURE_WRAP_S GL_CLAMP glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T GL_CLAMP glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_NEAREST glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_NEAREST glTexParameteri GL_TEXTURE_2D 0 r> r> first2 0 r> GL_UNSIGNED_BYTE f glTexImage2D ; : (attach-framebuffer-texture) ( texture attachment -- ) swap >r >r GL_FRAMEBUFFER_EXT r> GL_TEXTURE_2D r> 0 glFramebufferTexture2DEXT gl-error ; : (make-framebuffer) ( color-texture normal-texture depth-texture -- framebuffer ) 3array gen-framebuffer dup [ swap GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT1_EXT GL_DEPTH_ATTACHMENT_EXT 3array [ (attach-framebuffer-texture) ] 2each check-framebuffer ] with-framebuffer ; : dispose-framebuffer ( draw -- ) dup framebuffer-dim>> [ { [ framebuffer>> [ delete-framebuffer ] when* ] [ color-texture>> [ delete-texture ] when* ] [ normal-texture>> [ delete-texture ] when* ] [ depth-texture>> [ delete-texture ] when* ] [ f >>framebuffer-dim drop ] } cleave ] [ drop ] if ; : remake-framebuffer-if-needed ( draw -- ) dup [ gadget>> dim>> ] [ framebuffer-dim>> ] bi over = [ 2drop ] [ [ dup dispose-framebuffer dup ] dip { [ GL_RGBA16F_ARB GL_RGBA (framebuffer-texture) [ >>color-texture drop ] keep ] [ GL_RGBA16F_ARB GL_RGBA (framebuffer-texture) [ >>normal-texture drop ] keep ] [ GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT (framebuffer-texture) [ >>depth-texture drop ] keep ] } 2cleave (make-framebuffer) >>framebuffer drop ] if ; : clear-framebuffer ( -- ) GL_COLOR_ATTACHMENT0_EXT glDrawBuffer 0.15 0.15 0.15 1.0 glClearColor GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT bitor glClear GL_COLOR_ATTACHMENT1_EXT glDrawBuffer 0.0 0.0 0.0 0.0 glClearColor GL_COLOR_BUFFER_BIT glClear ; : (pass1) ( geom draw -- ) dup framebuffer>> [ clear-framebuffer { GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT1_EXT } set-draw-buffers pass1-program>> (draw-cel-shaded-bunny) ] with-framebuffer ; : (pass2) ( draw -- ) init-matrices { [ color-texture>> GL_TEXTURE_2D GL_TEXTURE0 bind-texture-unit ] [ normal-texture>> GL_TEXTURE_2D GL_TEXTURE1 bind-texture-unit ] [ depth-texture>> GL_TEXTURE_2D GL_TEXTURE2 bind-texture-unit ] [ pass2-program>> { { "colormap" [ 0 glUniform1i ] } { "normalmap" [ 1 glUniform1i ] } { "depthmap" [ 2 glUniform1i ] } { "line_color" [ 0.1 0.0 0.1 1.0 glUniform4f ] } } [ { -1.0 -1.0 } { 1.0 1.0 } rect-vertices ] with-gl-program ] } cleave ; M: bunny-outlined draw-bunny [ remake-framebuffer-if-needed ] [ (pass1) ] [ (pass2) ] tri ; M: bunny-outlined dispose [ pass1-program>> [ delete-gl-program ] when* ] [ pass2-program>> [ delete-gl-program ] when* ] [ dispose-framebuffer ] tri ;