Realtime raytracing test

Flash  Flash 10  Pixel Bender  3D 
16 December 2008

Having done a couple of experiments with Pixel Benders (#1, #2), I wanted to throw something more demanding at it.

The problem of doing raytracing in Pixel Benders is the lack of looping, so basically the whole scene would have to be hard-coded. It would be possible to make a generic renderer with some fancy pre-processing of the source code, but it's a big project and the whole effort would be in vain if/when loops are supported in a future version.

Raytracing involves some quite advanced mathematics, so I had to think twice for some of this stuff. A good summary is this document. Initially I wanted to raytrace the torus, but quicky gave up this as I would need to solve this equation, which can only be solved numerically - it's defintely not something I want to do per-pixel. 

I discovered another limitation of Pixel Benders by doing this test: No support for boolean datatype when exporting for Flash (if exporting for After Effects both loops and booleans can be used). It can be a drag when creating optimized SIMD based code, which Pixel Benders supports through the float4 and similar datatypes.

<languageVersion : 1.0;>

 

kernel Crossfade

<   namespace : "SpinningRT";

    vendor : "Spinning Owl";

    version : 1;

    description : "Raytracing for Flash Player 10+. Optimized with faked distance calcs"; >

{   

    parameter float3 sphere1    // (x,y,z) .. radius always = 1

    <

        defaultValue : float3(-.5, 0., 5.);

        minValue : float3(-10., -10., -10.);

        maxValue : float3(10., 10., 10.);

    >;

 

    parameter float3 sphere2    // (x,y,z) .. radius always = 1

    <

        defaultValue : float3(.5, 0., 5.);

        minValue : float3(-10., -10., -10.);

        maxValue : float3(10., 10., 10.);

    >;

 

 

    parameter float2 screensize    // (x,y)

    <

        defaultValue : float2(512., 512.);

        minValue : float2(0., 0.);

        maxValue : float2(2048., 2048.);

    >;

 

    parameter float tweak

    <

        defaultValue : float(1.);

        minValue : float(.2);

        maxValue : float(2.);

    >;

 

 

    output pixel4 dst;

 

    void evaluatePixel()

    {

        // ray of current pixel

        float3 ray_dir = -.5 + float3(outCoord().x / screensize.y, outCoord().y / screensize.y, 2.5);

 

        // Code independent of render path

        float2 aa;

        aa.x = aa.y = dot(ray_dir, ray_dir);

        float2 bb = -2. * float2(dot(ray_dir, sphere1), dot(ray_dir, sphere2));

        float2 root = bb * bb - 4. * aa * (float2(dot(sphere1, sphere1), dot(sphere2, sphere2)) - 1.);

 

 

        // Render paths:

        if(root[0] < 0. && root[1] < 0.)

 

            // No intersections

            dst = pixel4(0., 0., 0., 1.);

 

        else if(root[0] > 0. && root[1] < 0.) {

 

            // Intersecting only 1st sphere

            root[0] = sqrt(root[0]); 

            aa[0] = -.5 / aa[0];

            float2 t = float2((-bb[0] + root[0])*aa[0], (-bb[0] - root[0])*aa[0]);

            dst = pixel4(0., 0., t[1]-t[0], 1.);           

        }

        else if(root[0] < 0. && root[1] > 0.) {

 

            // Intersecting only 2nd sphere

            root[1] = sqrt(root[1]); 

            aa[1] = -.5 / aa[1];

            float2 t = float2((-bb[1] + root[1])*aa[1], (-bb[1] - root[1])*aa[1]);

            dst = pixel4(t[1]-t[0], 0., 0., 1.);

        }           

        else if(root[0] > 0. && root[1] > 0.) {

 

            // Intersecting both spheres

            float2 dist = float2(0., 0.);      // distance travelled inside spheres

            root = sqrt(root);

            aa = -.5 / aa;                 // coefs combined

 

            float2 t1 = (-bb + root)*aa;    // coef of 1st root both spheres

            float2 t2 = (-bb - root)*aa;    // coef of 2nd root both spheres

 

            dist[0] = (t2[0] - t1[0]);      // dist[0] = distance(ray_dir * t1[0], ray_dir * t2[0]);

            dist[1] = (t2[1] - t1[1]);      // dist[1] = distance(ray_dir * t1[1], ray_dir * t2[1]);

 

            if(t2[1] > t2[0])       // apply material properties

                dst = pixel4(dist[1], 0., dist[0]-dist[1]*tweak, 1.);

            else

                dst = pixel4(dist[1]-dist[0]*tweak, 0., dist[0], 1.);

        }

    }   

}

<languageVersion : 1.0;>

 

kernel Crossfade

<   namespace : "SpinningRT";

    vendor : "Spinning Owl";

    version : 1;

    description : "Raytracing for Flash Player 10+. Test #2"; >

{   

    parameter float3 sphere1    // (x,y,z) .. radius always = 1

    <

        defaultValue : float3(-.5, 0., 5.);

        minValue : float3(-10., -10., -10.);

        maxValue : float3(10., 10., 10.);

    >;

 

    parameter float3 sphere2    // (x,y,z) .. radius always = 1

    <

        defaultValue : float3(.5, 0., 5.);

        minValue : float3(-10., -10., -10.);

        maxValue : float3(10., 10., 10.);

    >;

 

 

    parameter float2 screensize    // (x,y)

    <

        defaultValue : float2(512., 512.);

        minValue : float2(0., 0.);

        maxValue : float2(2048., 2048.);

    >;

 

    parameter float2 tweak

    <

        defaultValue : float2(1., .5);

        minValue : float2(.2, 0.);

        maxValue : float2(2., 1.);

    >;

 

 

    output pixel4 dst;

 

    void evaluatePixel()

    {

        // ray of current pixel

        float3 ray_dir = float3(-.5*screensize.x/screensize.y, -.5, 0) + float3(outCoord().x / screensize.y, outCoord().y / screensize.y, 2.);

 

        // Code independent of render path

        float2 aa;

        aa.x = aa.y = dot(ray_dir, ray_dir);

        float2 bb = -2. * float2(dot(ray_dir, sphere1), dot(ray_dir, sphere2));

        float2 root = bb * bb - 4. * aa * (float2(dot(sphere1, sphere1), dot(sphere2, sphere2)) - 1.);

 

 

        // Render paths:

        if(root[0] < 0. && root[1] < 0.)

 

            // No intersections

            dst = pixel4(0., 0., 0., 1.);

 

        else if(root[0] > 0. && root[1] < 0.) {

 

            // Intersecting only 1st sphere

            root[0] = sqrt(root[0]); 

            aa[0] = -.5 / aa[0];

            float2 t = float2((-bb[0] + root[0])*aa[0], (-bb[0] - root[0])*aa[0]);

            dst = pixel4(0., 0., t[1]-t[0], 1.);           

        }

        else if(root[0] < 0. && root[1] > 0.) {

 

            // Intersecting only 2nd sphere

            root[1] = sqrt(root[1]); 

            aa[1] = -.5 / aa[1];

            float2 t = float2((-bb[1] + root[1])*aa[1], (-bb[1] - root[1])*aa[1]);

            dst = pixel4(t[1]-t[0], 0., 0., 1.);

        }           

        else if(root[0] > 0. && root[1] > 0.) {

 

            // Intersecting both spheres

            float2 dist;

            root = sqrt(root);

            aa = -.5 / aa;                 // coefs combined

 

            float2 t1 = (-bb + root)*aa;    // coef of 1st root both spheres.

            float2 t2 = (-bb - root)*aa;    // coef of 2nd root both spheres

 

            dist[0] = (t2[0] - t1[0]);      // dist[0] = distance(ray_dir * t1[0], ray_dir * t2[0]);

            dist[1] = (t2[1] - t1[1]);      // dist[1] = distance(ray_dir * t1[1], ray_dir * t2[1]);

 

 

            float dist12;

            float dist_overlap = 0.;

            float dist34;

 

            if(t2[0] > t2[1]) {       // 1st intersection sphere 1 ?

                if(t1[0] < t2[1]) {  

                    // order t2[0], t2[1], t1[0], t1[1]

                    dist12 = t2[0] - t2[1];

                    dist_overlap = t2[1] - t1[0];

                    dist34 = t1[0] - t1[1];

                }

                else {

                    // order t2[0], t1[0], t2[1], t1[1]

                    dist12 = t2[0] - t1[0];

                    dist34 = t2[1] - t1[1];

                }

                dst = pixel4(dist34 + dist_overlap - dist12, 0., dist12+dist_overlap, 1.);

            }

            else

                if(t2[0] > t1[1]) {

                    // order t2[1], t2[0], t1[1], t1[0]

                    dist12 = t2[1] - t2[0];

                    dist_overlap = t2[0] - t1[1];

                    dist34 = t1[1] - t1[0];

                }

                else {

                    // order t2[1], t1[1], t2[0], t1[0]

                    dist12 = t2[1] - t1[1];

                    dist34 = t2[0] - t1[0];

                }

                dst = pixel4(dist12+dist_overlap, 0., dist34 + dist_overlap - dist12, 1.);

           }

        }

    }   

 

}

 

 0 comment(s)



