Thomas Diewald is a prolific graphics programmer in Java, Javascript, OpenGL/WebGL, GLSL, OpenCL, C++, C. He has developed a number of libraries for vanilla processing, and we explore their use in JRubyArt and propane (actually we barely scratch the surface) in this and in subsequent posts, but hope that we can provide inspiration for others to follow.

PixelFlow library

A Processing/Java library for high performance GPU-Computing (GLSL). See PixelFlow library on github and the PixelFlow website.

Here an example sketch translated for JRubyArt for the sketch to run you need install both peasycam and PixelFlow libraries using the processing ide:-

load_library :PixelFlow, :peasycam

# java_imports
module AA
  java_import 'com.thomasdiewald.pixelflow.java.DwPixelFlow'
  java_import 'com.thomasdiewald.pixelflow.java.antialiasing.FXAA.FXAA'
  java_import 'com.thomasdiewald.pixelflow.java.antialiasing.GBAA.GBAA'
  java_import 'com.thomasdiewald.pixelflow.java.antialiasing.SMAA.SMAA'
  java_import 'com.thomasdiewald.pixelflow.java.dwgl.DwGLTextureUtils'
  java_import 'com.thomasdiewald.pixelflow.java.geometry.DwCube'
  java_import 'com.thomasdiewald.pixelflow.java.geometry.DwMeshUtils'
  java_import 'com.thomasdiewald.pixelflow.java.imageprocessing.filter.DwFilter'
  java_import 'com.thomasdiewald.pixelflow.java.render.skylight.DwSceneDisplay'
  java_import 'com.thomasdiewald.pixelflow.java.utils.DwMagnifier'
  java_import 'peasy.PeasyCam'
  java_import 'processing.core.PShape'
end

include AA

VIEWPORT_W = 1280
VIEWPORT_H = 720
VIEWPORT_X = 230
VIEWPORT_Y = 0
GAMMA = 2.2
BACKGROUND_COLOR = 32.0
attr_reader :context, :fxaa, :smaa, :gbaa, :pg_render_noaa, :magnifier, :display
attr_reader :pg_render_smaa, :pg_render_fxaa, :pg_render_msaa, :pg_render_gbaa
attr_reader :peasycam, :aamode, :smaamode, :font12, :font48, :shp_scene
# replace java enum with ruby symbol and use hash key to select via keyboard
SMAA_MODE = { 'q' => :EDGES, 'w' => :BLEND, 'e' => :FINAL }.freeze
AA_MODE = { '1' => :NoAA,
            '2' => :MSAA,
            '3' => :SMAA,
            '4' => :FXAA,
            '5' => :GBAA }.freeze

def settings
  size(VIEWPORT_W, VIEWPORT_H, P3D)
  smooth(0)
end

def setup
  surface.set_location(VIEWPORT_X, VIEWPORT_Y)
  @aamode = :NoAA
  @smaamode = :FINAL
  # camera
  @peasycam = PeasyCam.new(self, -4.083, -6.096, 7.000, 2_000)
  peasycam.set_rotations(1.085, -0.477, 2.910)
  # projection
  perspective(60.radians, width / height.to_f, 2, 6_000)
  # processing font
  @font48 = create_font('SourceCodePro-Regular', 48)
  @font12 = create_font('SourceCodePro-Regular', 12)
  @context = DwPixelFlow.new(self)
  context.print
  context.printGL
  # MSAA - main render-target for MSAA
  @pg_render_msaa = create_graphics(width, height, P3D)
  pg_render_msaa.smooth(8)
  pg_render_msaa.textureSampling(5)
  # NOAA - main render-target for FXAA and MSAA
  @pg_render_noaa = create_graphics(width, height, P3D)
  pg_render_noaa.smooth(0)
  pg_render_noaa.textureSampling(5)
  # FXAA
  @pg_render_fxaa = create_graphics(width, height, P3D)
  pg_render_fxaa.smooth(0)
  pg_render_fxaa.textureSampling(5)
  # FXAA
  @pg_render_smaa = create_graphics(width, height, P3D)
  pg_render_smaa.smooth(0)
  pg_render_smaa.textureSampling(5)
  # GBAA
  @pg_render_gbaa = create_graphics(width, height, P3D)
  pg_render_gbaa.smooth(0)
  pg_render_gbaa.textureSampling(5)
  scene_display = lambda do |canvas|
    # lights
    canvas.directional_light(255, 255, 255, 200, 600, 400)
    canvas.directional_light(255, 255, 255, -200, -600, -400)
    canvas.ambient_light(64, 64, 64)
    # canvas.shape(shape)
    scene_shape(canvas)
  end
  # AA post-processing modes
  @fxaa = FXAA.new(context)
  @smaa = SMAA.new(context)
  @gbaa = GBAA.new(context, scene_display)
  mag_h = (height / 2.5).to_i
  @magnifier = DwMagnifier.new(self, 4, 0, height - mag_h, mag_h, mag_h)
  @shp_scene ||= nil
  frame_rate(1_000)
