<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Conal Elliott &#187; 3D</title>
	<atom:link href="http://conal.net/blog/tag/3d/feed" rel="self" type="application/rss+xml" />
	<link>http://conal.net/blog</link>
	<description>Inspirations &#38; experiments, mainly about denotative/functional programming in Haskell</description>
	<lastBuildDate>Thu, 25 Jul 2019 18:15:11 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.1.17</generator>
	<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2F&amp;language=en_US&amp;category=text&amp;title=Conal+Elliott&amp;description=Inspirations+%26amp%3B+experiments%2C+mainly+about+denotative%2Ffunctional+programming+in+Haskell&amp;tags=blog" type="text/html" />
	<item>
		<title>Thoughts on semantics for 3D graphics</title>
		<link>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics</link>
		<comments>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics#comments</comments>
		<pubDate>Mon, 23 Nov 2009 07:41:30 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[3D]]></category>
		<category><![CDATA[arrow]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[geometry]]></category>
		<category><![CDATA[semantics]]></category>
		<category><![CDATA[type class morphism]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=90</guid>
		<description><![CDATA[The central question for me in designing software is always What does it mean? With functional programming, this question is especially crisp. For each data type I define, I want to have a precise and simple mathematical model. (For instance, my model for behavior is function-of-time, and my model of images is function-of-2D-space.) Every operation [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: Thoughts on semantics for 3D graphics

Tags: semantics, design, 3D, arrow, type class morphism, geometry

URL: http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/

-->

<!-- references -->

<!-- teaser -->

<p>The central question for me in designing software is always</p>

<blockquote>
  <p>What does it mean?</p>
</blockquote>

<p>With functional programming, this question is especially crisp.
For each data type I define, I want to have a precise and simple mathematical model.
(For instance, my model for behavior is function-of-time, and my model of images is function-of-2D-space.)
Every operation on the type is also given a meaning in terms of that semantic model.</p>

<p>This specification process, which is denotational semantics applied to data types, provides a basis for</p>

<ul>
<li>correctness of the implementation,</li>
<li>user documentation free of implementation detail,</li>
<li>generating and proving properties, which can then be used in automated testing, and</li>
<li>evaluating and comparing the elegance and expressive power of design decisions.</li>
</ul>

<p>For an example (2D images), some motivation of this process, and discussion, see Luke Palmer&#8217;s post <em><a href="http://lukepalmer.wordpress.com/2008/07/18/semantic-design/" title="Blog post by Luke Palmer">Semantic Design</a></em>.
See also my posts on the idea and use of <em><a href="http://conal.net/blog/tag/type-class-morphism/" title="Posts on type class morphisms">type class morphisms</a></em>, which provide additional structure to denotational design.</p>

<p>In spring of 2008, I started working on a functional 3D library, <a href="http://haskell.org/haskellwiki/FieldTrip" title="Library wiki page">FieldTrip</a>.
I&#8217;ve designed functional 3D libraries before as part of <a href="http://conal.net/tbag/" title="Project web page">TBAG</a>, <a href="http://conal.net/papers/ActiveVRML/" title="Tech report: &quot;A Brief Introduction to ActiveVRML&quot;">ActiveVRML</a>, and <a href="http://conal.net/Fran" title="Functional reactive animation">Fran</a>.
This time I wanted a semantics-based design, for all of the reasons given above.
As always, I want a model that is</p>

<ul>
<li>simple, </li>
<li>elegant, and </li>
<li>general.</li>
</ul>

<p>For 3D, I also want the model to be GPU-friendly, i.e., to execute well on (modern) GPUs and to give access to their abilities.</p>

<p>I hadn&#8217;t thought of or heard a model that I was happy with, and so I didn&#8217;t have the sort of firm ground I like to stand on in working on FieldTrip.
Last February, such a model occurred to me.
I&#8217;ve had this blog post mostly written since then.
Recently, I&#8217;ve been focused on functional 3D again for GPU-based rendering, and then Sean McDirmid <a href="http://mcdirmid.wordpress.com/2009/11/20/designing-a-gpu-oriented-geometry-abstraction-part-one/">posed a similar question</a>, which got me thinking again.</p>

<!--
**Edits**:

* 2008-02-09: just fiddling around
-->

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p><span id="more-90"></span></p>

<h3>Geometry</h3>

<p>3D graphics involves a variety of concepts.
Let&#8217;s start with 3D geometry, using a <em>surface</em> (rather than a <em>solid</em>) model.</p>

<p>Examples of 3D (surface) geometry include</p>

<ul>
<li>the boundary (surface) of a solid box, sphere, or torus,</li>
<li>a filled triangle, rectangle, or circle,</li>
<li>a collection of geometry , and</li>
<li>a spatial transformation of geometry.</li>
</ul>

<h4>First model: set of geometric primitives</h4>

<p>One model of geometry is a set of geometric primitives.
In this model, <code>union</code> means set union, and spatial transformation means transforming all of the 3D points in all of the primitives in the set.
Primitives contain infinitely (even uncountably) many points, so that&#8217;s a lot of transforming.
Fortunately, we&#8217;re talking about what (semantics), and not how (implementation).</p>

<p><em>What is a geometric primitive?</em></p>

<p>We could say it&#8217;s a triangle, specified by three coordinates.
After all, computer graphics reduces everything to sets of triangles.
Oops &#8212; we&#8217;re confusing semantics and implementation.
Tessellation <em>approximates</em> curved surfaces by sets of triangles but loses information in the process.
I want a story that includes this approximation process but keeps it clearly distinct from semantically ideal curved surfaces.
Then users can work with the ideal, simple semantics and rely on the implementation to perform intelligent, dynamic, view-dependent tessellation that adapts to available hardware resources.</p>

<p>Another model of geometric primitive is a function from 2D space to 3D space, i.e., the &#8220;parametric&#8221; representation of surfaces.
Along with the function, we&#8217;ll probably want some means of describing the subset of 2D over which the surface is defined, so as to trim our surfaces.
A simple formalization would be</p>

<pre><code>type Surf = R2 -&gt; Maybe R3
</code></pre>

<p>where</p>

<pre><code>type R  -- real numbers
type R2 = (R,R)
type R3 = (R,R,R)
</code></pre>

<p>For shading, we&#8217;ll also need normals, and possibly tangents &amp; bitangents,
We can get these features and more by including derivatives, either just first derivatives or all of them.
See my <a href="http://conal.net/blog/tag/derivative/" title="Posts on derivatives">posts on derivatives</a> and paper <em><a href="http://conal.net/papers/beautiful-differentiation/" title="Paper: Beautiful differentiation">Beautiful differentiation</a></em>.</p>

<p>In addition to position and derivatives, each point on a primitive also has material properties, which determines how light is reflected by and transmitted through the surface at the point.</p>

<pre><code>type Surf = R2 -&gt; Maybe (R2 :&gt; R3, Material)
</code></pre>

<p>where <code>a :&gt; b</code> contains all derivatives (including zeroth) at a point of a function of type <code>a-&gt;b</code>.
See <em><a href="http://conal.net/blog/posts/higher-dimensional-higher-order-derivatives-functionally/" title="blog post">Higher-dimensional, higher-order derivatives, functionally</a></em>.
We could perhaps also include derivatives of material properties:</p>

<pre><code>type Surf = R2 :~&gt; Maybe (R3, Material)
</code></pre>

<p>where <code>a :~&gt; b</code> is the type of infinitely differentiable functions.</p>

<h4>Combining geometry values</h4>

<p>The <code>union</code> function gives one way to combine two geometry values.
Another is morphing (interpolation) of positions and of material properties.
What can the semantics of morphing be?</p>

<p>Morphing betwen two <em>surfaces</em> is easier to define.
A surface is a function, so we can interpolate <em>point-wise</em>: given surfaces <code>r</code> and <code>s</code>, for each point <code>p</code> in parameter space, interpolate between (a) <code>r</code> at <code>p</code> and (b) <code>s</code> at <code>p</code>, which is what <code>liftA2</code> (on functions) would suggest.</p>

<p>This definition works <em>if</em> we have a way to interpolate between <code>Maybe</code> values.
If we use <code>liftA2</code> again, now on <code>Maybe</code> values, then the <code>Just</code>/<code>Nothing</code> (and <code>Nothing</code>/<code>Just</code>) cases will yield <code>Nothing</code>.
Is this semantics desirable?
As an example, consider a flat square surface with hole in the middle.
One square has a small hole, and the other has a big hole.
If the size of the hole corresponds to size of the portion of parameter space mapped to <code>Nothing</code>, then point-wise interpolation will always yield the larger hole, rather than interpolating between hole sizes.
On the other hand, the two surfaces with holes might be <code>Just</code> over exactly the same set of parameters, with the function determining how much the <code>Just</code> space gets stretched.</p>

<p>One way to characterize this awkwardness of morphing is that the two functions (surfaces) might have <em>different domains</em>.
This interpretation comes from seeing <code>a -&gt; Maybe b</code> as encoding a function from a <em>subset</em> of <code>a</code> (i.e., a <em>partial</em> function on <code>a</code>).</p>

<p>Even if we had a satisfactory way to combine surfaces (point-wise), how could we extend it to combining full geometry values, which can contain any number of surfaces?
One idea is to model geometry as an <em>structured</em> collection of surfaces, e.g., a list.
Then we could combine the collections element-wise.
Again, we&#8217;d have to deal with the possibility that the collections do not match up.</p>

<h3>Surface tuples</h3>

<p>Let&#8217;s briefly return to a simpler model of surfaces:</p>

<pre><code>type Surf = R2 -&gt; R3
</code></pre>

<p>We could represent a collection of such surfaces as a structured collection, e.g., a list:</p>

<pre><code>type Geometry = [Surf]
</code></pre>

<p>But then the type doesn&#8217;t capture the number of surfaces, leading to mismatches when combining geometry values point-wise.</p>

<p>Alternatively, we could make the number of surfaces explicit in the type, via tuples, possibly nested.
For instance, two surfaces would have type <code>(Surf,Surf)</code>.</p>

<p>Interpolation in this model becomes very simple.
A general interpolator works on vector spaces:</p>

<pre><code>lerp :: VectorSpace v =&gt; v -&gt; v -&gt; Scalar v -&gt; v
lerp a b t = a ^+^ t*^(b ^-^ a)
</code></pre>

<p>or on affine spaces:</p>

<pre><code>alerp :: (AffineSpace p, VectorSpace (Diff p)) =&gt;
         p -&gt; p -&gt; Scalar (Diff p) -&gt; p
alerp p p' s = p .+^ s*^(p' .-. p)
</code></pre>

<p>Both definitions are in the <a href="http://haskell.org/haskellwiki/vector-space" title="Library wiki page">vector-space</a> package.
That package also includes <code>VectorSpace</code> and <code>AffineSpace</code> instances for both functions and tuples.
These instances, together with instances for real values suffice to make (possibly nested) tuples of surfaces be vector spaces and affine spaces.</p>

<h3>From products to sums</h3>

<p>Function pairing admits some useful isomorphisms.
One replaces a product with a product:</p>

<!-- $$(a to b) times (a to c) cong a to (b times c)$$ -->

<pre><code>(a → b) × (a → c) ≅ a → (b × c)
</code></pre>

<p>Using this product/product isomorphism, we could replace tuples of surfaces with a single function from <em>R<sup>2</sup></em> to tuples of <em>R<sup>3</sup></em>.</p>

<p>There is also a handy isomorphism that relates products to sums, in the context of functions:</p>

<!-- $$(b to a) times (c to a) cong (b + c) to a$$ -->

<pre><code>(b → a) × (c → a) ≅ (b + c) → a
</code></pre>

<p>This second isomorphism lets us replace tuples of surfaces with a single &#8220;surface&#8221;, if we generalize the notion of surface to include domains more complex than <em>R<sup>2</sup></em>.</p>

<p>In fact, these two isomorphisms are uncurried forms of the general and useful Haskell functions <code>(&amp;&amp;&amp;)</code> and <code>(|||)</code>, defined on arrows:</p>

<pre><code>(&amp;&amp;&amp;) :: Arrow       (~&gt;) =&gt; (a ~&gt; b) -&gt; (a ~&gt; c) -&gt; (a ~&gt; (b,c))
(|||) :: ArrowChoice (~&gt;) =&gt; (a ~&gt; c) -&gt; (b ~&gt; c) -&gt; (Either a b ~&gt; c)
</code></pre>

<p>Restricted to the function arrow, <code>(|||) == either</code>.</p>

<p>The second isomorphism, <code>uncurry (|||)</code>, has another benefit.
Relaxing the domain type to allow sums opens the way to other domain variations as well.
For instance, we can have types for triangular domains, shapes with holes, and other flavors of bounded and unbounded parameter spaces.
All of these domains are two-dimensional, although they may result from several patches.</p>

<p>Our <code>Geometry</code> type now becomes parameterized:</p>

<pre><code>type Geometry a = a -&gt; (R3,Material)
</code></pre>

<p>The first isomorphism, <code>uncurry (&amp;&amp;&amp;)</code>, is also useful in a geometric setting.
Think of each component of the range type (here <code>R3</code> and <code>Material</code>) as a surface &#8220;attribute&#8221;.
Then <code>(&amp;&amp;&amp;)</code> merges two compatible geometries, including attributes from each.
Attributes could include position (and derivatives) and shading-related material, as well as non-visual properties like temperature, elasticity, stickiness, etc.</p>

<p>With this flexibility in mind, <code>Geometry</code> gets a second type parameter, which is the range type.
Now there&#8217;s nothing left of the <code>Geometry</code> type but general functions:</p>

<pre><code>type Geometry = (-&gt;)
</code></pre>

<p>Recall that we&#8217;re looking for a <em>semantics</em> for 3D geometry.
The <em>type</em> for <code>Geometry</code> might be abstract, with <code>(-&gt;)</code> being its semantic model.
In that case, the model suggests that <code>Geometry</code> have all of the same type class instances that <code>(-&gt;)</code> (and its full or partial applications) has, including <code>Monoid</code>, <code>Functor</code>, <code>Applicative</code>, <code>Monad</code>, and <code>Arrow</code>.
The semantics of these instances would be given by the corresponding instances for <code>(-&gt;)</code>.
(See posts on <a href="http://conal.net/blog/tag/type-class-morphism/" title="Posts on type class morphisms">type class morphisms</a> and the paper <em><a href="http://conal.net/blog/posts/denotational-design-with-type-class-morphisms/" title="blog post">Denotational design with type class morphisms</a></em>.)</p>

<p>Or drop the notion of <code>Geometry</code> altogether and use functions directly.</p>

<h3>Domains</h3>

<p>I&#8217;m happy with the simplicity of geometry as functions.
Functions fit the flexibility of programmable GPUs, and they provide simple, powerful &amp; familiar notions of attribute merging (<code>(&amp;&amp;&amp;)</code>) and union (<code>(|||)</code>/<code>either</code>).</p>

<p>The main question I&#8217;m left with: what are the domains?</p>

<p>One simple domain is a one-dimensional interval, say [-1,1].</p>

<p>Two useful domain building blocks are sum and product.
I mentioned sum above, in connection with geometric union (<code>(|||)</code>/<code>either</code>)
Product combines domains into higher-dimensional domains.
For instance, the product of two 1D intervals is a 2D interval (axis-aligned filled rectangle), which is handy for some parametric surfaces.</p>

<p>What about other domains, e.g., triangular, or having one more holes?  Or multi-way branching surfaces?  Or unbounded?</p>

<p>One idea is to stitch together simple domains using sum.
We don&#8217;t have to build any particular spatial shapes or sizes, since the &#8220;geometry&#8221; functions themselves yield the shape and size.
For instance, a square region can be mapped to a triangular or even circular region.
An infinite domain can be stitched together from infinitely many finite domains.
Or it can be mapped to from a single finite domain.
For instance, the function <code>x -&gt; x / abs (1-x)</code> maps [-1,1] to [-∞,∞].</p>

<p>Alternatively, we could represent domains as typed predicates (characteristic functions).
For instance, the closed interval [-1,1] would be <code>x -&gt; abs x &lt;= 1</code>.
Replacing <code>abs</code> with <code>magnitude</code> (for <a href="http://hackage.haskell.org/packages/archive/vector-space/latest/doc/html/Data-VectorSpace.html#t%3AInnerSpace">inner product spaces</a>), generalizes this formulation to encompass [-1,1] (1D), a unit disk (2D), and a unit ball (3D).</p>

<p>I like the simple generality of the predicate approach, while I like how the pure type approach supports interpolation and other pointwise operations (via <code>liftA2</code> etc).</p>

<h3>Tessellation</h3>

<p>I&#8217;ve intentionally formulated the graphics semantics over continuous space, which makes it resolution-independent and easy to compose.
(This formulation is typical for 3D geometry and 2D vector graphics.
The benefits of continuity apply to generally <a href="http://conal.net/Pan/Gallery" title="Pan image gallery">imagery</a> and to <a href="http://conal.net/Fran/tutorial.htm" title="Animated tutorial: &quot;Composing Reactive Animations&quot;">animation/behavior</a>.)</p>

<p>Graphics hardware specializes in finite collections of triangles.
For rendering, curved surfaces have to be <em>tessellated</em>, i.e., approximated as collections of triangles.
Desirable choice of tessellation depends on characteristics of the surface and of the view, as well as scene complexity and available CPU and GPU resources.
Formulating geometry in its ideal curved form allows for automated analysis and choice of tessellation.
For instance, since triangles are linear, the error of a triangle relative to the surface it approximates depends on how <em>non-linear</em> the surface is over the subset of its domain corresponding to the triangle.
Using <a href="http://en.wikipedia.org/wiki/Talk:Interval_arithmetic" title="Wikipedia page on interval analysis/arithmetic">interval analysis</a> and <a href="http://conal.net/blog/tag/derivative/" title="Posts on derivatives">derivatives</a>, non-linearity can be measured as a size bound on the second derivative or a range of first derivative.
Error could also be analyzed in terms of the resulting image rather than the surface.</p>

<p>For a GPU-based implementation, one could tessellate dynamically, in a &#8220;geometry shader&#8221; or (I presume) in a more general framework like CUDA or OpenCL.</p>

<h3>Abstractness</h3>

<p>A denotational model is &#8220;fully abstract&#8221; when it equates observationally equivalent terms.
The parametric model of surfaces is not fully abstract in that reparameterizing a surface yields a different function that looks the same as a surface.
(Surface reparametrization alters the relationship between domain and range, while covering exactly the same surface, geometrically.)
Properties that are independent of particular parametrization are called &#8220;geometric&#8221;, which I think corresponds to full abstraction (considering those properties as semantic functions).</p>

<p>What might a fully abstract (geometric) model for geometry be?</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=90&amp;md5=3a9d3f59c6f8c7d6110dbfab2555bddf"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/thoughts-on-semantics-for-3d-graphics/feed</wfw:commentRss>
		<slash:comments>18</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2Fthoughts-on-semantics-for-3d-graphics&amp;language=en_GB&amp;category=text&amp;title=Thoughts+on+semantics+for+3D+graphics&amp;description=The+central+question+for+me+in+designing+software+is+always+What+does+it+mean%3F+With+functional+programming%2C+this+question+is+especially+crisp.+For+each+data+type+I+define%2C+I+want...&amp;tags=3D%2Carrow%2Cdesign%2Cgeometry%2Csemantics%2Ctype+class+morphism%2Cblog" type="text/html" />
	</item>
		<item>
		<title>3D rendering as functional reactive programming</title>
		<link>http://conal.net/blog/posts/3d-rendering-as-functional-reactive-programming</link>
		<comments>http://conal.net/blog/posts/3d-rendering-as-functional-reactive-programming#comments</comments>
		<pubDate>Mon, 12 Jan 2009 05:38:58 +0000</pubDate>
		<dc:creator><![CDATA[Conal]]></dc:creator>
				<category><![CDATA[Functional programming]]></category>
		<category><![CDATA[3D]]></category>
		<category><![CDATA[FRP]]></category>
		<category><![CDATA[functional reactive programming]]></category>
		<category><![CDATA[monoid]]></category>
		<category><![CDATA[semantics]]></category>

		<guid isPermaLink="false">http://conal.net/blog/?p=75</guid>
		<description><![CDATA[I&#8217;ve been playing with a simple/general semantics for 3D. In the process, I was surprised to see that a key part of the semantics looks exactly like a key part of the semantics of functional reactivity as embodied in the library Reactive. A closer look revealed a closer connection still, as described in this post. [&#8230;]]]></description>
				<content:encoded><![CDATA[<!-- 

Title: 3D rendering as functional reactive programming

Tags: 3D, semantics, FRP, functional reactive programming, monoid

nURL: http://conal.net/blog/posts/3d-rendering-as-functional-reactive-programming/

-->

<!-- references -->

<!-- teaser -->

<p>I&#8217;ve been playing with a simple/general semantics for 3D.
In the process, I was surprised to see that a key part of the semantics looks exactly like a key part of the semantics of functional reactivity as embodied in the library <em><a href="http://haskell.org/haskellwiki/Reactive" title="Wiki page for the Reactive library">Reactive</a></em>.
A closer look revealed a closer connection still, as described in this post.</p>

<!--
**Edits**:

* 2008-02-09: just fiddling around
-->

<!-- without a comment or something here, the last item above becomes a paragraph -->

<p><span id="more-75"></span></p>

<h3>What is 3D rendering?</h3>

<p>Most programmers think of 3D rendering as being about executing sequence of side-effects on frame buffer or some other mutable array of pixels.
This way of thinking (sequences of side-effects) comes to us from the design of early sequential computers.
Although computer hardware architecture has evolved a great deal, most programming languages, and hence most programming thinking, is still shaped by the this first sequential model.
(See John Backus&#8217;s Turing Award lecture <em><a href="www.stanford.edu/class/cs242/readings/backus.pdf" title="Turing Award lecture by John Backus">Can Programming Be Liberated from the von Neumann Style?  A functional style and its algebra of programs</a></em>.)
The invention of monadic <em><a href="http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.53.2504" title="paper by Simon Peyton Jones and Philip Wadler">Imperative functional programming</a></em> allows Haskellers to think and program within the imperative paradigm as well.</p>

<p>What&#8217;s a <em>functional</em> alternative?
Rendering is a function from something to something else.
Let&#8217;s call these somethings (3D) &#8220;Geometry&#8221; and (2D) &#8220;Image&#8221;, where <code>Geometry</code> and <code>Image</code> are types of functional (immutable) values.</p>

<pre><code>type Rendering = Image Color

render :: Geometry -&gt; Rendering
</code></pre>

<p>To simplify, I&#8217;m assuming a fixed view.
What remains is to define what these two types <em>mean</em> and, secondarily, how to represent and implement them.</p>

<p>An upcoming post will suggest an answer for the meaning of <code>Geometry</code>.
For now, think of it as a collection of curved and polygonal surfaces, i.e., the <em>outsides</em> (boundaries) of solid shapes.
Each point on these surfaces has a location, a normal (perpendicular direction), and material properties (determining how light is reflected by and transmitted through the surface at the point).
The geometry will contain light sources.</p>

<p>Next, what is the meaning of <code>Image</code>?
A popular answer is that an image is a rectangular array of finite-precision encodings of color (e.g., with eight bits for each of red, blue, green and possibly opacity).
This answer is leads to poor compositionality and complex meanings for operations like scaling and rotation, so I prefer another model.
As in <a href="http://conal.net/Pan" title="project web page">Pan</a>, an image (the meaning of the type <code>Image Color</code>) is a function from infinite continuous 2D space to colors, where the <code>Color</code> type includes partial opacity.
For motivation of this model and examples of its use, see <em><a href="http://conal.net/papers/functional-images/" title="book chapter">Functional images</a></em> and the corresponding <a href="http://conal.net/Pan/Gallery" title="gallery of functional images">Pan gallery</a> of functional images.
<em>Composition</em> occurs on infinite &amp; continuous images.</p>

<p>After all composition is done, the resulting image can be sampled into a finite, rectangular array of finite precision color encodings.
I&#8217;m talking about a conceptual/semantic pipeline.
The implementation computes the finite sampling without having to compute the values for entire infinite image.</p>

<p>Rendering has several components.
I&#8217;ll just address one and show how it relates to functional reactive programming (FRP).</p>

<h3>Visual occlusion</h3>

<p>One aspect of 3D rendering is <a href="en.wikipedia.org/wiki/Hidden_surface_determination">hidden surface determination</a>.
Relative to the viewer&#8217;s position and orientation, some 3D objects may fully or partially occluded by nearer objects.</p>

<p>An image is a function of (infinite and continuous) 2D space, so specifying that function is determining its value at every sample point.
Each point can correspond to a number of geometric objects, some closer and some further.
If we assume for now that our colors are fully opaque, then we&#8217;ll need to know the color (after transformation and lighting) of the <em>nearest</em> surface point that is projected onto the sample point.
(We&#8217;ll remove this opacity assumption later.)</p>

<p>Let&#8217;s consider how we&#8217;ll combine two <code>Geometry</code> values into one:</p>

<pre><code>union :: Geometry -&gt; Geometry -&gt; Geometry
</code></pre>

<p>Because of occlusion, the <code>render</code> function cannot be compositional with respect to <code>union</code>.
If it were, then there would exist a functions <code>unionR</code> such that</p>

<pre><code>forall ga gb. render (ga `union` gb) == render ga `unionR` render gb
</code></pre>

<p>In other words, to render a union of two geometries, we can render each and combine the results.</p>

<p>The reason we can&#8217;t find such a <code>unionR</code> is that <code>render</code> doesn&#8217;t let <code>unionR</code> know how close each colored point is.
A solution then is simple: add in the missing depth information:</p>

<pre><code>type RenderingD = Image (Depth, Color)  -- first try

renderD :: Geometry -&gt; RenderingD
</code></pre>

<p>Now we have enough information for compositional rendering, i.e., we can define <code>unionR</code> such that</p>

<pre><code>forall ga gb. renderD (ga `union` gb) == renderD ga `unionR` renderD gb
</code></pre>

<p>where</p>

<pre><code>unionR :: RenderingD -&gt; RenderingD -&gt; RenderingD

unionR im im' p = if d &lt;= d' then (d,c) else (d',c')
 where
   (d ,c ) = im  p
   (d',c') = im' p
</code></pre>

<p>When we&#8217;re done composing, we can discard the depths:</p>

<pre><code>render g = snd . renderD g
</code></pre>

<p>or, with <em><a href="http://conal.net/blog/posts/semantic-editor-combinators/" title="blog post">Semantic editor combinators</a></em>:</p>

<pre><code>render = (result.result) snd renderD
</code></pre>

<h3>Simpler, prettier</h3>

<p>The <code>unionR</code> is not very complicated, but still, I like to tease out common structure and reuse definitions wherever I can.
The first thing I notice about <code>unionR</code> is that it works pointwise.
That is, the value at a point is a function of the values of two other images at the same point.
The pattern is captured by <code>liftA2</code> on functions, thanks to the <code>Applicative</code> instance for functions.</p>

<pre><code>liftA2 :: (b -&gt; c -&gt; d) -&gt; (a -&gt; b) -&gt; (a -&gt; c) -&gt; (a -&gt; d)
</code></pre>

<p>So that</p>

<pre><code>unionR = liftA2 closer

closer (d,c) (d',c') = if d &lt;= d' then (d,c) else (d',c')
</code></pre>

<p>Or</p>

<pre><code>closer dc@(d,_) dc'@(d',_) = if d &lt;= d' then dc else dc'
</code></pre>

<p>Or even</p>

<pre><code>closer = minBy fst
</code></pre>

<p>where</p>

<pre><code>minBy f u v = if f u &lt;= f v then u else v
</code></pre>

<p>This definition of <code>unionR</code> is not only simpler, it&#8217;s quite a bit more general, as type inference reveals:</p>

<pre><code>unionR :: (Ord a, Applicative f) =&gt; f (a,b) -&gt; f (a,b) -&gt; f (a,b)

closer :: Ord a =&gt; (a,b) -&gt; (a,b) -&gt; (a,b)
</code></pre>

<p>Once again, simplicity and generality go hand-in-hand.</p>

<h3>Another type class morphism</h3>

<p>Let&#8217;s see if we can make <code>union</code> rendering simpler and more inevitable.
Rendering is <em>nearly</em> a homomorphism.
That is, <code>render</code> nearly distributes over <code>union</code>, but we have to replace <code>union</code> by <code>unionR</code>.
I&#8217;d rather eliminate this discrepancy, ending up with</p>

<pre><code>forall ga gb. renderD (ga `op` gb) == renderD ga `op` renderD gb
</code></pre>

<p>for some <code>op</code> that is equal to <code>union</code> on the left and <code>unionR</code> on the right.
Since <code>union</code> and <code>unionR</code> have different types (with neither being a polymorphic instance of the other), <code>op</code> will have to be a method of some type class.</p>

<p>My favorite binary method is <code>mappend</code>, from <code>Monoid</code>, so let&#8217;s give it a try.
<code>Monoid</code> requires there also to be an identity element <code>mempty</code> and that <code>mappend</code> be associative.
For <code>Geometry</code>, we can define</p>

<pre><code>instance Monoid Geometry where
  mempty  = emptyGeometry
  mappend = union
</code></pre>

<p>Images with depth are a little trickier.
Image already has a <code>Monoid</code> instance, whose semantics is determined by the principle of <a href="http://conal.net/blog/tag/type-class-morphism/" title="Posts on type class morphisms">type class morphisms</a>, namely</p>

<blockquote>
  <p><em>The meaning of an instance is the instance of the meaning</em></p>
</blockquote>

<p>The meaning of an image is a function, and that functions have a <code>Monoid</code> instance:</p>

<pre><code>instance Monoid b =&gt; Monoid (a -&gt; b) where
  mempty = const mempty
  f `mappend` g =  a -&gt; f a `mappend` g a
</code></pre>

<p>which simplifies nicely to a standard form, by using the <code>Applicative</code> instance for functions.</p>

<pre><code>instance Applicative ((-&gt;) a) where
  pure      = const
  hf &lt;*&gt; xf =  a -&gt; (hf a) (xf a)

instance Monoid b =&gt; Monoid (a -&gt; b) where
  mempty  = pure   mempty
  mappend = liftA2 mappend
</code></pre>

<p>We&#8217;re in luck.
Since we&#8217;ve defined <code>unionR</code> as <code>liftA2 closer</code>, so we just need it to turn out that <code>closer == mappend</code> and that <code>closer</code> is associative and has an identity element.</p>

<p>However, <code>closer</code> is defined on pairs, and the standard <code>Monoid</code> instance on pairs doesn&#8217;t fit.</p>

<pre><code>instance (Monoid a, Monoid b) =&gt; Monoid (a,b) where
  mempty = (mempty,mempty)
  (a,b) `mappend` (a',b') = (a `mappend` a', b `mappend` b')
</code></pre>

<p>To avoid this conflict, define a new data type to be used in place of pairs.</p>

<pre><code>data DepthG d a = Depth d a  -- first try
</code></pre>

<p>Alternatively,</p>

<pre><code>newtype DepthG d a = Depth { unDepth :: (d,a) }
</code></pre>

<p>I&#8217;ll go with this latter version, as it turns out to be more convenient.</p>

<p>Then we can define our monoid:</p>

<pre><code>instance Monoid (DepthG d a) where
  mempty  = Depth (maxBound,undefined)
  Depth p `mappend` Depth p' = Depth (p `closer` p')
</code></pre>

<p>The second method definition can be simplified nicely</p>

<pre><code>  mappend = inDepth2 closer
</code></pre>

<p>where</p>

<pre><code>  inDepth2 = unDepth ~&gt; unDepth ~&gt; Depth
</code></pre>

<p>using the ideas from <em><a href="http://conal.net/blog/posts/prettier-functions-for-wrapping-and-wrapping/" title="blog post">Prettier functions for wrapping and wrapping</a></em> and the notational improvement from Matt Hellige&#8217;s <em><a href="http://matt.immute.net/content/pointless-fun" title="blog post by Matt Hellige">Pointless fun</a></em>.</p>

<h3>FRP &#8212; Future values</h3>

<p>The <code>Monoid</code> instance for <code>Depth</code> may look familiar to you if you&#8217;ve been following along with my <a href="http://conal.net/blog/tag/future-value/" title="Posts on futures values">future value</a>s or have read the paper <em><a href="http://conal.net/papers/simply-reactive" title="Paper: &quot;Simply efficient functional reactivity&quot;">Simply efficient functional reactivity</a></em>.
A <em>future value</em> has a time and a value.
Usually, the value cannot be known until its time arrives.</p>

<pre><code>newtype FutureG t a = Future (Time t, a)

instance (Ord t, Bounded t) =&gt; Monoid (FutureG t a) where
  mempty = Future (maxBound, undefined)
  Future (s,a) `mappend` Future (t,b) =
    Future (s `min` t, if s &lt;= t then a else b)
</code></pre>

<p>When we&#8217;re using a non-lazy (flat) representation of time, this <code>mappend</code> definition can be written more simply:</p>

<pre><code>  mappend = minBy futTime

  futTime (Future (t,_)) = t
</code></pre>

<p>Equivalently,</p>

<pre><code>  mappend = inFuture2 (minBy fst)
</code></pre>

<p>The <code>Time</code> type is really nothing special about time.
It is just a synonym for the <a href="http://hackage.haskell.org/packages/archive/reactive/latest/doc/html/Data-Max.html" title="module documentation"><code>Max</code> monoid</a>, as needed for the <code>Applicative</code> and <code>Monad</code> instances.</p>

<p>This connection with future values means we can discard more code.</p>

<pre><code>type RenderingD d = Image (FutureG d Color)
renderD :: (Ord d, Bounded d) =&gt; Geometry -&gt; RenderingD d
</code></pre>

<p>Now we have our monoid (homo)morphism properties:</p>

<pre><code>renderD mempty == mempty

renderD (ga `mappend` gb) == renderD ga `mappend` renderD gb
</code></pre>

<p>And we&#8217;ve eliminated the code for <code>renderR</code> by reusing and existing type (future values).</p>

<h3>Future values?</h3>

<p>What does it mean to think about depth/color pairs as being &#8220;future&#8221; colors?
If we were to probe outward along a ray, say at the speed of light, we would bump into some number of 3D objects.
The one we hit earliest is the nearest, so in this sense, <code>mappend</code> on futures (choosing the earlier one) is the right tool for the job.</p>

<p>I once read that a popular belief in the past was that vision (light) reaches outward to strike objects, as I&#8217;ve just described.
I&#8217;ve forgotten where I read about that belief, though I think in a book about perspective, and I&#8217;d appreciate a pointer from someone else who might have a reference.</p>

<p>We moderns believe that light travels to us from the objects we see.
What we see of nearby objects comes from the very recent past, while of further objects we see the more remote past.
From this modern perspective, therefore, the connection I&#8217;ve made with future values is exactly backward.
Now that I think about it in this way, of course it&#8217;s backward, because we see (slightly) into the past rather than the future.</p>

<p>Fixing this conceptual flaw is simple: define a type of &#8220;past values&#8221;.
Give them exactly the same representation as future values, and deriving its class instances entirely.</p>

<pre><code>newtype PastG t a = Past (FutureG t a)
  deriving (Monoid, Functor, Applicative, Monad)
</code></pre>

<p>Alternatively, choose a temporally neutral replacement for the name &#8220;future values&#8221;.</p>

<h3>The bug in Z-buffering</h3>

<p>The <code>renderD</code> function implements continuous, infinite Z-buffering, with <code>mappend</code> performing the z-compare and conditional overwrite.
Z-buffering is the dominant algorithm used in real-time 3D graphics and is supported in hardware on even low-end graphics hardware (though not in its full continuous and infinite glory).</p>

<p>However, Z-buffering also has a serious bug: it is only correct for fully opaque colors.
Consider a geometry <code>g</code> and a point <code>p</code> in the domain of the result image.
There may be many different points in <code>g</code> that project to <code>p</code>.
If <code>g</code> has only fully opaque colors, then at most one place on <code>g</code> contributes to the rendered image at <code>p</code>, and specifically, the nearest such point.
If <code>g</code> is the <code>union</code> (<code>mappend</code>) of two other geometries, <code>g == ga `union` gb</code>, then the nearest contribution of <code>g</code> (for <code>p</code>) will be the nearer (<code>mappend</code>) of the nearest contributions of <code>ga</code> and of <code>gb</code>.</p>

<p>When colors may be <em>partially</em> opaque, the color of the rendering at a point <code>p</code> can depend on <em>all</em> of the points in the geometry that get projected to <code>p</code>.
Correct rendering in the presence of partial opacity requires a <code>fold</code> that combines all of the colors that project onto a point, <em>in order of distance</em>, where the color-combining function (alpha-blending) is <em>not</em> commutative.
Consider again <code>g == ga `union` gb</code>.
The contributions of <code>ga</code> to <code>p</code> might be entirely closer than the contributions of <code>gb</code>, or entirely further, or interleaved.
If interleaved, then the colors generated from each cannot be combined into a single color for further combination.
To handle the general case, replace the single distance/color pair with an ordered <em>collection</em> of them:</p>

<pre><code>type RenderingD d = Image [FutureG d Color]  -- multiple projections, first try
</code></pre>

<p>Rendering a <code>union</code> (<code>mappend</code>) requires a merging of two lists of futures (distance/color pairs) into a single one.</p>

<h3>More FRP &#8212; Events</h3>

<p>Sadly, we&#8217;ve now lost our monoid morphism, because list <code>mappend</code> is <code>(++)</code>, not the required merging.
However, we can fix this problem as we did before, by introducing a new type.</p>

<p>Or, we can look for an existing type that matches our required semantics.
There is just such a thing in the <em><a href="http://haskell.org/haskellwiki/Reactive" title="Wiki page for the Reactive library">Reactive</a></em> formulation of FRP, namely an <em>event</em>.
We can simply use the FRP <code>Event</code> type:</p>

<pre><code>type RenderingD d = Image (EventG d Color)

renderD :: (Ord d, Bounded d) =&gt; Geometry -&gt; RenderingD d
</code></pre>

<h3>Spatial transformation</h3>

<p>Introducing depths allowed rendering to be defined compositionally with respect to geometric union.
Is the depth model, enhanced with lists (events), sufficient for compositionality of rendering with respect to other <code>Geometry</code> operations as well?
Let&#8217;s look at spatial transformation.</p>

<pre><code>(*%)  :: Transform3 -&gt; Geometry -&gt; Geometry
</code></pre>

<p>Compositionally of rendering would mean that we can render <code>xf *% g</code> by rendering <code>g</code> and then using <code>xf</code> in some way to transform that rendering.
In other words there would have to exist a function <code>(*%%)</code> such that</p>

<pre><code>forall xf g. renderD (xf *% g) == xf *%% renderD g
</code></pre>

<p>I don&#8217;t know if the required <code>(*%%)</code> function exists, or what restrictions on <code>Geometry</code> or <code>Transform3</code> it implies, or whether such a function could be useful in practice.
Instead, let&#8217;s change the type of renderings again, so that rendering can accumulate transformations and apply them to surfaces.</p>

<pre><code>type RenderingDX = Transform3 -&gt; RenderingD

renderDX :: (Ord d, Bounded d) =&gt; Geometry -&gt; RenderingDX d
</code></pre>

<p>with or without correct treatment of partial opacity (i.e., using futures or events).</p>

<p>This new function has a simple specification:</p>

<pre><code>renderDX g xf == renderD (xf *% g)
</code></pre>

<p>from which it follows that</p>

<pre><code>renderD g == renderDX g identityX
</code></pre>

<p>Rendering a transformed geometry then is a simple accumulation, justified as follows:</p>

<pre><code>renderDX (xfi *% g)

  == {- specification of renderDX -}

 xfo -&gt; renderD (xfo *% (xfi *% g))

  == {- property of transformation -}

 xfo -&gt; renderD ((xfo `composeX` xfi) *% g)

  == {- specification of renderDX  -}

 xfo -&gt; renderDX g (xfo `composeX` xfi)
</code></pre>

<p>Render an empty geometry:</p>

<pre><code>renderDX mempty

  == {- specification of renderDX -}

 xf -&gt; renderD (xf *% mempty)

  == {- property of (*%) and mempty -}

 xf -&gt; renderD mempty

  == {- renderD is a monoid morphism -}

 xf -&gt; mempty

  == {- definition of pure on functions -}

pure mempty

  == {- definition of mempty on functions -}

mempty
</code></pre>

<p>Render a geometric union:</p>

<pre><code>renderDX (ga `mappend` gb)

  == {- specification of renderDX -}

 xf -&gt; renderD (xf *% (ga `mappend` gb))

  == {- property of transformation and union -}

 xf -&gt; renderD ((xf *% ga) `mappend` (xf *% gb))

  == {- renderD is a monoid morphism -}

 xf -&gt; renderD (xf *% ga) `mappend` renderD (xf *% gb)

  == {- specification of renderDX  -}

 xf -&gt; renderDX ga xf `mappend` renderDX gb xf

  == {- definition of liftA2/(&lt;*&gt;) on functions -}

liftA2 mappend (renderDX ga) (renderDX gb)

  == {- definition of mappend on functions -}

renderDX ga `mappend` renderDX gb
</code></pre>

<p>Hurray!
<code>renderDX</code> is still a monoid morphism.</p>

<p>The two properties of transformation and union used above say together that <code>(xf *%)</code> is a monoid morphism for all transforms <code>xf</code>.</p>
<p><a href="http://conal.net/blog/?flattrss_redirect&amp;id=75&amp;md5=a47ae6e1e1a51016836d913e562dbd3e"><img src="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png" srcset="http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@2x.png 2xhttp://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white.png, http://conal.net/blog/wp-content/plugins/flattr/img/flattr-badge-white@3x.png 3x" alt="Flattr this!"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://conal.net/blog/posts/3d-rendering-as-functional-reactive-programming/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		<atom:link rel="payment" title="Flattr this!" href="https://flattr.com/submit/auto?user_id=conal&amp;popout=1&amp;url=http%3A%2F%2Fconal.net%2Fblog%2Fposts%2F3d-rendering-as-functional-reactive-programming&amp;language=en_GB&amp;category=text&amp;title=3D+rendering+as+functional+reactive+programming&amp;description=I%26%238217%3Bve+been+playing+with+a+simple%2Fgeneral+semantics+for+3D.+In+the+process%2C+I+was+surprised+to+see+that+a+key+part+of+the+semantics+looks+exactly+like+a+key+part...&amp;tags=3D%2CFRP%2Cfunctional+reactive+programming%2Cmonoid%2Csemantics%2Cblog" type="text/html" />
	</item>
	</channel>
</rss>