Implementing a Tags system

Umbraco  CSS  XSLT 
04 December 2008

There are different ways of doing it, and it's actually built into Umbraco, but I wanted to implement it from scratch as a training exercise to learn Umbraco. It involves several steps, so it's nice to summarize the process to get an overview.

Creating screenshots is rather time consuming, so I'll skip it this time.

My approach (thanks to Kenny / Xeed for pointing me in the right direction) :

  • Create a new datatype called Tags with the Checkbox list renderControl, and adding prevalues for each of the tags. 
  • In my Blog Post document type, add a Tags datatype instance 
  • Then check the tags that applies to each Blog Post instance in the content tree
  • The xml output will for each blog post instance be on the form

    <node attributes>

      <!-- blog post instance -->

      <data alias="bodyText" attributes>xhtml blob</data>

      <data alias="tags" attributes>comma seperated list of the checked tags</data>

    </node>

  • For the list of blog posts, implement a filter which only displays the the blog posts which has the requested tag checked. This involves getting a querystring argument into XSLT, which I will store as a variable called filterBy, and using it when iterating through the list of blog posts.

    <xsl:param name="currentPage"/>

    <xsl:template match="/">

      <xsl:variable name="filterBy" select="umbraco.library:RequestQueryString('tag')" />

      <xsl:for-each select="$currentPage/node [contains(./data[@alias='tags'], $filterBy)]">

        displaying the blog post here

      </xsl:for-each>

    </xsl:template>

  • Then output the comma seperated list of tags as a list of links using the tag name as a filter:

    <xsl:variable name="tags" select="umbraco.library:Split(./data [@alias = 'tags'], ',')" />

    <xsl:for-each select="$tags/value">

      <a href="/blog.aspx?tag={.}">

        <xsl:value-of select="." />

      </a>&nbsp;

    </xsl:for-each>