end

def draw
  case aamode
  when :MSAA
    display_scene_wrap(pg_render_msaa)
    # RGB gamma correction
    DwFilter.get(context).gamma.apply(pg_render_msaa, pg_render_msaa, GAMMA)
  when :NoAA, :SMAA, :FXAA
    display_scene_wrap(pg_render_noaa)
    # RGB gamma correction
    DwFilter.get(context).gamma.apply(pg_render_noaa, pg_render_noaa, GAMMA)
    fxaa.apply(pg_render_noaa, pg_render_fxaa) if aamode == :FXAA
    if aamode == :SMAA
      smaa.apply(pg_render_noaa, pg_render_smaa)
      # only for debugging
      filter = DwFilter.get(context).copy
      filter.apply(smaa.tex_edges, pg_render_smaa) if smaamode == :EDGES
      filter.apply(smaa.tex_blend, pg_render_smaa) if smaamode == :BLEND
    end
  when :GBAA
    display_scene_wrap(pg_render_noaa)
    # RGB gamma correction
    DwFilter.get(context).gamma.apply(pg_render_noaa, pg_render_noaa, GAMMA)
    gbaa.apply(pg_render_noaa, pg_render_gbaa)
  end
  @display = case aamode
             when :MSAA
               pg_render_msaa
             when :SMAA
               pg_render_smaa
             when :FXAA
               pg_render_fxaa
             when :GBAA
               pg_render_gbaa
             else
               pg_render_noaa
             end
  magnifier.apply(display, mouse_x, mouse_y)
  magnifier.display_tool
  peasycam.beginHUD
  # display Anti Aliased result
  blend_mode(REPLACE)
  clear
  image(display, 0, 0)
  blend_mode(BLEND)
  # display magnifer
  magnifier.display(g)
  # display AA name
  mode = aamode.to_s
  buffer = ''
  if aamode == :SMAA
    buffer = " [#{smaamode}]" if smaamode == :EDGES
    buffer = " [#{smaamode}]" if smaamode == :BLEND
  end
  no_stroke
  fill 0, 150
  rect 0, height - 65, magnifier.w, 65
  tx = 10
  ty = 20
  text_font font12
  fill 200
  text('[1] NoAA', tx, ty)
  text('[2] MSAA - MultiSample AA', tx, ty += 20)
  text('[3] SMAA - SubPixel Morphological AA', tx, ty += 20)
  text('[4] FXAA - Fast Approximate AA', tx, ty += 20)
  text('[5] GBAA - GeometryBuffer AA', tx, ty + 20)
  text_font font48
  tx = 20
  ty = height - 20
  fill 0
  text mode << buffer, tx + 2, ty + 2
  fill(255, 200, 0)
  text mode, tx, ty
  peasycam.endHUD
  # some info, window title
  format_string = 'Anti Aliasing | fps: (%6.2f)'
  surface.set_title(format(format_string, frame_rate))
end

def display_scene_wrap(canvas)
  canvas.begin_draw
  DwGLTextureUtils.copy_matrices(g, canvas)
  background_color_gamma = (BACKGROUND_COLOR / 255.0)**GAMMA * 255.0
  # background
  canvas.blend_mode BLEND
  canvas.background background_color_gamma
  display_scene canvas
  canvas.end_draw
end

# render something
def display_scene(canvas)
  # lights
  canvas.directional_light(255, 255, 255, 200, 600, 400)
  canvas.directional_light(255, 255, 255, -200, -600, -400)
  canvas.ambient_light(64, 64, 64)
  scene_shape canvas
end

