tag:blogger.com,1999:blog-17010977410880125002024-03-06T12:02:23.424-08:00volgogradetzzzMath, physics, flash.Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.comBlogger28125tag:blogger.com,1999:blog-1701097741088012500.post-41061153403542889392014-09-28T08:00:00.002-07:002014-09-28T08:03:40.392-07:00Planet Shooter. Optimizations.<div dir="ltr" style="text-align: left;" trbidi="on">
So, I made fixes I mentioned in my last post. Plus I made everything as I could [Inline]. And this gave tremendous performance gain - up to +30%! Now I can handle 800 objects with 60 fps!<br />
<br />
<a name='more'></a>The video:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/q9ybyjl4Dnw?feature=player_embedded' frameborder='0'></iframe></div>
<br />
Some interesting things I learned about [Inline] in as3 (in addition to well known, such as mandatory to function be final, static, global):<br />
<ul style="text-align: left;">
<li>you can't use <b>super</b> keyword inside method marked as [Inline]. That's logical - compiler just take the method's code and places it instead of function call. So in the new place there's simply no such <b>super</b>.</li>
<li>you can't use optional parameters with method marked as [Inline]. You'll get compiler error if you'll try.</li>
<li>you can't debug a code succesfully inside method marked as [Inline].</li>
</ul>
For now I can say that I finished the core engine for my game. I heavily optimized it, can explain every line of code, fixed all bugs that I found. It seems that it's stable and fast. I don't have memory leaks - this is how the game session looks in <b>Scout</b>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiclFhcwPV1vfDxjf7d-xX2G9UFUi9AX6InkQ2bvIrcnt8Qd7BppoHjjJvFblsATecj3WDEe_EFQ8fP8IsGsqauI_pP0K3tdNdWD-ZFc34GkHu46neRl7_MN-qwyQshk7AxKDvMyLrIUUc/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiclFhcwPV1vfDxjf7d-xX2G9UFUi9AX6InkQ2bvIrcnt8Qd7BppoHjjJvFblsATecj3WDEe_EFQ8fP8IsGsqauI_pP0K3tdNdWD-ZFc34GkHu46neRl7_MN-qwyQshk7AxKDvMyLrIUUc/s1600/5.png" height="179" width="320" /></a></div>
<br />
<div>
As you can see, the only memory allocations I have are done with player runtime and are unavoidable.<br />
<br />
So from now on I'm starting to work on graphics! No more ugly cubes and spheres!</div>
</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-80351961090965613692014-09-28T07:35:00.002-07:002014-09-28T08:03:24.040-07:00Parallax Mapping Agal2 update.<div dir="ltr" style="text-align: left;" trbidi="on">
Very quick post. Just recompiled my <a href="http://volgogradetzzz.blogspot.de/2013/04/parallaxmapping-stage3d-conference.html" target="_blank">parallax experiment</a>. Previously it worked with FP11.6 beta only, which is very hard to find this days. Now you can see the demo if you have FP15.<br />
<a href="https://dl.dropboxusercontent.com/u/77219109/ParallaxAgal2/index.xhtml" target="_blank">Parallax Mapping Agal2</a> and <a href="https://dl.dropboxusercontent.com/u/77219109/ParallaxAgal2_2/index.xhtml" target="_blank">one more</a></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com10tag:blogger.com,1999:blog-1701097741088012500.post-30004640085789698492014-09-25T13:40:00.001-07:002014-09-28T08:02:59.726-07:00Planet Shooter. Physics 2.<div dir="ltr" style="text-align: left;" trbidi="on">
In my last post I mentioned that using brute-force approach for collision detection I can handle 200 objects on my machine with 60 fps. My next step was to add some spatial structure for fast collisions.<br />
<br />
<a name='more'></a> For that I choosed uniform grid - it's easy to manage and simple to implement. For debugging purposes I created grid mesh. But it was hard to see anything in this mess of cubes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKKRTY0TxLI3RpYWeIwp_ygOtgjc6EwhYNbRXkLLUVp2K1Y98tw2xAT5yCqKFRQVq-xt1i5CKejUE3MkUd5-Rj00RP_MDwWBIxVJXm3j7dn_AX8U-1DsZ_U3Nlo2kClxRLq0tw90WVaew/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKKRTY0TxLI3RpYWeIwp_ygOtgjc6EwhYNbRXkLLUVp2K1Y98tw2xAT5yCqKFRQVq-xt1i5CKejUE3MkUd5-Rj00RP_MDwWBIxVJXm3j7dn_AX8U-1DsZ_U3Nlo2kClxRLq0tw90WVaew/s1600/4.png" height="240" width="320" /></a></div>
<br />
So my next (and current) approach is just to show a cell that is occupied by an object. Like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="https://i.ytimg.com/vi/yxl_Tc3oVto/0.jpg" height="266" width="320"><param name="movie" value="https://www.youtube.com/v/yxl_Tc3oVto?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="https://www.youtube.com/v/yxl_Tc3oVto?version=3&f=user_uploads&c=google-webdrive-0&app=youtube_gdata" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object> </div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The color of cell depends on the number of objects inside it.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Using this grid I can now have 500 colliding objects with 60 fps.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/0XRsstPT3TU?feature=player_embedded' frameborder='0'></iframe></div>
<br />
In the end you can see how simulation explodes. And the bad thing that fps dramatically drops. I'll try to explain why.<br />
<br />
When I'm finding that object collides I separate it from another. Now, with a new position I make a new test with another object and if there's colision again I separate them again. Probably again to the old position. And so on. When my main ship stands still all other cubes move to it. Actually they all move to one point - center of my ship. There's a big competition for the place. Because of that object can jitter a lot. And when it change it's position, it's matrix should be recalculated. Moreover, in one single time in one cell can bee too many objects.<br />
<br />
For grid implementation I choosed a <b>vector </b>for storing objects in one cell, i.e. when a ship goes to new cell it should be removed from the old one and added in the new. Since I'm using a <b>vector </b>the only way I know for object to be removed is <b>splice()</b> method. And this method causes a memory leak and performance issues (because of <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/statements.html#..._%28rest%29_parameter" target="_blank">rest parameter</a>). Again, when an object jitters it changes cells very often causing <b>splice()</b> to be called more intensivelly. Of course I'll change it to something different, linked list probably.<br />
<br />
My next step will be not to set new position every time object collides (and recalculate a matrix), but to use some sort of accumulation buffer. For every collision I'll add offset to it and apply translation only once - after all calculations are done. This is of course not accurate, but I think it worth to try to see how it'll behave.<br />
<br />
Another optimisation I'll try is a final matrix calculation. Currently object's transform consists of three matrices - scale, rotation, translation. In my current game I don't use scale at all. For some objects rotation or translation not needed. So why to use them, that's a lot of calculations. I'll introduce maybe a flag which will tell should I use some matrix or not.<br />
<br />
That's it, stay tuned.</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-22555451347142564122014-09-14T15:11:00.002-07:002014-09-28T08:03:09.047-07:00Planet Shooter. Physics.<div dir="ltr" style="text-align: left;" trbidi="on">
Hi dear readers. Today I'm starting a series of posts about a game I'm working on in my free time. I'm seriously planning to release it. And here I'll write about my progress.<br />
<br />
<a name='more'></a>Current state of the game can be found here:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/NpmCunQoWs8?feature=player_embedded' frameborder='0'></iframe></div>
<br />
It's not pretty. Yet. But I'm planning to make an eye candy because I know that with flash it's possible. In the end I want to be close to the great <a href="https://www.youtube.com/results?search_query=Super+Stardust" target="_blank">Super Stardust</a> game for PlayStation. But I don't want to make pure clone. Anyway, this blog is not about game design but about technology.<br />
<br />
I don't want to use 3rd party libraries and my game uses only my code, no any engines. Even vectors and matrices are mine. I chose an approach of "lazy" update of game objects. This means that every time when I'm asking for position or rotation of an object, code checks if it was changed and if it was - it calculates necessary data. Currently I'm thinking about switching to another system, so I'll not write about it in detail.<br />
<br />
For physics I'm using bounding spheres. That will be the only volumes I'll use. They're very simple and fast. For fast moving objects (bullets) I'll use continuous collision detection with spheres. In the posted video I'm using a brute force - I'm checking all objects with each other. I'm pretty happy with performance, but my next step will be spatial hierarchy for physics. I'll use simple uniform grid.<br />
<br />
Currently I have this stats on my machine:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1S07IFWjzCC57RZUK469KYG1sp93gHCarL5E2y3xMt9l1B8VaSVIKwDEUi5LcdY7Eg0to6iaA6H3TjlzI8mI-ajxiBv2Mv8Fg5BjrSKlrRrBJTJcrcfxwMMmn0vB3dMvs9gQmQSRNbJY/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1S07IFWjzCC57RZUK469KYG1sp93gHCarL5E2y3xMt9l1B8VaSVIKwDEUi5LcdY7Eg0to6iaA6H3TjlzI8mI-ajxiBv2Mv8Fg5BjrSKlrRrBJTJcrcfxwMMmn0vB3dMvs9gQmQSRNbJY/s1600/2.png" height="179" width="320" /></a></div>
<br />
This is for 200 objects. Not too much. Here you can see that most of the time was spent on colision detection. We'll see how it'll be improved once spatial grid will be ready.<br />
<br />
Couple of interesting moments:<br />
<ul style="text-align: left;">
<li>On this slide you can see a big performance drop. This is caused by some "magic" <b>Waiting for Next frame</b>.</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigTPkz6zAqhUAC_JWSI04T7jm-KItcgs8dbsGBED4BL3ije5ssNtPMJU5rWbN2_1ia5zudpsLDAA7ecxKFZB906NOXCFyAbGsEiU21hlCJdx6lZbOyJ9hW6djXmTWjKFjx4OptmFsJays/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigTPkz6zAqhUAC_JWSI04T7jm-KItcgs8dbsGBED4BL3ije5ssNtPMJU5rWbN2_1ia5zudpsLDAA7ecxKFZB906NOXCFyAbGsEiU21hlCJdx6lZbOyJ9hW6djXmTWjKFjx4OptmFsJays/s1600/1.png" height="179" width="320" /></a></div>
<br />
To be honest I don't know what it is. All I could find is that waiting appears because my CPU is busy with some other task. But that's strange - this spike appears periodically - maybe once per second. If you have a clue what is this - please drop me a comment.<br />
<br />
<ul style="text-align: left;">
<li>Next interesting thing is memory allocation.</li>
</ul>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKXt6ecjLVlwdR7cn-GbRR0S4FsPe2rwk6Qdzeb678Me6yso49vhei1twSQ-Ur4Z93h81ucaP-sOb6a-K1_BsAMGz5vHl6h4H6dpjVbtoYYdNwxH-Narztzia9EuGqeWG1qoSmrtXkt_E/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKXt6ecjLVlwdR7cn-GbRR0S4FsPe2rwk6Qdzeb678Me6yso49vhei1twSQ-Ur4Z93h81ucaP-sOb6a-K1_BsAMGz5vHl6h4H6dpjVbtoYYdNwxH-Narztzia9EuGqeWG1qoSmrtXkt_E/s1600/3.png" height="179" width="320" /></a></div>
<br />
I'm little crasy about optimization, so I don't allow to create new objects during a game. I'm using pools intensively. But some allocations are impossible to avoid - native events, for example. It's ok, we're living with it for a long time and it will never change. But on the slide you can see some <b>MethodClosure</b> in allocations. This is caused by <b>getTimer()</b> call. And this is not good. I don't like such surprises from a flash player. And in this case I can't avoid it - there's no meaningful replacement for <b>getTimer()</b>. So All I can do is just hope that Adobe someday will fix it. Good news - they fixed new string creation on <b>context3d.driverInfo</b> call!<br />
<br />
That's all for today. Stay tuned. It'll be interesting.<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com2tag:blogger.com,1999:blog-1701097741088012500.post-75881227573600234962014-03-26T15:03:00.000-07:002014-03-26T15:03:03.810-07:00Open Letter to Adobe<div dir="ltr" style="text-align: left;" trbidi="on">
Adobe! You're loosing the most devoted fans. It's time to think about platform monetization while it's not too late. Please, don't kill Flash.<br />
<br />
<a href="http://plugin.io/open-letter-to-adobe/" target="_blank">http://plugin.io/open-letter-to-adobe/</a></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-81426427377731632032014-03-11T01:59:00.000-07:002014-03-12T00:26:05.179-07:00OIT - real world example<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
Today just a quick demo - real world demo of my last experiment.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdr0Im_uE-di8hVhmMfAh08ePCDCYFaayKvFygTil9ydX0CZ9E4PcIB_TD5yKShSQZ91OglQgjFci3bFYPtdG77i1uUlEDMfcDeijCCu92Mcs6RNcV_u2ctCdLJteuB-VA7dclMAyMKxc/s1600/screen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdr0Im_uE-di8hVhmMfAh08ePCDCYFaayKvFygTil9ydX0CZ9E4PcIB_TD5yKShSQZ91OglQgjFci3bFYPtdG77i1uUlEDMfcDeijCCu92Mcs6RNcV_u2ctCdLJteuB-VA7dclMAyMKxc/s1600/screen.png" height="320" width="320" /></a></div>
<br />
<a name='more'></a>Use slider to change number of peels.</div>
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropboxusercontent.com/u/77219109/oit/Main2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object></div>
<br />
P.S.: still not working in pepper plugin. And also I realized today that all my demos not working in firefox (blogger fault).<br />
<br />
Upd.: here's a <a href="https://dl.dropboxusercontent.com/u/77219109/oit/index.xhtml" target="_blank">link</a> to external swf. Should work in all browsers. </div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-51314872222408056322014-03-08T11:52:00.003-08:002014-03-08T12:09:36.190-08:00Order independent transparency in flash<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. The word of warning in the beginning - my approach is some sort of depth peeling algorithm, but it's not true depth peeling - it's impossible in flash yet. It also not uses different buffers - A, K etc - simply because we don't have it in flash. My Approach uses too many passes to be useful real-time technique. It's just a proof of concepts and was a good train for my brain :)<br />
<br />
<a name='more'></a>The algorithm is quite simple - we choose the number of layers we want to draw, draw that layers by order starting from the closest to the viewer and blending results.<br />
<br />
Step by step it looks like:<br />
- clear main depth buffer to 0.<br />
- clear main color buffer.<br />
for every layer:<br />
- draw to render target with depth compare LESS. If current depth is greater than depth in main depth buffer, discard pixel. Now we have depth for current layer.<br />
- draw color to render target. If current depth is equal to the depth for current layer - draw color. Now we have color for current layer.<br />
- blend newly created color layer with main color buffer.<br />
- combine layer depth with main depth - write in main buffer greatest depth.<br />
<br />
It sounds complicated, but it's very simple. Let's make it step by step.<br />
<br />
1. Imagine that we want to draw 3 planes. This is side view, so planes are just lines. The arrow is our looking direction:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAY5oG0lsxfVsfqiuIUodppjjk1OGcf5RSHmCUqjlG3EMY7LbV4Wrg68ax-DnxgYiutWAHeUxEVXBs2_-3CmfXTiqAy0OKQSILOrpEWFhCzjFwlsfIcKtKgK0a8x20wEKv8MwYImAaQXA/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAY5oG0lsxfVsfqiuIUodppjjk1OGcf5RSHmCUqjlG3EMY7LbV4Wrg68ax-DnxgYiutWAHeUxEVXBs2_-3CmfXTiqAy0OKQSILOrpEWFhCzjFwlsfIcKtKgK0a8x20wEKv8MwYImAaQXA/s1600/1.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
2. We draw to depth buffer. Since we have compare mode set to LESS we'll get this depths:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguye86IW29RwjGYiN2OagD1pZq4reGQpa103zObNVU_bpJvT0WII3mXFpiIasLd6FeXGstKzUYzktzVJgFb68y6g3G7DkDNQ-dd4C7xLw4SauHa1b8MhHfNgA1ySdrh3SLud_0vX5mHhM/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguye86IW29RwjGYiN2OagD1pZq4reGQpa103zObNVU_bpJvT0WII3mXFpiIasLd6FeXGstKzUYzktzVJgFb68y6g3G7DkDNQ-dd4C7xLw4SauHa1b8MhHfNgA1ySdrh3SLud_0vX5mHhM/s1600/2.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
i.e. the closest pixels to camera (red lines).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
3. Now we draw a new layer. We draw the same geometry again, but this time we compare depths. If depth is the same as in previous layers or greater we discard pixel. And, again, since we have compare mode set to LESS we'll receive closest pixels to camera. But since we discard pixels that already drawn we get next closest to camera layer (green lines)!</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSysrTAXbZDiZcxEoTKNnbb0qfQSFe485fa_EJE20OkiqqIy1RcavGY9PlfJmmA0anqpf8-fhgiAqM_EjBGraPWv3DPm_uikcTPJNi3elAM_pkuV8CIfR9yAIoiKfF7LYuxapR1N55liY/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSysrTAXbZDiZcxEoTKNnbb0qfQSFe485fa_EJE20OkiqqIy1RcavGY9PlfJmmA0anqpf8-fhgiAqM_EjBGraPWv3DPm_uikcTPJNi3elAM_pkuV8CIfR9yAIoiKfF7LYuxapR1N55liY/s1600/3.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
4. We need to combine depths from previous and current layers and this resulting depth in next layer draw. That's simple - we read depths from both depth buffers and write the greatest one. So, next time we'll draw new layer we'll discard already drawn pixels. The combined depth buffer now should look like this (purple lines):</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2aFLUewlmgLoG4jhWiW6BC-U4_ZyC5RY4YkMoI2lD-BFFOG3m0zPAU8FmKkbNgF9JCG8Jjq2gYbil_zSyjaKHJcmqGqxixaIerrqzBwrn0IB7iP1XK9ktxJKZ49B0_y8t2s7c5Dm6cR8/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2aFLUewlmgLoG4jhWiW6BC-U4_ZyC5RY4YkMoI2lD-BFFOG3m0zPAU8FmKkbNgF9JCG8Jjq2gYbil_zSyjaKHJcmqGqxixaIerrqzBwrn0IB7iP1XK9ktxJKZ49B0_y8t2s7c5Dm6cR8/s1600/4.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
5. Draw a new layer. Same as point 3 (blue line).</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEickkw7Vhs2sUgKOe1EX_UMmzA2DPNytGxUKWXCdKcEoBdRlSC9f7SAogGwC7tisTTCkQJY5p2brScEDFc1FhIPZW04bYV3OVBA4n632jQvHdZ-fFye1kf0XYDeCCGfgZvjqD6JQs2efUs/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEickkw7Vhs2sUgKOe1EX_UMmzA2DPNytGxUKWXCdKcEoBdRlSC9f7SAogGwC7tisTTCkQJY5p2brScEDFc1FhIPZW04bYV3OVBA4n632jQvHdZ-fFye1kf0XYDeCCGfgZvjqD6JQs2efUs/s1600/5.png" height="320" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
That was how we manage depths. And knowing a depth for a layer we simply draw geometry (yeah, again) and write only pixels that belongs to that layer - comparing depths (they should be equal).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Having a color map for a layers we need to blend them. We need a trick - we can't have separate texture for every layer. I use 3 textures. Imagine that we have already one layer drawn - it will be in some texture "A". Next, we need to add new layer under it (recall - we're drawing layers from front to back) - this new layer we hold in some texture "B". We take this 2 textures "A" (destination) and "B" (source) and draw to some texture "C" with some blending formula. I use standard alpha blending as <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#setBlendFactors()" target="_blank">here</a> at first, but this gives not good-looking effect (it's a simple and fast formula, but not correct). And I took this formula (from Wikipedia):</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://upload.wikimedia.org/math/0/6/3/063478901c560e94a62ff0d69710157d.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://upload.wikimedia.org/math/0/6/3/063478901c560e94a62ff0d69710157d.png" height="54" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So, our texture "C" with blended "A" and "B" now will be used in next pass as source (blended over new layer). And so on.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The demo shows all this in action (notice that packing and unpacking float to rgba channels is not very precise, so I added some epsilon value when compare depths. And this introduce some artifacts if depths of layers nearly equal or depth is near zero):</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<object height="500" width="500">
<param name="movie" value="https://dl.dropboxusercontent.com/u/77219109/oit/Oittest.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Source can be found <a href="https://github.com/nikitablack/tests/tree/master/oit_test" target="_blank">here</a>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Upd.: don't know why, but this demo doesn't work in pepper plugin. Will try to find out.<br />
<br />
PS: I heard that it's possible to achieve same thing using stencil operation. I tried a lot, but I can't make it work. If someone will point me to such algorithm - it would be great ).<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com4tag:blogger.com,1999:blog-1701097741088012500.post-58639666034588293642014-01-17T12:44:00.000-08:002014-01-17T12:51:44.009-08:00Air 4.0 and new graphics stuff<div dir="ltr" style="text-align: left;" trbidi="on">
Hello everyone. It was silent here for a long time. I was quite busy - my son was born and I changed a job and moved to new country. Now it seems I have some time for blog.<br />
<br />
So, what we have? As you may already know new Air sdk was released recently with some stage3d API improvements. The new method for context request and new buffer usage flag were introduced.<br />
<br />
<a name='more'></a>New stage3d request is simple as cake - you just call <b>Stage3D.requestContext3DMatchingProfiles()</b> method with <b>Vector</b> of profile strings (<b>Context3DProfile</b> enum). The highest possible profile will be created.<br />
<div>
<br /></div>
<div>
The next new thing is much interesting - new buffer usage flag. Now in <b>Context3D.createVertexBuffer()</b> you can pass <b>bufferUsage</b> flag (<b>Context3DBufferUsage</b> enum). Currently there're two flags to use - <b>staticDraw</b> and <b>dynamicDraw</b>. Static draw is what we had before. Dynamic draw is good if you change your buffer data often, for example, every frame. You can find more info here - <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb147263%28v=vs.85%29.aspx#Using_Dynamic_Vertex_and_Index_Buffers" target="_blank">buffer usage on msdn</a> and here - <a href="http://www.opengl.org/wiki/Buffer_Object#Buffer_Object_Usage" target="_blank">buffer usage on opengl.org</a>. I expecting some performance improvements.<br />
<br />
I made a small test - for buffer I reupload data every frame. For desktop I upload 40000 floats 4 times per frame. For mobile I upload 4000 floats 4 times per frame. There're separate tests for <b>ByteArray</b> and <b>Vector</b> data containers.</div>
<div>
<br /></div>
<div>
Here're results:</div>
<div>
<br /></div>
<div>
1. Dynamic buffer usage flag. ByteArray. Desktop.</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopByteArrayDynamic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopByteArrayDynamic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">2. Static buffer usage flag. ByteArray. Desktop.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopByteArrayStatic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopByteArrayStatic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">3. Dynamic buffer usage flag. Vector. Desktop.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopVectorDynamic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopVectorDynamic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">4. Static buffer usage flag. </span><span style="text-align: left;">Vector</span><span style="text-align: left;">. Desktop.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopVectorStatic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/desktopVectorStatic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">5. Dynamic buffer usage flag. ByteArray. Mobile.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileByteArrayDynamic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileByteArrayDynamic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">6. </span><span style="text-align: left;">Static </span><span style="text-align: left;">buffer usage flag. ByteArray. </span><span style="text-align: left;">Mobile</span><span style="text-align: left;">.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileByteArrayStatic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileByteArrayStatic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">7. Dynamic buffer usage flag. </span><span style="text-align: left;">Vector</span><span style="text-align: left;">. </span><span style="text-align: left;">Mobile</span><span style="text-align: left;">.</span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileVectorDynamic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileVectorDynamic.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;"><br /></span></div>
<div class="separator" style="clear: both; text-align: justify;">
<span style="text-align: left;">8. </span><span style="text-align: left;">Static </span><span style="text-align: left;">buffer usage flag. </span><span style="text-align: left;">Vector</span><span style="text-align: left;">. </span><span style="text-align: left;">Mobile</span><span style="text-align: left;">.</span></div>
<div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileVectorStatic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://dl.dropboxusercontent.com/u/77219109/bufferUsageTest/mobileVectorStatic.png" width="320" /></a></div>
<div>
<br /></div>
<div>
As you can see on mobile there's absolutely no difference. I made tests on Samsung Nexus 7 with Tegra 3. Maybe all this stuff is gpu-sensitive, so I will not say that this flags are not working on mobile.</div>
<div>
<br /></div>
<div>
The interesting thing is on desktop (I tested on AMD FirePro 2270). Scout says that as3 execution time is dramatically different for different flags. For dynamic usage as3 part is very fast. Much faster then for static usage. But if you'll look carefully onScout chart you'll notice the grey bars and description says - waiting for gpu 25 ms. So, in total we get the same fps in both cases. But with static buffer we're waiting for gpu finish reading data and at this time cpu is locked. And with dynamic buffer usage we can do another stuff while waiting for gpu. So this time can be spent on physics and ai - the total performance of application can be increased. I think that frameworks with Starling-like architecture will benefit a lot with this flag.<br />
<br />
The interesting question - why do we need static buffer at all? Honestly, I don't know and hope that someone will bring some light. I can only asume that static buffer can be faster on gpu due to some position in the memory - after all it's static.<br />
<br />
The source code for tests can be found <a href="https://github.com/nikitablack/tests/tree/master/bugger_usage_test" target="_blank">here</a>.</div>
</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com3tag:blogger.com,1999:blog-1701097741088012500.post-53362743397489570292013-06-11T00:29:00.000-07:002013-06-11T04:17:41.503-07:00Small optimization trick for heavy shaders<div dir="ltr" style="text-align: left;" trbidi="on">
As you may know depth buffers allows to draw 3d objects in any order and they will appear correctly - closer one will obscure farther one. But draw order is important anyway. For example, consider a scene where you're drawing from back to front. You draw an object, it passes the depth test for particular pixel and that means that pixel shader will be executed for this pixel. Next you draw another object. But since our order from back to front, this object will pass depth test and pixel shader will be executed again. Ans so on...<br />
<br />
<a name='more'></a>There's obvious solution - sort object front to back. Then, in modern hardware early depth test will be executed and obscured pixels will be discarded early. But this sorting should be done on CPU and this is expensive.<br />
<br />
I found recently an interesting trick to simulate such sorting:<br />
<ul style="text-align: left;">
<li>Disable color write. Enable depth write. Set Context3DCompareMode.LESS depth test. Render to back buffer.</li>
<li>Enable color write. Disable depth write. Set Context3DCompareMode.EQUAL depth test. Render to back buffer.</li>
</ul>
<div>
In plane english it looks like this: you're writing to depth buffer only in first pass. And it's a very fast operation. You make usual depth test - update depth buffer only when new fragment closer to camera. In the second pass you don't update depth buffer but check current depth with depth in that buffer (from the first pass it contains only "closest" pixels). So if current pixel is obscured it will be rejected by hardware.</div>
<div>
<br /></div>
<div>
In code:</div>
<pre class="prettyprint">_context3D.setColorMask(false, false, false, false); // disable color write
_context3D.setDepthTest(true, Context3DCompareMode.LESS); // enable depth write, < compare mode
draw();
_context3D.setColorMask(true, true, true, true); // enable color write
_context3D.setDepthTest(false, Context3DCompareMode.EQUAL); // disable depth write, == compare mode
draw();</pre>
<div>
<br /></div>
<div>
Here's a demos that illustrates a concept.</div>
<div>
This demo uses brute forse - make a draw call for every object without any depth buffer state change:<br />
<br /></div>
<object height="500" width="500">
<param name="movie" value="https://dl.dropboxusercontent.com/u/77219109/depthTest/DepthBufferTest1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<div>
<br /></div>
<div>
This demo uses a trick described above.<br />
<br /></div>
<object height="500" width="500">
<param name="movie" value="https://dl.dropboxusercontent.com/u/77219109/depthTest/DepthBufferTest2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<div>
<br /></div>
<div>
P.S.: some interesting notes. In my desktop first I've used teapots with phong lightning. I've got fps drop with 300 teapots (draw calls) approximatelly. And state changes didn't give any result at all. This is because GPU works so fast that I've got my fps drop due to number of draw calls only. Next, I've added dummy instructions to my vertex and pixel shaders. Many instructions - up to the limit. And again the same thing - fps drop at 300 teapots. So I can measure difference only with texture calls, which is heaviest operation on GPU. In the demos above I've used something about 60 tex instructions. But this is all about good desktop video card. On laptops and mobile things are diferent...</div>
</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com2tag:blogger.com,1999:blog-1701097741088012500.post-60869543308860926522013-04-12T10:51:00.002-07:002013-06-11T04:17:15.390-07:00ParallaxMapping Stage3D conference speaking slides and source<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. Today just links to the slides and sourse code from my speak on stage3d conference.<br />
<br />
<a href="https://dl.dropboxusercontent.com/u/77219109/ParallaxMappingConf.zip">https://dl.dropboxusercontent.com/u/77219109/ParallaxMappingConf.zip</a><br />
<br />
P.S.: in order to run source you need to set path to the 11.6 beta player (only player that supports extended profile) in FD properties.<br />
<br />
P.P.S: you can see conference video here:<br />
<a href="http://gonchar.me/blog/goncharposts/1780">http://gonchar.me/blog/goncharposts/1780</a><br />
or<br />
<a href="http://www.youtube.com/watch?v=md_6gzhm9b4">http://www.youtube.com/watch?v=md_6gzhm9b4</a></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-31310041826056131822013-02-06T03:39:00.000-08:002013-02-06T03:51:22.245-08:00Isometric sort. New approach.<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. Today I want to share some idea about isometric sorting. This is not stage3D article. Example was compiled against 10.1 flash player.<br />
<br />
<a name='more'></a>So, what's wrong with existing solutions? Imagine two walls intersects. No matter what you do, one of the walls will be always above.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropbox.com/u/77219109/trueIso/pic1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://dl.dropbox.com/u/77219109/trueIso/pic1.png" /></a></div>
<br />
<br />
Of course this problem can be solved easily by not letting them intersect, or prepare correct asset. But I found a way to fix it in runtime. Take a look at demo.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/trueIso/TrueIso2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
Use mouse to select an object, WASD or arrows for moving in xy plane, SHIFT + W/S/Up/Down for moving along z axis.<br />
<br />
Algorithm is quite simple and uses PixelBender filters. Here's a brief description:<br />
The heart of algorithm are depth maps for every object. In that map 0 means the farthest point and 1 - closest. Then I create something like a depth buffer and compare pixels from that map with buffer. So I can hide pixels that obscured by another.<br />
<br />
What are this maps and how they look? Here a map for red fence:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://dl.dropbox.com/u/77219109/trueIso/pic3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://dl.dropbox.com/u/77219109/trueIso/pic3.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Very strange... But why? In my first approach I decoded depth in single channel of bitmapdata. So, in RGBA only R was used. This is 1 byte, 8 bits, integers 0 to 255 (recall that all depths are in 0..1 range). And this is the reason of some sort of inaccuracy. Imagine for example object with physical depth of 1000 pixels. But we can decode only values from 0 to 255. So, for every 4 pixels approximately I have the same depth value. And this leads to artifacts (z-fightning). To beat it we need to store depth in more than 1 channel. I choose 3 channels - RGB. I'm avoiding to use alpha channel because flash uses premultipled RGB by alpha, but PixelBender not. Quick search in goggle for words "baking float in 24 bits" gave me good solution, so I generate a couple of maps.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Here're steps:</div>
<div class="separator" style="clear: both; text-align: left;">
1. Take bitmapdata with black fill, that means 0 depth.</div>
<div class="separator" style="clear: both; text-align: left;">
2. With blend filter (blendShader) creates depth buffer - for every object I compare depth in map with current depth in buffer. If objects depth is greater than this value is written to buffer. If lesser, that means that current pixel is behind, so depth value stays as it was.</div>
<div class="separator" style="clear: both; text-align: left;">
3. For every object I set filter which again compares depth in map with final depth in buffer. If values are approximatelly equals that means that pixel is visible. If not, I set pixels alpha to 0.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Several steps are needed because of PixelBender limitations - we can output only to one source.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This approach have an advantage - it's not neccessary to manage display object depths - blend filter will do it work and we'll get correct looking picture.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Sad, but there're a lot of disadvantages:</div>
<div class="separator" style="clear: both; text-align: left;">
1. It's veeeeeery slow.</div>
<div class="separator" style="clear: both; text-align: left;">
2. Need for depth maps creating. I wrote an utility that builds maps by several points. But for complex objects it will be hard.</div>
<div class="separator" style="clear: both; text-align: left;">
3. Only integer values for x, y, z can be used or artifacts will appear.</div>
<div class="separator" style="clear: both; text-align: left;">
4. There's a problem with transparent objects.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
As you can see there're more minuses than pluses. For me it was a challenging task - I met a couple of new algorithms, improve my PixelBender skills. But I think method can't be used for any production. Just as nice demo ).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
P.S.: article available in russian (with source code) - <a href="http://www.flasher.ru/forum/blog.php?b=630" target="_blank">link</a></div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-3071233769212105402013-01-01T08:38:00.002-08:002013-02-12T22:52:23.681-08:00Parallax Mapping with Stage3D<div dir="ltr" style="text-align: left;" trbidi="on">
Hello and Happy New Year!!! Today I'm just put here my last experiment with AGAL - parallax mapping demo. Be sure that you have 11.6 beta player!<br />
<br />
<a name='more'></a><br />
Use mouse to rotate object. Arrows to move light source. Slider to change mesostructure depth.<br />
<br />
This is just test to ensure that it possible to do with AGAL. There're some visual bugs and shadow bugs. May be I'll fix that someday. Also I used normal mapping.<br />
<br />
All I can say about writing shader code is... it's awful. Assembler is unmaintainable and ugly. And it will be really hard to someone to explore code. This is the reason I don't want to spend hours debugging this mess of opcodes. This is sad (.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/parallaxMapping/ParallaxMapping.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
Update: with official release of flash player 11.6 there's still no features required to run demo. Still works om flash player 11.6 beta.</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com4tag:blogger.com,1999:blog-1701097741088012500.post-55833101913108611222012-12-11T00:09:00.002-08:002013-01-01T23:42:06.568-08:00AGAL condition operators in flash player 11.6<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. Flash player beta 11.6 brings good news to us. Now we can use more serious stuff in shaders. Condition operators are among them.<br />
<br />
<a name='more'></a>And as usual - thre's no info about new features. That's sad. Let's try to investigate it by ourselfs.<br />
First thing we should do to make new stauff works is to request <b>context3d </b>with extended mode:<br />
<br />
<pre class="prettyprint">stage3D.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE_EXTENDED);</pre>
<br />
Next, we should use new AGALMiniAssembler (goodbye my lovely AGALMacroAssembler :'( and hello again ugly assembler). We should take new method <b>assemble2() </b>which returns created <b>program3D.</b><br />
<br />
<pre class="prettyprint">var assembler:AGALMiniAssembler = new AGALMiniAssembler(true);
var program:Program3D = assembler.assemble2(_context3D, 2, vertexShader, fragmentShader);
_context3D.setProgram(program);</pre>
<br />
And here our shaders. Vertex first:<br />
<br />
<pre class="prettyprint">var vertexShader:String = 'm44 op, va0, vc0'; // nothing unusual</pre>
<br />
Fragment:<br />
<br />
<pre class="prettyprint">var fragmentShader:String = '';
fragmentShader += 'mov ft0, fc3' + '\n'; // move constant to temporary register simply because we can't compare constants (Error #3625: AGAL validation failed: Bad AGAL source operands. Both are constants (this must be precomputed) at token 2 of fragment program.)
fragmentShader += 'ife ft0.x, fc2.x' + '\n'; // if(ft0.x == 1)
fragmentShader += 'mov ft1, fc0' + '\n'; // ft1 = (1, 0, 0, 1) - can't write to oc directly (Error #3751: AGAL validation failed: Output registers can not be written inside conditionals.)
fragmentShader += 'els' + '\n'; // else
fragmentShader += 'mov ft1, fc1' + '\n'; // ft1 = (0, 1, 0, 1)
fragmentShader += 'mul ft1, ft1, fc1.w' + '\n'; // ft1 * 1 - just to show that several statements inside condition works
fragmentShader += 'eif' + '\n'; // end if
fragmentShader += 'mov oc, ft1' + '\n'; // oc = ft1</pre>
<br />
Here <b>fc0</b> - red color, <b>fc1</b> - green color, <b>fc2.x</b> - 1 (one), and <b>fc3.x</b> varies depending on input. Inside my loop I check right mouse. If it pressed, I set <b>fc3.x</b> to 0 (zero) otherwise to 1 (one). Take look at demo (remember - you should install flash player 11.6) and press right mouse button.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/116playerTest/116PlayerTest.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
That's all for today.</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com6tag:blogger.com,1999:blog-1701097741088012500.post-55735436084289503852012-12-10T02:04:00.001-08:002013-01-01T23:41:34.130-08:00Rotations in 3D. Part 2 - Rotation in arbitrary point in space<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. Today I'll show you how to rotate 3D object about cardinal axis in arbitrary point in space.<br />
<br />
<a name='more'></a>From <a href="http://volgogradetzzz.blogspot.ru/2012/09/rotations-in-3d-part1-euler-angles.html" target="_blank">previous tutorial</a> you know how to rotate about one of the main axes. Let's refresh:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/arbitraryAxeRotation_1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
In this demo cube is in it's origin. Applying rotation matrix to it cause it to spin about <b>y</b> axis. In pseudocode this looks like this<br />
<br />
<pre class="prettyprint">obj * R; // multiply every vertex in cube by rotation matrix</pre>
<br />
Now let's move cube a little to the right. In pseudocode:<br />
<br />
<pre class="prettyprint">obj * T; // T - translation matrix
</pre>
<br />
After applying same matrix <b>R</b> we'll get strange result:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/arbitraryAxeRotation_2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
Cube rotates about origin, not about it's local axe. In order to rotate about local <b>y</b> we need several steps:<br />
<div style="text-align: left;">
</div>
<ol style="text-align: left;">
<li>Translate cube to origin.</li>
<li>Rotate.</li>
<li>Translate cube back.</li>
</ol>
In pseudocode:<br />
<br />
<pre class="prettyprint">//T - translation matrix of the cube
//M - final matrix to apply
M = inv(T) * M * T;
obj * M;
</pre>
<br />
<div style="text-align: left;">
Here inv(<b>T</b>) - is inverse matrix of <b>T</b> - simply negate of current x, y, z of the cube. Recal that before applying this matrix cube have transformation <b>T</b>. Here's a demo:<br />
<br /></div>
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/arbitraryAxeRotation_3.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
<div style="text-align: left;">
As you can see, all works fine.</div>
<div style="text-align: left;">
Same steps required in order to rotate object about any chosen point in space:</div>
<div style="text-align: left;">
</div>
<ol style="text-align: left;">
<li>We need to ranslate that point to origin and at the same time translate our object by same amount.</li>
<li>Rotate.</li>
<li>Translate object back.</li>
</ol>
Final demo shows this in action:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/arbitraryAxeRotation_4.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
That's all for today.</div>
</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-45477882156622736332012-11-05T00:00:00.000-08:002013-01-01T23:40:54.918-08:002D Light Demo<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn88Rt_Tb1SvPYYlbq_q3fAl4xFe7z2j5Kgjldwggc7wB7RnadCoRKTfTw80RQ3KqwX0SqFeKpqphR1W1mdTJL7N6bMueJQGHBieagqkpjg_hY3JyfMJ6w8FG9dLBmOvio1DHHde_t2Eg/s1600/male_warrior_stanalone.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn88Rt_Tb1SvPYYlbq_q3fAl4xFe7z2j5Kgjldwggc7wB7RnadCoRKTfTw80RQ3KqwX0SqFeKpqphR1W1mdTJL7N6bMueJQGHBieagqkpjg_hY3JyfMJ6w8FG9dLBmOvio1DHHde_t2Eg/s320/male_warrior_stanalone.png" width="320" /></a></div>
<br />
<a name='more'></a>Hi. Not a complete tutorial today - just a demo of what I've been working on. Click anywhere for displacement effect.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/AnimationExtractorGPU.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
Here you can tweak sliders to add multiple lights. There're many interesting things I met when worked on stuff.<br />
<br />
1. The animation you see was created in Flash IDE. And I have to find a way to show this animation with stage3D not changing source at all. The most challenging part was to get transformations of "bones". Translation and scale was not a problem, but skew transform is something tough. Adobe help gives a wrong description of it's own skew in Matrix class. Maybe next tutorial will be about skew transform.<br />
<br />
2. For light effect I used simplified normal mapping from my previous tutorial. Normal map was created with <a href="http://www.crazybump.com/" target="_blank">crazybump</a> programm. In demo I normalize vector to light only in vertex shader. Althought it's not correct it's visually the same as normalizing vector in pixel shader. Tangent and bitangent vectors are constant for every triangle and in vertex shader they multiplied by full transform of the triangle, i.e. scale and skew (but without translation). LightX light is specular light. As can be seen there're no difference betwee normal map and specular map, but the last uses more agal instructions.<br />
<br />
3. I used following algorithm for demo:<br />
<br />
a) set vertex data for background.<br />
b) set background texture.<br />
c) render background to texture.<br />
d) collect vertex data for left animation (yes it's happens every frame).<br />
e) set vertex data for left animation.<br />
f) set texture and normal map for left animation.<br />
g) render left animation with normal mapping shader to same texture as background was rendered.<br />
h) repeat steps d-g for right animation (althought in this particular demo they same).<br />
i) set vertex data for displacement effect.<br />
j) set map texture for displacement effect.<br />
k) render displacement effect in separate texture.<br />
l) finally render texture with background and animations with displacement effect to back buffer.<br />
<br />
A lot of steps. The most sad part that I need to rebuild vertex buffers for animations every frame and send it to GPU. Changing textures are also very bad. On my good desktop computers and "middle age" notebook I got pretty good performance - 60/60 fps. But on older notebooks the situation is very, VERY bad (. Passing textures and vertex buffers are EXTREMELLY slow. For example, sending spritesheet texture and normal map takes 15 ms! In release player. With error checking setting to false. It's awful.<br />
<br />
That's all. If you have a question, feel free to ask.</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com2tag:blogger.com,1999:blog-1701097741088012500.post-60493003004990572092012-09-07T01:47:00.001-07:002013-01-01T23:41:05.314-08:00Rotations in 3D. Part 1 - Euler Angles.<div dir="ltr" style="text-align: left;" trbidi="on">
Hello. Today I want to talk about rotations in 3D. I hope to cover several possibilities to rotate an object, so stay tuned. In first part I'll try to explain what the <b>Euler Angles</b> are.<br />
<br />
<a name='more'></a>Althout that article should be about <b>Euler angles</b>, I'll start with <b>Matrix Form</b>.<br />
<br />
As you may be know, in order to rotate vector, we need to multiply it by some matrix called rotation matrix. In <b>Stage3D</b> API we can use <b>Matrix3D </b>class (I choose different approach - I use my own math classes. You can read more here - <a href="http://volgogradetzzz.blogspot.com/2012/07/i-dont-need-flashgeommatrix3d-class.html" target="_blank">I don't need flash.geom.Matrix3D class anymore</a>) that already have all nessesary methods to tune various transformations. So, for example, you can tell your matrix to tune it's rotation like this:<br />
<br />
<pre class="prettyprint">var m:Matrix3D = new Matrix3D();
m.appendRotation(45, Vector3D.Y_AXIS);
</pre>
<br />
This code just setups entries inside matrix <b>m</b>. And here're this entries for three cardinal axes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOrUF-BoREAkZxdCXEkZcWTZgm8XF6SMeYESjUA07Zq_nx_kLc-vdcDLstuTeoARgyyc_SW_W5M4EdFLnuM7m1yjk0e8VPbWKjQl45veJWBDQHLIv2iA4wJ7zGcR9LwIt7OwQikv3keDY/s1600/rx.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOrUF-BoREAkZxdCXEkZcWTZgm8XF6SMeYESjUA07Zq_nx_kLc-vdcDLstuTeoARgyyc_SW_W5M4EdFLnuM7m1yjk0e8VPbWKjQl45veJWBDQHLIv2iA4wJ7zGcR9LwIt7OwQikv3keDY/s200/rx.png" width="200" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJay9ucFip3vsgyvg3JAECXwIwY0RT1_S9PrF2_xY3ih3P0A-WPb96rtbDRtzLNBxpMsnvb0AaVjBb_Q9kSLoUqlIS_z_X1c3poLIVf_X0V5ZmqIS5VwyLAfYWCh_8xtsv0K1qiSe8gzI/s1600/ry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJay9ucFip3vsgyvg3JAECXwIwY0RT1_S9PrF2_xY3ih3P0A-WPb96rtbDRtzLNBxpMsnvb0AaVjBb_Q9kSLoUqlIS_z_X1c3poLIVf_X0V5ZmqIS5VwyLAfYWCh_8xtsv0K1qiSe8gzI/s200/ry.png" width="200" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEJh29F_hH6DQlpl2il1DigMUDsSw83-4JbJjIIbH_dsa42Zgr9jWc2dOWSCcrtS91e1Oq8qDx5Fvsjfo8K8MUJASmifHZdSEL9w4n_d-ejBKdzjd11tOZZ8Ky1zIVLtE-PHqcTJUEyCU/s1600/rz.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="102" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEJh29F_hH6DQlpl2il1DigMUDsSw83-4JbJjIIbH_dsa42Zgr9jWc2dOWSCcrtS91e1Oq8qDx5Fvsjfo8K8MUJASmifHZdSEL9w4n_d-ejBKdzjd11tOZZ8Ky1zIVLtE-PHqcTJUEyCU/s200/rz.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Here <b>sin</b> and <b>cos </b>are just sine and cosine of some angle (in radians) and they of course can be different for each matrix. If we want to combine several rotations we just multiply matrices.</div>
<br />
And here <b>Euler Angles</b> comes. These angles - is just values of rotation about some axes (althought any axis is proper, often cardinal axes XYZ is used). More of this, it's not nessesary to use XYZ axis - you can use XYX or ZXZ etc. Also there can be different order of rotations like XYZ and YXZ. More famous are so called combinations <b>Roll, Pitch, Yaw </b>or <b>Heading, Pitch, Bank </b>that uses X, Y and Z axis (but order can be different - best to think of it in object's space - like right, forward, up). So all you need to do - is to set angles in appropriate matrix and multiply all matrices together. And you'll get final rotation.<br />
<br />
Here is an example:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/Rotations1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
You can use sliders to rotate object about different axes.<br />
<br />
Tha beauty of <b>Euler Angles</b> is in their simplicity. For example, it's easy to visualise. Also it's the most compact format - you need only 3 numbers to store orientation. It's easy to convert to <b>Matrix Form </b>and back.<br />
<br />
But there're some problems with them. For example, take a look again to the previous example. Try to move <b>bank</b> slider. You see that object rotates along axis that passes through it's front face. Now set it to 0. Next, set <b>pitch </b>to 90. Next, move <b>heading</b> slider. Did you see that???!!! Object rotates again about front face (althought it not see in the screen)! This is so called <b>Gimbal Lock </b>and we loose one degree of freedom. Avoid such situation is posiible - just use angles in proper range - in my example simply don't use <b>pitch </b>more than 90. Also you can different approach - such as <b>Quaternions </b>(wait for later post).<br />
<br />
Next problem arises when you want interpolate from one set of <b>Euler Angles</b> to another. Take a look:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/rotations/Rotations2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
Here buttons set1 and set2 just sets diferent <b>Euler Angles</b> to object. Next., on animate button click I just interpolate linearly between angles of set1 and set2. You can see that animation is not what you want to see. Usually you want rotate object by smallest path.<br />
<br />
That's all. If you have questions or notice a mistake you're welkome in comments!</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-55845252704730598122012-08-09T00:49:00.000-07:002013-01-01T23:40:14.969-08:00Stage3D and premultiplied alpha<div dir="ltr" style="text-align: left;" trbidi="on">
Hi everyone. Today I want to tell about problem I met when used transparent image in Stage3D.<br />
<br />
<a name='more'></a>When I load a simple white circle - a strange artifact occur - ugly black outline.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/alpha/alpha_1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
<br />
<br />
What is this? Why? The answer is simple - when you save png image in flash or photoshop, the tool premultiplies color values by alpha. For example, we have white color with 50% transparency [1.0, 1.0, 1.0, 0.5]. The resulting color will be saved as [0.5, 0.5, 0.5, 0.5]. Next, Stage3d thinks that we use image with NO premultiplied color values. And it multiplies it again, resulting to final output color as [0.25, 0.25, 0.25, 0.5] - leading to dark color.<br />
<br />
The simplest solution is to save images without premultiplying by alpha. But I dont't know such tool (it's lazy to google, huh). Another solution described here <a href="http://pixelante.com/index.php?b=fix-pre-multiplied-alpha-in-pngs-stage3d-tutorial" target="_blank">good tutorial</a>.<br />
<br />
So, in AGAL we have [0.5, 0.5, 0.5, 0.5] color. Next we divide each component by alpha to restore original color, i.e. [1.0, 1.0, 1.0, 0.5]. And finally API do it's work and we have good looking picture.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/alpha/alpha_2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object>
</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-32396202640934256902012-08-02T01:47:00.004-07:002013-01-01T23:39:11.988-08:00Fast isEven() function<div dir="ltr" style="text-align: left;" trbidi="on">
Just a note for myself...<br />
<br />
This is a way I used to know if int is even or odd:<br />
<pre class="prettyprint">(i % 2) == 0;
</pre>
And here's a faster way<br />
<pre class="prettyprint">((i * 0.5) - (i >> 1)) == 0;
</pre>
I was afraid that first part<br />
<pre class="prettyprint">i * 0.5;
</pre>
can give 1.0000001, for example, due to round error. But it sems that integer numbers divided by 2 without presision lost. If someone have another information, please let me know.<br />
<br />
Also the best performance gain experienced if above code used inline. Calling a method like this<br />
<pre class="prettyprint">private function isEven(i:int):Boolean{
<span class="Apple-tab-span" style="white-space: pre;"> </span>return ((i * 0.5) - (i >> 1)) == 0;
}</pre>
is 10 times slower!<br />
<br />
Upd.: method that you can find in comment below or here - <a href="http://jacksondunstan.com/articles/1935" target="_blank">great blog</a> - is even faster. I definetely will use this approach.</div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com3tag:blogger.com,1999:blog-1701097741088012500.post-70677627874760634962012-07-13T05:38:00.002-07:002012-07-13T05:40:38.414-07:00I don't need flash.geom.Matrix3D class anymore<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
I don't like native math classes for several reasons. For example, I don't have control over it and I can't say exactly is it my error or class implementation error when something going wrong. And according to this post <a href="http://www.richardlord.net/blog/bugs-in-vector3d-and-matrix3d">Bugs in Vector3D and Matrix3D</a> there're a lot of bugs. Also every matrix.transformVector() call creates new vector instance. Also in some cases I need 3x3 matrix instead of 4x4 for 3D math.<br />
<a name='more'></a>One day I decided to beg off from Matrix3D class and use my own implementation. But you may say it's impossible, since Stage3D API need this class to pass matrix data to AGAL. Yes, it is. But there's another way to pass same data. Recall when you call (poem :-))
<br />
<pre class="prettyprint">context.setProgramConstantsFromMatrix(0, matrix);
</pre>
and you want to add one more matrix, you set it at ID 4 - NOT at ID 1. Furthermore in AGAL you can access to attributes va0, va1, va2, va3. This means that Stage3D passes matrix as 4 vectors. So can we!
<br />
<br />
As you know, there is a difference between multiplying vector by matrix and matrix by vector. In my framework I chose matrix with row layout so I multiply vector by matrix (v * m). Next, if I pass rows of my matrix through <b>context.setProgramConstantsFromVector()</b> I will not get desired result. It's because data storage for matrices in AGAL organized in different way. In order to make things work I need to transpose my matrix or simply pass to <b>context.setProgramConstantsFromVector()</b> matrix columns. That's all.
</div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-44261721147657024722012-06-08T03:06:00.003-07:002013-01-01T23:38:44.403-08:00Stage3D Normal Mapping<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitsOGlBtDANwCXOP-qWZYHBNG_vvCPdbMzAteZnvEsf6WY3Dw3GhpGUlhk8P6FFv27zNDr2HtcVWahZfUCKjWp42KXYWJqt0c305-E7EqoGLtm9PrU2SvpPiM0qnsS1g2eqSr5Vy-1mA0/s1600/normalMapping.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="350" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitsOGlBtDANwCXOP-qWZYHBNG_vvCPdbMzAteZnvEsf6WY3Dw3GhpGUlhk8P6FFv27zNDr2HtcVWahZfUCKjWp42KXYWJqt0c305-E7EqoGLtm9PrU2SvpPiM0qnsS1g2eqSr5Vy-1mA0/s400/normalMapping.png" width="350" /></a></div>
<br />
<br />
<a name='more'></a><br /></div>
Hello. Today I want to show you what normal mapping is. In simple words - it's a mapping tehnique that adds mesostructures to your model - such as noise, cracks, scars - i.e. small bumby details. Of course it can be done with just triangles but in that case we need a lot of them and it will slow down our scene.<br />
<br />
What is normal map? Normal map is just a picture that holds normal for certain fragment. <a href="https://www.google.ru/search?q=normal+map&hl=ru&newwindow=1&biw=1395&bih=864&prmd=imvns&tbm=isch&tbo=u&source=univ&sa=X&ei=kxYEUKvAJsrWrQfO9eWEBg&ved=0CFcQsAQ">Here's</a> a bunch of normal maps. XYZ coordinates encoded as rgb values - usually red responsible for X, green for Y and blue for Z coordinate. Direction of such normal is just a matter of convention but usually 0-0.5 red means that normal points to the left (i.e. x is negative) and 0.5-1 red means that normal points to the right. Similary with green. Blue more interesting. Since we assume that normal always points from surface, not into it, blue component varies from 0.5 to 1. That's why normal maps have bluish color.<br />
<br />
So, how can we use it? It's easy - in fragment shader we take our normal and direction to light. If they point in same direction we light up fragment. The more their directions differs the more darker fragment become.
The most challenging part is to understand how to transform normal to apropriate space. Take a look at the picture:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSr1VuSydgZSr3K_l1nEh-F1fPQ1HF25l33J3ThADboBLcf2-BJQfRj26wkEKdF_ig06uQVsm_4aePpm2u7OZblyRYXC9TkNiy3rewEdK2yaH-heQ9JGf93aU5Q9C8wX1GP5U4Z2TNji0/s1600/normalMapping1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="303" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSr1VuSydgZSr3K_l1nEh-F1fPQ1HF25l33J3ThADboBLcf2-BJQfRj26wkEKdF_ig06uQVsm_4aePpm2u7OZblyRYXC9TkNiy3rewEdK2yaH-heQ9JGf93aU5Q9C8wX1GP5U4Z2TNji0/s320/normalMapping1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Consider some simple model - cube - and apply same normal map to all of it's faces. Next, take normal from center of the front face - let's say it's direction is [0, 0, -1] - i.e. directly from monitor. Next, let's take normal from the center of the right face. Snce maps are the same it's value also [0, 0, -1], but it's wrong - we know that it should be [1, 0, 0]!
Somehow we need a way to convert our normals. And tangent (or texture) space is our saviour. Recall that texture have coordinates called [u, v]. And we assign that coordinats to vertices as attributes. Knowing that coordinates can help us to contruct correct space where normal 'lives' and we can transform light direction vector into that space and calculate direction between normal and that vector. Normal is easy to get - we can take cross product of two edges (be aware of handedness). Calculating tangents is bit harder. I followed this <a href="http://www.terathon.com/code/tangent.html">resource</a>. Next our normals and tangets are passed as attributes and in fragment shader we calculate third basis vector as their cross product. And our tangent space ready! Here's a vertex shader:
<br />
<br />
<pre class="prettyprint">//attributes
alias va0, pos;
alias va1, tangent;
alias va2, normal;
alias va3, uv;
//constants
alias vc0, LIGHT_WORLD_POSTION; // position of light in world coordinates
alias vc1, ZERO_VECTOR; // [0, 0, 0, 1]
alias vc2, VIEW_PROJECTION_MATRIX; // cameraInverseMatrix * perspectiveProjectionMatrix
alias vc6, MODEL_MATRIX; // model transform
alias vc10, MODEL_ROTATION_MATRIX; // only models rotation
//temps
alias vt0, worldPosition;
alias vt1, normalModel;
alias vt2, tangentModel;
alias vt3, binormalModel;
alias vt4, lightDistance;
//varyings
alias v0, uvOut;
alias v1, lightDistanceOut;
//shader
worldPosition = mul4x4(pos, MODEL_MATRIX); // vertex position in world space
lightDistance = LIGHT_WORLD_POSTION - worldPosition; // vertex-light distance in world space
op = mul4x4(worldPosition, VIEW_PROJECTION_MATRIX);
lightDistanceOut = ZERO_VECTOR;
// next we need to rotate normals and tangents. That's why modelRotationMatrix needed
tangentModel = mul4x4(tangent, MODEL_ROTATION_MATRIX); // vertex tangent in world space
normalModel = mul4x4(normal, MODEL_ROTATION_MATRIX); // vertex normal in world space
crs binormalModel.xyz, normalModel, tangentModel; // vertex binormal in world space
/* the most interesting part. I have 3 basis vectors - tangent, normal and binormal that forms tangent space. Matrix is formed as that bases as rows:
|tx ty tz|
|bx by bz|
|nx ny nz|
In order to transform light direction into tangent space, I need to multiply that vector by tangent space transform inverse. Since tangent space matrix is rotation only - it's inverse is transpose:
|tx bx nx|
|ty by ny|
|tz bz nz|
Since I use row-layout for matrix, I need to use row layout vector. Then, multipliyng gves a result:
...............|tx bx nx|
|dx dy dz| * |ty by ny| =
...............|tz bz nz|
= |dx * tx + dy * ty + dz * tz, dx * bx + dy * by + dz * bz, dx * nx + dy * ny + dz * nz|
Or simply |dot(d, t), dot(d, b), dot(d, n)|
*/
dp3 lightDistanceOut.x, lightDistance, tangentModel;
dp3 lightDistanceOut.y, lightDistance.xyz, binormalModel.xyz;
dp3 lightDistanceOut.z, lightDistance, normalModel;
uvOut = uv;
</pre>
<br />
Fragment shader is much simpler:
<br />
<br />
<pre class="prettyprint">//constants
alias fc0.x, ONE;
alias fc0.y, TWO;
//textures
alias fs0, diffuseTexture;
alias fs1, normalTexture;
//temps
alias ft0, color;
alias ft1, normal;
alias ft2, light;
alias ft3, ang;
alias ft4, temp;
alias ft5, normalColor;
//varyings
alias v0, uvIn;
alias v1, lightDistanceIn;
//shader
tex color, uvIn, diffuseTexture<2d, linear, miplinear, repeat>; // get fragment color from color texture
tex normalColor, uvIn, normalTexture<2d, linear, miplinear, repeat>; // get normal from map
// in map all colors stored in 0-1 range. So normals components are in 0-1 range. We need to bring it to -1-1 range.
normalColor *= TWO;
normalColor -= ONE;
// here I negate component to change bumpiness drection (in or out). It can be omitted
neg normalColor.y, normalColor.y;
// calculate direction between normal and light distance
nrm light.xyz, lightDistanceIn.xyz;
dp3 ang.x, normalColor.xyz, light.xyz;
// since dot product gives us result in -1-1 range we need to clamp negative values to 0.
sat ang.x, ang.x;
// finaly multiply color from texture by direction value.
color.xyz *= ang.x;
oc = color;
</pre>
<br />
Time for demos. Notice that I used only normal mapping and no other light like ambient or specular.<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/normalMapping/NormalMappedCube.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/normalMapping/NormalMappedSphere.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object></div>
Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-17714718507569818412012-06-04T02:11:00.001-07:002012-07-18T00:09:47.176-07:00Stage3D wireframe<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe6rG0aTM2H5JmkptDT-_3CTSSnTUc2DfK8krEqcl_yEuoyRgJfkHN00yjcXDkbz_hWzT2qfW6cxkL_goYIGXfKSAwNTn2AvHcKFBJm3lbeSuRh_og3hwIkXOPRM3CpsT_8EjCgCNnzlg/s1600/teapotWire.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe6rG0aTM2H5JmkptDT-_3CTSSnTUc2DfK8krEqcl_yEuoyRgJfkHN00yjcXDkbz_hWzT2qfW6cxkL_goYIGXfKSAwNTn2AvHcKFBJm3lbeSuRh_og3hwIkXOPRM3CpsT_8EjCgCNnzlg/s400/teapotWire.png" width="400" /></a></div>
<br />
<a name='more'></a><br /></div>
Hello. Lets talk about wireframe rendering.<br />
<br />
Why bother with it? Well, it can show geometry compexity - i.e. you can see number of triangles. For example you can show your amazing shader mesostructure effect and then show that there's only two triangles per plane!<br />
<br />
The shader I implemented based on nvidia's paper (<a href="http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/SolidWireframe/Doc/SolidWireframe.pdf">source pdf</a>). And it's quite simple. The main idea is to calculate fragment position on triangle. If it near the edge we shade it and at the end we get triangle outline. The magic is in interpolation of vertex-edge distances in vertex shader. All we need to know - is the distances from vertices to opposite edges.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjepFnMzGfOxDQBQrlLGNHGeA1LLpbHw-S6zJAHZ00uNdR7qj2oN7dJrNjaORJxqX9wrN34LucTDDZfHAmmEZEfp57tstBfCJY-OyQWy-DoBziT2I1IFYsmVZobGbS5Sp2evoBRql_1PdE/s1600/Untitled-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjepFnMzGfOxDQBQrlLGNHGeA1LLpbHw-S6zJAHZ00uNdR7qj2oN7dJrNjaORJxqX9wrN34LucTDDZfHAmmEZEfp57tstBfCJY-OyQWy-DoBziT2I1IFYsmVZobGbS5Sp2evoBRql_1PdE/s400/Untitled-1.jpg" width="260" /></a></div>
<br />
<br />
Notice also that for vertex <b>a</b> we write distances in vector form as (da, 0, 0), for vertex <b>b</b> as (0, db, 0) and for <b>c</b> as (0, 0, dc). Then we pass this distances form vertex shader to fragment shader. Next, in fragment shader we choose smallest from da, db and dc ant that's our corect distance of fragment to edge.<br />
<br />
<div style="text-align: -webkit-auto;">
The main problem that we can't calculate this distances in geometry shader as nvidia's paper do (simply because we DON'T HAVE geometry shader). We must calculate them beforehand and supply as vertex attribute (see formula on picture above). This means that we need to duplicate a lot of data - each vertex must be unique - i.e. it must not be shared between several triangles.<br />
<br /></div>
So, we have correct distance in fragment shader. What next? We simply check that distance with some value (line thickness) and shade this fragment only if it in correct position. Here's a demo:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/wireframe/Wireframe1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
As you can see - it works but wireframe is jaggy, not antialiased. To fix that I use smoothing function from paper. The smoothness applied on 2px range in range from [lineThickness - 1] to [lineThickness + 1]. The result (you can change line width with slider):<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/wireframe/Wireframe2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
Vertex shader:<br />
<pre class="prettyprint">//attributes
alias va0, pos;
alias va1, distance;
//constants
alias vc0, TRANSFORM_MATRIX;
//temps
//varyings
alias v0, distanceOut; //for vertex <b>a</b> Vector.<number>([da, 0, 0])
//shader
op = mul4x4(pos, TRANSFORM_MATRIX);
distanceOut = distance;
</number></pre>
<br />
Fragment shader:<br />
<pre class="prettyprint">//constants
alias fc0, LINE_COLOR;
alias fc1.x, ONE;
alias fc1.y, MINUS_TWO;
alias fc1.z, TWO;
alias fc1.w, POW;
alias fc2.x, THICKNESS_MINUS_ONE;
alias fc2.y, THICKNESS_PLUS_ONE;
//textures
//temps
alias ft0, d; //correct fragment distance
alias ft1, oneOrZero;
alias ft2, color;
alias ft3, smoothValue;
alias ft4, temp;
//varyings
alias v0, distanceIn;
//shader
color = LINE_COLOR;
min d.x, distanceIn.x, distanceIn.y;
min d.x, d.x, distanceIn.z;
//get smooth value - it's a value in range [+1] -> [~0] on 2px interval
temp = THICKNESS_PLUS_ONE - d.x;
temp = TWO - temp;
pow temp temp POW;
temp *= MINUS_TWO;
exp smoothValue temp;
//if fragment distance [d] < [thickess - 1], then 'temp' set to 1 otherwise set to smooth value
slt oneOrZero d.x THICKNESS_MINUS_ONE;
temp = ONE * oneOrZero;
oneOrZero = ONE - oneOrZero;
smoothValue *= oneOrZero;
temp += smoothValue;
color.w *= temp;
//if fragment distance greater then max allowed thickness, then discard it (set alpha to 0)
slt oneOrZero d.x THICKNESS_PLUS_ONE;
temp = oneOrZero - ONE;
//if fragment outside line thicknes + smooth range - discard it
kill temp.x;
oc = color;
</pre>
<br />
And this is wireframe in action on complex geometry:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/wireframe/Teapot.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
P.S.: you can rotate geometry with left and right mouse button and move it forward/backward wth +/-.</div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com3tag:blogger.com,1999:blog-1701097741088012500.post-4179463475747215322012-05-18T03:45:00.002-07:002012-07-17T02:09:23.890-07:00Motion Blur<div dir="ltr" style="text-align: left;" trbidi="on">
<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG8rwMSpEl52_RRIPCg3pB8Eb6RJuhAMUkPkjX_z3Yy5FpIDn4KsVk4LD8mwfpoo-3qlGHtXlZ99B5tE_UxpmiUOxfPHq9m2mIKkAulOi1GFqZL647j6BAw3GfQe_hBVHBVhq4ceNkK94/s1600/motionBlur.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG8rwMSpEl52_RRIPCg3pB8Eb6RJuhAMUkPkjX_z3Yy5FpIDn4KsVk4LD8mwfpoo-3qlGHtXlZ99B5tE_UxpmiUOxfPHq9m2mIKkAulOi1GFqZL647j6BAw3GfQe_hBVHBVhq4ceNkK94/s400/motionBlur.png" width="400" /></a></div>
<br />
<br />
<a name='more'></a></div>
Hello. today I want to show my implementation of motion blur effect. First of all, lets see simple clip without any effect. Here it is:<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/motionBlur/MotionBlur1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
As you might guess ball represented with 2 triangles ant this texture (note that there're free space around colored area):<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9i50V9lW7tRT6_sicf49Rnqcbzmg17rLhsdKRfNwA26zSS72-BoVSqJFyHtCArhXZRopNw9gBlKUq39sQEExZyMmJK3XlDUd48K08ZAXOuxmGHfq0yevppYuVIutl8qXuzQd2HSzdXU8/s1600/texture.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="128" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9i50V9lW7tRT6_sicf49Rnqcbzmg17rLhsdKRfNwA26zSS72-BoVSqJFyHtCArhXZRopNw9gBlKUq39sQEExZyMmJK3XlDUd48K08ZAXOuxmGHfq0yevppYuVIutl8qXuzQd2HSzdXU8/s400/texture.png" width="128" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
So, what wrong with such simple animation? Yeah, you right - it's boring. Let add some dynamics:<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/motionBlur/MotionBlur2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
Here I've added motion blur by sample texture in direction of velocity. Velocity is computed as difference in positions in current and previous frames. Also texture sampling done in positive and negative direction of velocity. Step of texture lookup offset is computed in vertex shader and passes to fragment shader as varing. There're limitations on this step size - it's done to avoid jaggies in case of too big offset.<br />
<br />
To improve visual appearance I've added geometry stretching in direction of velocity. To be honest, my current implementation not good. But for this particular image it looks more or less pleasurable. Stretching is implemented simply by adding some scale of velocity to vertex. Also I take into account position of vertices, i.e. forward or backward. Here is the demo (you can see stretching better if you decrease frame rate with numeric stepper):<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/motionBlur/MotionBlur3.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
The last demo shows combination of motion blur and geometry stretching:<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/motionBlur/MotionBlur4.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
The shaders I wrote using AGALMacroAssembler. It's much better than raw AGAL but it's buggy. Hey, Adobe, we definitely need new high level shader language!<br />
<br />
Vertex shader:<br />
<br />
<pre class="prettyprint">
//attributes
alias va0, pos;
alias va1, uv;
//constants
alias vc0, projectionMatrix;
alias vc4, prevTransformMatrix;
alias vc8, currTransformMatrix;
alias vc12, CONST_12(3000, 0.0001, 0.01, 1);
alias vc13, CONST_13(5000, 0, 0, 0);
//temps
alias vt0, prevPos;
alias vt1, currPos;
alias vt2, velocity;
alias vt3, temp;
alias vt4, oneOrZero;
alias vt5, offsetSign;
//varyings
alias v0, velocityOut;
alias v1, uvOut;
//shader
prevPos = mul4x4(pos, prevTransformMatrix);
currPos = mul4x4(pos, currTransformMatrix);
velocity = currPos - prevPos;
velocity /= 3000; // scale down to get values appropriate for fragment shader without further divisions
//all this stuff is only for velocity magnitude check. If it greater than some treshold, that set it to that treshols
dp3 temp velocity velocity;
slt oneOrZero temp 0.0001;
nrm temp.xyz velocity;
velocity *= oneOrZero;
temp *= 0.01;
oneOrZero = 1 - oneOrZero;
temp *= oneOrZero;
velocity += temp;
//here I move vertex along velocity according to direction, i.e. 'first' vertices I move forward and 'last' bakward
dp3 temp pos velocity;
offsetSign = temp;
abs temp temp;
offsetSign /= temp;
temp = velocity;
temp *= offsetSign;
temp *= 5000;
currPos.xy += temp.xy;
op = mul4x4(currPos, projectionMatrix);
velocityOut = velocity;
uvOut = uv;
</pre>
Fragment shader is ugly because of repeating of the same part several times. And I don't know how to make macroses work - I get an error if I use macros with texture sample opcode:<br />
<br />
<pre class="prettyprint">//constants
alias fc0, CONST_0(21, 0, 0, 0);
//textures
alias fs0, texture;
//temps
alias ft1, color;
alias ft2, stepLen;
alias ft3, uvOffsetPlus;
alias ft4, uvOffsetMinus;
alias ft5, colorOffset;
//varyings
alias v0, velocityIn;
alias v1, uvIn;
//shader
tex color, uvIn, texture<2d, linear, nomip>;
stepLen = velocityIn;
uvOffsetPlus = uvIn;
uvOffsetMinus = uvIn;
//1
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//2
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
//color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//3
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//4
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//5
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//6
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//7
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//8
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//9
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
//10
uvOffsetPlus += stepLen;
tex colorOffset, uvOffsetPlus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
uvOffsetMinus -= stepLen;
tex colorOffset, uvOffsetMinus, texture<2d, linear, nomip, clamp>;
color += colorOffset;
oc = color / 21;
</pre>
</div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com3tag:blogger.com,1999:blog-1701097741088012500.post-31924951922521376782012-05-16T01:33:00.001-07:002012-05-30T00:42:59.968-07:00Stateless particle system on GPU<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjosRuvmVDTgK_0NluI30Mfxa5rVQcjufheeRS1q1jFtlntl7G5gJsWlYP_g5TfOJ0oNgIeVXZXygSaxR495c9hKoYNPCYMV0-31p8TlVxNMmdvvsSAcpg8mb7cjCtr8eg7Hl0PZnWTZMM/s1600/particles.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjosRuvmVDTgK_0NluI30Mfxa5rVQcjufheeRS1q1jFtlntl7G5gJsWlYP_g5TfOJ0oNgIeVXZXygSaxR495c9hKoYNPCYMV0-31p8TlVxNMmdvvsSAcpg8mb7cjCtr8eg7Hl0PZnWTZMM/s400/particles.png" width="396" /></a></div>
<a name='more'></a><br />
Hello. I want to show you how to make particle system that runs entirely on GPU. And as it runs on graphics card it's blazingly fast! On my modern card I get 1.7 million particles on 60 FPS! Adding more particles causes stage3D exception - there's a limitation on triangles number. The main disadvantage of such system is that it's stateless. That means that you only have 'start' data for each particle and you can't save it as data changes. The particle moves according to some rule that you pass to GPU. For example, remember school physics and formula of free falling body position (formula from wikipedia):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbUzEsXruDUy4XJeLiUnhWiVALPeM-mRCduwqaTlgjCco0oJs46CF_9Eldel2R1p5Xp_kxUOGGHdBvud0JY8xE8e5YORemynMG6mjZWxU3HATTq0k-Uk-ltdriov51ZOVY1-3pIGQS0uU/s1600/ee67789588b734209fd91b80fb3e6cdc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="41" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbUzEsXruDUy4XJeLiUnhWiVALPeM-mRCduwqaTlgjCco0oJs46CF_9Eldel2R1p5Xp_kxUOGGHdBvud0JY8xE8e5YORemynMG6mjZWxU3HATTq0k-Uk-ltdriov51ZOVY1-3pIGQS0uU/s400/ee67789588b734209fd91b80fb3e6cdc.png" width="203" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
As you can see, if you know time, you can calculate current position of the body. You got the idea.<br />
<br />
Here's a demo with single particle:<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/gpuParticles/ParticlesGPU1.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
Here the steps of simulation:<br />
<br />
-pass vertices to the GPU. I use 4 vertices and 2 triangles.<br />
-pass particles start positions to the GPU. Bad news that you need to pass such data per vertex, not per particle. It leads to data duplication and memory use.<br />
-pass colors. Per vertex.<br />
-pass additional data such as travel distance and duration of travel. Again, you need to duplicate data.<br />
-on every update pass current time. According to that time shader computes current position simply as end position minus start position multiplied by time delta (from 0 to one).<br />
<br />
Vertex shader:<br />
<pre class="prettyprint">
var vertexShaderString:String =
'add vt0 va0.xy va1.xy\n' + // va0 - local vertex position, va1 - global vertex position
'mov vt0.zw vc4.xx\n' + // fill data with 1
'div vt1.x vc4.y va3.z\n' + // vc4.y - getTimer(), va3.z - travel duration
'frc vt1.x vt1.x\n' + // get value from 0 to 1
'mul vt1.xy va3.xy vt1.xx\n' + // va3.xy - travel length on x and y axes
'add vt0.xy vt0.xy vt1.xy\n' +
'm44 op vt0 vc0\n' +
'mov v0 va2'; // pass color
</pre>
<pre class="prettyprint"></pre>
Fragment shader:<br />
<pre class="prettyprint">
var fragmentShaderString:String =
'mov oc v0';
</pre>
<pre class="prettyprint"></pre>
Here's a demo with tons of particles (add more with button):
<br />
<br />
<object height="500" width="500">
<param name="movie" value="https://dl.dropbox.com/u/77219109/gpuParticles/ParticlesGPU2.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
</object><br />
<br />
<br /></div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com0tag:blogger.com,1999:blog-1701097741088012500.post-59540601200978481422012-05-11T02:02:00.003-07:002012-05-28T11:57:44.496-07:00Stage3D DisplacementMapFilter<div dir="ltr" style="text-align: left;" trbidi="on">
Hi. Today I want to show how to achieve famous 'displacement map filter effect' using AGAL. This effect can be done as postprocess so there can be infinetely many sources of space distortions. Be careful - there's a huge number of word 'DISPLACEMENT' in the text.<br />
<br />
<a name='more'></a><br /><br />
First of all we need a map (not treasure, but displacement) that looks like this (found somewhere in the internet):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgViCKgfL7gZ0b_ztU85D5tMybece7U9K4-Be1ERW58D_FUVPypLh4wKvkC3NAvBbCuv5ly25Q7ogX4l9wZIV2bOhGPRQr4FC34HP91_5gmUYOSdY4eqMw8_oggUOUJIr5KEy7YytnPwzk/s1600/map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgViCKgfL7gZ0b_ztU85D5tMybece7U9K4-Be1ERW58D_FUVPypLh4wKvkC3NAvBbCuv5ly25Q7ogX4l9wZIV2bOhGPRQr4FC34HP91_5gmUYOSdY4eqMw8_oggUOUJIr5KEy7YytnPwzk/s320/map.png" width="320" /></a></div>
<br />
We are only intresting in two components - red that response for horizontal displacement and green that response for vertical displacement. I took 0.5 as point with no displacement, 0 to 0.5 - displacement to the left/up for red/green chanel and 0.5 to 1 - displacement to the right/down for red/green chanel. I made a quad with map and every update I draw it to special texture. This is the result (hover with mouse):<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/DisplacementTestMap.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
<object type="http://dl.dropbox.com/u/77219109/MassiveSpritesTest.swf" width="500" height="500" allowfullscreen="true" wmode="direct"></object>
</object><br />
<br />
Notice that color of texture initialy is 0.5 for red and green chanels that means that there're no displacement in 'empty' places.<br />
<br />
The steps quite simple:<br />
- set projection matrix<br />
- set vertex buffer for quad with map<br />
- set texture where we will draw a map<br />
- say to GPU that we will render to texture instead of back buffer<br />
- clear texture to r = 0.5, g = 0.5<br />
- set shaders<br />
- draw triangles<br />
<br />
The shaders are straightforward and no need in description:<br />
<pre class="brush: as3">var vertexShaderString:String =
'm44 op va0 vc0\n' +
'mov v0 va1';
var fragmentShaderString:String =
'tex oc, v0, fs0 <2d,linear,nomip>';
</pre>
Now the most interesting part:<br />
- set projection matrix<br />
- set fragment constants as vector Vector( -0.5, -0.5, <b>scale</b>, 0). Here <b>scale</b> is strenght of displacement. 0 means no displacement and higher values means more displacement. <br />
- set vertex buffer for geometry (quad with texture in my example)<br />
- set texure for background<br />
- set previously drawn map texture<br />
- say to GPU that we will render to back buffer<br />
- set shaders<br />
- draw triangles<br />
<br />
In the example vertex shader is the same - its purpose only to draw quad with background texture. And this is fragment shader:<br />
<pre class="brush: as3">var fragmentShaderString:String =
'tex ft0, v0, fs1 <2d,linear,nomip>\n' + //get color of displacement map - red and green chanels say us which direction we should displace
'add ft1 fc0 ft0\n' + //add color to constant. Recal that 0.5 means no displacement, so ading it to constant (0.5) leads to zero.
'mul ft1 ft1 fc0.zz\n' + //scale our displacement direction by strength. For example, if red, say, is 0, then we get -0.5 as basic displacement and we need to scale it to desired value.
'add ft2 v0 ft1\n' + //result direction we should add to original texture coordinates that came as varing parameter frm vertex shader
'tex oc, ft2, fs0 <2d,linear,nomip>'; //finaly sample texture with new texture coordinates
</pre>
For pulse effect I choose scale as:<br />
<pre class="brush: as3">Math.abs(Math.sin(getTimer() * 0.001) * 0.1; //notice values range - they sould be small enougth for proper effect.
</pre>
And finaly the result (hover with mouse):<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/DisplacementTest.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
<object type="http://dl.dropbox.com/u/77219109/MassiveSpritesTest.swf" width="500" height="500" allowfullscreen="true" wmode="direct"></object>
</object>
</div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com12tag:blogger.com,1999:blog-1701097741088012500.post-24757989829972114562012-05-03T00:50:00.002-07:002012-05-28T12:02:43.425-07:00My Stage3D 2D :)<div dir="ltr" style="text-align: left;" trbidi="on">
Hi everyone. I want to show some tests that I made recently. It's simple 2D engine that use the power of Stage3D API from Adobe.<br />
<br />
<br />
<a name='more'></a><br /><br />
The first test is displaying massive amount of sprites. It was a nice surprise when I saw 16000 moving sprites on 30FPS despite the fact that all transformation is done on the CPU. And it sadly that API have a limit of 0x10000 vertices per buffer. But of cource it's possible to use several buffers and I think that 16K is not a limit.<br />
<br />
Here the result:<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/MassiveSpritesTest.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
<object type="http://dl.dropbox.com/u/77219109/MassiveSpritesTest.swf" width="500" height="500" allowfullscreen="true" wmode="direct"></object>
</object>
<br />
<br />
For second test I took a warrior character from our game <a href="http://vk.com/album-32864578_148279589"><b>Mana</b></a>. Every animation have 19 parts. As you can see this test is much slower than first. I think it's because of use of texture (in first test there was no texture - only colors passed as vertex attribute) and because of hierarchy - i.e. each animation have children that positions need to be recalculated every update.<br />
<br />
Here the second one:<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/Stage2DBoneAnimation.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
<object type="http://dl.dropbox.com/u/77219109/Stage2DBoneAnimation.swf" width="500" height="500" allowfullscreen="true" wmode="direct"></object>
</object>
<br />
<br />
And the last test uses hierarchy. Each chunk contains parent->child ierarchy, i.e. first sprite contains child, then child contains another child and so on. There 10 spritest total in chunk (more sprites need more computation and will work slowly).<br />
<br />
<object height="500" width="500">
<param name="movie" value="http://dl.dropbox.com/u/77219109/HierarchyTest.swf" />
<param name="allowfullscreen" value="true" />
<param name="wmode" value="direct" />
<object type="http://dl.dropbox.com/u/77219109/HierarchyTest.swf" width="500" height="500" allowfullscreen="true" wmode="direct"></object>
</object>
<br />
<br />
P.S.: remember - there will be exception if there too many vertices.
</div>Anonymoushttp://www.blogger.com/profile/09675717467543731098noreply@blogger.com2