tag:blogger.com,1999:blog-7838930358730194932024-03-16T03:09:56.731+02:00KhayyamExperimentation with 3D posing and animationAnonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.comBlogger49125tag:blogger.com,1999:blog-783893035873019493.post-84033183282017713142015-02-12T14:10:00.002+02:002015-02-12T14:10:35.219+02:00Sorbus aucupariaSorbus aucuparia (rowan, mountain-ash) is a tree in family Rosaceae, native to most of Northern Eurasia. It is famous for its plentiful red "berries" (morphologically these are not berries but tiny apples) that ripen at autumn and are important food for many birds.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmevqFgzZJkm76vPiZWdT-jYxQhj5p8soBoeKxVCKeEsJX-OSW4tSV3jKDwYZgq7-QQEr3EZOMcr4TZWBjmG36xsF7-gkuH9L615l4BI8oxc8bYKrjEjPWBmG4i7oouZTmcu-W6Py9TIM-/s1600/sorbus_1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmevqFgzZJkm76vPiZWdT-jYxQhj5p8soBoeKxVCKeEsJX-OSW4tSV3jKDwYZgq7-QQEr3EZOMcr4TZWBjmG36xsF7-gkuH9L615l4BI8oxc8bYKrjEjPWBmG4i7oouZTmcu-W6Py9TIM-/s1600/sorbus_1.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A group of trees, Blender render</td></tr>
</tbody></table>
<br />
I have done 4 separate tree models (from 4m to 9m tall) and 3 variants of shoots. All models have 3 LOD versions.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGfnv1f26Q8JGmJhaCuJHnIa6qsj7q60B6BXL9HTT3NGkcrBScZB8ooxLnsv5KcvAcC8ZfawgE3Q6g_4fG7PJ78q0XetWysjXIODLvPSRHkm_auHIF2BI86j9YWCTnxmHBb3d5_XkkYrUJ/s1600/sorbus_4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGfnv1f26Q8JGmJhaCuJHnIa6qsj7q60B6BXL9HTT3NGkcrBScZB8ooxLnsv5KcvAcC8ZfawgE3Q6g_4fG7PJ78q0XetWysjXIODLvPSRHkm_auHIF2BI86j9YWCTnxmHBb3d5_XkkYrUJ/s1600/sorbus_4.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">All 7 variants</td></tr>
</tbody></table>
<br />
Vertex counts (LOD0/LOD1/LOD2):<br />
Tree A: 3903/1104/8<br />
Tree B: 2338/503/8<br />
Tree C: 1631/353/8<br />
Tree D: 1125/257/8<br />
Shoot A: 186/62/8<br />
Shoot B: 182/62/8<br />
Shoot C: 99/44/8<br />
<br />
Texture (2048x2048) is included in Blender file.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQKF9WnC-G3W_biyXT4GtsVFcWz37hs8Kih4RUhdlOhXEUDOHYeZwyMqmywGmgEWCmXYBMs8rrtpzBsAJdIGBaWjAb1XO6DDOFdVJWDEbHnViSZYGXf5STLgBc8BL9iw555pJEzUSA5W6E/s1600/texture.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQKF9WnC-G3W_biyXT4GtsVFcWz37hs8Kih4RUhdlOhXEUDOHYeZwyMqmywGmgEWCmXYBMs8rrtpzBsAJdIGBaWjAb1XO6DDOFdVJWDEbHnViSZYGXf5STLgBc8BL9iw555pJEzUSA5W6E/s1600/texture.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Texture preview</td></tr>
</tbody></table>
<span style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13.1999998092651px; line-height: 18.4799995422363px;"><br /></span>
<span style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13.1999998092651px; line-height: 18.4799995422363px;"><br /></span>
The models are free for non-commercial use (under CC-BY-SA-NC license). Commercial (royalty-free) license costs 20 EUR, please contact me by email for further details.<br />
<br />
<a href="http://lauris.kaplinski.com/models/Sorbus_aucuparia_20150211.blend">Download model</a><br />
<br />
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" rel="license"><img alt="Creative Commons License" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" style="border-width: 0;" /></a><br />
<span property="dct:title" xmlns:dct="http://purl.org/dc/terms/">Sorbus aucuparia model</span> by <a href="http://khayyam.kaplinski.com/2015/02/sorbus-aucuparia.html" property="cc:attributionName" rel="cc:attributionURL" xmlns:cc="http://creativecommons.org/ns#">Lauris Kaplinski</a> is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" rel="license">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.<br />
Permissions beyond the scope of this license may be available at <a href="mailto:lauris@kaplinski.com" rel="cc:morePermissions" xmlns:cc="http://creativecommons.org/ns#">mailto:lauris@kaplinski.com</a>.Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com22tag:blogger.com,1999:blog-783893035873019493.post-58938595931169464272015-01-29T22:11:00.000+02:002015-01-30T10:56:16.294+02:00Alnus incanaI have spent many weeks photographing foliage textures, cutting and adjusting them and then modeling plants for Shinya. It took lot of time but the results look pretty good and IMHO it would be shame to keep everything under carpet. So here is the first model, free for non-commercial use. Hopefully more will follow soon.<br />
<br />
<a href="http://en.wikipedia.org/wiki/Alnus_incana">Alnus incana (grey alder)</a> is a small tree growing in temperate Northern Hemisphere, preferring alluvial soils.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSrQIdMrCYpetwnwvJa4wB7QG866_wnwGjtpmj8N3iz9OPEoVO7HwuvQb-Zr-Oa2M9aX4ZY0xf8hZiYLvlN9pF-gjyDlDBJQ1G3N0ziR90UmyAVpYsQuq0TZZaqaKAGUHUDO50aBF20rT4/s1600/Alnus_incana.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSrQIdMrCYpetwnwvJa4wB7QG866_wnwGjtpmj8N3iz9OPEoVO7HwuvQb-Zr-Oa2M9aX4ZY0xf8hZiYLvlN9pF-gjyDlDBJQ1G3N0ziR90UmyAVpYsQuq0TZZaqaKAGUHUDO50aBF20rT4/s1600/Alnus_incana.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A group of trees, Blender render</td></tr>
</tbody></table>
<br />
The trees are modeled in Blender. There are five variants, the highest being 14m and the smallest 2m something tall. All but the smallest have 3 LOD levels.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJcpu0T0iuPzriG4KdhWNKKF6j_jHX6fZ2yEQ2nSN6YFEmZyj3ZPcgCPkF8PADVVA-xnSKLnwcP3x2fLRJc9yKhi6EeyRX7LQJV-_Yxxu_V1-GKYTrvXvI_KF9bHFkqRdTD_2SUTTebOeC/s1600/Alnus_4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJcpu0T0iuPzriG4KdhWNKKF6j_jHX6fZ2yEQ2nSN6YFEmZyj3ZPcgCPkF8PADVVA-xnSKLnwcP3x2fLRJc9yKhi6EeyRX7LQJV-_Yxxu_V1-GKYTrvXvI_KF9bHFkqRdTD_2SUTTebOeC/s1600/Alnus_4.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">All 5 variants</td></tr>
</tbody></table>
<br />
Vertex counts (LOD0/LOD1/LOD2)<br />
Tree A: 2704/776/8<br />
Tree B: 1834/588/8<br />
Tree C: 714/204/8<br />
Tree D: 330/100/8<br />
Tree E: 63/12<br />
<br />
Texture (2048x2048) is included in Blender file.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPD973ncNb_hoJh8dWlbeRTP4iYtEsAS5Ur8vnahwLu458VPysPWaL-S2c7nY_RZycmdlgdIoqKKVyVt4tfahoW9aG79IrVs8x12I4Ag7oX5KgpFyt7as2iEGXPlQsiTujaXspagmkDlP1/s1600/texture.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPD973ncNb_hoJh8dWlbeRTP4iYtEsAS5Ur8vnahwLu458VPysPWaL-S2c7nY_RZycmdlgdIoqKKVyVt4tfahoW9aG79IrVs8x12I4Ag7oX5KgpFyt7as2iEGXPlQsiTujaXspagmkDlP1/s1600/texture.png" height="320" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Texture preview</td></tr>
</tbody></table>
<br />
The models are free for non-commercial use (under CC-BY-SA-NC license). Commercial (royalty-free) license costs 20 EUR, please contact me by email for further details.<br />
<br />
<a href="http://lauris.kaplinski.com/models/Alnus_incana_20150130.blend">Download model</a><br />
<br />
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" rel="license"><img alt="Creative Commons License" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" style="border-width: 0;" /></a><br />
<span property="dct:title" xmlns:dct="http://purl.org/dc/terms/">Alnus incana model</span> by <a href="http://khayyam.kaplinski.com/2015/01/alnus-incana.html" property="cc:attributionName" rel="cc:attributionURL" xmlns:cc="http://creativecommons.org/ns#">Lauris Kaplinski</a> is licensed under a <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" rel="license">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.<br />
Permissions beyond the scope of this license may be available at <a href="mailto:lauris@kaplinski.com" rel="cc:morePermissions" xmlns:cc="http://creativecommons.org/ns#">mailto:lauris@kaplinski.com</a>.
Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com20tag:blogger.com,1999:blog-783893035873019493.post-59476002715243560482014-05-21T23:11:00.000+03:002014-05-21T23:11:41.504+03:00Rigging tutorial 3<span style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18.479999542236328px;">This is the english translation of the second part of a rigging tutorial I wrote.</span><br />
<br />
<ul>
<li><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-1.html" style="font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18.479999542236328px;">The first part - setup and body</a></li>
<li><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-2.html">The second part - legs</a></li>
</ul>
<div>
<br /></div>
<h3>
Hand and palm rig</h3>
Hands have much more freedom in movement than legs but fortunately they do not need an animation system for "sticking" to ground and rolling over heel like legs do. Thus we can use simpler rig here.<br />
<br />
First we create handles that determine the location and direction of palms.<br />
In <b>Edit Mode</b> select the palm bone of left hand and make a copy of it (<b>Shift-D</b>). Because the original is mirrored the copy will be mirrored too. Rename the new bones to <b><i>IK_Hand_L</i></b> ja <b><i>IK_Hand_R</i></b>.<br />
NB! The new bones are coincident with the original ones thus to select them it may be necessary to right-click two times.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vmSvvPGWOKuWe2qGZfF54zZAk-YCwGglOs3KYe77ZboEJybtks0niSYAlwKjCBTfe09vlsl7MkYbZ22hCaHW5pCZCxPfVCVArCVE5qEnjZYHm-uS1yaoexcj-A-4G7zWmlKvAl9ffBxr/s1600/tutorial24.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vmSvvPGWOKuWe2qGZfF54zZAk-YCwGglOs3KYe77ZboEJybtks0niSYAlwKjCBTfe09vlsl7MkYbZ22hCaHW5pCZCxPfVCVArCVE5qEnjZYHm-uS1yaoexcj-A-4G7zWmlKvAl9ffBxr/s1600/tutorial24.png" height="296" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Palm handle in Edit Mode</td></tr>
</tbody></table>
<br />
The new bones are connected to forearm as are hands. We want to make them children of chest and not connected to parent's endpoint so that they move together with upper body but allow us to move them individually as well.<br />
Select <b><i>IK_Hand_L</i></b>, confirm that the <b>Connected </b>checkbox is disabled and chose chest bone on the <b>Parent </b>field. Repeat the same procedure with the handle of right hand.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVxdG221RGsAbxDSLSceU0AzG1WEb2Lyfhp2kLESY9YkRnWzJe5vQKUpVry52e7rsT_bq5wn0pYwR8fgtkPBF8WDqag7ZYUTGCUdhHkZ6kyrUC-f_dNZcxDTjsP62ukNraUKtCcBo6lIRf/s1600/tutorial25.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVxdG221RGsAbxDSLSceU0AzG1WEb2Lyfhp2kLESY9YkRnWzJe5vQKUpVry52e7rsT_bq5wn0pYwR8fgtkPBF8WDqag7ZYUTGCUdhHkZ6kyrUC-f_dNZcxDTjsP62ukNraUKtCcBo6lIRf/s1600/tutorial25.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone properties panel for IK_Hand_L</td></tr>
</tbody></table>
<br />
Now change the shape of handles to something more intuitive, for example flat box roughly the same shape as hand.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn2hkw10annXoDDdja207dGWivd-XiYIeGY4gqS2-j0shOatIieFODG82MAvm84qCkzyF0UuzHn7CkU_Cj111r1LR-93cP-uilv2YCJV6ZEC61-hTXxwmywVgdqPqwjGeqciddLVzIqHY2/s1600/tutorial26.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn2hkw10annXoDDdja207dGWivd-XiYIeGY4gqS2-j0shOatIieFODG82MAvm84qCkzyF0UuzHn7CkU_Cj111r1LR-93cP-uilv2YCJV6ZEC61-hTXxwmywVgdqPqwjGeqciddLVzIqHY2/s1600/tutorial26.png" height="283" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The shape of palm handle</td></tr>
</tbody></table>
<br />
Like the one of leg the <b>IK </b>chain of hand needs an additional handle that determines the direction of elbow. We create it as a child of hand <b>IK </b>handle <b><i>IK_Hand_L/R</i></b>.<br />
In <b>Edit Mode</b> select the endpoint of <b><i>IK_Hand_L </i></b>and create mirrored bones (<b>Shift-E</b>) extending backwards. Rename these new bones to <b><i>IK_Elbow_L </i></b>ja <b><i>IK_Elbow_R</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizUgGiGQUgjWqfxmux0Tz1isTaDY19OC8ASpi4HT9wdZSjHBvdezm6Sw7TT_-sBLbpOmQhHTLz6hLpXGJcrfZwmRYa8iFLfkNZr8_NXv4NSGlBXHUx1_NzQeLOJ_ppIhhmFCLZD4UFd426/s1600/tutorial27.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizUgGiGQUgjWqfxmux0Tz1isTaDY19OC8ASpi4HT9wdZSjHBvdezm6Sw7TT_-sBLbpOmQhHTLz6hLpXGJcrfZwmRYa8iFLfkNZr8_NXv4NSGlBXHUx1_NzQeLOJ_ppIhhmFCLZD4UFd426/s1600/tutorial27.png" height="250" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Creating IK_Elbow_L</td></tr>
</tbody></table>
<br />
Give them some intuitive shape (for example the same as knee targets), switch off <b>Connected </b>checkbox on <b>Bone Properties</b> panel and them move these to about 1m behind elbows.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_n7OaUc8iZdAn9AeeEwJCWmXaxICbRpgT6UCmWErZfOT7dv2ArpY41CS8aKr1bSv3J_l5i8J6LHuCPowfiCfkz5MI_MRPqOGaDijykHetUQPvrA5igL-9gnE9bQSLvGxbBMFcjzrmhUSP/s1600/tutorial28.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_n7OaUc8iZdAn9AeeEwJCWmXaxICbRpgT6UCmWErZfOT7dv2ArpY41CS8aKr1bSv3J_l5i8J6LHuCPowfiCfkz5MI_MRPqOGaDijykHetUQPvrA5igL-9gnE9bQSLvGxbBMFcjzrmhUSP/s1600/tutorial28.png" height="222" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">IK_Elbow_L location</td></tr>
</tbody></table>
<br />
Now select the left forearm in <b>Pose Mode</b> and add <b>Inverse Kinematic</b> constraint to it.<br />
<br />
Chose <b><i>IK_Hand_L </i></b>as target and <b><i>IK_Elbow_L </i></b>as <b>Pole Target</b>. The length of the cain is two bones. Although shoulder has to move together with the arm, it does not animate well if attached to the <b>IK </b>chain. Thus we will animate it manually.<br />
If the elbow will twist to unnatural direction change the <b>Pole Angle</b> value (-90, 90 or 180 degrees) so that elbow would point to <b><i>IK_Elbow_L</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoK0-lGH9VRYbNPKpm8xgFsGGwDPLpo29k3U2K_VAIOHQ5e5LndxBrOKuOB-liTzMWtGpdVMXgxs7lj4qfD4chKzsvfZ7t3-DnjU6ax2nt83J2rQlgmv5xOmERI4J5RR2bPS97mx7sibZs/s1600/tutorial29.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoK0-lGH9VRYbNPKpm8xgFsGGwDPLpo29k3U2K_VAIOHQ5e5LndxBrOKuOB-liTzMWtGpdVMXgxs7lj4qfD4chKzsvfZ7t3-DnjU6ax2nt83J2rQlgmv5xOmERI4J5RR2bPS97mx7sibZs/s1600/tutorial29.png" height="320" width="228" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Arm IK chain properties</td></tr>
</tbody></table>
<br />
Select the palm handle and drag it around. Hand should move along with it trying to keep the wrist at the starting point of palm.<br />
Create similar same <b>IK </b>constraint for the right arm.<br />
Next we have to make a hand to follow not only the location but also the rotation of <b><i>IK_Hand_L/R</i></b> so it can be turned easily by turning the handle.<br />
Select the bone of the left palm, add <b>Copy Rotation</b> constraint to it and select <b><i>IK_Hand_L </i></b>as target bone.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT3xzuXNFAjLRo7ggjHeViBd97Yo3DFHkhk_CiutvvlDF6Prywha01LVMaFNSsbzkPvhFbhZVTGC1eIe4R2Ue8VPuVin65c9cPn3OlhL89h7QeTZkchSOxvrCPKjN0faP6tdzCEZ6YxhfO/s1600/tutorial30.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT3xzuXNFAjLRo7ggjHeViBd97Yo3DFHkhk_CiutvvlDF6Prywha01LVMaFNSsbzkPvhFbhZVTGC1eIe4R2Ue8VPuVin65c9cPn3OlhL89h7QeTZkchSOxvrCPKjN0faP6tdzCEZ6YxhfO/s1600/tutorial30.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Copy Rotation constraint of the left hand</td></tr>
</tbody></table>
<br />
Confirm that if the handle is rotated the palm rotates along.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8bafLFNxl9fA8M0Pq6qtZXsdpHGKfYdkApJ8P_6Z3FSHWfAvGYC3VxgGm-6EUvRiPxRg7COzQIiJnQJUq0xk1uZ4pWFOR0XySi1GtydQDwoY4Hq-XmtWRvK7kWPbRyoDhO7eoAbh2B88j/s1600/tutorial31.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8bafLFNxl9fA8M0Pq6qtZXsdpHGKfYdkApJ8P_6Z3FSHWfAvGYC3VxgGm-6EUvRiPxRg7COzQIiJnQJUq0xk1uZ4pWFOR0XySi1GtydQDwoY4Hq-XmtWRvK7kWPbRyoDhO7eoAbh2B88j/s1600/tutorial31.png" height="301" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Positioning arm and hand by IK handle</td></tr>
</tbody></table>
<br />
Add a similar constraint to the right palm.<br />
The rig of arms is now complete.<br />
Notice that the elbow targets <b><i>IK_Elbow_L/R</i></b> move together with palms. In most "normal" arm positions it places elbows quite naturally but time to time we have to adjust it by hand to position elbows the correct way.<br />
<br />
<h3>
Finger rig</h3>
Animating all finger bones individually is hard and time-consuming. In most situations fingers do not move separately but bend together into fist of if grabbing objects. To make creating such animations easier we add <b>Copy Rotation</b> constraint to all finger bones.<br />
<br />
Select the second bone of left index finger and add <b>Copy Rotation</b> constraint to it. Select the first finger bone as target. Enable the <b>Offset</b> checkbox so it can rotated individually if needed. At last select <b>Local Space</b> from both coordinate system field (<b>Space</b>).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDAQBKabrZyy_7CtjoERFo4cW4dS_p5mi9kg5RCb-bnp_Mj6bQxaSHKX1Fy2-pXf6-3Nvyq3wQlOb0jWuVdH0SPXopgEmgr9OrHjiV602Wo3xy2HQqi3YfS3WF90tdQv8GFdR8z4iWj-q2/s1600/tutorial32.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDAQBKabrZyy_7CtjoERFo4cW4dS_p5mi9kg5RCb-bnp_Mj6bQxaSHKX1Fy2-pXf6-3Nvyq3wQlOb0jWuVdH0SPXopgEmgr9OrHjiV602Wo3xy2HQqi3YfS3WF90tdQv8GFdR8z4iWj-q2/s1600/tutorial32.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Finger constraint panel</td></tr>
</tbody></table>
<br />
Add similar constraint to the third bone choosing the second as target.<br />
Confirm that if the first bone of the index finger is turned the whole finger curls.<br />
<div>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYi1i1HgU51WkRXBH_iscg5U-e-DTQOpUD2RMsWWEf-KcUnbu2y4hQVLOm-BkNZ8v3-yrUUv7W76QC70YQkrtA2XFGNx96z2H8MGB6PhsIdN6qm91SMkXiW-Vr4haJc5nHhebapNpcovib/s1600/tutorial33.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYi1i1HgU51WkRXBH_iscg5U-e-DTQOpUD2RMsWWEf-KcUnbu2y4hQVLOm-BkNZ8v3-yrUUv7W76QC70YQkrtA2XFGNx96z2H8MGB6PhsIdN6qm91SMkXiW-Vr4haJc5nHhebapNpcovib/s1600/tutorial33.png" height="297" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bending of rigged finger</td></tr>
</tbody></table>
NB! For the finger bones to turn naturally the same local coordinate system axes (either <b>X</b> or <b>Z</b>) have to be aligned with the anatomical rotation axis for all finger bones.<br />
<br />
Repeat the same procedure for other fingers and thumbs.<br />
<br />
Now there is an easy method to curl fingers individually. In real animations all fingers of the same hand usually move together. To simplify such movement we create helper bone that bends all fingers together.<br />
<br />
In <b>Edit Mode</b> select the end of the left hand bone and create mirrored bones (<b>Shift-E</b>) extending upwards from the hand at right angle. Rename these to <b><i>IK_Fingers_L</i></b> and <b><i>IK_Fingers_R</i></b>. Adjust the <b>Roll</b> field at <b>Bone Properties</b> panel so that the local <b>X</b> axis of it is aligned with the <b>X</b> axes of the finger bones.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5r0lg7UHDqCxCuep3TETWno_FCmfZ_tcoLWV8_PNSVvZlW7fR3FljdWc5Aoe5NppM8bLjVVY5lsQrDeB7dexHs-Mp8Y6jV5anaXoViwABmETxz5-4G7I3MhDDWWCGjLY-7TcLSgrKMhMU/s1600/tutorial34.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5r0lg7UHDqCxCuep3TETWno_FCmfZ_tcoLWV8_PNSVvZlW7fR3FljdWc5Aoe5NppM8bLjVVY5lsQrDeB7dexHs-Mp8Y6jV5anaXoViwABmETxz5-4G7I3MhDDWWCGjLY-7TcLSgrKMhMU/s1600/tutorial34.png" height="353" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Finger handle</td></tr>
</tbody></table>
<br />
Give a more intuitive shape to the new bone, for example a flattened box.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivE9hcXqrb0-YNPkmZbRkGseKuuMb3vZb4QVeUjHkdXfFw-H5Cg0KleeIIxCv5czYZydLRd6Bk28ZfxU-UDQD8Zi9gxp6Tv42cXoOtfOq7m0a-RcDix7QoC6WrQHUhwMhg8k4kBpoDLtHD/s1600/tutorial35.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivE9hcXqrb0-YNPkmZbRkGseKuuMb3vZb4QVeUjHkdXfFw-H5Cg0KleeIIxCv5czYZydLRd6Bk28ZfxU-UDQD8Zi9gxp6Tv42cXoOtfOq7m0a-RcDix7QoC6WrQHUhwMhg8k4kBpoDLtHD/s1600/tutorial35.png" height="355" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Finger handle shape</td></tr>
</tbody></table>
<br />
Now add <b>Copy Rotation</b> constraint to the first bones of all fingers using the new bone (<b><i>IK_Fingers_L/R</i></b>) as the target. Like for finger bones enable <b>Offset</b> checkbox and chose <b>Local Space</b> for both coordinate systems.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkVJlVcYCAkwBhNFVbtPCraeDeroizNKq1Qk7uIG8qHVT80HP1kAZ3i_BrQDNiXTv_My_V3VT1neRJkHrSoR6JsGldFjxo28_CPqv0khAX3NXtfA2egAEZpXtn5bDPA6ZkAX6Mt3B8mLVC/s1600/tutorial36.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkVJlVcYCAkwBhNFVbtPCraeDeroizNKq1Qk7uIG8qHVT80HP1kAZ3i_BrQDNiXTv_My_V3VT1neRJkHrSoR6JsGldFjxo28_CPqv0khAX3NXtfA2egAEZpXtn5bDPA6ZkAX6Mt3B8mLVC/s1600/tutorial36.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Finger handle constraint panel</td></tr>
</tbody></table>
<br />
Confirm now that by rotating IK_Fingers_L/R the hand bends into fist.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBqa_IfRcGxNV51EkJ2_AIL83Wgi2kQHxUcKAblQdiKrs7Kn1cfU8r1YI4YPgN53YMh4vY4fRrHDv2VtbksEAjWzcYM-4l5uVEqpV9ztdaOkFlnQW3fWDkB9FnjMehJHzAASTm4T97lhYn/s1600/tutorial37.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBqa_IfRcGxNV51EkJ2_AIL83Wgi2kQHxUcKAblQdiKrs7Kn1cfU8r1YI4YPgN53YMh4vY4fRrHDv2VtbksEAjWzcYM-4l5uVEqpV9ztdaOkFlnQW3fWDkB9FnjMehJHzAASTm4T97lhYn/s1600/tutorial37.png" height="292" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The action of finger handle</td></tr>
</tbody></table>
<br />
<div style="margin-bottom: 0cm;">
The hand rig is now complete.</div>
<div style="margin-bottom: 0cm;">
<br /></div>
<h3 style="margin-bottom: 0cm;">
Upper body rig</h3>
<div style="margin-bottom: 0cm;">
Normally the bones of upper body (backbone and chest) do not bend individually. Also neck and head tend to bend together with upper body. For animation rig we make chest and neck to copy the rotation of backbone but keep head separate.</div>
<div style="margin-bottom: 0cm;">
Select chest and add <b>Copy Rotation</b> constraint to it. Select backbone as target, enable <b>Offset </b>and chose <b>Local Space</b> on both coordinate system fields.</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggHL8O4IWojEg4vw-Y5Qsa3kUbYappKgevMdbFXAdljPQx1DeNnT3Rhwjk3vzQ1fYND8V-vcJSwi_eluzTqu7K-Z041IhlxVUthzVJx7t9_j0ng-R0tGujML0U0tUDoMQAb50_55cWJBPP/s1600/tutorial38.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggHL8O4IWojEg4vw-Y5Qsa3kUbYappKgevMdbFXAdljPQx1DeNnT3Rhwjk3vzQ1fYND8V-vcJSwi_eluzTqu7K-Z041IhlxVUthzVJx7t9_j0ng-R0tGujML0U0tUDoMQAb50_55cWJBPP/s1600/tutorial38.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Chest constraint panel</td></tr>
</tbody></table>
<br />
Add similar Copy Rotation constraint to neck selecting chest as target.<br />
Confirm that by bending or turning backbone the whole upper body bends. Because Offset is enabled we are able to move chest and neck individually as well.<br />
The upper body rig is now complete.<br />
<br />
<h3>
Eyes</h3>
Animating eyes by hand is hard because even slightest difference between the movement of left and right eye makes character squint. To make it easier we add additional bone and make it to be the target that the character looks at. We create it as a child of <b><i>Base </i></b>so that even if we change the body position (either by <b><i>COG</i></b>, backbone or head) eyes will always look at the same point.<br />
In <b>Edit Mode</b> select the end of <b><i>Base </i></b>and create a new bone. Disable <b>Connected </b>checkbox and rename it to <b><i>IK_Eyes</i></b>.<br />
Move it to about 1,5m in front of and 20 cm below eyes. Give it a more intuitive shape (for example the same as knee handles).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnTuIpGx4xp4nJ29HMIXvl_fe293P3N_8rPtLAIr7c-L55bdXXEPwaWdX1X5HqHHMy7mHgARHXWQBpFkAYBjAIF0DuSHB_VnNBUyMBWWFbipT3i-KLW3xEAziyO5uhPAdSUscMVmH_Srm-/s1600/tutorial39.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnTuIpGx4xp4nJ29HMIXvl_fe293P3N_8rPtLAIr7c-L55bdXXEPwaWdX1X5HqHHMy7mHgARHXWQBpFkAYBjAIF0DuSHB_VnNBUyMBWWFbipT3i-KLW3xEAziyO5uhPAdSUscMVmH_Srm-/s1600/tutorial39.png" height="241" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The placement of IK_Eyes</td></tr>
</tbody></table>
<br />
Now select the bone of left eye and add <b>Track To</b> constraint to it. Chose the new bone(<b><i>IK_Eyes</i></b>) as the target. Because eyes are normally symmetrical we do not need to set up direction (default global <b>Z</b> will work).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSNTUffkGp0QlHp46jkWfICpuEx_1p3SXr43zS5cJ2zas9kVdU1h2ZEyJ0yftGwrta4qmfLwycA7oD2y3R5X_LP7bSBQs7A9bd20FwGPwLDeDpITbGpsqk6rpoMgyrRB_-XSGUJI5hYnXP/s1600/tutorial40.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSNTUffkGp0QlHp46jkWfICpuEx_1p3SXr43zS5cJ2zas9kVdU1h2ZEyJ0yftGwrta4qmfLwycA7oD2y3R5X_LP7bSBQs7A9bd20FwGPwLDeDpITbGpsqk6rpoMgyrRB_-XSGUJI5hYnXP/s1600/tutorial40.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Eye constraint panel</td></tr>
</tbody></table>
<br />
Add similar constraint to the right eye.<br />
Confirm that by moving <b><i>IK_Eyes </i></b>the character looks at it (it should work also if we bring the target closer to face).<br />
The eye rig is now complete.<br />
<br />
<h3>
Finalizing</h3>
Move all rig handles to separate skeleton layer.<br />
<br />
<ul>
<li><b><i>Movement</i></b></li>
<li><b><i>Base</i></b></li>
<li><b><i>IK_Foot_L/R</i></b></li>
<li><b><i>IK_Toe_L/R</i></b></li>
<li><b><i>IK_Knee_L/R</i></b></li>
<li><b><i>IK_Hand_L/R</i></b></li>
<li><b><i>IK_Elbow_L/R</i></b></li>
<li><b><i>IK_Fingers_L/R</i></b></li>
<li><b><i>IK_Eyes_L/R</i></b></li>
</ul>
<br />
All skeletal bones (the ones associated wit the mesh by weights) should go to another layer. Helper bones that are never animated directly go also to separate layer.<br />
In addition to <b>IK </b>handles we will animate the following skeletal bones:<br />
<br />
<ol>
<li>Pelvis</li>
<li>Backbone</li>
<li>Chest and Neck (if needed)</li>
<li>Shoulders</li>
<li>Individual finger bones (if needed)</li>
<li>Head</li>
<li>Jaw</li>
</ol>
<br />
Try to move all abovementioned bones and confirm that the character moves as expected.<br />
<br />
The simple animation rig is now complete.Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com4tag:blogger.com,1999:blog-783893035873019493.post-19118361956233761042014-05-15T23:47:00.001+03:002014-05-21T23:15:23.801+03:00Rigging tutorial 2This is the english translation of the second part of a rigging tutorial I wrote.<br />
<ul style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18.479999542236328px; margin: 0.5em 0px; padding: 0px 2.5em;">
<li style="margin: 0px 0px 0.25em; padding: 0px;"><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-1.html" style="color: #888888; text-decoration: none;">The first part - setup and body</a></li>
<li style="margin: 0px 0px 0.25em; padding: 0px;"><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-3.html">The third part - hands, body, eyes</a></li>
</ul>
<h3>
Leg and foot rig</h3>
Leg and foot rig requires several additional bones. Three of these are <b>IK </b>"handles" which are manipulated during animation and three are invisible targets used internally by <b>IK </b>solver.<br />
<br />
<h4>
Targets</h4>
We normally want major skeletal bones to be connected and placed in anatomically correct way (foot attached to shin, toe attached to foot) to avoid unnatural stretching of body. On the other hand rigging requires targets to be attached to handles so they move together in animation. Thus we have to add three additional invisible bones that will determine the placement of heel, foot and toes.<br />
<br />
Select the skeleton, switch to <b>Edit Mode</b> and select the endpoint of left shin (heel). Create mirrored bones (<b>Shift-E</b>) that extends 20cm backwards. Rename these new bones to <i><b>Tgt_Heel_L</b> </i>and <b><i>Tgt_Heel_R</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8q_yeMfgp7r7yOUkkJwwEYjl42FFxq_Zr0Or002D39qMpIcSnMOr5fSAK_6PUyQ7vRPoU55h_1XxqGIyYCm5QMkgV9tA466ZZhjo4eC16o2JdZYIUfJz_UFERIfaCqU-FHrpiG7jxCYoA/s1600/tutorial7.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8q_yeMfgp7r7yOUkkJwwEYjl42FFxq_Zr0Or002D39qMpIcSnMOr5fSAK_6PUyQ7vRPoU55h_1XxqGIyYCm5QMkgV9tA466ZZhjo4eC16o2JdZYIUfJz_UFERIfaCqU-FHrpiG7jxCYoA/s1600/tutorial7.png" height="198" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Tgt_Heel_L placement</td></tr>
</tbody></table>
<br />
In similar way create a pair of bones at the end of foot and another pair at the end of toe. Rename the first to <b><i>Tgt_Foot_L/R</i></b> and the second to <b><i>Tgt_Toe_L/R</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhezL7afRP_C0CwNAYEkXP7bFPV4Zoyg5AP7Pye5XxwPHe-gAJixnM4AC3E6ans7P7b_vfFr6B4BBDxlNGfEwVIGz7jezd7YrYEHWrLbjT4fYro_5u4Xn3NUmOuR98sxciIH1qYQquwei09/s1600/tutorial8.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhezL7afRP_C0CwNAYEkXP7bFPV4Zoyg5AP7Pye5XxwPHe-gAJixnM4AC3E6ans7P7b_vfFr6B4BBDxlNGfEwVIGz7jezd7YrYEHWrLbjT4fYro_5u4Xn3NUmOuR98sxciIH1qYQquwei09/s1600/tutorial8.png" height="228" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The placement of foot and toe targets</td></tr>
</tbody></table>
<br />
These are the targets of leg <b>IK</b> solver. Later we connect these to a proper parent bones and move to an invisible skeleton layer because these are never animated directly.<br />
<br />
<h4>
Handles</h4>
The most important one is foot handle that determines the placement of heel and the direction of foot.<br />
Select the endpoint of left shin (heel) and create pair of bones (<b>Shift-E</b>) extending about 20cm forwards. Rename these to <b><i>IK_Foot_L</i></b> and <b><i>IK_Foot_R</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_yFCxxyOH3DVhoe3qfPq2K6z_X3kGziBPAZSNoa9tnCSxhmJ3sjct4q7HUspCYedZs3FWP2R_-eiVJdy2WECtN_5tSMpecK4_wkaFJj4P7RLBQ9u-zkto9aXDHsoKiZyIXQc6FZjrm4U0/s1600/tutorial9.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_yFCxxyOH3DVhoe3qfPq2K6z_X3kGziBPAZSNoa9tnCSxhmJ3sjct4q7HUspCYedZs3FWP2R_-eiVJdy2WECtN_5tSMpecK4_wkaFJj4P7RLBQ9u-zkto9aXDHsoKiZyIXQc6FZjrm4U0/s1600/tutorial9.png" height="171" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">IK_Foot_L</td></tr>
</tbody></table>
<br />
Foot handle has to be the child of Base and it has to lay exactly and ground level. It's origin has to be at the "rolling" point of heel (the back border of heel mesh) and endpoint below the toe joint.<br />
Switch to wireframe mode (<b>Z</b>) and select <b><i>IK_Foot_L</i></b>.<br />
From the bone properties panel switch off <b>Connected</b> checkbox and from <b>Parent </b>menu select <b><i>Base</i></b> as the new parent.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXruV02cmdB3LEN0hga_NlmqhQKDzOXEXUE_u3YJ_Q7g6If1Rkk7llGabc-JQ3VvJzwYLwl86t3TS1JAJfQ0zN53ISrNUSaReQU4HSbsKVWM-svaQyQ5SgHxJMj6XMBaMHvmC71rIcN5aB/s1600/tutorial10.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXruV02cmdB3LEN0hga_NlmqhQKDzOXEXUE_u3YJ_Q7g6If1Rkk7llGabc-JQ3VvJzwYLwl86t3TS1JAJfQ0zN53ISrNUSaReQU4HSbsKVWM-svaQyQ5SgHxJMj6XMBaMHvmC71rIcN5aB/s1600/tutorial10.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone Properties panel</td></tr>
</tbody></table>
<br />
Repeat the same procedure with the mirrored partner (<b><i>IK_Foot_R</i></b>).<br />
Now select <b><i>IK_Foot_L </i></b>again and move it exactly to the ground level, then place the origin to the back edge of the heel and stretch the endpoint to below the toe joint.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjhjQCkdZ2Uz4OkHyk1JAT2-HqdWFhAjzpgxFIKyO5TRfWBrM_te4nLqXOBSIOYBZhZuVanXZUG8XqU80tEydE4g8fkGVcR3UHtdb5zgvbv_uoAMSu20ym1hrx09J3oRoH0CBF5HkySEwi/s1600/tutorial11.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjhjQCkdZ2Uz4OkHyk1JAT2-HqdWFhAjzpgxFIKyO5TRfWBrM_te4nLqXOBSIOYBZhZuVanXZUG8XqU80tEydE4g8fkGVcR3UHtdb5zgvbv_uoAMSu20ym1hrx09J3oRoH0CBF5HkySEwi/s1600/tutorial11.png" height="231" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The placement of IK_Foot_L</td></tr>
</tbody></table>
From the end of <b><i>IK_Foot_L </i></b>create mirrored bones (<b>Shift-E</b>) extending forwards. Rename these to <b><i>IK_Toe_L </i></b>and <b><i>IK_Toe_R</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7KEC-dRZMjXgrq2uNQy32idz8eV8bAiH2cqRrEoWJbe5Y7pNPCrEDnlz-JoxkVet04Vl9hc8v6Gs1mtLD8Am63O9eC4NXJ8gH19dhQG5IDuyNFoIjRI-2IFM0tY3HaZYW1tYCBhjCOZqc/s1600/tutorial12.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7KEC-dRZMjXgrq2uNQy32idz8eV8bAiH2cqRrEoWJbe5Y7pNPCrEDnlz-JoxkVet04Vl9hc8v6Gs1mtLD8Am63O9eC4NXJ8gH19dhQG5IDuyNFoIjRI-2IFM0tY3HaZYW1tYCBhjCOZqc/s1600/tutorial12.png" height="205" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">IK_Toe_L</td></tr>
</tbody></table>
<br />
Assign more intuitive shapes to all handles. Simple blocks below ground will do.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NN7r3rUN9GKCCZpbnVCs-8Y_vhOqmrVvldvYGJHcZh0yjVdtMuxFdd9_ymHSnNDzK0v_YjyRVXcYAHTjACUg1_Hy-YI6ua2EoETrx1URsr3AqCqDWC9nWBxAtnPdHlFTdlM9HMb8NB0G/s1600/tutorial13.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-NN7r3rUN9GKCCZpbnVCs-8Y_vhOqmrVvldvYGJHcZh0yjVdtMuxFdd9_ymHSnNDzK0v_YjyRVXcYAHTjACUg1_Hy-YI6ua2EoETrx1URsr3AqCqDWC9nWBxAtnPdHlFTdlM9HMb8NB0G/s1600/tutorial13.png" height="250" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Shaped foot and toe handles</td></tr>
</tbody></table>
<br />
Now we have to set up the proper hierarchy of target bones.<br />
Disconnect <b><i>Tgt_Heel_L </i></b>(disable <b>Connected </b>checkbox) and set it's parent to <b><i>IK_Foot_L</i></b>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj25Z91aNYTRue8ekifVWhlGvvfGPktxRWyFrjgENLBqmHB4LmVRcqGOC3Hi8LmlFV0wApj_tDuildDJXflk6fnTIx9g9O8hWAQFoULCRmX03jblIPQj2BQGOGZvalxZ-StLppOfzJNmtln/s1600/tutorial14.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj25Z91aNYTRue8ekifVWhlGvvfGPktxRWyFrjgENLBqmHB4LmVRcqGOC3Hi8LmlFV0wApj_tDuildDJXflk6fnTIx9g9O8hWAQFoULCRmX03jblIPQj2BQGOGZvalxZ-StLppOfzJNmtln/s1600/tutorial14.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone Relations panel</td></tr>
</tbody></table>
<br />
Do the same for <b><i>Tgt_Heel_R </i></b>(it's parent is, of course, <b><i>IK_Foot_R</i></b>).<br />
Disconnect <b><i>Tgt_Foot_L/R</i></b> and set their parents to <b><i>IK_Foot_L/R</i></b>.<br />
Disconnect <b><i>Tgt_Toe_L/R</i></b> and set their parents to <b><i>IK_Toe_L/R</i></b>.<br />
All target and handle bones are now properly placed and connected.<br />
<br />
Next we create the main <b>IK </b>chain of leg.<br />
Select left shin and add <b>Inverse Kinematics</b> constraint from <b>Bone Constraints</b> panel.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlFWXTGlCGfT6MZFA4XIVLmeJXGLZSpxeQsfztLFFnByGNb7iknYwouUgLrWHmnu4rFgN3slm3f8i2SZlBBvfftc-qby3bw-oFwS5dkNJsaCml7TsXPxN0GLBYkilzjXmAzz2OVAZLx9df/s1600/tutorial15.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlFWXTGlCGfT6MZFA4XIVLmeJXGLZSpxeQsfztLFFnByGNb7iknYwouUgLrWHmnu4rFgN3slm3f8i2SZlBBvfftc-qby3bw-oFwS5dkNJsaCml7TsXPxN0GLBYkilzjXmAzz2OVAZLx9df/s1600/tutorial15.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">IK constraint panel</td></tr>
</tbody></table>
<br />
On Target field select the armature object. On the new field <b>Bone </b>that appears select heel target (<b><i>Tgt_Heel_L</i></b>)<br />
Set the length of the chain to 2 bones (thigh and shin).<br />
Confirm now that by moving foot handle (<b><i>IK_Foot_L</i></b>) the whole leg bends, trying to keep the heel joint at the place determined by heel target bone.<br />
Unfortunately sometimes leg twists unnaturally because <b>IK </b>solver does not "know" at which direction the knee should bend. To solve this we add two "handles" that determine the direction of knees.<br />
<br />
Select the end of <b><i>IK_Foot_L </i></b>and create mirrored bones (<b>Shift-E</b>). Rename these to <b><i>IK_Knee_L </i></b>and <b><i>IK_Knee_R</i></b>.<br />
For both created bones disable <b>Connected </b>on <b>Bone Properties</b> panel and move them to a place approximately in front of the knees.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7ASHbN9agX3dwGsUl8Dypdjoqn8nT3JB2U0M0bxOeHJKi5CMGlgNTJq9m6pKJLJc4fZRMqnJ3s3Tmue0DH5T4OG1X1HG2rFgoFkXMI6Khx_C0YJaNjRT9q48EdQ4s_CjUZCdZ_aN9Xne0/s1600/tutorial16.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7ASHbN9agX3dwGsUl8Dypdjoqn8nT3JB2U0M0bxOeHJKi5CMGlgNTJq9m6pKJLJc4fZRMqnJ3s3Tmue0DH5T4OG1X1HG2rFgoFkXMI6Khx_C0YJaNjRT9q48EdQ4s_CjUZCdZ_aN9Xne0/s1600/tutorial16.png" height="302" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The placement of knee targets</td></tr>
</tbody></table>
<br />
Select again the shin of the left leg. On <b>Bone Constraints</b> panel set <b>Pole Target</b> to the armature object and corresponding bone to <b>IK_Knee_L</b>.<br />
Depending on how the local coordinate system of thigh bone is oriented the knee may now twist 90-180 degrees. <b>Pole Target</b> orients one of knee axes (default <b>X</b>) to the target object and if the <b>X</b> axis is, for example, oriented sideways the knee twists.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8zazbuk1ayVF8mpcw-tXE62NC9mfZQg-pI5t_RhlJlTzpsr5T4xcM4kcV0Cx5e3_yboQh1HnPone1rd-P3TrgTfEPJEvfIvisExsdz_yJGpLYDE85TLrhi342UGvav6n1T8PwkbhPTYJg/s1600/tutorial17.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8zazbuk1ayVF8mpcw-tXE62NC9mfZQg-pI5t_RhlJlTzpsr5T4xcM4kcV0Cx5e3_yboQh1HnPone1rd-P3TrgTfEPJEvfIvisExsdz_yJGpLYDE85TLrhi342UGvav6n1T8PwkbhPTYJg/s1600/tutorial17.png" height="387" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Twisted leg</td></tr>
</tbody></table>
<br />
If the bone is twisted change the <b>Pole Angle</b> field to either 90, 180 or -90 degrees until the knee is oriented correctly (by modifying this field we force some other local direction is oriented towards pole target instead of <b>X</b> axis).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYih0ABWJKcKEYAfy6O-HSTrxfNVR1qrzEr1JsoTpZ3P-T1824lVDWqgz4tAgK300OqnCmFmxzRgi_8Pi6UMQMkeSlbTECXtLRJLMUPifWNeyl0amaCPbuYn5FIdEFpFBVVzBF4qEB_AYV/s1600/tutorial18.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><span style="color: black;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYih0ABWJKcKEYAfy6O-HSTrxfNVR1qrzEr1JsoTpZ3P-T1824lVDWqgz4tAgK300OqnCmFmxzRgi_8Pi6UMQMkeSlbTECXtLRJLMUPifWNeyl0amaCPbuYn5FIdEFpFBVVzBF4qEB_AYV/s1600/tutorial18.png" height="320" width="233" /></span></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small;">Adjusting Pole Angle</span></td></tr>
</tbody></table>
Now change the shape of knee handles to something intuitive (for example icosahedron).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRwm2var1AGGZg_1CT3kGaXcWfXaw3O6hGkUmqRg_J-oTQIgvGLQlTIgrrab5pPG8T_czwbvMqO1tauZ5Uc1mzVb2MNp_2d5dsdCO7jMV7g1KHMOKQMVulARoHP7UvpYRzfF3Tf4rMdpl-/s1600/tutorial19.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRwm2var1AGGZg_1CT3kGaXcWfXaw3O6hGkUmqRg_J-oTQIgvGLQlTIgrrab5pPG8T_czwbvMqO1tauZ5Uc1mzVb2MNp_2d5dsdCO7jMV7g1KHMOKQMVulARoHP7UvpYRzfF3Tf4rMdpl-/s1600/tutorial19.png" height="259" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Knee direction handle</td></tr>
</tbody></table>
<br />
Confirm that by moving knee handle left and right the leg rotates so that the knee is always oriented towards the handle.<br />
The main <b>IK </b>chain of left leg is now complete. Identical chain has to be created for right leg as well.<br />
<br />
<h4>
Rotating feet</h4>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdS8GE5s2YZXqRXslSY0ah7Cl0VNtzH4j-h1QDkv5X2FpaTH8bFjHq1SU1KnhEBHu3UOhmKAMyEh1llnL0ZhPX4ADF74csA6rlSokT70Tzta-PZUn6PG_Hj-V0zWPFmQovupI0ug2ZQ5nh/s1600/tutorial20.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdS8GE5s2YZXqRXslSY0ah7Cl0VNtzH4j-h1QDkv5X2FpaTH8bFjHq1SU1KnhEBHu3UOhmKAMyEh1llnL0ZhPX4ADF74csA6rlSokT70Tzta-PZUn6PG_Hj-V0zWPFmQovupI0ug2ZQ5nh/s1600/tutorial20.png" height="213" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Incorrect foot orientation</td></tr>
</tbody></table>
<br />
To guide the turning of foot ensure first that the local Z axes of foot and toe bones is oriented upwards. If they are not (like on figure) select the bone, switch to Edito Mode and adjust the Roll value on Bone Properties panel.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhogeCLsYOw30W1u5v6pQvAlmMFQBCHYxqh9TP5JiLH1zqiDxpdV85VJz4aogKMOEYyvw9-WnIFMXsIAmzlYj0lqGVsTt5q3MMqTblxfUUqdJj8sEV4lrS_1VH1uMtn2E2I1aYN1nllfLgL/s1600/tutorial21.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhogeCLsYOw30W1u5v6pQvAlmMFQBCHYxqh9TP5JiLH1zqiDxpdV85VJz4aogKMOEYyvw9-WnIFMXsIAmzlYj0lqGVsTt5q3MMqTblxfUUqdJj8sEV4lrS_1VH1uMtn2E2I1aYN1nllfLgL/s1600/tutorial21.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone Roll setting</td></tr>
</tbody></table>
<br />
Repeat the same procedure with toe bone if needed.<br />
Now switch to <b>Pose Mode</b> and add <b>Track To</b> constraint to the left foot. On <b>Target </b>field chose the armature object and on <b>Bone </b>field <b><i>Tgt_Foot_L</i></b>. Enable <b>Target Z</b> checkbox so that the local <b>Z</b>-axis of foot would always be oriented as close as possible to the local <b>Z</b>-axis of target bone.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLWEa-ZjNWMmzxL2I_uxpzr0ZiYMjY521nTtLb3NyrZyZB_tReq6ju_8tT2jcxi9DAiKrKe76VbSB784rF2x8NpFUBxnQDXcObJ1uIfGrtszEmoxlpzdrtNSlsv4DB411-sa_3KzjJ87bT/s1600/tutorial22.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLWEa-ZjNWMmzxL2I_uxpzr0ZiYMjY521nTtLb3NyrZyZB_tReq6ju_8tT2jcxi9DAiKrKe76VbSB784rF2x8NpFUBxnQDXcObJ1uIfGrtszEmoxlpzdrtNSlsv4DB411-sa_3KzjJ87bT/s1600/tutorial22.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Track To constraint panel</td></tr>
</tbody></table>
<br />
Confirm now that if the foot handle(<b><i>IK_Foot_L</i></b>) is rotated the foot rotates with it.<br />
<br />
Now select the toe bone (it is easier to do that in wireframe mode) and add <b>Track To</b> constraint to it. On <b>Target </b>field select armature object and on Bone field <b><i>Tgt_Toe_L</i></b>.<br />
Confirm that if the toe handle is rotated toe rotates.<br />
Add similar constraints to the right foot as well.<br />
<br />
The <b>IK</b> of legs is now complete.<br />
<br />
The hierarchy of skeleton root should be following:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh18ljsJM9tFG5y20NixtfQtcA3Ov-5K5bWea2908RQUnFjdcZ-9n6_qoOSVyAUkFRcqFUQHZU6t7-g6kiiusd8flbIJpQxh8SyDzO-CLmcKwfGmreQ2bgT8O5XVO0o6eBZsec_rBgHTNFD/s1600/tutorial23.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh18ljsJM9tFG5y20NixtfQtcA3Ov-5K5bWea2908RQUnFjdcZ-9n6_qoOSVyAUkFRcqFUQHZU6t7-g6kiiusd8flbIJpQxh8SyDzO-CLmcKwfGmreQ2bgT8O5XVO0o6eBZsec_rBgHTNFD/s1600/tutorial23.png" height="305" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The root of armature hierarchy</td></tr>
</tbody></table>
<br />
<a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-3.html">The third part of the tutorial: arms, upper body, eyes</a><br />
<br />Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com3tag:blogger.com,1999:blog-783893035873019493.post-15735907748537505762014-05-14T01:05:00.001+03:002014-05-21T23:13:39.467+03:00Rigging tutorial 1I have been teaching analytical geometry and low-poly modeling in local art school for three years now.<br />
This is the english translation of the first part of a rigging tutorial I wrote. Other parts will follow as soon as I have time to translate them.<br />
<ul style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18.479999542236328px; margin: 0.5em 0px; padding: 0px 2.5em;">
<li style="margin: 0px 0px 0.25em; padding: 0px;"><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-2.html" style="color: #888888; text-decoration: none;">The second part - legs</a></li>
<li style="margin: 0px 0px 0.25em; padding: 0px;"><a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-3.html">The third part - hands, body, eyes</a></li>
</ul>
<h3>
Introduction</h3>
<br />
This tutorial shows how to build very simple animation rig of humanoid character. The rig allows easy creation of walkcycle, articulating hands and fingers and adjusting eyes. Much more advanced rigs are normally used in serious animation, but we keep things simple so the general ideas and methods can be understood more clearly. Hopefully everyone is able to build on top of this tutorial to make the rig more powerful.<br />
We start with a normal humanoid skeleton that is attached from <b><i>COG</i></b>. I.e. the rootmost bone is the center of gravity of the character, placed inside the body somewhere near belly button. Immediately from <b><i>COG</i></b> upper and lower body branch – the lower body starts from pelvis and upper from backbone.<br />
<br />
<h3>
Terms</h3>
<br />
<ul>
<li><b>Forward kinematics (FK)</b> – an animation method were each bone is rotated (moved, scaled) individually by animator. Associated mesh then is deformed according to the weights. Forward kinematics is inconvenient for animating bone chains (e.g. limbs) because one has to adjust the rotation of each constituent bone in keyframes to keep the chain moving properly (for example keeping feet attached to ground).</li>
<li><b>Inverse kinematics (IK)</b> – an animation method where additonal bones and constraints are added to skeleton so that instead of rotationg individual bones one can animate intuitive "handles" or "targets". Special constraints are added to a skeleton so other bones track the locations and rotations of "handles". When a target bone of a limb is moved all constituent bones of the limb bend so that the end is always automatically placed to the point specified by handle.</li>
<li><b>IK</b> in narrow sense involves only automatic calculation of bone angles to keep an end at the place certain target. In wide sense it is often use synonymously to rigging thus including other rules and constraints. For example, a way to bend all fingers together by moving a single handle so that a hand of a model grabs certain object.</li>
</ul>
<br />
<h3>
Rigging a skeleton</h3>
<br />
Rigging rules are added to skeleton by creating additional "virtual" bones and defining bone constraints. The latter can be specified individually to each bone from Bone constraints panel.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="196px;" src="https://lh5.googleusercontent.com/5tgFSmmvEmNMnI8E0rgqrCpyUxTauEliti9hML90ruywNCpYmNgEdN0QJ2Wa12bq1hk1gdE7YJiKqUY5y6TEl2IVwF1a_0h6Xka55CPeOp9ertDwtzbtQ9O5nUnffywYBA" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="270px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><span style="font-size: small; text-align: start;">Bone constraints panel</span></td></tr>
</tbody></table>
<br />
<h3>
Constraints used in this tutorial</h3>
<ul>
<li><b>Copy Location</b> – the location (the origin of local coordinate system) of a bone is automatically moved to the location of some other bone (or object). If location offset is enabled the bone can still be moved by user – in such case the relative (animated) offset from the origin is preserved.</li>
<li><b>Copy Rotation</b> – the rotation (the direction of the base vectors of local coordinate system) of a bone is automatically adjusted to coincide with the rotation of some other bone (or object). If offset rotation is enabled the bone can still be rotated by user.</li>
<li><b>Track To</b> – the direction of a bone (the <b>Y</b> basis vector of local coordinate system) is directed to point to some other target bone (or object). In addition to the bone itself the up direction (the direction of <b>Z</b> basis vector) can be specified.</li>
<li><b>Inverse Kinematics</b> – allows one to specify a chain of bones so that all it's bones rotate automatically to keep the end of the chain at specified target. Additionally the direction of bends (joints) can be specified by additional bone (or object).</li>
</ul>
<div>
<br /></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGBhCPUxRxJeL9BBDdXEsLcGb__oTAiqdYuhtgMJDsEBeq2WESMSmJ3Zltc5iWCxovR-oTA3ewVUXfqaC_thXf0CaAmC4Te01mxOS3ZRURyZiAj1iHjPo87jQefQtvQBNAGeUJ70FeVRAL/s1600/skeleton.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGBhCPUxRxJeL9BBDdXEsLcGb__oTAiqdYuhtgMJDsEBeq2WESMSmJ3Zltc5iWCxovR-oTA3ewVUXfqaC_thXf0CaAmC4Te01mxOS3ZRURyZiAj1iHjPo87jQefQtvQBNAGeUJ70FeVRAL/s1600/skeleton.png" height="400" width="246" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Handles and targets of simple rig</td></tr>
</tbody></table>
<br />
<div>
<h3>
Additional bones</h3>
<div>
<br />
To rig a skeleton we have to create several additional "bones" that are not bound to the mesh by weights. Instead some of them function as "handles" that intuitively define the movement of other skeletal bones. Others are only used internally by <b>IK</b> algorithms as specific targets of certain bones or chains.</div>
<div>
The handles in our rig are:</div>
<div>
<ul>
<li><b><i>Movement</i></b> – specifies the horizontal placement of body in walkcycle</li>
<li><b><i>Base</i></b> – allows on to switch on/off whether the body tracks Movement or not</li>
<li><b><i>IK Foot</i></b> – specifiy the placement and direction of feet</li>
<li><b><i>IK Toe</i></b> – rotate toes</li>
<li><b><i>IK Knee</i></b> – specify the direction of knees</li>
<li><b><i>IK Hand</i></b> – specify the location and direction of palms</li>
<li><b><i>IK Fingers</i></b> – bends all fingers together</li>
<li><b><i>IK Elbow</i></b> – specify the direction of elbows</li>
<li><b><i>IK Eyes</i></b> – specifies the direction of both eyes</li>
</ul>
<div>
<br /></div>
</div>
</div>
<div>
<h3>
Movement and Base</h3>
<div>
<b><i><br /></i></b>
<b><i>Movement</i></b> is a "virtual bone" that is used to give horizontal movement to walkcycle. I.e. if the length of the double-step is 1,5m and the character makes one double-step per second, <b><i>Movement </i></b>moves forward with uniform speed one meter per second. In starting and stopping animations <b><i>Movement </i></b>correspondingly accelerates or decelerates.</div>
<div>
Moving character ahead in walkcycle is needed to ensure that the feet "stick" to ground and do not slide forward or backwards.</div>
<div>
It is possible to create more advanced rigs where the "sticking" of feet is achieved by special constraints but in given tutorial we keep things simple.</div>
<div>
<br /></div>
<div>
Select the armature, enter <b>Edit Mode</b>, make all skeleton layers visible and make the display mode of bones to be octahedron.<br />
<br /></div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="400" src="https://lh4.googleusercontent.com/4HAtGR2y_u7hGGKMaEDt-APZPCIRS4VPWMKRieZcnIoJyB9O1AIIuDQEuEOBXeFpn_QRIf85Pc0ArHUrlVd4LTDIJYzzimrb8FJMPSEvCIX6QUEk7Esseeb5gxpipi4vFw" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="343" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Skeleton in Edit Mode</td></tr>
</tbody></table>
<br />
<span id="docs-internal-guid-ba72f73f-f77f-94bc-051b-807d745bc98a"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
</div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="313px;" src="https://lh5.googleusercontent.com/2sX-Ii4IBGxDgROGBQvJjrVrcDciFz2hty2vA-X5UBjWNQT_G4RAjF0cWWBYvRmSCqz3QlcO0SBHmnHExxJImnl6zKrc0VyCBeK0buMO2JKoNr_Q6p-OyL7jFDuZWJaaIQ" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="204px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Armature panel</td></tr>
</tbody></table>
<br />
<div>
<b><i>Movement </i></b>will be the "handle" that changes the location of the whole skeleton. Thus we have to make it root bone (without parent).</div>
<div>
Select the start of <b><i>COG</i></b>, switch to side view (<b><i>NumPad 3</i></b>) and create new bone (<b>E</b>), extruding it horizontally 1m to the back of the character. In my example it is coincident with <b><i>COG</i></b>.</div>
<div>
Select the new bone (it's default name is <b><i>COG.001</i></b>) and rename it to <b><i>Movement</i></b>.</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="184px;" src="https://lh5.googleusercontent.com/_TI9HS2mnCdC0oLo7J-dAwHD-rI5217h6h7tuSVn9AWHidkAsm1ljSx60nauRNA9x1288sxQ4jtaHee1mjUetNWcfhcs0qseQEOmSfuFGIfxf7pwk6l7bNeuZpecutggEA" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="490px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Movement</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f781-631f-d815-bf2b13988ac0"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
Move it to the origin of coordinate system (<b>Shift-S -> Cursor to Center</b>, <b>Shift-S -> Selection to Cursor</b>)</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvzASVsFBIMYBfHLuyj9lCoxeARw0pvawx9w5hrkOBuHE3REY4Z7bCwXgDghyj3t2KbirelrldAJAht9n9ac43HHZjUAG2ruumQWHclBaWrB6E7Fw6FNWW2gwPL7hZa93ZUtATVWvLtq-2/s1600/tutorial1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvzASVsFBIMYBfHLuyj9lCoxeARw0pvawx9w5hrkOBuHE3REY4Z7bCwXgDghyj3t2KbirelrldAJAht9n9ac43HHZjUAG2ruumQWHclBaWrB6E7Fw6FNWW2gwPL7hZa93ZUtATVWvLtq-2/s1600/tutorial1.png" height="138" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Correctly placed Movement bone</td></tr>
</tbody></table>
<br />
<div>
<div>
This bone will move the whole character. To make it's role intuitively clear and to distinguish it from other bones we assign it a special shape.</div>
<div>
Switch to <b>Object Mode</b> and activate some empty layer (for example the last one). Create a new cube (<b>Add -> Mesh -> Cube</b>).</div>
<div>
If object is used as she shape of a bone, the virtual start of the bone is at the origin and the end is 1m along <b>Y</b> axis.</div>
<div>
Enter <b>Edit Mode</b> and model the cube into 3D arrow that is placed exactly below <b>XY</b> plane.</div>
<div>
NB! Do not move, scale or rotate the object in <b>Object Mode</b>. Only the mesh datablock is used as bone shape and thus transformations in <b>Object Mode</b> have no effect (but they make understanding the actual shape harder).</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="190px;" src="https://lh5.googleusercontent.com/COFofZFglqNJDpQgNT-w9ZXY3yRZo0SSDMXx9B-5vk6EWun0sZ4XzCvvJ9N2suHLgQdf6SVXRyIAncHeiIKY3Eas1_stW3JN9N2Dd480kHh82q5OvuSvS_1bu5mbz_xPAQ" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="292px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The shape of movement bone</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f786-790a-0e75-122f24f0d78a"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
Rename the object to <b><i>Shape_Movement</i></b>.</div>
<div>
It is a good idea to rename "helper objects" according to some schema so they can easily be found in lists. In given tutorials the names of all shape objects start with <b><i>Shape_</i></b>, animation handles starts with <b><i>IK_</i></b> (except <b><i>Movement</i></b> and <b><i>Base</i></b>) and <b>IK </b>targets start with <b><i>Target_</i></b>.</div>
<div>
Switch to <b>Object Mode</b>, enable the layer where the armature is, select the armature and switch to <b>Pose Mode</b>. Now select <b><i>Movement</i></b>.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTCQ37I4mQr5x3VRKSLUtT_o2RyLNgiAfBoUTJObEmZGv6OWysykCE0OQdLamhngunP6LKZqTX9O3j4R6AVMnzJlOLfDwvq9RtSb6vAJ_EtKRkRbtRbnCeIFzZOFIZaxbjYzvoiMCvjMn/s1600/tutorial2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOTCQ37I4mQr5x3VRKSLUtT_o2RyLNgiAfBoUTJObEmZGv6OWysykCE0OQdLamhngunP6LKZqTX9O3j4R6AVMnzJlOLfDwvq9RtSb6vAJ_EtKRkRbtRbnCeIFzZOFIZaxbjYzvoiMCvjMn/s1600/tutorial2.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone Display panel</td></tr>
</tbody></table>
<br />
<div>
<div>
From the <b>Display </b>subpanel of <b>Bone Properties</b> panel chose the created object (<b><i>Shape_Movement</i></b>) in <b>Custom Shape</b> menu and enable <b>Wireframe </b>display mode.</div>
<div>
Now <b><i>Movement </i></b>has the same shape as the created object in <b>Pose Mode</b> (in <b>Edit Mode</b> all bones are always shown using default shapes).</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ-MlpEKHBHZFTAB8A0oZF3yQXsytjcXDP1BC-_GJEcOuQkevtQjIhL3NNy5B1Wr0aMwMeEKvDmZmNsE3QI9oGf3qnKF6mmqrAxjmRHYSNci1qcy-6GsTfLHDTl6Q2lqkRwb8x4McFy6gx/s1600/tutorial3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ-MlpEKHBHZFTAB8A0oZF3yQXsytjcXDP1BC-_GJEcOuQkevtQjIhL3NNy5B1Wr0aMwMeEKvDmZmNsE3QI9oGf3qnKF6mmqrAxjmRHYSNci1qcy-6GsTfLHDTl6Q2lqkRwb8x4McFy6gx/s1600/tutorial3.png" height="201" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A new shape of Movement</td></tr>
</tbody></table>
<br />
<div>
<div>
The first animation handle is now complete.</div>
<div>
If we now change the position of <b><i>Movement </i></b>in <b>Pose Mode</b> nothing happens. Before we can bind skeleton movement to it we have to create the next handle called <b><i>Base</i></b>.</div>
<div>
<br /></div>
<div>
<b><i>Base </i></b>is a virtual bone (handle) that makes it possible to switch the movement of the whole character on and off. It is needed to make the creation of walkcycle animations easier. Whenever we are fine-tuning the body movements during walkcycle we often want to keep the character at one place to ensure that the movement of bones are truly cyclical and smooth.</div>
<div>
<ul>
<li><b><i>Movement </i></b>– makes it possible to ensure that feet are "glued" to the ground.</li>
<li><b><i>Base </i></b>– disables movement to make the whole animation smoothing easier</li>
</ul>
</div>
<div>
<br /></div>
<div>
Create <b><i>Base </i></b>the same way as <b><i>Movement </i></b>and move it to the origin.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQop7JxaX7LV95YfUOXfm-RmB3bqdKveMqNQw770mcM32EDw2QPhxBUP7qWTqedRJjpZqEs4USAA-CGooV74TtUCCW1DSLGjGqFi4p8JftutKn1O5sbHwaEzWM7fCqZqJiMbwMI4kl317Z/s1600/tutorial4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQop7JxaX7LV95YfUOXfm-RmB3bqdKveMqNQw770mcM32EDw2QPhxBUP7qWTqedRJjpZqEs4USAA-CGooV74TtUCCW1DSLGjGqFi4p8JftutKn1O5sbHwaEzWM7fCqZqJiMbwMI4kl317Z/s1600/tutorial4.png" height="139" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Base</td></tr>
</tbody></table>
<br />
<div>
<div>
NB! All bones have default shapes in <b>Edit Mode</b> and thus <b><i>Movement </i></b>and <b><i>Base </i></b>are completely coincident.</div>
<div>
Create a custom shape object for <b><i>Base </i></b>– for example a small cube that is placed near the end of the bone (you can use <b><i>Shape_Movement</i></b> as reference to adjust the shape in <b>Edit Mode</b>). Rename this new object to <b><i>Shape_Base</i></b>.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="228px;" src="https://lh6.googleusercontent.com/tEqLiI1HNsNA4J5YjDeealhIfgpaIxrCrQUaLzh6pShxOzPcTM5lZep4REBm4wzWS7OWhD6OJCmIY-BNHPnjueLqWv81x_7L03snMV5Rv8u6_kPlPs3rkLcYkpf0fymO2Q" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="340px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Shape_Base</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f78d-5a6e-391f-5829c3d6762f"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
Specify <b><i>Shape_Base </i></b>as the custom shape of <b><i>Base</i></b>.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="236px;" src="https://lh6.googleusercontent.com/rlF2LoDN9fpyzdFWyo9m74JC7vZLohi0PcklLMGr5JjJV-iP51vH2M5OooYlgD7RAer4lOAc2N26nAnLoVJrH5kiGGrf_kyJX0DxYWuSxl8wTPNimYJtFYUMYLngEhdt0w" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="360px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A new shape of Base</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f78d-eccb-5ced-32195e046211"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
<b><i>Base</i></b> is not connected with the armature at moment and thus nothing happens if you move it around.</div>
<div>
<br /></div>
<div>
Next we create the first two constraints.</div>
<div>
First we force <b><i>COG </i></b>to track the position of <b><i>Base </i></b>horizontally. Thus is <b><i>Base </i></b>moves during walkcycle, the origin of the skeleton moves together with it.</div>
<div>
<br /></div>
<div>
Select <b><i>COG</i></b>, open the <b>Bone Constraints</b> panel and add new constraint <b>Copy Location</b>.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="405px;" src="https://lh4.googleusercontent.com/3bxsGNhXvggs12QQfFqoL0dKFfqTVpLmVPVpgI-gKozGQS5Kml93XS0mph4X0mnBQqvs1GTIoJgFAH_Bxyv-sz6VnWhqKBUTDiVRKmrJuZy1dQKO3soPbz0hkznYMhRbpw" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="630px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Adding first constraint to COG</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f78e-df5c-3485-a8b05ec95413"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
The color of <b><i>COG </i></b>changes to green, indicating that it has a constraint associated with it. The name field of the constraint is currently red. This indicates that the constraint is "invalid" as it's target is not specified yet.</div>
<div>
NB! Target can be any object or a single bone from an armature. Currently we want it to be the bone <b><i>Base </i></b>so that <b><i>COG </i></b>to follows the location of it.</div>
<div>
<br /></div>
<div>
Chose the current armature object on <b>Target</b> field. A new field <b>Bone </b>appears. On that field chose <b><i>Base</i></b>.</div>
<div>
Skeleton now "jumps" into the ground. This is because constraint was activated (it name field is now gray inidicating it is valid) and the location of <b><i>COG </i></b>was moved to the location of <b><i>Base</i></b>.(that should be at the origin).</div>
<div>
We want <b><i>COG </i></b>to track only the horizontal position of <b><i>Base</i></b>. Thus we have to disable the <b>Z</b> checkbox on <b>Copy Location</b> panel. Skeleton now jumps to the correct location.</div>
<div>
In addition we want to be able to manually adjust the location of <b><i>COG </i></b>during a walkcycle. It is needed because during a walkcycle <b><i>Movement </i></b>moves uniformly forward but the centre of body should swing slightly up-down, left-right and forward-backwards. To make this adjustment possible enable <b>Offset </b>checkbox so that only the relative movement of <b><i>COG </i></b>is added to the location copied from <b><i>Base</i></b>.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh44jNbZacEONCiRvL_SbiHby9LKab4WYytAhV5dg1fZfCXWkFufODpP8WAvQd3l_Xy2Puq65q0HFEmM_cF4qwutqvi4pjwu2WeOR2IYQQz05s5Am_HMlIpckzvPQoqjYYfIB1Wp9g06FQb/s1600/tutorial5.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh44jNbZacEONCiRvL_SbiHby9LKab4WYytAhV5dg1fZfCXWkFufODpP8WAvQd3l_Xy2Puq65q0HFEmM_cF4qwutqvi4pjwu2WeOR2IYQQz05s5Am_HMlIpckzvPQoqjYYfIB1Wp9g06FQb/s1600/tutorial5.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">COG constraint panel</td></tr>
</tbody></table>
<br />
<div>
<div>
Confirm now that if <b><i>Base </i></b>is moved the whole skeleton moves together with it horizontally. Also we can move the skeleton separately by moving <b><i>COG</i></b>.</div>
<div>
<br /></div>
<div>
The first rigging constraint is now complete.</div>
<div>
<br /></div>
<div>
Next we add <b>Copy Location</b> constraint to <b><i>Base </i></b>and specify <b><i>Movement </i></b>as target.</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1BRESa-ox-C8FOckr8wJKnfK8sL1AZlHF0rdAf2oup6UpDptOWitFWYyZXB1ZZNnXUaCN9nHvb9ZKFBYWiTSoFX6j6PuEkjTKw_jqyZw0dHBlREH0E89OM57ifa5Tj9HMxqUm5y4dlv46/s1600/tutorial6.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1BRESa-ox-C8FOckr8wJKnfK8sL1AZlHF0rdAf2oup6UpDptOWitFWYyZXB1ZZNnXUaCN9nHvb9ZKFBYWiTSoFX6j6PuEkjTKw_jqyZw0dHBlREH0E89OM57ifa5Tj9HMxqUm5y4dlv46/s1600/tutorial6.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Base constraint panel</td></tr>
</tbody></table>
<br />
<div>
<div>
As you probably do not want to move <b><i>Base </i></b>independently from <b><i>Movement</i></b>, keep <b>X</b>, <b>Y</b> and <b>Z</b> checked and do not enable <b>Offset</b>.</div>
<div>
<br /></div>
<div>
Confirm now that by moving <b><i>Movement </i></b>the whole skeleton moves along and <b><i>Base </i></b>cannot be moved separately anymore.</div>
<div>
<br /></div>
<div>
The forward movement can now be toggled on/off by enabling/disabling the <b>Copy Location</b> constraint of <b><i>Base </i></b>(small eye icon at the header of constraint panel).</div>
</div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><img height="133px;" src="https://lh3.googleusercontent.com/B-_Ov2opZBNEC6ONbUd5PyLBQepWItCf7g9a3CgPO-QMOjv-oiBX1rExboWp0Wt9t007hpVnsKHrcnmqgLO9g9W0KscyT0raesOb2r08P9T9nvFCBesKk_eC4C5NUekW0A" style="-webkit-transform: rotate(0rad); border: none; margin-left: auto; margin-right: auto;" width="257px;" /></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Constraint enable/disable toggle</td></tr>
</tbody></table>
<br />
<div>
<span id="docs-internal-guid-ba72f73f-f794-154b-b0de-aa44f3911428"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
<div>
<div>
The handles and constraints moving the whole skeleton are now complete.</div>
<div>
<br /></div>
<div>
It is a good idea to move all rig handles to separate skeleton layer. Also all the bones that are not intended to be modified by user can be moved to separate layer so that they can be hidden during animation.</div>
</div>
<div>
<br />
<a href="http://khayyam.kaplinski.com/2014/05/rigging-tutorial-2.html">Part 2 of the tutorial: Leg rig</a></div>
<span id="docs-internal-guid-ba72f73f-f780-2eb2-a9fd-66135621987c"><span style="font-size: 16px; vertical-align: baseline; white-space: pre-wrap;"></span></span></div>
Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com7tag:blogger.com,1999:blog-783893035873019493.post-68383126284364271332012-09-04T10:32:00.001+03:002012-09-04T10:32:57.254+03:00Prototype gameplay videoI recorded a short video of prototype/techdemo gameplay.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/AE5f76EPexA?feature=player_embedded' frameborder='0'></iframe></div>
<br />
It is just Sara running in town and talking with two NPC-s. As you can see, the town is composed mostly of placeholder boxes.<br />
In cathedral you can see Annette, who is not present in techdemo. Also the sight IK is working - thus people are actually looking at the face of Sara while talking.<br />
<br />
Also I am currently working on new semi-realistic texture for Sara.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhu55ahxjfq9iMB_-xSBNT5w7-dCgUSAKUmlnffaN_iP5SFBKzBo0LajNn_q_KlvkXBP89jnGJ_jOwgipcP54GM3wTQcSBWWbz2JMm4OMhRGFFg1LhEP_xxIjBQ1rkwJMwRPof-IqofOtwZ/s1600/thumbnail_256.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhu55ahxjfq9iMB_-xSBNT5w7-dCgUSAKUmlnffaN_iP5SFBKzBo0LajNn_q_KlvkXBP89jnGJ_jOwgipcP54GM3wTQcSBWWbz2JMm4OMhRGFFg1LhEP_xxIjBQ1rkwJMwRPof-IqofOtwZ/s1600/thumbnail_256.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">New face texture</td></tr>
</tbody></table>
I do not plan to make shinya strictly photorealistic, although most textures will be based on photos. Everything will be a bit simplified and more colorful. The correct balance needs some tweaking, but the techdemo hopefully shows a little bit of final visual feeling.<br />
<br />Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com6tag:blogger.com,1999:blog-783893035873019493.post-6366950429844948202012-08-22T02:00:00.002+03:002012-08-22T02:02:24.230+03:00A little nostalgiaDigging through my drawers I found some old pieces of concept art and hijacked these for the benefit of Shinya.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE9_jtnhgcRsfkhHXFFgVLTxOD8O9S-DgVrlINqLQvP0c5eoc1BF05HZKIEQMjx2nlFzMN31yHALp0_D8QbswWExj71zUjEqrfp4EphkmtURKWVzCOKm3hMLNDdahkt1LSX4gpm3NpsefX/s1600/Irene-loss-1-1200.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE9_jtnhgcRsfkhHXFFgVLTxOD8O9S-DgVrlINqLQvP0c5eoc1BF05HZKIEQMjx2nlFzMN31yHALp0_D8QbswWExj71zUjEqrfp4EphkmtURKWVzCOKm3hMLNDdahkt1LSX4gpm3NpsefX/s400/Irene-loss-1-1200.jpg" width="256" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A castle at the end of the world</td></tr>
</tbody></table>
<br />
I do not even remember the time when I created this (it has few more views and floorplan as well). The castle was an illustration to a story which I now only remember vaguely. It is situated on desolated seashore, ruled by dark and violent knights and hides short but bloody history.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSel7LpS7ykUtuEvoQ6G_TbJuk-CG-sBEfea2gVcU3cULBlwH9oudOyr2nvaOvj40ffDLPaDly9XBiTOZjdllt0sPStDPyCU0_qgiSfSmyhSmwHWHUdHS2BQ8iURTlHbRU76C6lEQfc9Rn/s1600/Irene-loss-3-1200.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSel7LpS7ykUtuEvoQ6G_TbJuk-CG-sBEfea2gVcU3cULBlwH9oudOyr2nvaOvj40ffDLPaDly9XBiTOZjdllt0sPStDPyCU0_qgiSfSmyhSmwHWHUdHS2BQ8iURTlHbRU76C6lEQfc9Rn/s400/Irene-loss-3-1200.jpg" width="255" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The castle of Irene</td></tr>
</tbody></table>
This one was created for a unfinished visual novel "The Song of Mountains and Water". It was planned along the lines of Japanese dating sims with plentitude of charactes and complex relationship.<br />
"The Song of Mountains and Water" never took off, but it lent the geography and many characters to Shinya story. Among others Sara - the protagonist of Shinya - comes from "The Song", although she had a secondary role there.<br />
The castle ruins were intended to be situated in southern mountains - where you can see the castle gates in techdemo.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQZG1kQDgQ-2SDHTBJE_lw-7FmiHzn8tzraup9VXoDsBlj14h3nQXA8PSR-dHsFtgqjH1NEwT2O9l9s2z0BUhe67W8euaDGZzy1ZsOhdbb4ddKH0R-_Iy4Wx-L1L33TmIWkPQ6qTMYN5SG/s1600/veski-1-1200.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQZG1kQDgQ-2SDHTBJE_lw-7FmiHzn8tzraup9VXoDsBlj14h3nQXA8PSR-dHsFtgqjH1NEwT2O9l9s2z0BUhe67W8euaDGZzy1ZsOhdbb4ddKH0R-_Iy4Wx-L1L33TmIWkPQ6qTMYN5SG/s400/veski-1-1200.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A mill</td></tr>
</tbody></table>
This again is from "The Song of Mountains and Water". It was planned to be at the same place where there is now a mill in Shinya techdemo.Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com3tag:blogger.com,1999:blog-783893035873019493.post-82458607974508374242012-08-02T13:32:00.000+03:002012-08-02T13:32:12.787+03:00AnnetteAnnette is an older lady who will also appears as minor character in Shinya. She is also the most recent and advanced character model in Shinya.<br />
<br />
Modeled in Blender 2.63.<br />
3269 vertexes, 3115 faces.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaf8Mle9b6CwlvCWKVyKqlL8W8rdXNaccnWxLR0YpRroZuWyHGZjr3yrKZs8U-i5I3jUf2_-4LVcidLoQPiowRcVvonyS3vEModrcbiLF349Ks_TYTY17Wj0WegBR0QfTObiIUuwRwqf07/s1600/preview-3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaf8Mle9b6CwlvCWKVyKqlL8W8rdXNaccnWxLR0YpRroZuWyHGZjr3yrKZs8U-i5I3jUf2_-4LVcidLoQPiowRcVvonyS3vEModrcbiLF349Ks_TYTY17Wj0WegBR0QfTObiIUuwRwqf07/s400/preview-3.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Annette</td></tr>
</tbody></table>
<br />
<br />
<h3>
Topology</h3>
The model is 99% quads with clean edge-loop topology - the only exceptions being few triangles on hands.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizV9lNGJdSVXetQiz8N6xY7RKTYBFTD59dtt7l9vU46Ja0qmiF1j_vfqnUE3Ti_YURWSj44h3QhhbuHsm0pc6_-Yxoil8I2G1D6iHWHfhj8eUbPOefJq_DfqS-x5epBgVLFosvWTD3LDjO/s1600/topology-3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizV9lNGJdSVXetQiz8N6xY7RKTYBFTD59dtt7l9vU46Ja0qmiF1j_vfqnUE3Ti_YURWSj44h3QhhbuHsm0pc6_-Yxoil8I2G1D6iHWHfhj8eUbPOefJq_DfqS-x5epBgVLFosvWTD3LDjO/s400/topology-3.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Annette topology</td></tr>
</tbody></table>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<h3>
Skeleton</h3>
She has full deformation skeleton and simple but effective animation rig.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBqK67TxCx1oj356VY4g_RAbzRoJXrXOxoQtTuKGqtN29GIwt8lmH3REiJ5alTTyGJpptCi71nAym9mqT8WBvTFnJAiOVpRW6bfKwlmS6wzoPNyFtpc_ckFQdM4D1HYGQ5B5P3W8t8GgcT/s1600/topology-6.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBqK67TxCx1oj356VY4g_RAbzRoJXrXOxoQtTuKGqtN29GIwt8lmH3REiJ5alTTyGJpptCi71nAym9mqT8WBvTFnJAiOVpRW6bfKwlmS6wzoPNyFtpc_ckFQdM4D1HYGQ5B5P3W8t8GgcT/s400/topology-6.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Skeleton</td></tr>
</tbody></table>
<br />
There are also bones for basic facial expressions and hair and clothes movement.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0MrUwcoYCiS-HUjm-QZtSb_1dGN5-abb1g09N9vikIIGWEyArmSqG-TuO2CQIuos_f_YiUFVCerVATTSX1luLihenQwymFKJPYL8CfNvzqzgKxkXgbJ76MUyyuhqRSE-RbjK1q8uLHK0N/s1600/emotions.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0MrUwcoYCiS-HUjm-QZtSb_1dGN5-abb1g09N9vikIIGWEyArmSqG-TuO2CQIuos_f_YiUFVCerVATTSX1luLihenQwymFKJPYL8CfNvzqzgKxkXgbJ76MUyyuhqRSE-RbjK1q8uLHK0N/s400/emotions.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Facial emotions</td></tr>
</tbody></table>
<br />
<br />
<h3>
Texture</h3>
Texture is non-overlapping (except hair).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdUx-KLRUxbl-MDynRDXKzek65dv-PrvItdiImGHFaBfZ6SRE8bQawVsXIzhUvM_mTfpIq13_uuGuDNHMH-NNCh_ipJ9jnqx3JiFvruJJRhnViZEFXoqocG3MpN93_BHbwMmAEKwv_cXg2/s1600/anette_1024.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdUx-KLRUxbl-MDynRDXKzek65dv-PrvItdiImGHFaBfZ6SRE8bQawVsXIzhUvM_mTfpIq13_uuGuDNHMH-NNCh_ipJ9jnqx3JiFvruJJRhnViZEFXoqocG3MpN93_BHbwMmAEKwv_cXg2/s400/anette_1024.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Texture layout</td></tr>
</tbody></table>
<br />
Annette is available from Turbosquid and The3DStudio for 49$.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.turbosquid.com/3d-models/3d-lady-medieval/682921?referral=lauris71" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;">
<img border="0" src="https://www.turbosquid.com/Images/v35/Guild/mm-banner-half-general.gif" /></a>
<a href="http://www.the3dstudio.com/product_search.aspx?id_affiliate=597849&id_author=597849"><img alt="View my work at The3dStudio.com" border="0" src="http://www.the3dstudio.com/images/author1.jpg" title="View my work at The3dStudio.com" /></a></div>
<br />Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com0tag:blogger.com,1999:blog-783893035873019493.post-84351991731293239392012-04-15T00:05:00.000+03:002012-04-15T00:05:26.571+03:00Shinya techdemo for LinuxI took some time and managed to get Shinya techdemo to compile under Linux (more precisely 64 bit Ubuntu 11.10). It was surprisingly painless - seems that years of cross developing Sodipodi and Khayyam for Linux and Windows have payed off.<br />
<br />
The demo can be downloaded from <a href="http://lauris.kaplinski.com/shinya/download.html">Shinya homepage</a>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2oFmQ3tI3rHS28hMvNS0z3ElBpxK9NLzZzIfvTVH5_bcb6MEepOzDLN2DJ7-H0-Ot0muqGDmfCBH12ioOSIkuIBYEeeWp-qRCe83Yt1gW4CW8IBZMfJ2BzvfkcftjV7ozHxpQ5DUvmKZq/s1600/Screenshot-20120403-1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2oFmQ3tI3rHS28hMvNS0z3ElBpxK9NLzZzIfvTVH5_bcb6MEepOzDLN2DJ7-H0-Ot0muqGDmfCBH12ioOSIkuIBYEeeWp-qRCe83Yt1gW4CW8IBZMfJ2BzvfkcftjV7ozHxpQ5DUvmKZq/s400/Screenshot-20120403-1.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">City house at night</td></tr>
</tbody></table>
<span id="goog_822163883"></span><span id="goog_822163884"></span><br />
<br />
It requires SpiderMonkey, SDL and SDL_mixer libraries.<br />
There is only 64 bit version currently. I have no 32 bit Linux computer and I do not trust cross-compiling...<br />
<br />
The two biggest problems were NVidia drivers and fonts. There is some weird bug either in Shinya or NVidia drivers that causes Z value of directional light volume to go crazy. It happens only on some cards, and on certain NVidia cards only in release mode - whatever it means. The same bug appeared under Linux build too - as a temporary solution the light volume is clipped a bit closer than far plane. Thus some faraway mountains appear darker than they should.<br />
Fonts are rendered using libnrtype frontend to Fontconfig and FreeType2. Unfortunately I am not sure, which fonts with eastern-asian glyphs I can expect to be installed, so sometimes texts may not be very good. One possibility is to include some free font with Shinya.<br />
<br />
Also, for your viewing pleasure here is wolf walking animation rendered in Blender. It is still a bit stiff, but looks pretty good at Shinya resolution:<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/b5BCJ6AVo8c?feature=player_embedded' frameborder='0'></iframe></div>
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com0tag:blogger.com,1999:blog-783893035873019493.post-13216184285373973382012-04-05T02:13:00.000+03:002012-04-05T02:13:29.029+03:00Rendering foliage with highpass filterIf rendering foliage on has two options:<br />
<ol>
<li>Render it in opaque pass, using alpha test to discard transparent pixels</li>
<li>Render it in transparent pass, using true alpha blending </li>
</ol>
Alpha blending gives the best results but kills your rendering performance very quickly. First - the blending itself is much more fillrate intensive operation than opaque rendering. Second - you cannot use deferred rendering. And third - you have to sort polygons.<br />
<br />
Here is the transparent pass render of small test scene, as a reference:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOorRnt4IPUzGIQ7M68HNlTR30eAlK_IfWrwdySf7OG8oh5rW1aeSbEEjSy18JyT8KQ3LF0ZjJXSecfXDQAbyC5GVmcAhx3gMx-PKOuWqaUaCyM_fXN9oV5MVQ5ZZ7d5Ceg4Rdzm97pD5X/s1600/dactylis-materials-2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOorRnt4IPUzGIQ7M68HNlTR30eAlK_IfWrwdySf7OG8oh5rW1aeSbEEjSy18JyT8KQ3LF0ZjJXSecfXDQAbyC5GVmcAhx3gMx-PKOuWqaUaCyM_fXN9oV5MVQ5ZZ7d5Ceg4Rdzm97pD5X/s400/dactylis-materials-2.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A patch of Dactylis glomerata, rendered in transparent pass</td></tr>
</tbody></table>
<br />
Even small patch of forest or meadow will have tens of thousands of
polygons. Something like realistic landscape simply is not doable using
transparency on current generation hardware.<br />
<br />
<br />
This leaves us with the other option - opaque rendering.<br />
<br />
<br />
While writing foliage shaders for Shinya the biggest problem was,
that scaled down textures (i.e. higher mipmap levels) looked totally
crappy. Whatever was the original shape of plant - looking at it from
distance it morphed into rounded blob.<br />
The reason for it was the
minimization algorithm, that averaged alpha. Thus the parts of foliage
where there were only few "holes" - i.e. central parts - became
completely opaque. And the parts, where there were only few leaves -
i.e. outer regions - became fully transparent.<br />
<br />
The "original" opaque render, using default mipmap generation (box filer):<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCj-u1RuPkg1fEj9tUZSkmtDJcNbBQWtmYQD25fXpGqhcftLDOmqUAJsqe7BEPhhniG3dVhgXuqGg9PyBd3bGWjjAOHBXAOn_1Kx8qn0g5l4co4taOO1Mk3FrkcXhlRZzuYkMBX4plO3ex/s1600/dactylis-materials-1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCj-u1RuPkg1fEj9tUZSkmtDJcNbBQWtmYQD25fXpGqhcftLDOmqUAJsqe7BEPhhniG3dVhgXuqGg9PyBd3bGWjjAOHBXAOn_1Kx8qn0g5l4co4taOO1Mk3FrkcXhlRZzuYkMBX4plO3ex/s400/dactylis-materials-1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The same image, rendered in opaque pass with box-filtered mipmaps</td></tr>
</tbody></table>
<br />
While messing with textures and trying to find the best alpha
cutoff value, I finally got the right idea - we do not want to get the
average image but want to preserve "features" of image in minimization.
Features being both "holes" and "patches" (i.e. leaves). This can be
done by increasing high frequencies of image. I.e. we want to replace
the standard mipmap generation filter (normally box filter) with
something better.<br />
Fortunately I was already composing mipmaps by
hand while creating packed textures. Thus I added very simple
edge-detection filter with 3x3 kernel to the foliage mipmap generation (after the minimization with box filer). And the results were really good.<br />
<br />
The "enhanced" opaque render with edge detection filter:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi59nlaB9tJV1sQ-zMzndn7DYOt7RyFK6Sw3bH4hvAE1mNkVPpo4xXoFD7fvc2RfHlPgV85NKSwTS0PxabaO3oyqm2URN2QgUH9wiKxFJCAfGNpBNPYIF1TFErfLA9vD_BS7UR9oVVtCsvt/s1600/dactylis-materials-3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi59nlaB9tJV1sQ-zMzndn7DYOt7RyFK6Sw3bH4hvAE1mNkVPpo4xXoFD7fvc2RfHlPgV85NKSwTS0PxabaO3oyqm2URN2QgUH9wiKxFJCAfGNpBNPYIF1TFErfLA9vD_BS7UR9oVVtCsvt/s400/dactylis-materials-3.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The same image, rendered in opaque pass with edge-detection filter</td></tr>
</tbody></table>
<br />
As you can see, edge detection successfully preserves the general features of the texture - stems and narrow leaves, while simple box filter tends to smear these out.<br />
<br />
The kernel for edge-detection was:<br />
<br />
<pre>-1 -1 -1
-1 9 -1
-1 -1 -1
</pre>
<br />
And here is a screenshot of the foliage shader in action. The meadow size is about 40x40 meters, grass is about 50000 instanced polygons.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR2lig1Ew5rSlqgRJMGEVGnCoxXJ52jiPGfLZCyTVEr_EFMJkqUDsHvvjGcDgzyWUsjrn8DnNiT3rxper-9j8gSiOLT3Wf4-Oql0qRTFtWp3rf1_f3-basYViW21AvEFNXL7NTLiAqt0Db/s1600/Screenshot-20120403-8.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR2lig1Ew5rSlqgRJMGEVGnCoxXJ52jiPGfLZCyTVEr_EFMJkqUDsHvvjGcDgzyWUsjrn8DnNiT3rxper-9j8gSiOLT3Wf4-Oql0qRTFtWp3rf1_f3-basYViW21AvEFNXL7NTLiAqt0Db/s400/Screenshot-20120403-8.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara in meadow</td></tr>
</tbody></table>
<br />
<br />
You can download the <i>Dactylis glomerata</i> texture from my <a href="http://lauris71.deviantart.com/gallery/">DeviantArt gallery</a>.<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com0tag:blogger.com,1999:blog-783893035873019493.post-57289075675027706442012-03-31T23:13:00.000+03:002012-03-31T23:13:35.082+03:00Shinya technology demoThings are going pretty well in Shinya-land recently. For the first time it has materialized in the tangible form for people other than me. And got its own webpage as well: <a href="http://lauris.kaplinski.com/shinya/index.html">http://lauris.kaplinski.com/shinya/index.html</a>.<br />
You can find the technology demo at the <a href="http://lauris.kaplinski.com/shinya/download.html">download section</a> in Shinya homepage.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjC9HCF2MuAzsLQIsEfWHlFLQ7CNjKpBA5yhlyOpugXbhhY6YJUmMnE7R_-mCnktGc0kdmUHQwfozmLqpBolinjA7QepOnSZpV6Yqz58IS-b7aWfF8GCHiFmCcZDsQ3c_M4m4EbfGyohmLr/s1600/sara_and_wolf_20120331.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjC9HCF2MuAzsLQIsEfWHlFLQ7CNjKpBA5yhlyOpugXbhhY6YJUmMnE7R_-mCnktGc0kdmUHQwfozmLqpBolinjA7QepOnSZpV6Yqz58IS-b7aWfF8GCHiFmCcZDsQ3c_M4m4EbfGyohmLr/s400/sara_and_wolf_20120331.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara and wolf. Sorry - I don't have red riding hood model for her... </td></tr>
</tbody></table>
<br />
Some notes:<br />
<ul>
<li>It is currently only for Windows. When I'll find time and there is interest, I'll probably try to release Linux version as well.</li>
<li>It is compiled in Windows 7 with VS2010 express - I hope that all dll-s are included.</li>
<li>It is alpha quality code - do not be surprised if it crashes or does not work at all.</li>
<li>It uses OpenGL - id it does not work, you may try upgrading graphic card drivers from NVidia or ATI.</li>
<li>Very little of the actual game-land is present yet. Or more precisely - the land is present, but mostly empty.</li>
<li>Movement and collision system is not fully finished yet. </li>
<li>Vladimir is liar - the town is called Spielburg, not Midgard.</li>
<li>You cannot initiate talk with wolf.</li>
</ul>
<br />
Have fun!<br />
Lauris Kaplinski<br />
<br />Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com0tag:blogger.com,1999:blog-783893035873019493.post-22770107871622502862012-02-13T01:55:00.000+02:002012-02-13T02:04:30.962+02:00Shinya - the gameI have made too big progress with Shinya to not share it at last...<br />
<br />
It uses the same rendering engine as Khayyam (Sehle library). Most new (and unreleased) features in Khayyam are actually implemented so it can be used as scene builder for Shinya. Among other things there are now reflections, occlusion queries, volumetric lights and darkness, instanced vegetation, layered terrain and so on...<br />
No - I have not forgotten the new Khayyam release. The main problem holding me back is that there should be at least preliminary support for new materials for POVRay exporter. And things just progress too fast...<br />
<br />
Shinya playable tech-demo is nearly complete. The biggest roadblock at moment is fixing remaining shader bugs for ATI GPU-s (this is the unfortunate consequence of writing engine in OpenGL...)<br />
<br />
OK, I know - pics or it did not happen...<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5SRNM9IIe38PQwjo83DejKaPbIHc-dLdffluiLAXSY_mQwNCMUEsm-1EJ_nLPrNGbvkjHrfevV8uuOkA5_g2l2OwetYvZ5rnx-DGiIWIqFGzizAeBQFkqI4IwLDmLJ8nvVeEBCm6bmAng/s1600/shinya-ss-01.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5SRNM9IIe38PQwjo83DejKaPbIHc-dLdffluiLAXSY_mQwNCMUEsm-1EJ_nLPrNGbvkjHrfevV8uuOkA5_g2l2OwetYvZ5rnx-DGiIWIqFGzizAeBQFkqI4IwLDmLJ8nvVeEBCm6bmAng/s400/shinya-ss-01.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara looking down to Shinya world</td></tr>
</tbody></table>
<br />
Shinya will be adventure game, similar in style to Quest for Glory series. It takes place in (alternate and fictional) medieval times somewhere in central Europe.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEkly3rM1yY_4VHLnP9_XHuYFNbyZqzSRzblNCzavsTNXz8xXuQRqy52M7zlGsGZX0JVy22Wenj7ELvuXGfebQuUw6MsXWus4NIRKwdISsuurEAaD6MQccOsoRGjR5BMmIhZBO3xOB1SIH/s1600/shinya-ss-02.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEkly3rM1yY_4VHLnP9_XHuYFNbyZqzSRzblNCzavsTNXz8xXuQRqy52M7zlGsGZX0JVy22Wenj7ELvuXGfebQuUw6MsXWus4NIRKwdISsuurEAaD6MQccOsoRGjR5BMmIhZBO3xOB1SIH/s400/shinya-ss-02.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">There is a bit more vegetation in some areas</td></tr>
</tbody></table>
The main heroine is Sara - a bit complicated 12 years old girl, who is recently moved to the area with her aunt (she is orphan, as expected).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm1Gddqmh6c2x6M4oSrOn_2spnvM0RieBBsYL7tXNaxmKDU-mv4f5avdYquo6fh4nWOvc3cYPI_QbjoFTLN6reT1IG1uPg3nG-y7xaJmWoHShS9DG8CNi4pa-RMEYsDxyogLiracI9PRX_/s1600/shinya-ss-03.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm1Gddqmh6c2x6M4oSrOn_2spnvM0RieBBsYL7tXNaxmKDU-mv4f5avdYquo6fh4nWOvc3cYPI_QbjoFTLN6reT1IG1uPg3nG-y7xaJmWoHShS9DG8CNi4pa-RMEYsDxyogLiracI9PRX_/s400/shinya-ss-03.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Another human being!</td></tr>
</tbody></table>
She will involuntarily dragged into some intrigues, conflicts and mysteries that have lately developed in this otherwise tranquil place. Or maybe not so involuntarily - se is, after all, a bit complicated damsel...<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj2nAMeDW-rAcbss3nuK2XqLcFNHUU2sW-rvAOnd_cTJmffm9RPBPhuFDxwKJPRAf_0xerTEBsACku4m1tPtFYbnRb1BcBUhqh2bRikpIxAuu2PWK3OZaclDPNfJXagXzJmzFTDc6lbLgc/s1600/shinya-ss-04.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhj2nAMeDW-rAcbss3nuK2XqLcFNHUU2sW-rvAOnd_cTJmffm9RPBPhuFDxwKJPRAf_0xerTEBsACku4m1tPtFYbnRb1BcBUhqh2bRikpIxAuu2PWK3OZaclDPNfJXagXzJmzFTDc6lbLgc/s400/shinya-ss-04.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A mill at the main river </td></tr>
</tbody></table>
Modeling in Blender has been the other big time-consumer other than programming. I am slowly starting to get a grasp of this program - not a pro yet, of course.<br />
The most complex model so far is cathedral - LOD 0 is about 190 000 triangles (LOD 1 about 2500). I develop Shinya mostly on laptop with NVidia GT540M GPU - and thus hope, that for the time it will be released it runs smoothy on most desktop computers at least.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRn_tWfkjV_CjnG-m8X6UeBzBllPPdmaZMK1-cS-2E26YDOhVcasVx1RY1bsdW7lYPdVRyAEXPzvmJYPheARkFrB5JN2cIMJGS3443YQo6Vy6UTy4ra-sQotCvSrUksPFvm-Uudffd9PTA/s1600/shinya-ss-05.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRn_tWfkjV_CjnG-m8X6UeBzBllPPdmaZMK1-cS-2E26YDOhVcasVx1RY1bsdW7lYPdVRyAEXPzvmJYPheARkFrB5JN2cIMJGS3443YQo6Vy6UTy4ra-sQotCvSrUksPFvm-Uudffd9PTA/s400/shinya-ss-05.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Inside cathedral</td></tr>
</tbody></table>
The world of Shinya is 4x4 km freely roamable terrain. It has continuous day/night cycles - but you cannot stay awake indefinitely, even by using stamina potions. Thus NPC-s will have certain time to arrange things without player peeking over their shoulders.<br />
<br />
Ah, almost forgot - Shinya it is written <span style="font-size: large;">深夜</span><br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com3tag:blogger.com,1999:blog-783893035873019493.post-59199547684399316912012-01-17T01:22:00.000+02:002012-01-17T01:22:44.375+02:00Models and texturesI have been busy with modeling and texturing for Shinya - and rewriting parts of Khayyam/Sehle to support certain must-be features like vegetation instancing and geometry LODs. Some (semi)finished things are available for download - more will come. Although certain parts will probably remain secret fro now...<br />
<br />
<span style="font-size: large;">Models</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyBWxn7EXYwfE1vblcuBjuiyG7FW-L5COevKUC4fHtgYVtaoEQiYEySXjscPR4q7yGDw3GK-qiAimks0OuA1UXlJQ70yZiPDJnMSFYcDexLYFQkKA3gXHH5Wyf5IiKDZ4ehNz3anNEBslm/s1600/preview.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyBWxn7EXYwfE1vblcuBjuiyG7FW-L5COevKUC4fHtgYVtaoEQiYEySXjscPR4q7yGDw3GK-qiAimks0OuA1UXlJQ70yZiPDJnMSFYcDexLYFQkKA3gXHH5Wyf5IiKDZ4ehNz3anNEBslm/s400/preview.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara the tween girl (Blender render)</td></tr>
</tbody></table>
<br />
Textured and rigged, about 6000 polygons. Blender source file and some exported formats can be downloaded from <a href="http://www.turbosquid.com/3d-models/tween-girl-blend-free/583223">TurboSquid</a>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCmeYZCdNQY2ejfM9STx9di0VAxJpi8q_XvyRD0p9sRn8-e0-MNjUcA87xbEOK2nAUyP1WsJWqhMJHrXN7Bm99Cm1dJJvz9BbRGIwet2fTggdmJZPWq0PTk060crXJ9epR-E8lBHJW917V/s1600/QuercusA.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCmeYZCdNQY2ejfM9STx9di0VAxJpi8q_XvyRD0p9sRn8-e0-MNjUcA87xbEOK2nAUyP1WsJWqhMJHrXN7Bm99Cm1dJJvz9BbRGIwet2fTggdmJZPWq0PTk060crXJ9epR-E8lBHJW917V/s400/QuercusA.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Oak tree (Blender render)</td></tr>
</tbody></table>
Oak tree with 3 LOD levels.<br />
<ul>
<li>LOD0: 4407 vertices, 2791 faces</li>
<li>LOD1: 645 vertices, 442 faces</li>
<li>LOD2: 21 vertices, 7 faces</li>
<li>Collision (only trunk): 24 vertices, 16 faces</li>
</ul>
Blender source file can be downloaded from <a href="http://opengameart.org/content/oak-tree">OpenGameArt</a>.<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.blogger.com/video.g?token=AD6v5dwXnsaJjxWvfWcbiHbYybPdDqQVEypG2QWS_gXXB4-n35bJ5ba9u9olkJ6KreTwZ1tUZXdQYZPr1SuAzm95' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
Unfinished medieval house. About 10000 polygons, needs some agressive LODing.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSX4IgiRwQh1WYEmyl2T6EccC76tWJ6RjtlubuGbBHbxUoDsUa4icCjOm96FEoXGyATv38-ifaWZg2WG-OEfg5NqDkgzJFHE6ckykmGj9ZXp4NVtPriiB_-1O9tanxG8KmNwwzGyECq5F/s1600/Cathedral.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzSX4IgiRwQh1WYEmyl2T6EccC76tWJ6RjtlubuGbBHbxUoDsUa4icCjOm96FEoXGyATv38-ifaWZg2WG-OEfg5NqDkgzJFHE6ckykmGj9ZXp4NVtPriiB_-1O9tanxG8KmNwwzGyECq5F/s400/Cathedral.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cathedral interior (Blender render)</td></tr>
</tbody></table>
Unfinished cathedral. About 55000 polygons - half of those are window frames. Needs also some agressive LODing to be usable in game.<br />
<br />
<span style="font-size: large;">Textures</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNZc4sthgsPdXN8CXnMBkwXbQ9SM-KMc5t8u95gw-UPy2j_zY09vuHVUh-1H434laES0l_iq6Xwb2eUj8NEJ6Ooo50LdQ543R6Uv_KTV6gnRAu11mxE94yqCUoRadwDqgad5X92D7LDkxo/s1600/Ground_6693_1024.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNZc4sthgsPdXN8CXnMBkwXbQ9SM-KMc5t8u95gw-UPy2j_zY09vuHVUh-1H434laES0l_iq6Xwb2eUj8NEJ6Ooo50LdQ543R6Uv_KTV6gnRAu11mxE94yqCUoRadwDqgad5X92D7LDkxo/s400/Ground_6693_1024.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Seamless ground texture - dead leaves</td></tr>
</tbody></table>
<br />
Small collection of plant and ground textures is available from my <a href="http://lauris71.deviantart.com/gallery/">DeviantArt</a> page.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://opengameart.org/sites/default/files/Cutout-plants-1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="http://opengameart.org/sites/default/files/Cutout-plants-1.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Cut-out plants</td></tr>
</tbody></table>
<br />
A collection of cut-out plants at <a href="http://opengameart.org/content/cutout-plant-textures-1">OpenGameArt</a>.<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com3tag:blogger.com,1999:blog-783893035873019493.post-85295030485471433482011-12-17T19:42:00.000+02:002011-12-29T23:13:08.830+02:00Matching image colors with TexturewerkeIn the <a href="http://khayyam.kaplinski.com/2011/12/texturewerke-01.html">last post</a> I demonstrated how to use TextureWerke for masked polynome high-pass filtering. In addition to that, TextureWerke 0.1 has another feature - matching colors between two (masked) images.<br />
<br />
<span style="font-size: large;">Workflow</span><br />
<br />
Let's start with two images of stone wall we want to merge into one texture:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbeMWCIkp4xiO7wy9jywrZZyGxU2i-_uHLbXUSd4PbKIvF03QeTqKmTEzXTtnK5oOur4kb3KOcGH_13Oacri3UX7n6Q9OBD_kn1Ob2SL_Ee4kktZDRytd4u9_lL0_2CWVPGDX8QGNlzmy5/s1600/texturewerke-6.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbeMWCIkp4xiO7wy9jywrZZyGxU2i-_uHLbXUSd4PbKIvF03QeTqKmTEzXTtnK5oOur4kb3KOcGH_13Oacri3UX7n6Q9OBD_kn1Ob2SL_Ee4kktZDRytd4u9_lL0_2CWVPGDX8QGNlzmy5/s400/texturewerke-6.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">First image - a plain wall</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7Ve2V4dA-6Tvt-rNIhRONkNoOvayvV8joAnjd5lr_X94PRFn9pVsgefqoHIkGXWTloE1MzlY2LJ37MuRxHf4qxxYYSyYih4agpQOv9ncGGHMFhYZ6M-o2bU1PB0gwdbo8dPl7eEIOMRtN/s1600/texturewerke-7.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7Ve2V4dA-6Tvt-rNIhRONkNoOvayvV8joAnjd5lr_X94PRFn9pVsgefqoHIkGXWTloE1MzlY2LJ37MuRxHf4qxxYYSyYih4agpQOv9ncGGHMFhYZ6M-o2bU1PB0gwdbo8dPl7eEIOMRtN/s400/texturewerke-7.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Second image - a corner made of bricks</td></tr>
</tbody></table>
<br />
As you can see (among other things) the images have a bit different colors - the first one has more contrast and red. The bright background of the second picture influenced camera color adjustment logic so that the wall is more gray and has less contrast.<br />
We will correct this automatically. First we move both photos to the same image (not necessary, but easier to compare) and add masks. We mask out all details that we do not want to influence final color - i.e. roof, shaded areas, brick structures, grass and background.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha32TpBX36eAHmXVvbSSRE1JL2W_0lb1XHpEjOJWq2-V0R3mEymQ9hjLiJoaGSDlOfEA-uGIdbS2YSNc0MNYyNAAC1uc-rFzs5nRLkJMjyCDiNRtKuXAmUWYVy9R4AQ05bqqJgv2TTKWP2/s1600/texturewerke-9.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="265" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEha32TpBX36eAHmXVvbSSRE1JL2W_0lb1XHpEjOJWq2-V0R3mEymQ9hjLiJoaGSDlOfEA-uGIdbS2YSNc0MNYyNAAC1uc-rFzs5nRLkJMjyCDiNRtKuXAmUWYVy9R4AQ05bqqJgv2TTKWP2/s400/texturewerke-9.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Masked images on top to each other.</td></tr>
</tbody></table>
<br />
Next we select the masked layer whose colors we want to change (Layer 2), invoke TextureWerke and choose color matching mode.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGjttLcWzs1p9dZh5lM93C-iTC3cRLLNdYmXScO-TGl_XQOeAc5RSiSQ_AYoO0GWNkv3NUROGimCpU9zIgXIRkbQVGfKjWvfPv4ndYRC83R9if2MiUugjLX8SR8-T3eerWhSFQZ_Ja31-s/s1600/texturewerke-color.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGjttLcWzs1p9dZh5lM93C-iTC3cRLLNdYmXScO-TGl_XQOeAc5RSiSQ_AYoO0GWNkv3NUROGimCpU9zIgXIRkbQVGfKjWvfPv4ndYRC83R9if2MiUugjLX8SR8-T3eerWhSFQZ_Ja31-s/s320/texturewerke-color.jpg" width="216" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">TextureWerke dialog window in color mode</td></tr>
</tbody></table>
<br />
Template is the (masked) layer whose color profile we want to emulate in selected layer.<br />
Blurring reduces the noisiness of distribution curves and thus helps to overcome artefacts resulting from different sharpness of images. On the other hand it may mask out certain differences in color distribution and thus reduce the matching quality. You can always try and use what gives the best results.<br />
And at last we apply the filter.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoBgrwl1gN11PjwMY99qXC2wJd_AXFoteUvq-p6-Mqga_NZ-8CZboqqilD9ySTxJ9I-jvjFrs02rRsk-LnCY-sxeRtgowhYOua9UonF__yt_rtNPLwo9_u0l6N6Op-fwwwFn1HEkSIskmW/s1600/texturewerke-10.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoBgrwl1gN11PjwMY99qXC2wJd_AXFoteUvq-p6-Mqga_NZ-8CZboqqilD9ySTxJ9I-jvjFrs02rRsk-LnCY-sxeRtgowhYOua9UonF__yt_rtNPLwo9_u0l6N6Op-fwwwFn1HEkSIskmW/s400/texturewerke-10.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Image with adjusted colors</td></tr>
</tbody></table>
<br />
As you can see, the overall color and contrast of the (stone part of) wall matches with template image. On the other hand the areas that were masked out (bricks, backround...) are now completely off. But probably we did not want to use these anyways - except bricks maybe - but these can easily be copied and pasted from the original, and adjusted separately.<br />
<br />
FYI: Internally the algorithm works by comparing pairwise the cumulative distributions of each image channel (R,G,B,A) and building a transfer function that translates the values with one distribution to the values with another (template) distribution.<br />
<br />
And that's all. Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com9tag:blogger.com,1999:blog-783893035873019493.post-15973182931204224822011-12-08T01:07:00.001+02:002011-12-18T12:05:33.626+02:00Texturewerke 0.1While doing textures for Shinya (the game with Khayyam/Sehle engine) I got frustrated with GIMP built-in tools and plug-ins available for texturing and decided to write my own tool Texturewerke. The initial version is now available for download from SourceForge:<br />
<ul>
<li><a href="https://sourceforge.net/projects/floyd/files/texturewerke/">Tarball and win32 precompiled binary</a></li>
</ul>
At moment it can do two things:<br />
<ol>
<li>Adjust the colors of one (masked) image so it matches as well as possible with another. Thus you do not have to manually adjust color curves to merge several photographs into the same texture.</li>
<li>Filter out low frequencies from image (while keeping the average intact). This can be used both for the creation of seamless textures and for removing shade gradient from uniform surfaces (like walls, doors, whiteboards...) Highpass filter supports masking (in polynome mode), so you can mask out those details whose contribution you want to ignore (like pictures on wall).</li>
</ol>
Below is one possible usage scenario of Texturewerke highpass filter.<br />
<br />
<span style="font-size: large;">Polynomic highpass filter with masking</span><br />
<br />
We want to use the following image as a texture. Unfortunately the background color is non-uniform and adjusting it by hand is boring work <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimf3lswC2YLhipAJJkzGdAFwXMf307C3xFr_MkktifJiHjPJpK6tPvFhhyKBlV7Ug6mT2iA88C2mTV0n89hjvLbIJOrDfEiTNFdu5fHlFn9piLKKGicD2Pq3im6RFcfl1lelhIZmAEUlfP/s1600/IMGP0883.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimf3lswC2YLhipAJJkzGdAFwXMf307C3xFr_MkktifJiHjPJpK6tPvFhhyKBlV7Ug6mT2iA88C2mTV0n89hjvLbIJOrDfEiTNFdu5fHlFn9piLKKGicD2Pq3im6RFcfl1lelhIZmAEUlfP/s320/IMGP0883.JPG" width="240" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The original image - notice the non-uniform shading of the wall</td></tr>
</tbody></table>
We select the are that we want to have uniform color (the wall). It does not have to be precise - the filter samples few thousand points from target area and thus small regions of wrong color do not disturb the result much.<br />
In given case I used magic want to select white and then added and subtracted few rectangles.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTq6NQ1KIUq78YzUuKSXlLUpOsVLjrqLZex12y-HZg45LBALIO1IYw49zoYmb2Lg7jHUJGwikdzETMuDYF0vwucqTuN45jTiCOdiSZXmP7t6yEnirSwC0epEqM3QtzHgdt9SrMoZKAsw5J/s1600/texturewerke-2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTq6NQ1KIUq78YzUuKSXlLUpOsVLjrqLZex12y-HZg45LBALIO1IYw49zoYmb2Lg7jHUJGwikdzETMuDYF0vwucqTuN45jTiCOdiSZXmP7t6yEnirSwC0epEqM3QtzHgdt9SrMoZKAsw5J/s320/texturewerke-2.jpg" width="220" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Target region selected</td></tr>
</tbody></table>
Now we turn target region into mask, bu choosing "<b>Add Layer Mask</b>" from <b>Layer </b>menu.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8BoAauzKv_vRdmiFtCT75tXB0J7FhN1oWrZAtnvRl5vRpEs_tuV5H8yZ4Rv2Z6tqhp7LXuVD6cUgroZPFcHPQwLhNWEZdWJ-J5TPRJDAM78FyKZDY9u1aWhwUC19N-qk6EQ6eA_4lauch/s1600/texturewerke-3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="311" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8BoAauzKv_vRdmiFtCT75tXB0J7FhN1oWrZAtnvRl5vRpEs_tuV5H8yZ4Rv2Z6tqhp7LXuVD6cUgroZPFcHPQwLhNWEZdWJ-J5TPRJDAM78FyKZDY9u1aWhwUC19N-qk6EQ6eA_4lauch/s320/texturewerke-3.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Target region turned into mask</td></tr>
</tbody></table>
Next we invoke Texturewerke plugin and select "<b>Lowpass</b>" filter and "<b>Polynome</b>" mode. <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5Kzu0Tyv5_JWH1dJ1-6-EbypT_xByyyegutSqH3TKdMCcPeOaSovHA3hjBYujX2PgLG8AB1hDxc28gpwWJSSwODZIP0GAVgraSZnRSHhsLKKob0lCOuWJ-N2tiRDwEUqNJzFiPitotJJD/s1600/texturewerke-polynome.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5Kzu0Tyv5_JWH1dJ1-6-EbypT_xByyyegutSqH3TKdMCcPeOaSovHA3hjBYujX2PgLG8AB1hDxc28gpwWJSSwODZIP0GAVgraSZnRSHhsLKKob0lCOuWJ-N2tiRDwEUqNJzFiPitotJJD/s320/texturewerke-polynome.jpg" width="217" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The Texturewerke dialog window</td></tr>
</tbody></table>
It blurs image (internally) before calculating polynome approximation. The optimal size of kernel depends on image and the number of samples used. As general rule - the more samples it uses, the smaller can be the kernel.<br />
Next we apply filter.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCZ_lMHGLKYNTfgpLPLzFcRsmXDUK9qCTPkyZJWzbn837CV0n6fvvVxNRsN54A2Q0mwn-A4EhiwRkRP-FfjbZ5W8j0A2OxQ8ubLp_2EpOzRDh42zpiXVPl6FC2jKSesWspyj6uSohiSwAs/s1600/texturewerke-4.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCZ_lMHGLKYNTfgpLPLzFcRsmXDUK9qCTPkyZJWzbn837CV0n6fvvVxNRsN54A2Q0mwn-A4EhiwRkRP-FfjbZ5W8j0A2OxQ8ubLp_2EpOzRDh42zpiXVPl6FC2jKSesWspyj6uSohiSwAs/s320/texturewerke-4.jpg" width="220" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Masked image after applying polynome filter</td></tr>
</tbody></table>
As you can see, the unmasked area has almost uniform color/bightness now.<br />
As the last thing, we delete (or turn off layer mask).<br />
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm1MF1KCfakznDUYHgjCsgxsf_mKbzdn90xvc3VC1ftZy7uKGmW5XXBdV7DK9MWHJ3zQ2_DSjEHjzL3wOKU8n9-Tq1Xp7Wnzj82A3ZBMrf3GGsx22cmIokI9Sn7Srl_xuVXCo9AhFlAUVB/s1600/IMGP0883-adjusted.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm1MF1KCfakznDUYHgjCsgxsf_mKbzdn90xvc3VC1ftZy7uKGmW5XXBdV7DK9MWHJ3zQ2_DSjEHjzL3wOKU8n9-Tq1Xp7Wnzj82A3ZBMrf3GGsx22cmIokI9Sn7Srl_xuVXCo9AhFlAUVB/s320/IMGP0883-adjusted.jpg" width="240" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The final image - wall color is now uniform</td></tr>
</tbody></table>
And the final image has now nice uniform wall color.<br />
<br />
There are few things to notice:<br />
<ul>
<li>Polynome mode tries to approximate the average color of the image by two-dimensional polynome (up to 4th degree). The approximation process guarantees the correct behavior of the polynome only inside target region - the higher is its degree the faster it goes "wild" outside. Thus if you mask out some edges of the image the results probably will not look nice for anything above quadratic (2th order).</li>
<li>The unmasked area has to be at least 10% of original image</li>
<li> Samples are drawn randomly from unmasked area.</li>
<li>There is another highpass filter mode "blur", that subtracts blurred (lowpass) version of the same image. It does not support masking - but it may be more useful for the generation of highly uniform images (like grass and sand textures).</li>
</ul>
<br />
Have fun!<br />
<br />Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com5tag:blogger.com,1999:blog-783893035873019493.post-80577466585471209952011-10-04T21:42:00.000+03:002011-10-04T21:42:53.909+03:00Reflective water with GLSL, Part IIIn the <a href="http://khayyam.kaplinski.com/2011/09/reflective-water-with-glsl-part-i.html">first part</a> I explained how to implement a basic reflective surface. But as we could see, it was very poor approximation of true water, looking more like mirror lying on the ground. First because real water is almost never completely still. But it is also not perfect mirror - depending on viewing angle we can see more or less into the water.<br />
Before going to implementing ripples I'll show how to adjust water shaders so the angle-dependency of reflection strength is taken into account. And being already there, how to fake the light attenuation/scattering in water so it is not crystal-clear but has more realistic color.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">1. The relation of reflectivity and viewing angle</span><br />
<br />
If you imagine looking at water body from different angles it should be obvious, that the lower is viewing angle the more light it reflects. Sea may look almost like perfect mirror during tranquil sunset - but if you are looking daytime from the top of a cliff, you can see the blueish-greenish color of water and only a little reflection.<br />
<br />
The reflectivity of water comes from the difference in refractive indexes of air and water. As the speed of light is different in these mediums, some light is reflected and the light entering water changes slightly its direction - the latter is called refraction. Refraction is another phenomenon that can add realism, but we will not try to simulate it here.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRnWr_YmPH4If38R54Fimio7Kuzvn3mHIFLV8FMAS6NrzHRr6L57Ed-9R-VZ3YMVR8vMaw02PosXMKcOttFfvMj6sMFFdBkDQAOW62DPUTysSW9jCPvYsI3Vrmghx4SOkJDzLaOMjkb6yl/s1600/reflections+4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="308" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRnWr_YmPH4If38R54Fimio7Kuzvn3mHIFLV8FMAS6NrzHRr6L57Ed-9R-VZ3YMVR8vMaw02PosXMKcOttFfvMj6sMFFdBkDQAOW62DPUTysSW9jCPvYsI3Vrmghx4SOkJDzLaOMjkb6yl/s320/reflections+4.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A schematic diagram of reflection and refraction</td></tr>
</tbody></table>
<br />
Mathematically the amount of light reflected from the surface of water is described by Fresnel equations:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL_P3Qgaim4TDuPJfssugxJi6FGBGNU-pcQhCC_9t6eSAvr2QIV86Ci86ZD3IJDXtznmRNp5eXK9FavsDDmyaIbqohQmpPBG-PdrvPk-7snsVQ4ZRFch-LvHP9k2XAmSdroot4k1uh4EaL/s1600/fresnel.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL_P3Qgaim4TDuPJfssugxJi6FGBGNU-pcQhCC_9t6eSAvr2QIV86Ci86ZD3IJDXtznmRNp5eXK9FavsDDmyaIbqohQmpPBG-PdrvPk-7snsVQ4ZRFch-LvHP9k2XAmSdroot4k1uh4EaL/s400/fresnel.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fresnel reflection equations (source Wikipedia)</td></tr>
</tbody></table>
<br />
<i><b>R<span class="Apple-style-span" style="font-size: x-small;">s</span></b></i> and <i><b>R<span class="Apple-style-span" style="font-size: x-small;">t</span></b></i> are the reflectance values of vertically and horizontally polarized light.<br />
<i><b>θ<span class="Apple-style-span" style="font-size: x-small;">i</span></b></i> and <i><b>θ<span class="Apple-style-span" style="font-size: x-small;">t</span></b></i> are the angles between the surface normal and incident and refracted rays.<br />
<div>
<b><i>n<span class="Apple-style-span" style="font-size: x-small;">1</span></i></b> and <b><i>n<span class="Apple-style-span" style="font-size: x-small;">2</span></i></b> are the refractive indices of two media - in our case air and water. The relevant values are:</div>
<div>
<br /></div>
<div>
<b><i>n<span class="Apple-style-span" style="font-size: x-small;">1</span></i></b><i> = 1.000277 ≈ 1</i></div>
<div>
<b><i>n<span class="Apple-style-span" style="font-size: x-small;">2</span></i></b><i> = 1.3330</i><br />
<div>
<i><br /></i></div>
</div>
<div>
<div style="font-style: normal;">
We do not need <i><b>θ<span class="Apple-style-span" style="font-size: x-small;">t</span></b></i> because it can be derived from <i><b>θ<span class="Apple-style-span" style="font-size: x-small;">i</span></b></i> and refractive indices using Snell's law - look at the rightmost part of equations.</div>
<div style="font-style: normal;">
The reflectance values are different for differently polarized light. This explains the magic behind anti-glare sunglasses and optical filters - they cut off vertically polarized light, that is much more strongly reflected.</div>
<div style="font-style: normal;">
It is also interesting to know that skylight is in fact somewhat polarized. But for our simulation we ignore this and treat all light as the uniform mix of both polarizations. In that case, the total reflectance can be simply calculated as:</div>
<div style="font-style: normal;">
<br /></div>
<div>
<i><b>R</b> = (<b>R<span class="Apple-style-span" style="font-size: x-small;">s</span></b> + <b>R<span class="Apple-style-span" style="font-size: x-small;">t</span></b>) / 2</i>
<br />
<i>
</i></div>
<div style="font-style: normal;">
<br /></div>
</div>
<div>
<div style="font-style: normal;">
The full Fresnel equation above is a bit too complex for our shaders. It is physically correct - but our goal is natural or good-looking scene and we are ready to sacrify a little here ad there to save shader instructions for other things.</div>
<div style="font-style: normal;">
There is quite simple approximation available. Take a look at the following graph:</div>
<div style="font-style: normal;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="font-style: normal; margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirHjtIFSeGLc5luf0eyqh7Injh296myw7XpYR3-mJTl3mNWLLbJOWb0YV3t6lliHouP_LNErpU9_tb-2nMy2dAlw69OpQ29fESVWKiPaa1yb7JtdybcoqS9T990YdcxgZ_IaxFoWGfrQfU/s1600/fresnel-graph.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirHjtIFSeGLc5luf0eyqh7Injh296myw7XpYR3-mJTl3mNWLLbJOWb0YV3t6lliHouP_LNErpU9_tb-2nMy2dAlw69OpQ29fESVWKiPaa1yb7JtdybcoqS9T990YdcxgZ_IaxFoWGfrQfU/s400/fresnel-graph.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fresnel reflectance values Rs, Rp and R (blue, red, yellow) and our approximation (green) </td></tr>
</tbody></table>
<div style="font-style: normal;">
The green line represents function:</div>
<div style="font-style: normal;">
<br /></div>
<i><b>R</b> = <b>Rmin</b> + (1 - <b>Rmin</b>) * (1- cos <b>θ<span class="Apple-style-span" style="font-size: x-small;">i</span></b>)<sup>5</sup></i>
<br />
<br />
That can be used as good approximation.<br />
<b>R<span class="Apple-style-span" style="font-size: x-small;">min</span></b> is 0.02 for real water, but you may want to increase it to something like 0.1 or even more, unless you have very good HDR implementation. The problem is that real sky is <b>really </b>bright - if you are using dimmed down version of sky, its reflection is not visible at all from high angles.<br />
<br />
That's it. Now we have reflectance value calculated - but we cannot yet update our shaders. Unlike in our previous lesson, where the reflection was all that had to be rendered, we now have to render the water itself too - unless the reflectance is exactly 1.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">2. Rendering water</span><br />
<br />
Water is normally dense medium and strongly scatters light. The actual formula is quite complex and depends on the amount of different chemical compounds (such as oxygen, humic acids) and various inorganic (such as clay) and organic (like plankton) particles in water. But our goal is not to simulate procedurally the color and turbidity of water, but instead find a meaningful set of descriptive parameters, that will give us good enough approximation.<br />
<br />
Scattering changes the intensity of light (ray) in two ways:<br />
<ul>
<li>Out-scattering - if ray of light goes through medium, some fraction of light is scattered away from direct path and thus the final amount of light is lower.</li>
<li>In-scattering - as light is scattered to all directions, some light from rays originally going other directions is scattered to the direction of the ray of interest.</li>
</ul>
Because of out-scattering the bottom of deep water body is dark. Because of in-scattering objects inside water seem "tinted" with water color.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY5nAbORpMB81HrWeQDp8xlgWXSsuIzYQAmF_Owuj1kKOvw7A_X7QBgg_5ng_aXjcdR2TUjk43RrX2_uAn25aqYB-qFqB-Itcsrezpp9Uca3epBGdyQcqLWdhodUy9bUmC9rDh9TWwvusF/s1600/reflections+6.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY5nAbORpMB81HrWeQDp8xlgWXSsuIzYQAmF_Owuj1kKOvw7A_X7QBgg_5ng_aXjcdR2TUjk43RrX2_uAn25aqYB-qFqB-Itcsrezpp9Uca3epBGdyQcqLWdhodUy9bUmC9rDh9TWwvusF/s400/reflections+6.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Scattering in water. A - in-scattering. B - out-scattering.</td></tr>
</tbody></table>
In addition to scattering there is one more phenomenon causing less light to come from water - it is internal reflection. As light hits the bottom of water body or objects inside it and goes upwards from there, some of it is mirrored back into water from the air-water phase surface.<br />
We ignore this at moment and will use shortcut - instead of adding together the light coming from inside water and light from reflection, we draw the latter as semitransparent surface using reflectance as alpha value. Thus the higher is reflectance, the lower is the effect of light from inside water - which is generally correct, because the internal and external reflectance values are correlated.<br />
<br />
How good our water formula can be depends on whether we can read the color and depth values of the bottom of water body or underwater objects in shader or not. Here I present a simple approach, that does not use them, but instead adds the color of water to the reflection image.<br />
<br />
We ignore the exact scattering equations, that involve multiple integrals and instead combine the "tint" of water with the color of reflection (that is semitransparent now) and then render them as semitransparent surface over the already rendered bottom of water. For this I use a very simple formula:<br />
<br />
<i><b>C<span class="Apple-style-span" style="font-size: x-small;">tint</span></b> = <b>C<span class="Apple-style-span" style="font-size: x-small;">water</span></b> * (<b>O<span class="Apple-style-span" style="font-size: x-small;">min</span></b> + (1 - <b>O<span class="Apple-style-span" style="font-size: x-small;">min</span></b>) * sqrt (min (<b>thickness</b> / <b>D<span class="Apple-style-span" style="font-size: x-small;">opaque</span></b>, 1)))</i><br />
<br />
<i><b>C<span class="Apple-style-span" style="font-size: x-small;">tint</span></b></i> is the color that water adds to objects (premultiplied)<br />
<i><b>C<span class="Apple-style-span" style="font-size: x-small;">water</span></b></i> is the color of opaque water column<br />
<i><b>O<span class="Apple-style-span" style="font-size: x-small;">min</span></b></i> is the minimal opacity of water. It should be 0 for realistic simulation, but in reality values 0.1-0.2 give overall better effect.<br />
<i><b>D<span class="Apple-style-span" style="font-size: x-small;">opaque</span></b></i> is the depth of water column, that becomes fully opaque. The reasonable value is 2 m for freshwater bodies - the smaller the better, as it helps to hide the lack of refraction.<br />
<i><b>thickness</b></i> is the thickness of water in given direction until bottom or some underwater object is hit.<br />
<br />
Calculating thickness is tricky. The technically correct way would be to trace ray in refracted direction until bottom (line <b>AC</b> in following figure) - but we cannot afford to do that.<br />
If you can use depth buffer, you can ignore refraction and calculate the distance the original ray would cover underwater (line <b>AD</b>). This overestimates the thickness, but as the effect only becomes noticeable at low viewing angle, where reflection dominates, it should look quite good.<br />
Here I will use even simpler approximation. Just find the depth of water under given point of surface (point <b>B</b> on following figure), and pretend that water has uniform depth (line <b>AB<sub>1</sub></b>). It underestimates depth at the slopes directed away from viewer and overestimates at slopes directed to viewer, but if the bottom of water is reasonably smooth it is not too bad.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbqLwJ3QVS8jAxWfaG0KDjEgzOyPPqkYuewZsQdKAJFgdpdpm94AQm8c08fb5qvHM9MlFK3xLacGKvIHgnfs0UC5bvdI27MpLzl0fRA4oB8_w8a6NCLqWO-rMDnzHLJlwmUAW0T69Bey6g/s1600/reflections+5.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbqLwJ3QVS8jAxWfaG0KDjEgzOyPPqkYuewZsQdKAJFgdpdpm94AQm8c08fb5qvHM9MlFK3xLacGKvIHgnfs0UC5bvdI27MpLzl0fRA4oB8_w8a6NCLqWO-rMDnzHLJlwmUAW0T69Bey6g/s400/reflections+5.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A diagram of different possible methods for water thickness calculation</td></tr>
</tbody></table>
How to get the actual water depth under given point?<br />
Recall the previous tutorial. We set the Z coordinate of vertex to 0 (i.e. flatten our object), but kept the full original vertex coordinate in <b>interpolatedVertexDepth</b>.<br />
Thus if the object being rendered as water is actually the bottom of water body, it will render as flat surface, but we have access to the original Z coordinate of it. In other words - the water depth.<br />
Another approach would be to encode water depth into another vertex attribute. It has some good points - like no need to separate the bottom of water body from other terrain and the possibility to hand-code depth.<br />
<br />
Once we have calculated water thickness with whatever method applicable, we treat the tint color as another semitransparent layer, lying directly beneath the reflection layer. The final color and alpha values can be calculated by standard alpha blending formula:<br />
<br />
<span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"><i>C = A<sub>reflection</sub> * C<sub>reflection</sub> + (1 - A<sub>reflection</sub>) * C<sub>water</sub></i></span><br />
<span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"><i>A = A<sub>reflection</sub> + (1 - A<sub>reflection</sub>) * A<sub>water</sub></i></span><br />
<br />
Where C and A are color and alpha values.<br />
If the resulting alpha is below 1, bottom or underwater objects are partially visible.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">3. Shaders</span><br />
<br />
There is no need to change vertex shader.<br />
<br />
Fragment shader:<br />
<span class="Apple-style-span" style="background-color: white; color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: 13px; line-height: 18px;"></span><br />
<pre>uniform mat4 o2v_projection_reflection;
uniform sampler2D reflection_sampler;
uniform vec3 eye_object;
uniform float min_opacity, opaque_depth, opaque_color;
varying vec3 interpolatedVertexObject;
void main()
{
// Vertex on water surface
vec3 surfaceVertex = vec3(interpolatedVertexObject.xy, 0.0);
// Reflection angle
vec3 vertex2Eye = normalize (eye_object - surfaceVertex );
float cosT1 = vertex2Eye.z;
// Reflectance
float c = 1.0 - cosT1;
float R = min_reflectivity + (1.0 - min_reflectivity) * c * c * c * c * c;
// Water density
float depth = -interpolatedVertexObject.z;
float thickness = depth / max (cosT1, 0.01);
float dWater = min_opacity + (1.0 - min_opacity) * sqrt (min (thickness / opaque_depth, 1.0));
// Premultiply
vec3 waterColor = opaque_color * dWater;
vec4 vClipReflection = o2v_projection_reflection * vec4(interpolatedVertexObject , 1.0);
vec2 vDeviceReflection = vClipReflection.st / vClipReflection.q;
vec2 vTextureReflection = vec2(0.5, 0.5) + 0.5 * vDeviceReflection;
vec4 reflectionTextureColor = texture2D (reflection_sampler, vTextureReflection);
// Framebuffer reflection can have alpha > 1
reflectionTextureColor.a = 1.0;
// Combine colors
vec3 color = (1.0 - R) * waterColor + R * reflectionTextureColor.rgb;
float alpha = R + (1.0 - R) * dWater;
gl_FragColor = vec4(color, alpha);}</pre>
<br />
We have added another uniform (in addition to water color and opacity ones) - <b>eye_object</b>, that is simply camera position relative to water object local coordinate system.<br />
<br />
And real-time image from Shinya:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQxmVUsNpDGIkXszSbu9JAtQZ-yjWuooNpNPXFRdxxR6d3ZwhJGXJUhYsF55O2TY5SFsBg5erW30l9ONgA-AJIdnC1epJgD5Yp4MjpJFHuZpSflWYxz_A8vZl4CxFyG8sKDNSXl49kZZq9/s1600/shinya-water-2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQxmVUsNpDGIkXszSbu9JAtQZ-yjWuooNpNPXFRdxxR6d3ZwhJGXJUhYsF55O2TY5SFsBg5erW30l9ONgA-AJIdnC1epJgD5Yp4MjpJFHuZpSflWYxz_A8vZl4CxFyG8sKDNSXl49kZZq9/s400/shinya-water-2.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Simple scene from Shinya with partial reflection and water opacity </td></tr>
</tbody></table>
<br />
Now it is a bit better than last time - but still artificial and lifeless. Next time I show, how to make it live - i.e. add waves or ripples.<br />
<br />
<a href="http://khayyam.kaplinski.com/2011/09/reflective-water-with-glsl-part-i.html">Part 1 - reflective surface</a><br />
<br />
Have fun!<br />
<br />
<br /></div>
Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com7tag:blogger.com,1999:blog-783893035873019493.post-689914703044907512011-09-16T22:43:00.000+03:002011-10-03T02:10:38.072+03:00Reflective water with GLSL, Part IBeing it for physical accuracy or setting mood in game, water and reflections are something that can add lot to your rendering engine. While true reflections can only be done with ray-tracing, one can achieve surprisingly nice approximations by using quite simple scene setup and some GPU programming.<br />
<br />
Good water simulation should have at least the following features:<br />
<ul>
<li>True reflection (with correct parallax)</li>
<li> Clipping of underwater objects on reflection image</li>
<li>View angle dependent transparency/reflectivity of water</li>
<li>Ripples and/or waves</li>
<li>Water scattering (i.e. water becoming gradually opaque as depth increases)</li>
</ul>
Some more things, that can make things nicer but are not as visible, are:<br />
<ul>
<li>Refraction</li>
<li>Caustics - i.e. light spots at the bottom of shallow water</li>
<li>Reflected light - i.e. light spots reflected to objects near water</li>
</ul>
At moment I have only implemented features from the first list into Khayyam/Sehle/Shinya code. You can look at <a href="http://khayyam.kaplinski.com/2011/08/glsl-water-reflections.html">my previous post</a> for some in-engine images.<br />
Here I will describe the mathematics behind the scenes and give step-by-step guide to writing your own water system/object/rendering pass.<br />
<br />
<span style="font-size: large;">1.Rendering reflection texture</span><br />
<br />
Water without reflection looks totally uninteresting - just like any other semitransparent surface. Thus we start from implementing reflection and later go on to other effects.<br />
<br />
<b>1.1. Parallax </b><br />
Even if you have until now managed to render you scene in single pass, from this point on you need at least two passes (actually at least <b><span style="font-family: 'Courier New', Courier, monospace;">N+1</span></b>, where <b><span style="font-family: 'Courier New', Courier, monospace;">N</span></b> is the number of visible reflective surfaces).<br />
<br />
The reason is, that unfortunately we cannot recycle our main scene image for reflections. First because it could make view frustum insanely large (for example - if viewing the water surface from high angle we see only ground and water in our main view, but mostly sky in reflection). And second because of parallax. The reflection is unfortunately not the perfect copy of reflected scene, but copy of the view of the same scene from different viewpoint. The following image illustrates this.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgli4HkfhHmwF1Ept9GposHDSIzfP7s72vtJwaHyVeqk52TlNopHy7UJqwkP4jakhmXUxfggoFoZaMqKFFC6uFVS8hkUyi62VKwX-PNYnaTyh_i7CVfEDKmal5OzNXlLDS-QXUarsPQ0ps5/s1600/reflections.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgli4HkfhHmwF1Ept9GposHDSIzfP7s72vtJwaHyVeqk52TlNopHy7UJqwkP4jakhmXUxfggoFoZaMqKFFC6uFVS8hkUyi62VKwX-PNYnaTyh_i7CVfEDKmal5OzNXlLDS-QXUarsPQ0ps5/s400/reflections.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A diagram explaining the parallax effect on reflected image</td></tr>
</tbody></table>
<br />
It means that you need to have rendering to texture set up and working. We
will render reflection to texture and later use this texture while
rendering the water surface in main scene.<br />
<br />
Thus, to get reflection texture we first have to render our scene from the reflected camera viewpoint <b><span style="font-family: 'Courier New', Courier, monospace;">P'</span></b> to texture. First we have to find the reflected camera position - or more precisely the reflected view matrix (because we need camera orientation too in addition to the position).<br />
This can be done with the following formula:<br />
<br />
<i><b>M'<span style="font-size: x-small;">camera</span> = M<span style="font-size: x-small;">reflection</span> * M<span style="font-size: x-small;">camera</span></b></i><br />
<br />
Where <i><b>M<span style="font-size: x-small;">reflection</span></b></i> is the reflection matrix of mirror surface. It can trivially be calculated from the position of reflection plane:<br />
<br />
<pre> | 1-2Nx2 -2NxNy -2NxNz -2NxD |
Mreflection = | -2NxNy 1-2Ny2 -2NyNz -2NyD |
| -2NxNz -2NyNz 1-2Nz2 -2NzD |
| 0 0 0 1 |
</pre>
<br />
Where (<i><b>N<span style="font-size: x-small;">x</span>,N<span style="font-size: x-small;">y</span>,N<span style="font-size: x-small;">z</span>,D</b></i>) are the coefficients of plane equation (<i><b>xN<span style="font-size: x-small;">x</span> + yN<span style="font-size: x-small;">y</span> + zN<span style="font-size: x-small;">z</span> + D = 0</b></i>). Notice, that (<i><b>N<span style="font-size: x-small;">x</span>,N<span style="font-size: x-small;">y</span>,N<span style="font-size: x-small;">z</span></b></i>) is also the normal vector of given plane.<br />
<br />
<i><b>M<span style="font-size: x-small;">camera</span></b></i> is the transformation of camera as if it would be "normal" object in scene. To get ModelView matrix you will need the inverse of it<i><b>.</b></i><br />
<br />
<b>1.2. Mirrored geometry </b><br />
<div style="margin-bottom: 0cm;">
Actually we cheated a little in the previous image. We rotated the mirrored image 180<span style="font-family: 'Times New Roman', serif;">º</span> to make it more similar to the original image, so the effect of parallax can be seen. The actual mirrored image looks like this:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLKJfMiGe4O0Cq2mgw2AtOski1Uy5hD5XKHPizEFm47WawQh4ui_ZRXWSqBmhnYgw4SurcwY_gj5x-LnUk6V417Dzxly7llkr0s8eHaEHNCkAqGO59uOddRUZ23iqg8Q_2T6wIOLCHO5Ja/s1600/reflections-2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLKJfMiGe4O0Cq2mgw2AtOski1Uy5hD5XKHPizEFm47WawQh4ui_ZRXWSqBmhnYgw4SurcwY_gj5x-LnUk6V417Dzxly7llkr0s8eHaEHNCkAqGO59uOddRUZ23iqg8Q_2T6wIOLCHO5Ja/s320/reflections-2.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Different winding order on mirrored image</td></tr>
</tbody></table>
Notice, that the winding order of polygons in image is flipped on mirrored image - i.e. the triangle is oriented CCW on original but CW on reflection.<br />
<br />
This may or may not be problem for you. If all your materials are double sided (i.e. you do not do back face culling) or if you can set up rendering pipeline in such a way, that you can change culling direction it is OK. In my case though, I prefer to keep culling always on and have forward-facing always defined as CCW. So something has to be done with the reflected image - or otherwise geometry will not render properly.<br />
<br />
We will exploit the feature that camera is always (at least in most applications) rectangular and centered around view direction. Thus we can just flip camera in <b style="font-family: "Courier New",Courier,monospace;">Y</b> direction and the winding order will be correct again (it flips reflected image so it looks like (3) on the first picture).<br />
This can be done with one more reflection matrix:<br />
<br />
<i><b>M''<span style="font-size: x-small;">camera</span> = M<span style="font-size: x-small;">reflection</span> * M<span style="font-size: x-small;">camera</span></b></i><i><b> * M<span style="font-size: x-small;">flip</span></b></i><br />
<br />
Where <i><b>M<span style="font-size: x-small;">flip</span></b></i> is simply another reflection matrix that does reflection over <b style="font-family: "Courier New",Courier,monospace;">XZ</b> plane.<br />
Now if we render mirrored image using <i><b>M''<span style="font-size: x-small;">camera </span></b></i>as camera matrix, pipeline can be left intact. We, of course, have to save this matrix for later reference, because it is needed to properly map our texture to water object in main render stage.<br />
<br />
<b>1.3. Underwater clipping</b><br />
Take a look at the following picture:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuCcQ6u2CBYJqKhdGAp47bhG6koYo9jKjhqJRs5sjcD8nRsgLeyB8WNKjQIF1VnbMNpJfIk6PPe3Y75gJaJBV3643L6TuZmjfzHlOTH0y0gX2OjXCMY779OTnZA6WYXHWS-vf-KhArVJAn/s1600/reflections-3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuCcQ6u2CBYJqKhdGAp47bhG6koYo9jKjhqJRs5sjcD8nRsgLeyB8WNKjQIF1VnbMNpJfIk6PPe3Y75gJaJBV3643L6TuZmjfzHlOTH0y0gX2OjXCMY779OTnZA6WYXHWS-vf-KhArVJAn/s320/reflections-3.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A reflection with underwater object</td></tr>
</tbody></table>
<br />
<br />
We have added an underwater object <b style="font-family: "Courier New",Courier,monospace;">Q</b> to our scene. Now it should not appear on reflection, because it does not block the actual reflection rays <b style="font-family: "Courier New",Courier,monospace;">PB'B</b> and <b><span style="font-family: 'Courier New', Courier, monospace;">PA'A</span></b>. But we are not doing ray-tracing. We are instead moving camera to mirrored viewpoint <b><span style="font-family: 'Courier New', Courier, monospace;">P'</span></b> and rendering reflection like normal image. But as you can see, the object <b><span style="font-family: 'Courier New', Courier, monospace;">Q</span></b> blocks ray <b style="font-family: "Courier New",Courier,monospace;">P'A'A</b> and thus would show up in our reflection.<br />
<br />
Thus we have to make sure, that nothing that is under the reflection plane (water surface) will show up in mirror rendering. This can be achieved in three different ways:<br />
<ol>
<li>Use additional clipping plane on GPU. It can be very fast or very slow - depending on card and driver used.</li>
<li>Use oblique projection matrix during reflection rendering. You can read more about it <a href="http://www.terathon.com/code/oblique.html">here</a>. This is cool technique, but personally I have never got it to work well enough because it messes up camera far plane.</li>
<li>Clip manually in pixel shaders. It wastes some GPU cycles, but is otherwise easy and foolproof.</li>
</ol>
I went with option (3) because oblique projection matrix did not seem to play well with wide camera angles (far plane moved through infinity creating all kinds of weird effects). The clipping itself is as easy as adding the following code at the beginning of all pixel shaders (or more precisely the ones that are used for reflectable objects):<br />
<br />
<pre>uniform vec4 clip_plane;
varying vec3 interpolatedVertexEye;
void main()
{
float clipPos = dot (interpolatedVertexEye, clip_plane.xyz) + clip_plane.w;
if (clipPos < 0.0) {
discard;
}
...
}
</pre>
<br />
Of course you have to supply your shader with <b><span style="font-family: 'Courier New', Courier, monospace;">clip_plane</span></b> and calculate <b><span style="font-family: 'Courier New', Courier, monospace;">interpolatedVertexEye</span></b> in vertex shader (it is simply vertex coordinate in view/eye space: <b><span style="font-family: 'Courier New', Courier, monospace;">VertexEye = Mmodelview * Vertex</span></b>). If you do not need clipping, simply set <b><span style="font-family: 'Courier New', Courier, monospace;">clip_plane</span></b> normal (<b style="font-family: "Courier New",Courier,monospace;">xyz</b>) to zero and all pixels will be rendered.<br />
<br />
<b>1.4. Putting it all together</b><br />
Before starting the main render pass (being it forward or deferred) do the following:<br />
<ol>
<li>Create list of all objects that need reflections (and the parameters of all reflection planes). Then for each reflection plane:</li>
<li>Calculate the reflected camera matrix<i><b><br />M''<span style="font-size: x-small;">camera</span> = M<span style="font-size: x-small;">reflection</span> * M<span style="font-size: x-small;">camera</span></b></i><i><b> * M<span style="font-size: x-small;">flip</span></b></i></li>
<li>Set up camera matrices (you can optimize rendering by using clipped projection matrix, but this will not be discussed here).</li>
<li>Set clipping plane to reflection plane</li>
<li>Render full scene</li>
<li>Save the rendered image as texture to be used with reflective object</li>
</ol>
If you are using HDR you should not tone-map reflection texture - unless you want to achieve some very specific effect.<br />
<ol>
</ol>
<span style="font-size: large;">2. Rendering reflective object</span><br />
<br />
This is actually quite easy - provided that you have at hand all necessary parameters. You have still to decide at which render stage to do this. I use transparent stage, as water is basically just one semi-transparent surface in scene, but you can add another pass before or after transparency as well.<br />
<br />
You will need at hand:<br />
<ul>
<li>Reflected camera matrix <i><b>M''<span style="font-size: x-small;">camera</span></b></i></li>
<li>Projection matrix you used to render reflection <b><i>M<span style="font-size: x-small;">projectionreflection</span></i></b> (normally this is the same projection that you use for main camera)</li>
<li>Reflection texture</li>
</ul>
<br />
<ul>
</ul>
<b>2.1. Vertex shader</b><br />
<br />
<pre>attribute vec3 vertex;
uniform mat4 o2v_projection;
varying vec3 interpolatedVertexObject;
void main()
{
gl_Position = o2v_projection * vec4(vertex.xy, 0.0, 1.0);
interpolatedVertexObject = vertex;
}
</pre>
<br />
We add another constraint here - water surface will be at <b><span style="font-family: 'Courier New', Courier, monospace;">XY</span></b> plane of the object local coordinate system. It is strictly not necessary if you have the proper reflection plane, but I found it easier that way. Just use <b><span style="font-family: 'Courier New', Courier, monospace;">XY</span></b> plane as reflection plane and place your object (water body) appropriately.<br />
<br />
Actually this allows us to do another cool trick. We can use the bottom of water body (i.e. river, lake..) as our water object. It will be flattened in shader, but we can use the <b><span style="font-family: 'Courier New', Courier, monospace;">Z</span></b> data to determine the depth of water at given point. But more about this in next part.<b><span style="font-family: 'Courier New', Courier, monospace;"> </span></b><br />
<br />
<b><span style="font-family: 'Courier New', Courier, monospace;">o2v_projection</span></b> is simply my name for composite matrix <b>Projection * ModelView</b>. I prefer to name matrices with mnemonic names, describing the coordinate system transformations they do - in given case it is <b>Object To View</b>, multiplied with <b>Projection</b>.<b><span style="font-family: 'Courier New', Courier, monospace;"> </span></b><br />
<br />
<b><span style="font-family: 'Courier New', Courier, monospace;">interpolatedVertexObject</span></b> is simply vertex coordinate in object local coordinate system - we will need it to do lookup onto reflection texture.<br />
<br />
<b>2.2. Fragment shader</b><br />
<br />
<pre>uniform mat4 o2v_projection_reflection;
uniform sampler2D reflection_sampler;
varying vec3 interpolatedVertexObject;
void main()
{
vec4 vClipReflection = o2v_projection_reflection * vec4(interpolatedVertexObject.xy, 0.0 , 1.0);
vec2 vDeviceReflection = vClipReflection.st / vClipReflection.q;
vec2 vTextureReflection = vec2(0.5, 0.5) + 0.5 * vDeviceReflection;
vec4 reflectionTextureColor = texture2D (reflection_sampler, vTextureReflection);
// Framebuffer reflection can have alpha > 1
reflectionTextureColor.a = 1.0;
gl_FragColor = reflectionTextureColor;
}
</pre>
<br />
<b><span style="font-family: 'Courier New', Courier, monospace;">o2v_projection_reflection</span></b> is the composite matrix <b>Projection * ModelView</b> as it was used during reflection rendering. I.e:<br />
<br />
<b><i>M<span style="font-size: x-small;">projectionreflection * </span></i></b><i><b>(M''<span style="font-size: x-small;">camera)-1 * Mobject</span></b></i><br />
<br />
Like the name implies, it transforms from the object coordinate system to the clip coordinate system of reflection camera.<br />
<br />
In fragment shader we simply repeat the full transform pipeline during reflection rendering and use final 2D coordinates for texture lookup. For this we need initial, untransformed object vertices - thus they are interpolated from vertex shader (<b><span style="font-family: 'Courier New', Courier, monospace;">interpolatedVertexObject</span></b>).<br />
<br />
I'll set reflection <b>alpha </b>to <b>1.0</b> because I use HDR buffers and due to additive blending the final alpha can have some very weird values there.<br />
<br />
And the rendered image:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjud2RyPhpBmX25NemmIDnUe6WynFuHseuLbx6iwjSEiM6CpxvRPYKgPDw1Wzxj9X1Mjz-wFp2103VLdKs4A5_a-oUb1Ddo3lljoBi9LfSVc1ulGRZDKXld2KwAAK-Zv5iBDMFvO9iWx7QO/s1600/reflections-4.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjud2RyPhpBmX25NemmIDnUe6WynFuHseuLbx6iwjSEiM6CpxvRPYKgPDw1Wzxj9X1Mjz-wFp2103VLdKs4A5_a-oUb1Ddo3lljoBi9LfSVc1ulGRZDKXld2KwAAK-Zv5iBDMFvO9iWx7QO/s400/reflections-4.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Simple scene from Shinya showing water as perfect mirror</td></tr>
</tbody></table>
<br />
Not very realistic?<br />
Up to now we have implemented water as perfect mirror. This is very far from reality (look at the feature list in the first section).<br />
<br />
In the next parts I will show how to add viewing angle based transparency, water color and depth-dependent ripples to your water.<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com160tag:blogger.com,1999:blog-783893035873019493.post-30072350405473284622011-08-29T01:40:00.000+03:002011-08-29T01:40:01.101+03:00GLSL water reflectionsI have been mostly working on sehle (Khayyam rendering engine) based game framework lately. There have been some new things in Khayyam - that will be the scene/level builder for it - as well. The latest addition is reflective water material.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEietlADbfVjmKoYquDiwcoEUrIXXHWDQQqNxjXZnyyHfOrf0Z8PZj_XHpxRJYjPBUcDDlK4TtXADo_301ipuqm1pTgkjcigiCFkbxqLIgSpRoI2z0Y5s33xiJYZVYCJCUYFU139BV92nlmi/s1600/khayyam-20110828.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEietlADbfVjmKoYquDiwcoEUrIXXHWDQQqNxjXZnyyHfOrf0Z8PZj_XHpxRJYjPBUcDDlK4TtXADo_301ipuqm1pTgkjcigiCFkbxqLIgSpRoI2z0Y5s33xiJYZVYCJCUYFU139BV92nlmi/s400/khayyam-20110828.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Esk standing on near water - with ripples and reflection</td></tr>
</tbody></table>You can convert all static objects (OBJ, 3DS and Collada) to water bodies - although to get realistic results, they should be bowl- or plane shaped with surface at Z zero (in object coordinates). Everything is fake, of course - there are no real waves or refraction, but only distorted reflections and transparency. But it still looks reasonably good in my opinion.<br />
<br />
At moment there are following water properties implemented:<br />
<ul><li>Full reflection (everything that can be rendered can be reflected, except other reflective surfaces)</li>
<li>Viewing angle based reflectivity - i.e. if looking directly down, water is mostly transparent, looking along water surface it becomes almost perfect mirror</li>
<li>Depth-based transparency - shallow water is almost transparent, deeper water becomes opaque</li>
<li>Up to 8 simultaneous ripple generators, that can be assigned to "shore" vertices of water body</li>
</ul>At the image below you can see how the water color changes from almost fully transparent near bank to completely opaque brown at the middle of river. As the viewing angle is high, overall reflectivity is low and the colors of river bottom and water can be seen. Ripples change the apparent angle between viewer and water surface, so although sky is uniform color, ripples still show as they change the reflectivity of surface.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmg2Fcw1XS6_AiSHCAddb7SCJvmeSeRYKcXugpefxG6qOSAcIrDIiVedcwds9wSy5Otkrdqwfuh7v1gO0qS-P2IU7xwcSL8nJAyegVpqQWekBN6rIA536WwCWutjuWLbJcOoKrOQbOpxQ/s1600/khayyam-20110828-1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFmg2Fcw1XS6_AiSHCAddb7SCJvmeSeRYKcXugpefxG6qOSAcIrDIiVedcwds9wSy5Otkrdqwfuh7v1gO0qS-P2IU7xwcSL8nJAyegVpqQWekBN6rIA536WwCWutjuWLbJcOoKrOQbOpxQ/s400/khayyam-20110828-1.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A river from above showing water and bottom colors</td></tr>
</tbody></table> At the next image the viewing angle is low and thus the surface of water is almost completely reflective.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJKOIMVRZDHuuHqvjBhfzrQKvi_B6b1ExFyTRJd2wBsho1jMMn06D5NNsWzEcS4UAmixF-RG2tYUkEgrrxmOE5u_vR2k8aaBvFyalaBUza4iDsNJK1oXZuIPI3mjRKIo843JqbjvwiCXl3/s1600/khayyam-20110828-2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJKOIMVRZDHuuHqvjBhfzrQKvi_B6b1ExFyTRJd2wBsho1jMMn06D5NNsWzEcS4UAmixF-RG2tYUkEgrrxmOE5u_vR2k8aaBvFyalaBUza4iDsNJK1oXZuIPI3mjRKIo843JqbjvwiCXl3/s400/khayyam-20110828-2.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A river from low angle, showing almost perfect mirror</td></tr>
</tbody></table>Ripples are circular and their wavelengths, amplitudes and generation points are updated randomly over time. Thus the actual pattern almost never repeats - although sometimes it is not as nice as on the image above.<br />
<br />
<br />
When I find some free time, I'll write the technical details about the actual mathematics in shaders. And hopefully there will be new Khayyam release too.<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com14tag:blogger.com,1999:blog-783893035873019493.post-3592462426734621102011-07-18T03:54:00.000+03:002011-07-18T03:54:50.970+03:00Encoding normals for Gbuffer<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jqGVfcIIJWrawoBA56ecAmqbf7fbgNLvLR00fxB-mCtipXaitnJF1llzNDmExzTVKZ-eEC-5JzDbUSqyiFwPsbI3SM96ydw0LcAiwrbYDtEnDmdYcBJMKLEUO0Wf2nlL6knbpE1lLBYN/s1600/normal_packing_c.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jqGVfcIIJWrawoBA56ecAmqbf7fbgNLvLR00fxB-mCtipXaitnJF1llzNDmExzTVKZ-eEC-5JzDbUSqyiFwPsbI3SM96ydw0LcAiwrbYDtEnDmdYcBJMKLEUO0Wf2nlL6knbpE1lLBYN/s1600/normal_packing_c.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"></a></div>Deferred shading has nowadays become nearly ubiquitous in real-time rendering due to the separation of geometry and lighting passes. The general idea of this technique is to render all important properties (color, normal, reflectivity and so on) of all visible surfaces to a set of screen-sized textures. Later rendering passes (lighting) get surface parameters from those textures and thus complex geometries do not have to be rendered multiple times.<br />
<br />
One design problem of Gbuffer implementation is the efficient encoding of surface normals. Normals in three-dimensional space have three components: <br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;">N = Nx,Ny,Nz</span></b><br />
<br />
The most intuitive way would be to store those in three texture channels. But this would be wasteful, because we know, that:<br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;">Nx2 + Ny2 + Nz2 = 1</span></b><br />
<br />
Gbuffer requires many texture channels for other data, thus we want to cut off as many unnecessary channels as possible to conserve memory and fillrate.<br />
<br />
Another simple alternative would be to store only two components (Nx and Ny) and reconstruct third (Nz) in pixel shader. Unfortunately we will lose the sign of Nz in that process. While in ideal scene there should be no normals with negative Z (facing away from camera), they happen in real scenes because of realistic polygon counts.<br />
<br />
There exist several techniques for encoding normals while (at least partially) preserving Z component. Unfortunately no one of those is ideal. Either they are not able to encode full range of Z (various projections) or waste precision and calculation (polar coordinates).<br />
<br />
While implementing foliage shader for Khayyam I needed a way to encode full range of possible normals into two-component vector (that will later be stored in 4-component 8-bit RGBA texture of Gbuffer). The following is the brief introduction of the technique used. It solves all the problems I am aware of - with the price of slightly more shader instructions than simple projection encoders.<br />
<br />
<span style="font-size: large;">Efficient projection of normal space</span><br />
<br />
We forget for awhile the need of storing the sign of Z component and look, how we can store X and Y components most efficently.<br />
The trivial way is to simply forget Z and store X and Y. In geometric terms it means projecting the hemisphere of normal space to an unit circle on XY plane along Z vector as in following figure:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzVqTBnZwZ75zkz5NTAyMD-FZdl497tIfmS1SAOx3JUwxUKjz33EG-PJzc2jlvZQaKClLV11gAany2OhGmKUZKtUMRhZlQNNul6A8OU5dF7pS47_mC-LJ1szKPbQZWtjYySHpzvhodMwhU/s1600/normal_packing_a.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="375" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzVqTBnZwZ75zkz5NTAyMD-FZdl497tIfmS1SAOx3JUwxUKjz33EG-PJzc2jlvZQaKClLV11gAany2OhGmKUZKtUMRhZlQNNul6A8OU5dF7pS47_mC-LJ1szKPbQZWtjYySHpzvhodMwhU/s400/normal_packing_a.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The trivial projection of normal space to XY plane</td></tr>
</tbody></table><br />
The problem with this projection is, that the uniform angular difference between normals near equator gives much smaller difference in X and Y coordinates than near poles. If the precision of normal texture is limited to 16 bits (4x8 bit or 2x16 bit integer buffers) or even less (FP16 buffers) , the loss of precision may become visible. For example, for 16 bit buffers, the minimum representable angle near equator is:<br />
<div style="font-family: "Courier New",Courier,monospace;"><br />
</div><div style="font-family: "Courier New",Courier,monospace;"><b>acos (1 - 2e-15) = 1.44°</b></div><br />
Near poles it is much better:<br />
<br />
<b><span style="font-family: "Courier New",Courier,monospace;">asin (2e-15) = 0.002°</span></b><br />
<br />
Clearly we are wasting precision near poles and introduce errors near equator.<br />
<br />
To get better distribution of precision, we can switch projection from parallel to radial, with the projection center at the point (0,0,-1) as on the following figure:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1OLkkducZajUQ05J5e5mamr3P-GU8kZkTD6kzAfNxszRKuIF5H366psU57xpawSpIJtHXRYXCT2zUDK6C-prSSYJ0zcxCyDjRwtJdHUifhVK5Zctx_7duXayc5yNRZX6-WkEt4fahqsz_/s1600/normal_packing_b.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="375" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1OLkkducZajUQ05J5e5mamr3P-GU8kZkTD6kzAfNxszRKuIF5H366psU57xpawSpIJtHXRYXCT2zUDK6C-prSSYJ0zcxCyDjRwtJdHUifhVK5Zctx_7duXayc5yNRZX6-WkEt4fahqsz_/s400/normal_packing_b.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The radial projection of normal space</td></tr>
</tbody></table><br />
The distribution of angles is still not uniform but is clearly much better (it differs only by the factor of 2 at poles and equator).<br />
Actually, having the center of projection at (0,0,-sqrt(2)) would result in even better distribution, but I wanted to keep it at (0,0,-1) to simplify calculations a bit.<br />
<br />
The hemisphere is still projected at unit circle at XY plane, although now we cannot project the normals on negative hemisphere directly but have to take the absolute value of Z coordinate before projecting.<br />
<br />
This solves the problem of effecient use of normal texture precision. Now we can go on to encoding the sign of Z into the same buffer.<br />
<br />
<span style="font-size: large;">Encoding the sign of normal Z coordinate</span><br />
<br />
Our positive hemisphere of normal space got projected to unit circle on XY plane as in following figure:<br />
<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jqGVfcIIJWrawoBA56ecAmqbf7fbgNLvLR00fxB-mCtipXaitnJF1llzNDmExzTVKZ-eEC-5JzDbUSqyiFwPsbI3SM96ydw0LcAiwrbYDtEnDmdYcBJMKLEUO0Wf2nlL6knbpE1lLBYN/s1600/normal_packing_c.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jqGVfcIIJWrawoBA56ecAmqbf7fbgNLvLR00fxB-mCtipXaitnJF1llzNDmExzTVKZ-eEC-5JzDbUSqyiFwPsbI3SM96ydw0LcAiwrbYDtEnDmdYcBJMKLEUO0Wf2nlL6knbpE1lLBYN/s400/normal_packing_c.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The normal space projection to XY plane and compaction schema</td></tr>
</tbody></table><br />
<br />
As we can see, we are actually wasting the numerical space of normal texture. The texture is able to encode values in unit square, but only unit circle is used - the possible values on dashed areas can never happen in our projected normals. We could store normals with negative Z in those areas, but unfortunately there is no easy algorithm to do that.<br />
<br />
Now we do another conversion and pack all the values from unit circle into diamond-shaped area, given by the following formula:<br />
<div style="font-family: "Courier New",Courier,monospace;"><br />
</div><div style="font-family: "Courier New",Courier,monospace;"><b>|X| + |Y| <= 1</b></div><br />
This can be done with the following algorithm (let the normal be Nx', Ny' and the projection be Nx'', Ny''):<br />
<div style="font-family: "Courier New",Courier,monospace;"><br />
</div><div style="font-family: "Courier New",Courier,monospace;"><b>r = sqrt (Nx' * Nx' + Ny' * Ny')</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>D = |Nx'| + |Ny'|</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>Nx" = Nx' * r / D</b></div><div style="font-family: "Courier New",Courier,monospace;"></div><div style="font-family: "Courier New",Courier,monospace;"><b>Ny" = Ny' * r / D</b></div><br />
We lose some precision at the normals close to 45<b>°</b> angles, but the maximum loss is by the factor of sqrt(2).<br />
<br />
The resulting projection of normal space is shown in following figure:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvSpI6ReJ3KMMyusWeKHOIvTkMCjy6wwpJBfKz9AhySOpE0RyeM3RLyQZMT949Nac9SGpKNDllKGbGIGsiRXrX5sFXhCvjvEspLA0S32JmTOEY0cv7YzMpOnMe-79qgiRIDuea87FVGLv5/s1600/normal_packing_d.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvSpI6ReJ3KMMyusWeKHOIvTkMCjy6wwpJBfKz9AhySOpE0RyeM3RLyQZMT949Nac9SGpKNDllKGbGIGsiRXrX5sFXhCvjvEspLA0S32JmTOEY0cv7YzMpOnMe-79qgiRIDuea87FVGLv5/s400/normal_packing_d.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Compacted normal space and Z flipping directions</td></tr>
</tbody></table><br />
Now the whole projection of the positive hemisphere of normal space takes exactly the half of the size of unit square. The extra space can be used to encode normals with negative Z.<br />
<br />
We do this by mirroring the final normal projections by the closest one of diagonal lines (|x| + |y| = 1).<br />
<br />
Thus we have encoded the full normal space to unit square with minimal loss of precision.<br />
<br />
<span style="font-size: large;">Decoding normals</span><br />
<br />
To decode normals we have first to find the sign of Z coordinate. This is done with the following formula:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><b>Zsign = 1 if (|x| + |y|) <= 1</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>Zsign = -1 if (|x| + |y|) > 1</b></div><br />
Now, if Z is negative, we have to mirror our encoded normal by the closest diagonal line (|x| + |y| = 1) so the resulting normal is inside the diamond shaped area.<br />
<br />
We unpack normals to unit circle with reverse packing algorithm:<br />
<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><b>d = sqrt (Nx" * Nx" + Ny" * Ny")</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>r = |Nx"| + |Ny"|<br />
</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>Nx' = Nx" * d / r</b></div><div style="font-family: "Courier New",Courier,monospace;"><b>Ny' = Ny" * d / r</b></div><br />
From normal projection on unit sphere (N') unit sphere we unproject to normal on positive hemisphere (N).<br />
<br />
Finally we set the sign of the Z component of N from previously found Zsign.<br />
<br />
And that is it.<br />
<br />
<span style="font-size: large;">Encoding quality</span><br />
<br />
I ran simulated tests of this algorithm on CPU with 100 000 000 randomly generated normals, including normals with any 1 or 2 components zero. Encoded vector was written and then read back from 4-component unsigned char buffer to simulate storing of 2-component normal in RGBA8 texture.<br />
<br />
The maximum angular error between original and retrieved normal was<span style="font-family: inherit;"> 0.028</span><span style="font-family: inherit;">°.</span><br />
<br />
<span style="font-size: large;">GLSL shader code</span><br />
<br />
For those interested, here is current GLSL shader code of libsehle implementation. It is a bit heavy, but if your lighting is fill-rate bound, as is mine, you can spare some GPU cycles.<br />
<br />
<b>Encoding.</b><br />
<br />
<div style="font-family: "Courier New",Courier,monospace;"><span style="font-size: small;">vec2 encodeNormal (vec3 normal)<br />
{<br />
// Project normal positive hemisphere to unit circle<br />
// We project from point (0,0,-1) to the plane [0,(0,0,-1)]<br />
// den = dot (l.d, p.n)<br />
// t = -(dot (p.n, l.p) + p.d) / den<br />
vec2 p = normal.xy / (abs (normal.z) + 1.0);<br />
<br />
// Convert unit circle to square<br />
// We add epsilon to avoid division by zero<br />
float d = abs (p.x) + abs (p.y) + EPSILON;<br />
float r = length (p);<br />
vec2 q = p * r / d;<br />
<br />
// Mirror triangles to outer edge if z is negative<br />
float z_is_negative = max (-sign (normal.z), 0.0);<br />
vec2 q_sign = sign (q);<br />
q_sign = sign (q_sign + vec2 (0.5, 0.5));<br />
// Reflection<br />
// qr = q - 2 * n * (dot (q, n) - d) / dot (n, n)<br />
q -= z_is_negative * (dot (q, q_sign) - 1.0) * q_sign;<br />
<br />
return q;<br />
}</span></div><br />
<b>Decoding.</b><br />
<br />
<span style="font-family: "Courier New",Courier,monospace; font-size: small;">vec3 decodeNormal (vec2 encodedNormal)<br />
{<br />
vec2 p = encodedNormal;<br />
<br />
// Find z sign<br />
float zsign = sign (1.0 - abs (p.x) - abs (p.y));<br />
// Map outer triangles to center if encoded z is negative<br />
float z_is_negative = max (-zsign, 0.0);<br />
vec2 p_sign = sign (p);<br />
p_sign = sign (p_sign + vec2 (0.5, 0.5));<br />
// Reflection<br />
// qr = q - 2 * n * (dot (q, n) - d) / dot (n, n)<br />
p -= z_is_negative * (dot (p, p_sign) - 1.0) * p_sign;<br />
<br />
// Convert square to unit circle<br />
// We add epsilon to avoid division by zero<br />
float r = abs (p.x) + abs (p.y);<br />
float d = length (p) + EPSILON;<br />
vec2 q = p * r / d;<br />
<br />
// Deproject unit circle to sphere<br />
float den = 2.0 / (dot (q, q) + 1.0);<br />
vec3 v = vec3(den * q, zsign * (den - 1.0));<br />
<br />
return v;<br />
}</span>Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com7tag:blogger.com,1999:blog-783893035873019493.post-58975399947528828122011-06-13T02:47:00.001+03:002012-04-01T00:28:33.399+03:00More about Matrix to Euler angles conversionI previously posted the <a href="http://khayyam.kaplinski.com/2011/02/converting-rotation-matrix-to.html">algorithm to convert rotation matrix to arbitrarily ordered Euler angles</a>.<br />
It solved two problems of the trivial conversion method in <a href="http://www.j3d.org/matrix_faq/matrfaq_latest.html">Matrix and Quaternion FAQ</a>:<br />
<ul>
<li>Use arbitrarily ordered axes (like ZXY) in addition to XYZ</li>
<li>Remove the discontinuity happening at Ax = +/- 90 degrees</li>
</ul>
Meanwhile I discovered two remaining problems with my implementation:<br />
<ul>
<li>The algorithm always return the minimum set of absolute Euler angles (closest to {0;0;0}, as defined by the sum of squares of the angles) from the two alternatives. Often it is desirable to get the set of angles that are closest not to identity {0;0;0} but to some predefined rotation {Rx;Ry;Rz}.</li>
<li>When gimbal lock occured, the X rotation (or corresponding rotation if axes were reordered) was set to zero. This may not be the best solution, if we need the Euler angles to be as close to some specific set of values as possible.</li>
</ul>
The problem surfaces if we want to describe animations by Euler angles to allow easy adjustments by IK solvers (and have joint constraints defined) and interpolate between keyframes. In that case we absolutely have to keep the angles of adjacent keyframes as similar as possible - because although the alternate sets of angles make identical matrices, interpolations between them generally do not.<br />
<br />
<span style="font-size: large;">Stable conversio of matrix to Euler angles closest to given set</span><br />
<br />
The matrices have row-major order, so they have to be transposed for OpenGL.<br />
<br />
<b><span style="font-family: "Courier New","Courier",monospace;">Base</span></b> is a set of three angles {Bx;By;Bz} that will be used as reference while determining the best set of resulting angles.<br />
<br />
<pre>void
Matrix3x4f::getEulerAngles (f32 angles[], const f32 base[]) const
{
float aX, aY, aZ;
float aX2, aY2, aZ2;
aY = asin (c[2]);
aY2 = (aY >= 0) ? M_PI_F - aY : -M_PI_F - aY;
if (fabs (c[2]) < 0.99999f) {
<span style="color: blue;">// Over 0.25 degrees from right angle</span>
float C = cos (aY);
float C2 = cos (aY2);
float trX = c[10] / C;
float trY = -c[6] / C;
aX = atan2 (trY, trX);
aX2 = atan2 (-c[6] / C2, c[10] / C2);
trX = c[0] / C;
trY = -c[1] / C;
aZ = atan2 (trY, trX);
aZ2 = atan2 (-c[1] / C2, c[0] / C2);
} else {
<span style="color: blue;">// Less than 0.25 degrees from right angle</span>
<span style="color: blue;">// Gimbal lock occured</span>
aX = aX2 = base[0];
<span style="color: blue;">// Keep X rotation the same as base</span>
float Sx = sin (aX);
float Cx = cos (aX);
if (fabs (Cx) > fabs (Sx)) {
float Sz = (c[4] + (Sx/Cx) * c[8]) / (Sx*Sx/Cx + Cx);
aZ = aZ2 = asin (Sz);
} else {
float Sz = (c[8] + (Cx/Sx) * c[4]) / (Cx*Cx/Sx + Sx);
aZ = aZ2 = asin (Sz);
}
}
float d1X = clamp_pi (aX - base[0]);
float d1Y = clamp_pi (aY - base[1]);
float d1Z = clamp_pi (aZ - base[2]);
float d2X = clamp_pi (aX2 - base[0]);
float d2Y = clamp_pi (aY2 - base[1]);
float d2Z = clamp_pi (aZ2 - base[2]);
if ((d1X * d1X + d1Y * d1Y + d1Z * d1Z) < (d2X * d2X + d2Y * d2Y + d2Z * d2Z)) {
angles[X] = aX;
angles[Y] = aY;
angles[Z] = aZ;
} else {
angles[X] = aX2;
angles[Y] = aY2;
angles[Z] = aZ2;
}
}</pre>
<br />
If aY is close to +/- 90 degrees (gimbal lock occured) we want to set aX to the base value instead of setting it to zero.<br />
Also we calculate the differences of both alternate sets of angles from base and choose the final set based on the squared sum of those differences.<br />
<b><span style="font-family: "Courier New","Courier",monospace;">Clamp_pi</span></b> simply converts angle to -PI...PI range.<br />
<br />
<span style="font-size: large;">Conversion of matrix to arbitrarily ordered Eular angles</span> <br />
<br />
The matrices have row-major order, so they have to be transposed for OpenGL.<br />
<br />
<b><span style="font-family: "Courier New","Courier",monospace;">Order </span></b>is array of three integers, determining the order of rotations ({0;1;2} is XYZ, {2;0;1} is ZXY).<br />
<b><span style="font-family: "Courier New","Courier",monospace;">Base </span></b>values should be ordered according to order.<br />
<br />
<pre>void
Matrix3x4f::getEulerAngles (f32 angles[], const int order[], const f32 base[]) const
{
float sign = ((order[1] == (order[0] + 1)) || (order[2] == (order[1] + 1))) ? 1.0f : -1.0f;
<span style="color: blue;">// Permute matrix to switched axes space</span>
Elea::Matrix3x4f rs;
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
rs[4 * row + col] = c[4 * order[row] + order[col]];
}
}
rs[2] *= sign;
rs[6] *= sign;
rs[8] *= sign;
rs[9] *= sign;
<span style="color: blue;">// New base</span>
float nbase[3];
for (int i = 0; i < 3; i++) nbase[i] = base[i];
nbase[2] *= sign;
<span style="color: blue;">// Decompose to Euler angles</span>
rs.getEulerAngles (angles, nbase);
angles[2] *= sign;
}
</pre>
<br />
The trick is to permute the rotation matrix in such way, that the XYZ angles of new matrix are properly ordered decomposition angles of the original matrix and then do the trivial decomposition.<br />
Hopefully this time the algorithm is final ;-)<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com4tag:blogger.com,1999:blog-783893035873019493.post-23284150095430638662011-04-05T01:04:00.001+03:002011-04-05T01:04:33.720+03:00The future of KhayyamKhayyam 3D did not have very clear beginning. Few years ago I was experimenting with 3D rendering and animation under projects codenamed Floyd and SDLMu. The initial impulse was to write an engine and scene editor for my Ant-Attack inspred game.<br />
The unorganized codebase soon grew out of control, so I started from scratch. Or more precisely ported the <a href="http://khayyam.kaplinski.com/2010/10/khayyam-architecture.html">program structure</a> from my former project Sodipodi. When choosing name for the new project, I discovered <a href="http://en.wikipedia.org/wiki/Omar_Khayy%C3%A1m">Omar Khayyám</a> - a Persian mathematician, who sort of discovered the inherent link between geometry and algebra. And thus the program was born.<br />
<br />
As of now, Khayyam is evolving in several directions based on what I am interested in any given moment.<br />
<ul><li>Scene builder. My game is still in design stage, but at least I have the main character - Sara.</li>
<li>Virtual photography tool. It was partially inspired by <a href="http://www.hongfire.com/">HongFire</a> hentai game modding community and partially by Poser and DAZ applications.</li>
<li>Animation editor. This came from the need for some decent animations for Sara.</li>
<li>Model importer and converter. Implemented step-by step as needed.</li>
<li>3D engine. Mostly for learning, but also because I think I have some ideas, how it can be done right. </li>
</ul>As of now (April 2011) the program has at last become sort of usable for some simple tasks, like converting model formats, building POV-Ray scenes and so on. To go further, there should be a somewhat clearer working plan - which I have tried to lay out for myself.<br />
<br />
<span style="font-size: large;">1. Scene builder</span><br />
<br />
Khayyam itself is not game engine and never will - the two-level DOM structure is too heavy for gameplay. Instead there will eventually be an engine based on libsehle.<br />
There will be custom object formats. At least an octree-based map node, skeletal figures and static objects. Those can be imported from Blender Collada files, optimized and written to custom format.<br />
Objects need JavaScript bindings. I plan to make JavaScript the scripting language for game engine, so the actions should be programmable and testable in Khayyam.<br />
<br />
<span style="font-size: large;">2. Posing application </span><br />
<br />
The Poser figure loading and manipulating is still very flaky. This will get better eventually, so expect that in some future date Khayyam will be able to render (with the help of POV-Ray) everything Poser and DAZ studio can. Virtual photography is becoming more and more used, and Linux definitely needs a free application for that. The fist milestones are unlit objects (like backgrounds) and smoothscale channel implementation for full body morphs.<br />
<br />
<span style="font-size: large;">3. Animation editor</span><br />
<br />
Animations will be integrated globally into Khayyam document structure, so you can render not only still pictures, but full animation sequences.<br />
I am halfway implementing animation graph and biped walking controller for figures. Eventually you could import animations from other formats (like Biovision BVH), define sequences, merge, split, edit and join them, and build fully controllable animation graph from these. The controller will have IK solvers for fine limb/finger adjustments and will be controllable via JavaScript.<br />
<br />
<span style="font-size: large;">4. Model converter</span><br />
<br />
While several popular models are supported, the actual features vary a lot. There will be more model and animation formats (like MD5 animations) and more features. I also plan to support more game model formats - definitely all future Illusion games, but probably some others too.<br />
<br />
<span style="font-size: large;">5. 3D engine</span><br />
<br />
The big missing parts are proper shadow map algorithms, particle effects and post processing.<br />
<br />
Other than that, Kahhaym will have some UI overhauls in future. Tools will have their own dynamic property pages. Some extra tools, like bone and object pickers are needed. And there have to be UI controls for constraint placement. Also I am particularly not happy with Gtk+ default style - which wastes enormous amount of screen space. At least the animation editor needs more compact style<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe98mvQRnVwC5fl3ZJ-vPB7JsgvL9X5bb29OD3XhsqRn4-CZWntZ4TEIOzs6yA1L_rfsSwDwsTNqyNXcvdXfTH71OQOP7sXuqkhvYjdI5Kgmy5NApFTKTQaV5-3bqccEpN4uhyphenhypheneUrlnau3/s1600/sara-and-elephant.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhe98mvQRnVwC5fl3ZJ-vPB7JsgvL9X5bb29OD3XhsqRn4-CZWntZ4TEIOzs6yA1L_rfsSwDwsTNqyNXcvdXfTH71OQOP7sXuqkhvYjdI5Kgmy5NApFTKTQaV5-3bqccEpN4uhyphenhypheneUrlnau3/s400/sara-and-elephant.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara and african elephant (DAZ 3D model)</td></tr>
</tbody></table><br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com4tag:blogger.com,1999:blog-783893035873019493.post-56536138726071082232011-04-04T00:06:00.000+03:002011-04-04T00:06:11.643+03:00How to convert animations in KhayyamStarting from the 20110402 release Khayyam can import, convert and export Biovision BVH animations from one figure/skeleton to another. Following is a mini-tutorial guiding you through the process.<br />
You may take a look to the <a href="http://khayyam.kaplinski.com/2011/03/converting-animations-between-skeletons.html">overview of pose fitting algorithm</a> to better understand which joints will be moved and how.<br />
<br />
<span style="font-size: large;">0. The example problem</span><br />
<br />
Say, you have some good animations in BVH format and want to apply these to Poser characters. Unfortunately, skeleton topologies and/or bone orientations do not match, so they are not usable as-is.<br />
Khayyam can actually export BVH files from supported animation types (currently only Illusion game formats). How to do this is outside of this tutorial - but as a hint look at <b>skinAnimation</b> and <b>sequence</b> objects in document tree editor.<br />
<br />
<span style="font-size: large;">1. Setting up target</span><br />
<br />
Before we can convert animation to target figure, we need it's skeleton imported into Khayyam. Skeleton comes together with model, so at first we have to import Poser cr2 figure.<br />
It is probably good idea to use reduced resolution figures for this process, because animating the whole iteration 4 (V4, M4) models can be quite slow. I will use Chibibel for current example - she has much lower polycount and can show animations without too much frame-skipping.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDl7ktaZ4Npx71Qnsq72qCwoMdHDiIahhVn5-U-y1o47tiE97dGV-higPRy1pEFu8es8WWpzU9gkePpQjMJkFJGnknDR9UU0EyGPPUeZL9aP7zSrZaDFz_-T53zYqMQYu8Ii1m7NoKB554/s1600/chibi-imported.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDl7ktaZ4Npx71Qnsq72qCwoMdHDiIahhVn5-U-y1o47tiE97dGV-higPRy1pEFu8es8WWpzU9gkePpQjMJkFJGnknDR9UU0EyGPPUeZL9aP7zSrZaDFz_-T53zYqMQYu8Ii1m7NoKB554/s400/chibi-imported.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Chibibel imported to Khayyam</td></tr>
</tbody></table><br />
At moment Khayyam does not set up joint constraints automatically for cr2 objects, so you have to define at least elbow and knee constraints. Otherwise you risk, that legs or arms may suddenly bend sideways during animation.<br />
Select imported figure, switch to pose mode and select left shinbone.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhy0_ylxTDj2FqhVXKUtl9b9yH0AUWSrUE0mbNoiWtPTet2pErO_MjK8olkfm2dIlwDZiq5oORo9T70fGf3B1hRc-pGuE-EYIrwxMvzOlN0AZRQfxBhILRyGN6coxHV2NgAGC6jaLHkE0C/s1600/chibi-skeleton.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhy0_ylxTDj2FqhVXKUtl9b9yH0AUWSrUE0mbNoiWtPTet2pErO_MjK8olkfm2dIlwDZiq5oORo9T70fGf3B1hRc-pGuE-EYIrwxMvzOlN0AZRQfxBhILRyGN6coxHV2NgAGC6jaLHkE0C/s400/chibi-skeleton.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Chibibel skeleton with shinbone selected</td></tr>
</tbody></table><br />
The colors of rotation arrows mark the bone-relative axes of rotations (red - X, green - Y, blue Z). Normally knee bends only in one direction - in current case this is green rotation, i.e. Y axis. So you have to disable X and Z rotations.<br />
<br />
Open document tree dialog. It will automatically show property page for selected bone - i.e. left shin.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQJ_b1TjVYL2KEsQ96UjIEqU6MESuFPRDg6izyXYmaohyT-b91wUghrFSQtGmHPKxLznCaglDzGp8Mmcj6p_BdpNaJCRemOSp4sWTFPYsb4y-2dTzMrse84f5B-BfPJcVIMS4866vQoLue/s1600/chibi-bone-constraints.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQJ_b1TjVYL2KEsQ96UjIEqU6MESuFPRDg6izyXYmaohyT-b91wUghrFSQtGmHPKxLznCaglDzGp8Mmcj6p_BdpNaJCRemOSp4sWTFPYsb4y-2dTzMrse84f5B-BfPJcVIMS4866vQoLue/s400/chibi-bone-constraints.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The property page of left shinbone</td></tr>
</tbody></table><br />
In <b>constraints</b> frame select first <strong>X</strong>, then <strong>Z</strong> <b>channel</b> and uncheck the <b>Enabled</b> box for both. This turns off the rotations around those axes.<br />
On the same page you can set more fine-grained behavior by defining tension parameters for bones. Angles are basically maximal allowed rotations in either direction, rigidity values control, how "difficult" is given rotation. For example, knee joint moves very easily (in allowed range), but twisting shoulder is difficult, so character tries to minimize it. But if the pose is not achievable otherwise, the twisting still can happen - unlike the hard cutoff angles, that cannot be exceeded.<br />
In current example we fortunately do not need to set up tensions.<br />
Repeat the disabling <strong>X</strong> and <strong>Z</strong> channel rotations for right shinbone. Then select left forearm and disable <strong>X</strong> and <strong>Y</strong> rotations. The same with right forearm.<br />
Now we have completed the minimum setup needed for successful pose merge.<br />
<br />
<span style="font-size: large;">2. Converting animation</span><br />
<br />
In document tree select the main figure node. From the property page click on <b>Merge animations...</b> to open the converter dialog.<br />
The first step is to load source animation or pose. Click <b>Load animation...</b> and select your BVH file.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1krk-rTJi9mBQl2YSPng5T6prFELMB9IHEa7J7fBwwU-zfGPcokBiz-DAZhLVKByKKxwwfPc4g0bqaL8lBgubyvBhqATw7MvlxVIHVTLf4gdVyyAmKtqP3KO6Mwv1d1De5t7giUfbjYSJ/s1600/chibi-merge-animation.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1krk-rTJi9mBQl2YSPng5T6prFELMB9IHEa7J7fBwwU-zfGPcokBiz-DAZhLVKByKKxwwfPc4g0bqaL8lBgubyvBhqATw7MvlxVIHVTLf4gdVyyAmKtqP3KO6Mwv1d1De5t7giUfbjYSJ/s400/chibi-merge-animation.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Running animation applied to Chibibel skeleton</td></tr>
</tbody></table><br />
Once the animation is loaded, Khayyam tries to adjust the destination figure to starting pose. This is iterative process and may take more than one try. So if the pose is not close to the original, you may try to click <b>Adjust pose</b> few times.<br />
The red armature is the original animation skeleton. The blue one is the skeleton of target figure. These can be toggled on and off to see clearer picture.<br />
The other controls you may tweak are:<br />
<ul><li><strong>Scale</strong> - changes the size of source skeleton. This should be used (together with <strong>ZScale</strong>) to make the skeletons approximately the same height - otherwise shoulders and hips can not be posed correctly.</li>
<li><strong>ZScale</strong> - changes the height of source skeleton. Thus <strong>Scale</strong> can be used to make the widths of hips and shoulders approximately right, and <strong>ZScale</strong> then to make the height of character right.</li>
<li><strong>Guess scale</strong> - Khayyam tries to determine scale from the distances between hips and shoulders. This may or may not work correctly.</li>
<li><strong>EMax</strong> - tha maximum error tolerance - if the combined distance between poseable chains (body, limbs...) is less than <strong>EMax</strong>, iterative pose refinement will stop. Smaller value may give better adjusting but makes the process slower.</li>
<li><strong>TScale</strong> - the scaling factor of bone tensions. Usually this can be 1 for default tensions or some smaller value (0.01) if all tensions have been set up in skeleton.</li>
<li><strong>Runs</strong> - number of distinct runs of IK solver</li>
<li><strong>Iterations</strong> - the maximum number of tries in each run</li>
</ul>Playing with these may give better results. If animation is jerky, increasing the number of runs often helps. If limbs do not track the originals well, lowering <strong>TScale</strong> may help. Experiment!<br />
Also, you should keep in mind, that while hips and shoulders are placed using the absolute positions, other joints are adjusted by directions. <a href="http://khayyam.kaplinski.com/2011/03/converting-animations-between-skeletons.html">Previous blog entry</a> describes the process in more detail.<br />
Khayyam knows the bone names of some common skeletons and can determine the corresponding standard parts automatically. But sometimes it may not know, which standard humanoid bone certain name corresponds to. In that case you have to define bone names manually, using the bone mapper tool. This can be done by clicking <b>Map</b> buttons in source and destination skeleton frames.<br />
Not all skeletal bones are 1:1 mappable and this is not even needed. What is important is, that standard joints of movable body parts are mapped correctly.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXXwYHKnibz_dRKdz2SjfYKyMVGE0tlEWeOOIR01jSKG950va4oqXWkQSgLbUwTtwyTStyGgOu1runerLsRX_7b_Pt8ruJDya9SqtqPrqQFNtTHvD_ysFigTS-FiodjhdEkYJnzI1URonM/s1600/bone-mapper.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXXwYHKnibz_dRKdz2SjfYKyMVGE0tlEWeOOIR01jSKG950va4oqXWkQSgLbUwTtwyTStyGgOu1runerLsRX_7b_Pt8ruJDya9SqtqPrqQFNtTHvD_ysFigTS-FiodjhdEkYJnzI1URonM/s320/bone-mapper.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bone mapper dialog for Poser skeleton</td></tr>
</tbody></table><br />
In the example above, you can see the bone mapper dialog for standard Poser biped skeleton (V4, M4, Chibibel). In the left-hand tree there are all bone names that appear in skeleton file, at right are the corresponding Khayyam standardized body parts. Once set up the mapping can be saved and loaded (actually the last saved mapping will be loaded automatically).<br />
Once the bone mappings are set up (and saved), click <strong>Close</strong> and the conversion solver will update itself.<br />
You can play the animation with current conversion parameters using play button.<br />
<br />
3. Importing animation to document<br />
<br />
Once you are satisfied with the conversion results, click <strong>Import</strong>. Khayyam will then generate a tree of animation objects as child nodes of parent Figure node.<br />
Hint: You can set <strong>NRuns</strong> to bigger value and <strong>EMax</strong> to lower value for import so the actual conversion will be done with maximal accuracy.<br />
The object of interest for us is a <strong>sequence</strong> node, that will be created as the last child of <strong>figure</strong>. It defines the imported animation sequence and can be played from play button on its property page.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwC6YbFX937iQ4VLoRrKr734iLkXEvAdIX5cozRjyft4gxx2xY4Q_Ub79IqsLGAqGby5OA5dAnNTbBTCaE00n-QnY8VucIeCmuhyGS6IBf3N3WSP7e4Uk9Er5GzdiKefHTN2EOBq5CIEpX/s1600/chibi-animation-sequence.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwC6YbFX937iQ4VLoRrKr734iLkXEvAdIX5cozRjyft4gxx2xY4Q_Ub79IqsLGAqGby5OA5dAnNTbBTCaE00n-QnY8VucIeCmuhyGS6IBf3N3WSP7e4Uk9Er5GzdiKefHTN2EOBq5CIEpX/s400/chibi-animation-sequence.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The imported animation sequence object</td></tr>
</tbody></table><br />
If the imported sequence works as intended, you can export it to new BVH file. This new file uses already the proper skeleton of the target figure.<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.blogger.com/video.g?token=AD6v5dzEdcej2o571L9RlNyZAnWNvcYivyYTw74G5LHN3sc5JGBiddWI6kOzlTv1jGgAH63l-4hh6-HlVsBVe5s0PQ' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br />
And that's it. I have many ideas how to make this process smoother, so stay tuned!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com3tag:blogger.com,1999:blog-783893035873019493.post-50046890568389688422011-04-03T01:46:00.000+03:002011-04-03T01:46:56.908+03:00Snapshot release 20110402A new snapshot release 20110310 is available from <a href="https://sourceforge.net/projects/floyd/files/escher/">SourceForge page</a> as source tarball and precompiled Win32 binary.<br />
<br />
The most important new feature is refactored biped pose/animation converter:<br />
<ul><li>Both animations (from BVH files) and static poses (from other objects) can be converted</li>
<li>Animations can be saved to sequences (and exported to BVH again)</li>
<li>Almost all body parts of biped are adjusted (body, limbs, head, fingers)</li>
<li>IK solver parameters can be adjusted</li>
<li>Bone definitions can be set by user (and saved for future use)</li>
</ul>Some problems remain - collar bones do not move, you cannot set T-pose and so on.<br />
The better the constraint system of the destination figure, the better pose matching will be. At minimum you should set knee and elbow constraints to avoid wrong bendings.<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.blogger.com/video.g?token=AD6v5dwY_U1udHUThSeHLKMus6P-owXTUvx1tgjAHRZDQBG2HxSzzO1cGgcz_dn6MVua6DW_RQlRrTQ6dez-W-H8Qw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br />
A demonstration video of making Sara-chan to run. Red armature is the original BVH animation file, blue is the Sara skeleton. It is not ideal, because the BVH does not have finger poses.<br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com1tag:blogger.com,1999:blog-783893035873019493.post-4482813129973078572011-03-26T12:56:00.001+02:002011-03-26T22:56:18.709+02:00Converting animations between skeletonsI have written one more piece about the skeleton merge about half year ago - you can read it <a href="http://khayyam.kaplinski.com/2010/11/how-to-merge-animations.html">here</a>. What follows is the a much better version of the original algoritm.<br />
<br />
<span style="font-size: large;">The problem</span><br />
<br />
Say you have some good animations (there are many free BVH sites in web) and want to use these for your own models. Unfortunately - unless you made your models using exactly the same skeleton layout, the bones do not match and copying animations is not trivial problem.<br />
<br />
There are many ways, how skeletons may differ:<br />
<ul><li>Bone names do not match. This is easy to overcome - either build mapping or rename bones.</li>
<li>Rest pose orientations use different systems (like XYZ euler angles vs. ZXY euler angles vs. quaternions). This is also minor problem, as normally you want to convert everything to quaternions or matrices anyways.</li>
<li>Bone lengths do not match. Thus copying animation directly can make movement unnatural. </li>
<li>Rest pose bone positions (directions) do not match. Thus animations can not be immediately copied between skeletons, but have to be transformed to absolute space (object space) before.</li>
<li>Skeleton topologies do not match. This is often a showstopper, because if you do not even have similar bones, there is no way to copy animations. </li>
</ul><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyunupYEGeU36hHAkVA99zFXztz3NKZj12DWK9m3xfoPK40HeV2xNH6z6caqjEgtTxpOBLWoBxTi-m9HCkVMQ2kUuNIAGf0ZuRa44x8PYjCZWGsjpypEGNmzIE3yGF7ycVDIM6GU59LhxF/s1600/skeleton-topology.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyunupYEGeU36hHAkVA99zFXztz3NKZj12DWK9m3xfoPK40HeV2xNH6z6caqjEgtTxpOBLWoBxTi-m9HCkVMQ2kUuNIAGf0ZuRa44x8PYjCZWGsjpypEGNmzIE3yGF7ycVDIM6GU59LhxF/s320/skeleton-topology.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Example of different common skeleton topologies</td></tr>
</tbody></table><br />
The topology mismatch is the hardest problem to overcome. In the example above, if you, say have animation for the right skeleton, the 4 parts of backbone should move the 2 backbone parts and pelvis of the left skeleton. While in theory it should be possible to solve this problem analytically, it is not easy - and has to be done by hand for each combination of skeletons.<br />
Instead of that, using IK solvers gives us easier way to map those skeletons. It is still not universal - the mapping algorithms have to be adjusted to object itself - in current case a humanoid. But normally this is not big problem, because in 99% cases humanoid is precisely what you want to animate.<br />
<br />
In any case you need good IK solver, that can do at least 6-7 bones long chains, adjust both positions and rotations and use both absolute positions and bone directions for targets. Also, the solver has to support comparing up to 4 target points or directions simultaneously. I am currently using <a href="http://en.wikipedia.org/wiki/Nelder%E2%80%93Mead_method">simplex method</a> in Khayyam (the code is in <span style="font-family: "Courier New", "Courier", monospace;">helpers/ik.cpp</span>), but there are other options as well.<br />
Also the skeleton should have bone constraints defined - otherwise you may discover that elbows or knees will be bent to completely wrong direction.<br />
<br />
We start implementing the algorithm by a small observation. Namely - while there are different ways to construct skeletons, the joints have to be still placed to precise spots - otherwise the movement of joints will not be natural. Thus we can use the joints as anchor spots for the algorithm.<br />
<br />
<span style="font-size: large;">The body</span><br />
<br />
The first and hardest part is moving the main bodies of skeletons to the same position. First - the root bones may be arranged differently. Second - root bones may be joined with body either below or above pelvis. Third - there may be different number of backbone elements. Fourth - there may or may not be extra bones, like hips or collar bones.<br />
To solve this problem, we can do the following:<br />
<ul><li>Set 4 IK target points to the corner joints of body - both hips and both shoulders.</li>
<li>Scale the source skeleton so, that the distances between hips and shoulders are approximately the same, as in target skeleton.</li>
<li>Find the topmost common parents of both hips and shoulders Ah and As.</li>
<li>Find the topmost common parent of Ah and As B.</li>
<li>Build rotating IK chain of bones from B to Ah and from B to As, not including B.</li>
<li>Add both the rotations and translations of the rootmost bone R to chain. </li>
</ul><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKjtzWHU-naeai2R6XQtAtl3CT8lXPcUTYqEl7rcCjmXRLGWk2oS5tX6udLG_lZy3jbk6h95EaxxLNyYnaD3WUz7vQVVwN-vMFrFmEdE7_8G0LFtiEtk_nC2RsdkluLGk6UEUagRtncog/s1600/skeleton-body.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJKjtzWHU-naeai2R6XQtAtl3CT8lXPcUTYqEl7rcCjmXRLGWk2oS5tX6udLG_lZy3jbk6h95EaxxLNyYnaD3WUz7vQVVwN-vMFrFmEdE7_8G0LFtiEtk_nC2RsdkluLGk6UEUagRtncog/s320/skeleton-body.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The schematics of body IK solver (green: translate + rotate, red: rotate, blue: not moving)</td></tr>
</tbody></table>After solving this IK chain, the corner joins of body will be approximately aligned to the original. Normally you do not achieve perfect alignment, because the width of shoulders and hips is different in skeletons - but this is not big problem, because the direction of hip-hip and shoulder-shoulder lines should be aligned.<br />
It is important to do this alignment in one step - otherwise the alignment of hips may involve extensive rotations and translations of root bone R - and you cannot later align shoulders at all.<br />
<br />
<span style="font-size: large;">Limbs </span><br />
<br />
To align limbs, there are two strategies - depending on what we want to achieve.<br />
<ul><li>We can align the endpoints - if we want to model some specific action - like taking some item from table. In thus case the hand will be in correct place - but because the lengths of arms and forearm probably differ, elbow will be off.</li>
<li>Or we can align the directions of bones - for example if modeling hand movement during walking - in that case both elbow and wrist will be off, but the overall look of hand will be more similar.</li>
</ul>At the first case it is just ordinary single-target IK solver. Or if you want to keep the elbow/knee direction more similar to original, there should be additional directional target at midpoint.<br />
At the second case, the IK chain has to be solved with two directional targets (<b>shoulder-elbow</b> + <b>elbow-wrist</b> or <b>hip-knee</b> + <b>knee-ankle</b>) simultaneously. Otherwise the topmost bone may turn it to such direction, that 1 DOF joint at midpoint (elbow or knee) cannot move other bone to the right direction.<br />
<br />
<span style="font-size: large;">Palm</span><br />
<br />
There are probably different strategies for adjusting palms. In khayyam I did the following:<br />
<ul><li>Set 4 directional targets from wrist to the topmost joints of fingers</li>
<li>Add the 3 DOF rotations of palm to IK chain</li>
<li>If forearm includes extra rotational bone (like at the skeleton at left), include the axial rotation of it to chain </li>
</ul>By solving this chain, the positions of finger joints will be approximately aligned to the original.<br />
<br />
<span style="font-size: large;">Fingers</span><br />
<br />
Fingers can be aligned in the same way as limbs - but here we need three targets instead of two. Also, as the fingertips are normally the end sites, the last target does not coincide with any joint, but has to be placed about 2-3 cm from the last joint along the bone direction.<br />
<br />
<span style="font-size: large;">Head</span><br />
<br />
Head should be placed by adjusting three positional targets - eyes and skulltop. This can become tricky, because many skeletons may not include eye positions at all.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEz1KB7FgFt0SshqgRAM2ocDOhhvrmDR9bbATz7YRJJQFQFRFQBSQDhsUXmVqw32LaipEtrEzs8thBaaISSFkXN1TzNlgfyNPQeKz55ectWTR89_Z1ppWZfzRuoAX2D5NA8iCHiv3ehRAc/s1600/sara-dance-adjusted.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="316" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEz1KB7FgFt0SshqgRAM2ocDOhhvrmDR9bbATz7YRJJQFQFRFQBSQDhsUXmVqw32LaipEtrEzs8thBaaISSFkXN1TzNlgfyNPQeKz55ectWTR89_Z1ppWZfzRuoAX2D5NA8iCHiv3ehRAc/s400/sara-dance-adjusted.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Sara chan in skeleton merge editor (blue: figure skeleton, red: animation skeleton)</td></tr>
</tbody></table>At the above picture you can see the results of adjustment of Sara-chan skeleton to Illusion Characolle animated skeleton. Although the skeleton topologies and bone lengths differ, the pose is pretty close to the original.<br />
<br />
At moment the skeleton merge works only in <a href="http://sourceforge.net/projects/floyd/">CVS version</a> of Khayyam, but expect it in the next release.Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com1tag:blogger.com,1999:blog-783893035873019493.post-61488364023805833302011-03-16T10:23:00.000+02:002011-03-16T10:23:10.926+02:00Khayyam architecture: libtheraFor a quick overview of all Khayyam subcomponents, look at the <a href="http://khayyam.kaplinski.com/2010/10/khayyam-architecture.html">introduction</a>.<br />
<br />
<span style="font-size: large;">Overview </span><br />
<br />
Libthera is the backbone of Khayyam document system. It is very simple DOM-like tree of XML nodes, implementing file IO, transactions (think undo and redo) and property, tree and content mutation events.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC3DLr2FFqNGGq7kjE1wf1nlx6uhrh7YWa9a1U9SxiQCk8_KzpGXXwNPIQE1zfXGuOCB6hJgEKsGOC2lgdtBG8nQAhi9odU2H32ukxt0dIJyIp-vWIoI2omM5a6Unayx5izZSy2QO_3Lm2/s1600/thera.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC3DLr2FFqNGGq7kjE1wf1nlx6uhrh7YWa9a1U9SxiQCk8_KzpGXXwNPIQE1zfXGuOCB6hJgEKsGOC2lgdtBG8nQAhi9odU2H32ukxt0dIJyIp-vWIoI2omM5a6Unayx5izZSy2QO_3Lm2/s400/thera.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The schematic layout of Thera object tree</td></tr>
</tbody></table>All properties inside libthera tree are simple text strings. Thera nodes do not know anything about the semantics of attributes, content or child nodes. They only serve as the containers of base state for more semantic-aware objects in libmiletos.<br />
<br />
<span style="font-size: large;">Thera::Node</span><br />
<br />
The basic building block of Thera tree is Thera::Node - an object encapsulating XML node (either element, text or cdata). It has the following properties:<br />
<ul><li>Name (only for element nodes) </li>
<li>Backlink to containing document </li>
<li>One or more attributes (key/value pairs, only for element nodes)</li>
<li>Optional text content</li>
<li>Linked list of children</li>
<li>Linked list of eventlisteners</li>
</ul>The tree is modified by atomic methods of nodes - content change, attribute change, child insertion, child removal and child reordering. The API is kept as simple as possible - setting attribute to NULL deletes it, using NULL as ref value during child insertion prepends child and so on. The result is <strong>true</strong> if the action succeeds, <strong>false</strong> if not.<br />
<br />
<pre>bool Node::setAttribute (const char *key, const char *value);
bool Node::setTextContent (const char *newcontent);
bool Node::addChild (Node *child, Node *ref);
bool Node::removeChild (Node *child);
bool Node::relocateChild (Node *child, Node *ref);
</pre><br />
By default all methods above are allowed on all proper nodes (no attribute change or child management on text and cdata nodes). Actions can be vetoed by event listeners. The idea here is, that thera tree is not typed - all element nodes are similar and do not know anything about the semantic meaning of themselves or attributes. To implement semantic checks, another structure has to be added to thera tree, that can interpret the property changes and veto/allow these. In case of Khayyam, that additional layer is <strong>libmiletos</strong> scene graph library.<br />
<br />
<span style="font-size: large;">Event system </span><br />
<br />
For each property change (attribute or content change, child insertion and so on) node first invokes query events from linked <strong>EventVector</strong> list.<br />
<strong>EventVector</strong> is list of function pointers, which - if not NULL - will be called during mutation events. I did not implement real signalling system for two reasons. First - to keep things simple. Second - normally single <strong>Thera::Node</strong> is tracked by single <strong>Miletos::Object</strong> and thus having all listeners in one structure makes perfect sense.<br />
<br />
For example, if one tries to change the attribute of <strong>Thera::Node</strong>, the following happens:<br />
<ol><li>Old and new attribute are compared. <strong>True</strong> is returned immediately, if they are identical.</li>
<li><b><span style="color: blue;">change_attribute</span></b> listeners are called by function pointers from all linked <strong>EventVectors</strong>. If any of these returns <strong>false</strong>, attribute is left intact and <strong>false</strong> returned.</li>
<li>Actual attribute value of <strong>Thera::Node</strong> is set to new value</li>
<li><b><span style="color: blue;">attribute_changed</span></b> listeners are called by function pointers from all linked <strong>EventVectors</strong>. This is post-mutation callback and thus cannot veto the change anymore.</li>
<li>If no <span style="color: blue;"><strong>attribute_changed</strong></span> event was installed, <b><span style="color: blue;">downstream_attribute_changed</span></b> listeners are called from parent node <strong>EventVector</strong> list.</li>
<li>Containing <strong>Thera::Document</strong> is notified about attribute change</li>
</ol>Normally the libmiletos scene graph objects (subclasses of <strong>Miletos::Object</strong>) listen to <strong>attribute_changed</strong>, <strong>content_changed</strong>, <strong>child_inserted</strong> and other post-mutation events and update their internal state whenever something changes. The exceptions are two special attributes <strong>id</strong> and <strong>xmlns</strong>. As each object in scene graph has to have unique id for reference system to work, the uniqueness of the value of <strong>id</strong> attribute is checked in change_attribute handler and mutation blocked if new value is not unique. All <strong>xmlns</strong> changes are blocked, as those change the semantics of object, and would require replacing one scene graph node with another one.<br />
<br />
The reason for downstream listeners is to reduce the number of objects, that libmiletos has to implement. For example <strong><color></strong> nodes in Collada tree do not build their of objects (i.e. there is no <strong>Miletos::Collada::Color</strong> objects) but instead the containing nodes (ambient, diffuse, light nodes and so on) get signalled of color content changes by downstream propagation.<br />
<br />
<span style="font-size: large;">Transactions</span><br />
<br />
The untyped nature of thera tree makes it ideal place to handle generic editing functions, that have to be consistent for all scene graph nodes - undo, redo, cut, copy, paste, load and save. The invariant in Khayyam document system is, that the properties of <strong>Miletos::Object</strong> are determined only by the attributes and children of <strong>Thera::Node</strong>. So restoring the <strong>Thera::Node</strong> properties to previous value, also restores scene graph to previous state.<br />
<strong>Thera::Node</strong> has very few properties, so to implement undo and redo, we have to only record 5 different types of mutation events (attribute change, content change, child insertion, child removal and child relocation). This is managed by <strong>Thera::Document</strong> container.<br />
<br />
For each mutation event, a new record is appended in document undo list.<br />
<ul><li>For attribute changes keep node location, key and old value</li>
<li>For content changes keep node location and old content</li>
<li>For child insertion keep the location of new node</li>
<li>For child deletion keep the copy of child node and it's previous location</li>
<li>For child relocation keep the old and new locations of child</li>
</ul>Undo works by rolling back the record, and moving it from the top of undo list to the top of redo list.<br />
<br />
Transaction logging can be turned on and off. Normally it has to be always on for editable document, but in certain cases - like during ensuring unique <strong>id</strong> attributes to all objects during document creation, it is turned off (<strong>id</strong> uniqueness is required feature, so this procedure cannot be undone).<br />
In addition to transaction logging, there is transaction collation. If this is turned on, subsequent changes of the same attribute do not create new records, but only update the last record. It is used for tracking continuous numerical attribute changes - like the ones controlled by spinbutton or slider.<br />
<br />
Similarly cut, copy and paste are implemented by storing the clone of copied node(s). During paste, the clone is re-cloned into document tree - and libmiletos objects will be built and updated automatically by event listeners.<br />
<br />
<span style="font-size: large;">Some concerns</span><br />
<br />
While the untyped nature of thera tree makes editing functions simple and consistent throughout document, it has one drawback. As objects are often referenced by <strong>id</strong> values, and <strong>id</strong> has to be unique in document, copying reference pair does not work as intended. The <strong>id</strong> of new referee will be changed to some unique value, but the referencing attribute of referer will be not. So the referer will link back to the original object, instead of the new object.<br />
I have some ideas, how to handle it - by keeping a dictionary of <strong>id</strong> overwrites during libmiletos object building phase, but at moment it has to be adjusted by hand.<br />
<br />
Also, as libthera keeps all values as text, updating parsing these may take more time than is reasonable to spend during interactive editing. To dealt with this, many interactive functions work in two-stage way.<br />
<ul><li>When input is grabbed (mouse button pressed), the initial state of object is recorded</li>
<li>During editing (normally dragging) libthera is skipped and editing function applied directly to scene graph nodes</li>
<li>When editing is finished, the last state is written to libthera</li>
<li>If editing is canceled, object state is read back from libthera (which has the initial value) </li>
</ul>Thus interactive functions do not constantly update text attributes, but instead the state of scene nodes directly. As an extra bonus the single editing action (click-drag-release) is recorded as single document transaction, because the libthera values are updated only during release.<br />
<br />
That's all for now. Libthera can be donwloaded as part of khayyam source from <a href="http://sourceforge.net/projects/floyd/">SourceForge page</a>. Or the latest version can be fetched from Sodipodi SVN:<br />
<br />
<a href="https://sodipodi.svn.sourceforge.net/svnroot/sodipodi/trunk/thera">https://sodipodi.svn.sourceforge.net/svnroot/sodipodi/trunk/thera</a><br />
<br />
Have fun!Anonymoushttp://www.blogger.com/profile/01914659499851183442noreply@blogger.com1