def scene_shape(canvas)
  return canvas.shape(shp_scene) unless shp_scene.nil?
  @shp_scene = create_shape(GROUP)
  num_boxes = 50
  num_spheres = 50
  bb_size = 800
  xmin = -bb_size
  xmax = +bb_size
  ymin = -bb_size
  ymax = +bb_size
  zmin = 0
  zmax = +bb_size
  color_mode(HSB, 360.0, 1.0, 1.0)
  srand(0)
  (0..num_boxes).each do
    px = rand(xmin..xmax)
    py = rand(ymin..ymax)
    sx = rand(10..210)
    sy = rand(10..210)
    sz = rand(zmin..zmax)
    off = 45
    base = 0
    hsb_h = base + rand(-off..off)
    hsb_s = 1
    hsb_b = rand(0.1..1.0)
    shading = color(hsb_h, hsb_s, hsb_b)
    shp_box = create_shape(BOX, sx, sy, sz)
    shp_box.set_fill(true)
    shp_box.set_stroke(false)
    shp_box.set_fill(shading)
    shp_box.translate(px, py, sz / 2)
    shp_scene.add_child(shp_box)
  end
  cube_smooth = DwCube.new(4)
  cube_facets = DwCube.new(2)
  (0..num_spheres).each do
    px = rand(xmin..xmax)
    py = rand(ymin..ymax)
    pz = rand(zmin..zmax)
    rr = rand(50..100)
    facets = true # (i%2 == 0)
    off = 20
    base = 225
    hsb_h = base + rand(-off..off)
    hsb_s = rand(0.1..1.0)
    hsb_b = 1
    shading = color(hsb_h, hsb_s, hsb_b)
    shp_sphere = create_shape(PShape::GEOMETRY)
    if facets
      DwMeshUtils.create_polyhedron_shape(shp_sphere, cube_facets, 1, 4, false)
    else
      DwMeshUtils.create_polyhedron_shape(shp_sphere, cube_smooth, 1, 4, true)
    end
    shp_sphere.set_stroke(false)
    shp_sphere.set_stroke(color(0))
    shp_sphere.set_stroke_weight(0.01 / rr)
    shp_sphere.set_fill(true)
    shp_sphere.set_fill(shading)
    shp_sphere.reset_matrix
    shp_sphere.scale(rr)
    shp_sphere.translate(px, py, pz)
    shp_scene.add_child(shp_sphere)
  end
  colorMode(RGB, 255, 255, 255)
  shp_rect = create_shape(RECT, -1000, -1000, 2000, 2000)
  shp_rect.set_stroke(false)
  shp_rect.set_fill(true)
  shp_rect.set_fill(color(255))
  shp_scene.add_child(shp_rect)
end

def print_camera
  pos = peasycam.get_position
  rot = peasycam.get_rotations
  lat = peasycam.get_look_at
  dis = peasycam.get_distance
  cam_format = '%s: (%7.3f, %7.3f, %7.3f)'
  dist_format = 'distance: (%7.3f)'
  puts format(cam_format, 'position', pos[0], pos[1], pos[2])
  puts format(cam_format, 'rotation', rot[0], rot[1], rot[2])
  puts format(cam_format, 'look_at', lat[0], lat[1], lat[2])
  puts format(dist_format, dis)
end

def key_released
  @aamode = AA_MODE[key] if AA_MODE.key? key
  @smaamode = SMAA_MODE[key] if SMAA_MODE.key? key
  print_camera if key == 'c'
end


We can then load the libraries using the library_loader, and the translation to JRubyArt is relatively straight forward, apart from dealing with the callback.

processing version

// callback for scene display (used in GBAA)
DwSceneDisplay scene_display = new DwSceneDisplay() {  
  @Override
  public void display(PGraphics3D canvas) {
    displayScene(canvas);
  }
};

// render something
public void displayScene(PGraphics3D canvas){
  // lights
  canvas.directionalLight(255, 255, 255, 200,600,400);
  canvas.directionalLight(255, 255, 255, -200,-600,-400);
  canvas.ambientLight(64, 64, 64);
  sceneShape(canvas);
}

// init renderer
   gbaa = new GBAA(context, scene_display);

JRubyArt version

# callback for rendering scene, implements DwSceneDisplay interface
scene_display = lambda do |canvas|
  # lights
  canvas.directional_light(255, 255, 255, 200, 600, 400)
  canvas.directional_light(255, 255, 255, -200, -600, -400)
  canvas.ambient_light(64, 64, 64)
  scene_shape(canvas)
end

# init renderer
@gbaa = GBAA.new(context, scene_display)

snapshot of the running sketch from atom