Now that the tags are displayed for the blog, and filtering is implemented, it's time to make a listing of all the defined tags to ease navigation on the site.

First I did a simple listing of all the defined tags:

<xsl:for-each select="umbraco.library:GetPreValues(1099)//preValue">

  <a style="white-space: nowrap;" href="/blog.aspx?tag={.}">

    <xsl:value-of select="." />

  </a> &nbsp;

</xsl:for-each>

 

It works, but doesn't give the same impression as using a Tag cloud. Luckily, Christoph Ertl has already made an Umbraco package for this, so after downloading his ZIP file, it can be installed into Umbraco by going into the Developer section -> Packages -> Install local package. The dll is copied and registered and a XSLT and a Macro is automatically created and integrated into Umbraco. His package then needs to be pointed to my tags. This involves changing his method calls in his TagCloud.xslt to :

<xsl:value-of select="TagCloud.Helper:RenderTags($currentPage/ancestor-or-self::node/descendant-or-self::node[@nodeTypeAlias = 'BlogPost' and string(data [@alias='umbracoNaviHide']) != '1'], 'tags', '&lt;a class=tc{1} href=&quot;/blog.aspx?tag={0}&quot;&gt;{0}&lt;/a&gt; ', 6)" disable-output-escaping="yes"/>


Then it's just a matter of calling his Macro from my master template:

<umbraco:Macro Alias="TagCloud" runat="server"/>


and adding css styles for the generated links:

/* Tag cloud */

a.tc1 { font-size: 100%; font-weight: 200;  }

a.tc2 { font-size: 110%; font-weight: 300;  }

a.tc3 { font-size: 120%; font-weight: 500;  }

a.tc4 { font-size: 130%; font-weight: 600;  }

