在过去的三个月中,我一直在开发 Compose3D,它是令人惊叹的 Compose 包的 3D 扩展。我在 Compose3D 上的工作始于我与 Pranav T Bhat 一起上的计算机图形学课程项目,到课程结束时,我们已经完成了一个 Compose3D 的工作原型,它支持上下文和几何体,以及一个非常基础的 WebGL 后端。
我很高兴能以第一届 Julia Summer of Code 的身份,在 Shashi Gowda 和 Simon Danisch 的指导下继续这项工作,该项目得到了 Gordon and Betty Moore Foundation 的慷慨赞助。虽然我已经能够为 Compose3D 添加很多功能,但它还没有完全准备好发布。希望不久后它就能发布。但作为一项令人愉快的副作用,我已经能够将原始原型(以及更多!)提供的 WebGL 渲染功能抽象到一个名为 ThreeJS.jl 的单独包中,现在可以使用它在浏览器中使用 Julia 渲染 3D 图形,这为在 IJulia 笔记本和 Escher 中显示此类场景打开了可能性。
ThreeJS 现在负责 Compose3D 执行的所有 WebGL 渲染。它也可以用作其他图形包的独立包,以用作后端。
最初,我在 Compose3D 中渲染场景的方法是,只是将相应的 JavaScript 代码输出到 IJulia 笔记本中,然后执行它!这在 IJulia 笔记本中运行得很好,但很快显而易见,这种方法有几个缺点。
难以扩展。
与 Escher 的兼容性不佳。
它也不适用于 Interact 以提供交互性。
因此,Shashi 建议围绕优秀的 three.js 库实现一个 Polymer 包装器,以创建 threejs 网页组件。Polymer 团队在创建 threejs 组件方面做了一些工作,并准备好了一个基本实现,我立即将其 fork 并进行了一些调整,以添加我需要的功能。可以肯定地说,我在 JSoC 期间编写 JavaScript 的时间比编写 Julia 的时间更多!
切换到使用网页组件突然打开了 2 条主要途径。Compose3D 现在可以与 Escher 协同工作,并且还提供了交互性。ThreeJS 输出 Patchwork 元素,这使它能够使用 Patchwork 的巧妙的 diffing 功能,从而仅更新所需的 DOM 元素,并提高性能。
另一方面,网页组件在 IJulia 笔记本中引入了关于提供 ThreeJS 所需文件的问题。我仍在努力找到解决这个问题的有效方法,但目前,一个技巧可以让 ThreeJS 在 IJulia 中运行,尽管有一些限制。
无论如何,现在我们已经准备好绘制浏览器中的 3D 场景!例如,下面的代码片段将绘制一个从角落照亮的红色立方体。ThreeJS 绘制的场景中的相机可以使用鼠标或触控板旋转、缩放和平移,让你可以探索场景。
import ThreeJS
ThreeJS.outerdiv() << (ThreeJS.initscene() <<
[
ThreeJS.mesh(0.0, 0.0, 0.0) <<
[
ThreeJS.box(1.0,1.0,1.0),
ThreeJS.material(Dict(:kind=>"lambert",:color=>"red"))
],
ThreeJS.pointlight(3.0, 3.0, 3.0),
ThreeJS.camera(0.0, 0.0, 10.0)
])
目前,IJulia 中的交互性已损坏(切换到 Polymer 1.0 和新的隐秘 DOM 的副作用),因此,如果你想与 3D 场景进行交互,Escher 是你的首选。所以,一个例子可以是与之前相同的场景,但添加一个滑块,并使立方体的大小由滑块控制。
import ThreeJS
function main(window)
push!(window.assets, "widgets")
push!(window.assets, ("ThreeJS", "threejs"))
side = Input(1.0)
vbox(
slider(1.0:5.0) >>> side,
lift(side) do val
ThreeJS.outerdiv() << (ThreeJS.initscene() <<
[
ThreeJS.mesh(0.0, 0.0, 0.0) <<
[
ThreeJS.box(val, val, val),
ThreeJS.material(Dict(:kind=>"lambert",:color=>"red"))
],
ThreeJS.pointlight(3.0, 3.0, 3.0),
ThreeJS.camera(0.0, 0.0, 10.0)
])
end
)
end
可以使用 Escher 创建小规模动画。我们不使用滑块来更新元素,而是使用 every
函数或 fpswhen
函数在特定间隔内更新它。使用对上述代码的几处修改,就可以绘制一个旋转立方体的场景。
import ThreeJS
function main(window)
push!(window.assets, "widgets")
push!(window.assets, ("ThreeJS", "threejs"))
rx = 0.0
ry = 0.0
rz = 0.0
delta = fpswhen(window.alive, 60) #Update at 60 FPS
lift(delta) do _
rx += 0.5
ry += 0.5
rz += 0.5
ThreeJS.outerdiv() << (ThreeJS.initscene() <<
[
ThreeJS.mesh(0.0, 0.0, 0.0) <<
[
ThreeJS.box(2.0, 2.0, 2.0, rx = rx, ry = ry, rz = rz),
ThreeJS.material(Dict(:kind=>"lambert",:color=>"red"))
],
ThreeJS.pointlight(3.0, 3.0, 3.0),
ThreeJS.camera(0.0, 0.0, 10.0)
])
end
end
ThreeJS 支持渲染参数曲面,这基本上是典型 surf
图绘制的曲面类型。它还支持绘制像典型 mesh
图一样的线。可以通过传递要使用的颜色数组,将颜色映射应用于这些曲面。ThreeJS 会计算并选择要应用的颜色。这些颜色在与使用 vertex
的 colorkind
属性的材质组合在一起时才会生效。下面显示了 ThreeJS 绘制的此类曲面的屏幕截图。
Compose3D 对渲染库提供了一种抽象,让你可以像 Compose 库(它的灵感来源)一样,将基本图形组合在一起以构建场景。这使你能够用更少的代码创建非常有趣的结构!Compose3D 具有与 Compose 相似的功能,用户可以创建 3D 上下文,然后在其中使用相对和绝对度量,并将其他基本图形组合在一起。
我最喜欢的展示 Compose3D 的例子是 Sierpinski 金字塔示例。在这里,我们将父上下文拆分为我们想要的各个部分,然后在其中绘制金字塔!因此,3D 空间的下半部分被分成 4 部分,然后在上面排列一个金字塔。
using Compose3D
function sierpinski(n)
if n == 0
compose(Context(0w,0h,0d,1w,1h,1d),pyramid(0w,0h,0d,1w,1h)) #The basic unit
else
t = sierpinski(n - 1)
compose(Context(0w,0h,0d,1w,1h,1d),
(Context(0w,0h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0w,0h,0.5d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.5w,0h,0.5d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.5w,0h,0d,(1/2)w,(1/2)h,(1/2)d), t),
(Context(0.25w,0.5h,0.25d,(1/2)w,(1/2)h,(1/2)d), t)) #The top one
end
end
compose(Context(-5mm,-5mm,-5mm,10mm,10mm,10mm),sierpinski(3))
瞧!你得到了一个像下图所示的级别为 3 的 Sierpinski 金字塔。
切换到 ThreeJS 使 Compose3D 能够享受到 ThreeJS 带来的所有优势。这包括交互性和动画!
例如,同一个 Sierpinski 示例可以包含一些交互式元素,例如定义递归级别的滑块,以及可能控制金字塔颜色的滑块。这可以在 Escher 中轻松完成,就像使用 ThreeJS 一样。在定义了下面的 sierpinski
函数后,只需创建一个滑块并将其连接到 sierpinski
函数即可完成设置!
function main(window)
push!(window.assets, ("ThreeJS", "threejs")) #Push the threejs static assets
push!(window.assets, "widgets")
n = Input(0.0)
vbox(
slider(0.0:3.0) >>> n, #Set up the slider
lift(n) do i
#Draw the composed figure!
draw(
Patchable3D(100,100),
compose(
Context(-5mm,-5mm,-5mm,10mm,10mm,10mm), sierpinski(i)
)
)
end
)
end
作为动画的示例,我将 Ian Dunning 的 Escher 鱼群示例从 2D 移植到 3D,你可以在下面找到它的屏幕截图。
ThreeJS 中添加了一些新的基本图形,但 Compose3D 中还没有对应的基本图形。
在 ThreeJS 中添加对文本的支持,以便在图中使用标签。
能够使用 surf
和 mesh
自动在浏览器中绘制缩放的曲面图,以及围绕 ThreeJS 的基于 WebGL 的绘图库。
实际上让 Compose3D 准备好供公众使用!