Here is a shader sketch that will run with regular jruby and the JRubyArt gem, converted from ruby-processing. The original processing sketch by Andres Colubri used PVector, the use Vec3D allows us to chain vector operations, and further (

*thanks to the ShapeRender interface*) write them directly to the PShape verticies.

require 'jruby_art'
class Trefoil < Processing::AppGL
attr_reader :pg, :trefoil
def setup
size(1024, 768, P3D)
texture_mode(NORMAL)
no_stroke
@pg = create_graphics(32, 512, P3D)
pg.begin_draw
pg.background(0, 0)
pg.noStroke
pg.fill(255, 0, 0, 200)
pg.end_draw
@trefoil = create_trefoil(350, 60, 15, pg)
end
def draw
background(0)
pg.begin_draw
pg.ellipse(rand(0.0..pg.width), rand(0.0..pg.height), 4, 4)
pg.end_draw
ambient(250, 250, 250)
pointLight(255, 255, 255, 0, 0, 200)
push_matrix
translate(width/2, height/2, -200)
rotate_x(frame_count * PI / 500)
rotate_y(frame_count * PI / 500)
shape(trefoil)
pop_matrix
end
def create_trefoil(s, ny, nx, tex)
obj = create_shape()
obj.begin_shape(TRIANGLES)
obj.texture(tex)
renderer = ShapeRender.new(obj)
(0 ... nx).each do |j|
u0 = j.to_f / nx
u1 = (j + 1).to_f / nx
(0 ... ny).each do |i|
v0 = i.to_f / ny
v1 = (i + 1).to_f / ny
p0 = eval_point(u0, v0)
n0 = eval_normal(u0, v0)
p1 = eval_point(u0, v1)
n1 = eval_normal(u0, v1)
p2 = eval_point(u1, v1)
n2 = eval_normal(u1, v1)
n0.to_normal(renderer)
(p0 * s).to_vertex_uv(renderer, u0, v0)
n1.to_normal(renderer)
(p1 * s).to_vertex_uv(renderer, u0, v1)
n2.to_normal(renderer)
(p2 * s).to_vertex_uv(renderer, u1, v1)
p1 = eval_point(u1, v0)
n1 = eval_normal(u1, v0)
n0.to_normal(renderer)
(p0 * s).to_vertex_uv(renderer, u0, v0)
n2.to_normal(renderer)
(p2 * s).to_vertex_uv(renderer, u1, v1)
n1.to_normal(renderer)
(p1 * s).to_vertex_uv(renderer, u1, v0)
end
end
obj.end_shape
return obj
end
def eval_normal(u, v)
p = eval_point(u, v)
tang_u = eval_point(u + 0.01, v)
tang_v = eval_point(u, v + 0.01)
tang_u -= p
tang_v.cross(tang_u).normalize!
end
def eval_point(u, v)
a = 0.5
b = 0.3
c = 0.5
d = 0.1
s = TAU * u
t = (TAU * (1 - v)) * 2
sint = Math.sin(t)
cost = Math.cos(t)
sint15 = Math.sin(1.5 * t)
cost15 = Math.cos(1.5 * t)
r = a + b * cost15
x = r * cost
y = r * sint
z = c * sint15
dv = Vec3D.new(
-1.5 * b * sint15 * cost - y,
-1.5 * b * sint15 * sint + x,
1.5 * c * cost15)
q = dv.normalize
qvn = Vec3D.new(q.y, -q.x, 0).normalize!
ww = q.cross(qvn)
coss = Math.cos(s)
sins = Math.sin(s)
Vec3D.new(
x + d * (qvn.x * coss + ww.x * sins),
y + d * (qvn.y * coss + ww.y * sins),
z + d * ww.z * sins)
end
end
Trefoil.new(title: 'Trefoil', fullscreen: true, bgcolor: '#000000')