a.tc5 { font-size: 145%; font-weight: 800;  }

a.tc6 { font-size: 160%; font-weight: 900;  }

/* KEEPING default hover behaviour

a.tc1:hover, a.tc2:hover, a.tc3:hover, a.tc4:hover, a.tc5:hover, a.tc5:hover, a.tc6:hover { text-decoration:underline; }

*/


 56 comment(s)



Setting up hosting of umbraco v4 site on discountasp.net

Umbraco 
24 April 2010

Having developed the site on Umbraco v4 locally (started on beta 2, upgraded to RC1), I decided to use the DiscountASP.NET European datacenter (UK) for hosting.

Locally I was using the free SQLExpress (SQL Server 2005 based) and the IIS built into Vista Ultimate. When signing up I decided to use Microsoft 2008 Server, and Microsoft SQL Server 2008. Here's a summary of the steps involved :

  • Set Application Pool Pipeline Mode to "Classic" under IIS Tools in the discountasp.net web admin
  • Copy the whole wwwroot local directory and database backup onto the hosting (through FTP)
  • Restore the database backup through the discoutasp.net web interface
  • Start Visual Studio 2008 and Open website.. Choose FTP and supply credentials for the new domain. It works best for me if unchecking "Passive connection.."
  • Open web.config in VS and update the connectionstring under /configuration/appSettings umbracoDbDSN key and /configuration/connectionStrings
  • Delete the index.htm placed on the root by discountasp.net

That's it. It just works! :-)

It seems as DiscountASP.net has the right prerequisites to run Umbraco v4 (Full trust level etc). It's also popular amongst the other Umbraco developers.

 6784 comment(s)



Flash as background on web pages / stock selling

Flash  Flash 10  Actionscript 3  Pixel Bender 
20 November 2008

Doing some experimenting of putting a flash SWF as a background bellow HTML and creating a stock selling package for FlashDen.net.

After some initial trying and failing, I ended up with a minimalistic approach that seems to work on all major browsers. The solution is intuitive; absolutely positioning a DIV element (html based content) over a DIV containing the Flash SWF, and using the z-index css property to handle z-order. Export settings when building the SWF also needs to be correct. 

Having discovered FlashDen.Net, I decided to take my Recursive Zoomer and package it in the format FlashDen.Net requires. It literally took hours, as I had to make a preview package, preview image, and the purchase package. I set up a small mini site that will be used for previewing on their site, here it is:RecursiveZoomer Preview Site Thumbnail

Currently number 96 in their review queue.

UPDATE: After about 5 days in their review queue my package was rejected since they do not accept CS4 (Flash 10) stuff yet.

 3 comment(s)



Recursive Zoomer

Flash  Flash 10  Actionscript 3  Pixel Bender 
19 November 2008

Another small experiment from me. Reproducing in Pixel Bender that recursive zoomer effect from the Amiga demo scene ;-)

Use the mouse to change parameters on the interactive versions (mouse at the center for most stability). Configurable through a XML settings file.

 

Linear sampling :

Non-interactive fullscreen
Linear sampling :

Interactive in window mode
Nearest neighbor sampling :

Interactive in window mode
 

 3 comment(s)



Cloud and Landscape Generator

Flash  Flash 9  Actionscript 3  Fractals 
18 November 2008

Building my base of reusable code, I spent the last few days implementing, tweaking and making examples of a fractal based clound/landscape generator.

Since the Flash 10 penetration rate will need some time to get close to 100%, it's implemented for Flash 9. Everything is generated in code, so it would be perfect for projects with size limitations or when requiring to generate content dynamically.

 

Base algorithm:

Same result as Photoshop Filter menu -> Render -> Cloud ..
Landscape generator:

Heightmap and texture accessable independently (as 2D arrays or as Actionscript Sprite or Bitmap) ..
Dark (k)night psychedelic:

An example of use. Infintely looping 3 layer parallax scrolling using Flash blend modes, distance based bluring, and horizontal flip for faster init time and psychedelic effect.
Dark (k)night clean:

Similar but no filters or blend modes.
Clean blue skies:

 

 

 3 comment(s)



Pixel Bender test drive

Flash  Flash 10  Actionscript 3  Pixel Bender 
10 November 2008

Flash 10 is out, and delivers a host of new cool features. One in particular attracted my attention: Pixel Benders! It's time to make a minimalistic example and see how it all works.

It's just a simple test that combines the distances from the current pixel to 9 points that I move around using sin/cos in Flash. The Pixel Bender language v1.0 does not support loops, so they need to be unrolled unfortunatly.

<languageVersion : 1.0;>

kernel NewFilter

<

namespace : "PBTest";

vendor : "Dag Erlandsen";

version : 1;

description : "WobblySphere effect";

>

{

    parameter float2 imgsize

    <

        defaultValue : float2(512.0, 512.0);

        minValue : float2(0.0,0.0);

        maxValue : float2(4096.0,4096.0);

    >;

 

    parameter float ofs

    <

        defaultValue : float(5.0);

        minValue : float(0.0);

        maxValue : float(9.0);

    >;  

 

    parameter float2 sphere1;

    parameter float2 sphere2;

    parameter float2 sphere3;

    parameter float2 sphere4;

    parameter float2 sphere5;

    parameter float2 sphere6;

    parameter float2 sphere7;

    parameter float2 sphere8;

    parameter float2 sphere9;   

 

    output pixel4 dst;

 

    void evaluatePixel()

    {

        float2 pos = outCoord()/imgsize;

 

        float r1 = 1.-distance(pos,sphere1);    // [0,1]

        float r2 = 1.-distance(pos,sphere2);

        float r3 = 1.-distance(pos,sphere3);

        float r4 = 1.-distance(pos,sphere4);

        float r5 = 1.-distance(pos,sphere5);

        float r6 = 1.-distance(pos,sphere6);

        float r7 = 1.-distance(pos,sphere7);

        float r8 = 1.-distance(pos,sphere8);

        float r9 = 1.-distance(pos,sphere9);

 

 

        float r = r1+r2+r3+r4+r5+r6+r7+r8+r9 - ofs;

 

        dst = pixel4(r/2.,r,r/3.,1);

    }

}

 

 

 

There has been lots of confusion about the promised GPU acceleration in Flash Player 10. Tinic Uro ( http://kaourantin.net ) which seems to be part of the Adobe Flash development team, stated quite clearly in his May 20th blog post that GPU acceleration would not be enabled for Pixel Benders inside the Flash player, but given that his blog post was written half a year before release, and the Pixel Bender language is based on GLSL I had to give it a try and see for myself. So I exported the SWF with the wmode set to GPU in addition to the CPU ("no hardware acceleration") version.

Try the wmode="gpu" version:

GPU

Clearly Pixel Benders are not executed on the GPU inside Flash 10 player, but still delivers good performance as the JIT compiler produces a lot more efficient code than AS3.0, SIMD execution units on the CPU are utilized, and it is multithreaded by nature so it executes across all CPU cores. Testing the performance on a few different systems gives the following results (results from the Pixel Bender toolkit that runs the kernel on the GPU are also included):

  • Ultra low-end laptop: Celeron M 370 (single core Pentium M @1.5GHz) | 10 FPS | (100% CPU usage) Flash Player 10
  • Core 2 E6600 (dual core @ 2.4GHz) | 50 FPS | (90% CPU usage) Flash Player 10
  • Core 2 Q6600 (quad core @ 2.4GHz) | 90 FPS | (80% CPU usage) Flash Player 10
  • Geforce 7600GT | ~250 FPS | Pixel Bender Toolkit
  • Geforce 8800GTS | ~1200 FPS | Pixel Bender Toolkit

About 1200 FPS on a Geforce 8800GTS. Wow! What a shame this performance isn't available through Flash. The good news is Pixel Bender code is scaling really well across cores..

On a side note; Intel will release a "many core" chip called Larrabee, but it would probably only be accessable through simulated DirectX, OpenGL or a native API. See the August 08 paper. Realtime raytracing's getting closer! :-)

 8 comment(s)



Water Effect

Flash 9  Flash 10  Actionscript 3 
13 January 2009

Here's a water effect I made some time back.

 Flash 9 version :  Flash 10 version :
   

The only difference between the versions is I'm using typed arrays (Vector) in the Flash 10 version, which gives a nice performance boost.

It would run smoothly at higher resolutions if implemented as a Pixel Bender kernel, but I'm saving that for another day. :-)

 3 comment(s)