tag:blogger.com,1999:blog-55149747054255617442024-03-13T20:08:22.321-07:00Synthetic GraphicsUnknownnoreply@blogger.comBlogger32125tag:blogger.com,1999:blog-5514974705425561744.post-52834434730472074202018-04-09T03:58:00.000-07:002019-05-24T00:53:18.218-07:00Physically Based Rendering<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/lh280KQ7NRU/0.jpg" frameborder="0" height="400" src="https://www.youtube.com/embed/lh280KQ7NRU?feature=player_embedded" width="500"></iframe></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHaErA_afE9fY0kAWO0MYG8XEscqOqsFfXDm-jknRRDMEB4mDOwRIN1mo2hUKJWToQbQyhF7dyDCRhwz8ai86mhZb3RV6jyEQRUdz6fgfxQ-HrJgEb2hNeY1ZSg8f7CpbtARWNN6PKiekY/s1600/2018-09-25+%252815%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHaErA_afE9fY0kAWO0MYG8XEscqOqsFfXDm-jknRRDMEB4mDOwRIN1mo2hUKJWToQbQyhF7dyDCRhwz8ai86mhZb3RV6jyEQRUdz6fgfxQ-HrJgEb2hNeY1ZSg8f7CpbtARWNN6PKiekY/s320/2018-09-25+%252815%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid4PyaomO4xAtxUKHv0RgIaFhyphenhyphenc1h8dmNjHy543LoAvEgNhfc0VF8PjHezKWCZ2mmM7VtivpoPFQaUL0X89VpUt1IDnG15V1dEIp_3THNywUzCttH1Ufa16PaYJupENybMZhyNob9sEXrZ/s1600/2018-09-25+%252816%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid4PyaomO4xAtxUKHv0RgIaFhyphenhyphenc1h8dmNjHy543LoAvEgNhfc0VF8PjHezKWCZ2mmM7VtivpoPFQaUL0X89VpUt1IDnG15V1dEIp_3THNywUzCttH1Ufa16PaYJupENybMZhyNob9sEXrZ/s320/2018-09-25+%252816%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWlx1bK9PWxXgzDIOo79VBpH98K_YdK7nWjNg6bjDsXUNdfAjrOZ3zN8VDlRhtfXThk_IObEPNeyIsV5J8BhBLDqydcXK7jFWSTm5j9L27CdnuvHMvowVGPx-r3oLpxdZpwYEYUUWA7UJg/s1600/2018-09-25+%252817%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWlx1bK9PWxXgzDIOo79VBpH98K_YdK7nWjNg6bjDsXUNdfAjrOZ3zN8VDlRhtfXThk_IObEPNeyIsV5J8BhBLDqydcXK7jFWSTm5j9L27CdnuvHMvowVGPx-r3oLpxdZpwYEYUUWA7UJg/s320/2018-09-25+%252817%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDUUbe4GPz9DNgNkzLBU_Z2WF5LALPoKM0xVukt-66rG_UWY9YOpHizMkoGSMBgi5ZDzcUjrQakA-S84N7onOgRiZJ9Mi5padxIIQCJFRdR0lrP7EOrQ7O-48C3xLrDj0YImDScYDkJM7-/s1600/2018-09-25+%252818%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDUUbe4GPz9DNgNkzLBU_Z2WF5LALPoKM0xVukt-66rG_UWY9YOpHizMkoGSMBgi5ZDzcUjrQakA-S84N7onOgRiZJ9Mi5padxIIQCJFRdR0lrP7EOrQ7O-48C3xLrDj0YImDScYDkJM7-/s320/2018-09-25+%252818%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5HH05_cOBnyezqg8c_EGpUxqwcAJW6p1Rq6B4Yz-mF5QKq0vf2YTbGzPac0DdtUxu6oEX7tnzf0Ycb5rVK4pvIuyPmhoEv919oXf5BNbqCGZLbFNQL0JJYtX_4m1Lo9dQu4bJ_hs40wwA/s1600/2018-09-25+%252819%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5HH05_cOBnyezqg8c_EGpUxqwcAJW6p1Rq6B4Yz-mF5QKq0vf2YTbGzPac0DdtUxu6oEX7tnzf0Ycb5rVK4pvIuyPmhoEv919oXf5BNbqCGZLbFNQL0JJYtX_4m1Lo9dQu4bJ_hs40wwA/s320/2018-09-25+%252819%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2OexjlCRy4uQAM4DfwYNDWXJ6XEK5ErYNy_wR85d9epCiH223u88qgcwM4FNnzkTKbuyL7-3jfgLSIIivdYLkA4iRLIHFehP8pYZA5Jp4397fS2e8tdADCqML_5NB1628xGVI6gAzPLfN/s1600/2018-09-25+%252820%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2OexjlCRy4uQAM4DfwYNDWXJ6XEK5ErYNy_wR85d9epCiH223u88qgcwM4FNnzkTKbuyL7-3jfgLSIIivdYLkA4iRLIHFehP8pYZA5Jp4397fS2e8tdADCqML_5NB1628xGVI6gAzPLfN/s320/2018-09-25+%252820%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_pYEfOseESf9ohxJtaaXqkl_TJWOE1tnYuKh-mBQ7n0vzub6ePDC3dLVSV-_LNAXJCCu2BiEmcUhTivwlfqiWfCi3sc74GAI42YBwjO6SO9eD4CpAK6kwBHQotZZUVLTSVoz2huDSHs12/s1600/2018-09-25+%252821%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_pYEfOseESf9ohxJtaaXqkl_TJWOE1tnYuKh-mBQ7n0vzub6ePDC3dLVSV-_LNAXJCCu2BiEmcUhTivwlfqiWfCi3sc74GAI42YBwjO6SO9eD4CpAK6kwBHQotZZUVLTSVoz2huDSHs12/s320/2018-09-25+%252821%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsNVI578Si1qG1vEW7uC3md_wdkCBq-S6eWXXHMaYDhdHDIQE3-0Js1WBTGGx74fs0pxYct0ZHFJs1Y-QNuE00fuPC4FxZMp1i0MlcqiwTRhJVi5fE13To2n2bKPPvJ9zIvovv3rtbga4B/s1600/2018-09-25+%252822%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsNVI578Si1qG1vEW7uC3md_wdkCBq-S6eWXXHMaYDhdHDIQE3-0Js1WBTGGx74fs0pxYct0ZHFJs1Y-QNuE00fuPC4FxZMp1i0MlcqiwTRhJVi5fE13To2n2bKPPvJ9zIvovv3rtbga4B/s320/2018-09-25+%252822%2529.png" width="320" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-ZcPH-iKSokvG1KYRJHzGGgm-IJEBjbqzEW1lTqPDCw045rciOxXULUr0v3DiauiC_PCEJq_bdJCKhwfl8vIE-tsEwDEp2-UNv-DOf-Ab9taOLfvNGnwN5TcFKBF7_80GuMS_MGYP75LY/s1600/2018-10-01+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-ZcPH-iKSokvG1KYRJHzGGgm-IJEBjbqzEW1lTqPDCw045rciOxXULUr0v3DiauiC_PCEJq_bdJCKhwfl8vIE-tsEwDEp2-UNv-DOf-Ab9taOLfvNGnwN5TcFKBF7_80GuMS_MGYP75LY/s320/2018-10-01+%25281%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg99E8AxQeEnz1-bt4OvV08q_QHq6TzcWkbUBuKM-wPnBTHYrTsRd3oxFAqsw9QytIcwXc2qp_oQr-PtIcYjL5Pg6YwbUZufNgCbxtb8a9iq4GjXsuhuh133AxR_-l-uMrn0hryb0EMqVPP/s1600/2018-10-01+%25282%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg99E8AxQeEnz1-bt4OvV08q_QHq6TzcWkbUBuKM-wPnBTHYrTsRd3oxFAqsw9QytIcwXc2qp_oQr-PtIcYjL5Pg6YwbUZufNgCbxtb8a9iq4GjXsuhuh133AxR_-l-uMrn0hryb0EMqVPP/s320/2018-10-01+%25282%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgo6C3kYs_vGxlvWqKEf_If3M5gk13qopqUmH2HtdRxbuG6qMPQRILXqis_iCGASZq54q7J5Zhhdk1zNHNvSJAjPmYv2iZcVc8OAhmciJqOerWHZXi2ijP9F2iLpR-G0PIbI6irC5Ra3RM/s1600/2018-10-01+%25283%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgo6C3kYs_vGxlvWqKEf_If3M5gk13qopqUmH2HtdRxbuG6qMPQRILXqis_iCGASZq54q7J5Zhhdk1zNHNvSJAjPmYv2iZcVc8OAhmciJqOerWHZXi2ijP9F2iLpR-G0PIbI6irC5Ra3RM/s320/2018-10-01+%25283%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVRsQ4PDFa18Ll03Glx4_0lQsWuf_fhkKmcdMEwKE_k4E2yn6HELzVA4w9BwgcLhiL3VE3IhLyDWhxtzLLenVYAY22vKpIPn3O4BPwiCemVEdyWzOXpdKxq8CXIPgM5DFA94v-bYWMZOOm/s1600/2018-10-01+%25284%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVRsQ4PDFa18Ll03Glx4_0lQsWuf_fhkKmcdMEwKE_k4E2yn6HELzVA4w9BwgcLhiL3VE3IhLyDWhxtzLLenVYAY22vKpIPn3O4BPwiCemVEdyWzOXpdKxq8CXIPgM5DFA94v-bYWMZOOm/s320/2018-10-01+%25284%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-UfLRtck4ZguGzdWKp5x95EvVdhEBxCXZBkc698gw_JsENu5qGXkvYTdzLH4_-y-UGy7JWrVk727JPBS_8GPKjOVbbOxuxdIRA_01IMED7tEeKSegQloJz_Z1G2pi7fxyt9NENwejkHqg/s1600/2018-10-01+%25285%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-UfLRtck4ZguGzdWKp5x95EvVdhEBxCXZBkc698gw_JsENu5qGXkvYTdzLH4_-y-UGy7JWrVk727JPBS_8GPKjOVbbOxuxdIRA_01IMED7tEeKSegQloJz_Z1G2pi7fxyt9NENwejkHqg/s320/2018-10-01+%25285%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_YKTxE4fikNOBp5WmSVG4L7ksdwvujM5IGbQywN6EKflS7ZnUT8Mp9H0JbdHnXeBr1a1GSdxE-89PG85g9kWpl2kBmF1XtakNGlzMC1WBXKjPiSm-3IDGG9j-nEsQApwT7I3-gzTWyVj/s1600/2018-10-01+%25286%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh_YKTxE4fikNOBp5WmSVG4L7ksdwvujM5IGbQywN6EKflS7ZnUT8Mp9H0JbdHnXeBr1a1GSdxE-89PG85g9kWpl2kBmF1XtakNGlzMC1WBXKjPiSm-3IDGG9j-nEsQApwT7I3-gzTWyVj/s320/2018-10-01+%25286%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmOtEUMtjTP7A6eqOdzTWs_7lFYNS_WQwgUCTiQSoHEpU0rKGw9IQzYSgOKQjStMVcMNQcVVq1sj86ItFhWO517mD1Axv1HY7BQPDspK_4E1fk8ajTH3bLSQtxCvOeG3SDlN75TPdazgOg/s1600/2018-10-01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmOtEUMtjTP7A6eqOdzTWs_7lFYNS_WQwgUCTiQSoHEpU0rKGw9IQzYSgOKQjStMVcMNQcVVq1sj86ItFhWO517mD1Axv1HY7BQPDspK_4E1fk8ajTH3bLSQtxCvOeG3SDlN75TPdazgOg/s320/2018-10-01.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYoRSx0VHobm733ZOUuwbvXM4f_YQaQn_0o0JpJUhfxBY36lGzGNkzKshSq6lCl_DpBDI3s062oi51tTQVo75q0A8cFZa7dNfLzOj4pyQ7WeRKm-K43P20g19-8DCcmDqMxBQ-2FMfoSEi/s1600/2018-10-01+%252812%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYoRSx0VHobm733ZOUuwbvXM4f_YQaQn_0o0JpJUhfxBY36lGzGNkzKshSq6lCl_DpBDI3s062oi51tTQVo75q0A8cFZa7dNfLzOj4pyQ7WeRKm-K43P20g19-8DCcmDqMxBQ-2FMfoSEi/s320/2018-10-01+%252812%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbuIfbUGtY3JXbFG6qJopd8GHK5nSdXLYOcANu7jN39Azpv9ucZRNQZlBaDCAIIXoHQhuZsjbbPeYQnEx16mlVgQFs_fIgeka6GyHrE8yz5UQ_wAaJF-sK8lFRGros71bjvHW8U_YCZT7a/s1600/2018-10-02+%25286%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbuIfbUGtY3JXbFG6qJopd8GHK5nSdXLYOcANu7jN39Azpv9ucZRNQZlBaDCAIIXoHQhuZsjbbPeYQnEx16mlVgQFs_fIgeka6GyHrE8yz5UQ_wAaJF-sK8lFRGros71bjvHW8U_YCZT7a/s320/2018-10-02+%25286%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_fxJR4nq34_eRKwHk4MdNGO8Zbtmk0aQ428RBwTWHG7uQFJDCUo3MbYQuhD6YTLG1yIe01f3UrTikPuTokm1iFq4WNKM4dVyRH_FS6iSXCFpZSkmitDzJT3oEFVkck70DJKwiTdr2Y10c/s1600/2018-10-13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_fxJR4nq34_eRKwHk4MdNGO8Zbtmk0aQ428RBwTWHG7uQFJDCUo3MbYQuhD6YTLG1yIe01f3UrTikPuTokm1iFq4WNKM4dVyRH_FS6iSXCFpZSkmitDzJT3oEFVkck70DJKwiTdr2Y10c/s320/2018-10-13.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilR4TZwJ_BhqoUDoiTBP7iqEA_RCQMtuLqB6bZlXXRalF1B5GUW6zHqjeBa7eVKr5mHGDFr2C0FdRoq3ocB7hDEZKhY73dTus099G-cw0o0wpWXeZSGz_3P5WSblQX9FS2FS-aCR3bcOQ2/s1600/2018-10-14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilR4TZwJ_BhqoUDoiTBP7iqEA_RCQMtuLqB6bZlXXRalF1B5GUW6zHqjeBa7eVKr5mHGDFr2C0FdRoq3ocB7hDEZKhY73dTus099G-cw0o0wpWXeZSGz_3P5WSblQX9FS2FS-aCR3bcOQ2/s320/2018-10-14.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibdB5-qdHU899hrHEPyPxucqEpM0lO4Ci7JykIaltXJq1CkoH2lhU0turrN0BQXzw2_v4CszA3XK7uTjYNK5loGtyfEOFv0Q6PMGmLmVqbBeX90frcxXtgWVB77FuOTTlId3sagNeZ-vBN/s1600/2018-10-23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibdB5-qdHU899hrHEPyPxucqEpM0lO4Ci7JykIaltXJq1CkoH2lhU0turrN0BQXzw2_v4CszA3XK7uTjYNK5loGtyfEOFv0Q6PMGmLmVqbBeX90frcxXtgWVB77FuOTTlId3sagNeZ-vBN/s320/2018-10-23.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimFTMNxtlc7eMy9uOtb5rqen-MxGioN3uzL3JJTvGEF7-mHsqIzg5CwcvvkXNcty_u1FAhti-VnXliXA0fWezBI0ss4wnu5ty2smomezxL_3OJa3ufGQfZCf7flbT_5VLUl1UjgrOoEgkg/s1600/2018-10-23+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimFTMNxtlc7eMy9uOtb5rqen-MxGioN3uzL3JJTvGEF7-mHsqIzg5CwcvvkXNcty_u1FAhti-VnXliXA0fWezBI0ss4wnu5ty2smomezxL_3OJa3ufGQfZCf7flbT_5VLUl1UjgrOoEgkg/s320/2018-10-23+%25281%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiveBGfvDEplQBDGKDZtEwA1b7211UFMugvuixh6hbsg0fUgie6toKR78C0Z1hs9BDfUVBZ2ZOdAQOuX4TQuxgjmjvxMs1E7nHBdRz4Gb-I0d8u-lh8TbeOKegTbgC-4Rfyzr1gXsB541Qo/s1600/2018-10-23+%25282%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiveBGfvDEplQBDGKDZtEwA1b7211UFMugvuixh6hbsg0fUgie6toKR78C0Z1hs9BDfUVBZ2ZOdAQOuX4TQuxgjmjvxMs1E7nHBdRz4Gb-I0d8u-lh8TbeOKegTbgC-4Rfyzr1gXsB541Qo/s320/2018-10-23+%25282%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhONiC6nYNw7pZeGwWfkG4UA3mC4mRvb_v94XwuHMdCxCpB_btn8nPKWzlDn42AnS8C5OaR03P3cq9abs3tLtmU0cA28NtHCHkAYRHeQFTLWnzKKY8rx3lurK5ZjYE_L1bmHXJfnGWpng_a/s1600/2018-10-24+%25287%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhONiC6nYNw7pZeGwWfkG4UA3mC4mRvb_v94XwuHMdCxCpB_btn8nPKWzlDn42AnS8C5OaR03P3cq9abs3tLtmU0cA28NtHCHkAYRHeQFTLWnzKKY8rx3lurK5ZjYE_L1bmHXJfnGWpng_a/s320/2018-10-24+%25287%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUjtm11bsaAMnXwDFqDo0YpUersWtPtpIQjsaBM65zAQ9GcRjrcuqioqsF1qBzYLPb5TNaiM6-O4TUxAniRublHrtRNbXGIoxzwPY8NLvKtzvWw7n2nOdORg_MdeO-i5i-jQdLiW2Sa5PC/s1600/2018-10-24+%25285%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUjtm11bsaAMnXwDFqDo0YpUersWtPtpIQjsaBM65zAQ9GcRjrcuqioqsF1qBzYLPb5TNaiM6-O4TUxAniRublHrtRNbXGIoxzwPY8NLvKtzvWw7n2nOdORg_MdeO-i5i-jQdLiW2Sa5PC/s320/2018-10-24+%25285%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivJzxM32kfftar2Ba6bZAAJnbjOq745qF_5h91kftV1MLD3yIJShmrIJrk-YRsPiK7fh_j33BCl2TahxRju25MgqylC0kzGeF6mGgMs5tpoAdEb9Zidfz6P7oskO3YqG0gW_K1olBQgfNH/s1600/2018-10-24+%25286%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivJzxM32kfftar2Ba6bZAAJnbjOq745qF_5h91kftV1MLD3yIJShmrIJrk-YRsPiK7fh_j33BCl2TahxRju25MgqylC0kzGeF6mGgMs5tpoAdEb9Zidfz6P7oskO3YqG0gW_K1olBQgfNH/s320/2018-10-24+%25286%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3xCfRqlWXfemDYKNjPuXUVoLyR8scjBmmS96cphv-aDRJnI6xN_4yA2cQv36bF6BFYqadVI_l7eWeL8vVScdXRF2EXEYCSL_LVApMf5ne-2Zxja8pV48gQ4KyW-TKIANExJUHz9kxeycb/s1600/2018-10-24+%25284%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1600" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3xCfRqlWXfemDYKNjPuXUVoLyR8scjBmmS96cphv-aDRJnI6xN_4yA2cQv36bF6BFYqadVI_l7eWeL8vVScdXRF2EXEYCSL_LVApMf5ne-2Zxja8pV48gQ4KyW-TKIANExJUHz9kxeycb/s320/2018-10-24+%25284%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY5YSSzWy4d2GS97-6flOuUeLP3NUvOj6QqDVetRQfx1_Vy_r-Xv6Mzk9l-Wf8Veu_BauvoEdj9ycYNVUUxoNp1fR9MBl53fAUed78SMOZXyae8NrYq2F2BN0X0KEUClocLY9tKAFGXJPH/s1600/2019-05-09+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="675" data-original-width="1600" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY5YSSzWy4d2GS97-6flOuUeLP3NUvOj6QqDVetRQfx1_Vy_r-Xv6Mzk9l-Wf8Veu_BauvoEdj9ycYNVUUxoNp1fR9MBl53fAUed78SMOZXyae8NrYq2F2BN0X0KEUClocLY9tKAFGXJPH/s320/2019-05-09+%25281%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-79141342684523271792017-04-12T00:43:00.003-07:002017-04-12T00:45:40.917-07:00Скриптинг с использованием С++, как скриптового языка.<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/HVX61pxCEq4/0.jpg" frameborder="0" height="320" src="https://www.youtube.com/embed/HVX61pxCEq4?feature=player_embedded" width="560"></iframe></div>
<br />
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-43265040257744702922017-02-08T07:33:00.001-08:002019-05-24T00:52:49.851-07:00<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="clear: both; text-align: center;">
<span style="color: #0b5394;">
Разработка собственного игрового движка.</span></h2>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/ZyiXeFxlJ-g/0.jpg" frameborder="0" height="320" src="https://www.youtube.com/embed/ZyiXeFxlJ-g?feature=player_embedded" width="560"></iframe><br />
<br />
Начало разработки собственного игрового движка. Постараюсь делиться прогрессом.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjav7He900iCPS9buimDTjwG0snKTBhjdUSODVxbuj-SIuoKnmqlP8PWkIn9Plj3EtLKHpTHIdEnAQUwdobgSLYez9jD2_7uJOgBY7boptnA9utZOCH8YtWfXJXOjSk3q_dr2pVKCFxstqL/s1600/2017-04-18+%25284%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjav7He900iCPS9buimDTjwG0snKTBhjdUSODVxbuj-SIuoKnmqlP8PWkIn9Plj3EtLKHpTHIdEnAQUwdobgSLYez9jD2_7uJOgBY7boptnA9utZOCH8YtWfXJXOjSk3q_dr2pVKCFxstqL/s320/2017-04-18+%25284%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhAR6p8rFOOESRXWHk0pCb0nuuwX2jpTG0Uhw2UjZ1Se5OoVxMNcpbuoOHGs68Xymin4A2TKolqqJzM8KRUfA3mk8GlPeAYj9gtQCNBfeaTQmNJ6v_5VYktTNKgzL9Ok3FISb8siPwk6dS/s1600/2017-04-18+%25282%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhAR6p8rFOOESRXWHk0pCb0nuuwX2jpTG0Uhw2UjZ1Se5OoVxMNcpbuoOHGs68Xymin4A2TKolqqJzM8KRUfA3mk8GlPeAYj9gtQCNBfeaTQmNJ6v_5VYktTNKgzL9Ok3FISb8siPwk6dS/s320/2017-04-18+%25282%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5_sPT1bwYXmFIVCVdudbWOHRHzqF5XjsUXy9EiJXMT4cx5YlYNVzrXCqGilAUVU5vjs8uzedexQztRYj-ji79ieSlf-F0rvLlRAsvlf9tMnuQ9sYgx2vy78vanRS0K05JbDqm_aoZEE78/s1600/2017-04-18+%25283%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5_sPT1bwYXmFIVCVdudbWOHRHzqF5XjsUXy9EiJXMT4cx5YlYNVzrXCqGilAUVU5vjs8uzedexQztRYj-ji79ieSlf-F0rvLlRAsvlf9tMnuQ9sYgx2vy78vanRS0K05JbDqm_aoZEE78/s320/2017-04-18+%25283%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS6cvrToRsXM_7yFXvogE2rwqE31Li8xHXTdcwjt5AlTVaZ2kTG-aFzvmerCZLUhR0lbKfJTKq6aeA0mid_YmBwnqardDW_Tv2n6G0IDZSiGLzqAUs66QxyYx7mKKq9Ra6UB1ynTKJPU5a/s1600/2017-04-18+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgS6cvrToRsXM_7yFXvogE2rwqE31Li8xHXTdcwjt5AlTVaZ2kTG-aFzvmerCZLUhR0lbKfJTKq6aeA0mid_YmBwnqardDW_Tv2n6G0IDZSiGLzqAUs66QxyYx7mKKq9Ra6UB1ynTKJPU5a/s320/2017-04-18+%25281%2529.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfsNIodvUwXqUEUI4IzLPsn_tp6FEpNjl7sSgXt6L314VajVVtIaVuXmBGgRyYNbSwmCHdAnmWrYajBaRuhpdrpWrVOvA_zPUN_yb7ZHTQKCR9cPa2Q3ViSkWaHLn9xirGTHJZXHubSC1V/s1600/2017-04-19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfsNIodvUwXqUEUI4IzLPsn_tp6FEpNjl7sSgXt6L314VajVVtIaVuXmBGgRyYNbSwmCHdAnmWrYajBaRuhpdrpWrVOvA_zPUN_yb7ZHTQKCR9cPa2Q3ViSkWaHLn9xirGTHJZXHubSC1V/s320/2017-04-19.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ-WT7F1hmaRIgNCyIOdYJrLDDDmLsuUtvderZXgkeSQAvaUlyEAua-JZdYQreE5YBjtsNhUhjwgdV5iV0ocdYT8B57GzhS50CCRxg7b1yaxcHSNNvYpJLAto_zMwroNnrNljV3OSJ797W/s1600/2019-05-09+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="675" data-original-width="1600" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ-WT7F1hmaRIgNCyIOdYJrLDDDmLsuUtvderZXgkeSQAvaUlyEAua-JZdYQreE5YBjtsNhUhjwgdV5iV0ocdYT8B57GzhS50CCRxg7b1yaxcHSNNvYpJLAto_zMwroNnrNljV3OSJ797W/s320/2019-05-09+%25281%2529.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1mp3wbcA85w109MSNKNmi99nmq2gW-9tJpsut5t8V3zUMpy_0JVQDi14PGfvKvyPhKvmEVrVofjGqhm0P0FTqIlRb4A93p5Q1BFRFG7vb8ZCHogooWyuRSSEh0vs8DtS8s0O3U3blA9yI/s1600/2019-05-24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="675" data-original-width="1600" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1mp3wbcA85w109MSNKNmi99nmq2gW-9tJpsut5t8V3zUMpy_0JVQDi14PGfvKvyPhKvmEVrVofjGqhm0P0FTqIlRb4A93p5Q1BFRFG7vb8ZCHogooWyuRSSEh0vs8DtS8s0O3U3blA9yI/s320/2019-05-24.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br /></div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-82857166670187658342013-10-10T00:37:00.001-07:002013-10-11T01:06:35.825-07:00Unity3D Android IOS Game.<div dir="ltr" style="text-align: left;" trbidi="on">
Решил попробовать свои силы на Unity3D. В результате написал три небольшие игрушки:<br />
<br />
<h2 style="text-align: center;">
<b><span style="font-size: x-large;">Trip In Labyrinth</span></b></h2>
<a href="https://play.google.com/store/apps/details?id=com.Baybak.soft">https://play.google.com/store/apps/details?id=com.Baybak.soft</a><br />
<a href="https://itunes.apple.com/us/app/trip-in-labyrinth/id661128917?mt=8">https://itunes.apple.com/us/app/trip-in-labyrinth/id661128917?mt=8</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<object class="BLOGGER-youtube-video" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" data-thumbnail-src="http://img.youtube.com/vi/NHY36L_S9D4/0.jpg" height="266" width="320"><param name="movie" value="http://youtube.googleapis.com/v/NHY36L_S9D4&source=uds" /><param name="bgcolor" value="#FFFFFF" /><param name="allowFullScreen" value="true" /><embed width="320" height="266" src="http://youtube.googleapis.com/v/NHY36L_S9D4&source=uds" type="application/x-shockwave-flash" allowfullscreen="true"></embed></object></div>
<br />
<h2 style="text-align: center;">
<span style="font-size: x-large;">RoboWars</span></h2>
<div>
<a href="https://play.google.com/store/apps/details?id=com.Baybaksoft.Robowars">https://play.google.com/store/apps/details?id=com.Baybaksoft.Robowars</a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.youtube.com/embed/vroXUCQ1Wsk?feature=player_embedded' frameborder='0'></iframe></div>
<div>
<br />
<h2 style="text-align: center;">
<span style="font-size: x-large;">Space Defender</span></h2>
</div>
<div>
<a href="https://play.google.com/store/apps/details?id=com.BaybakSoft.Asteroids">https://play.google.com/store/apps/details?id=com.BaybakSoft.Asteroids</a></div>
<div>
<a href="https://itunes.apple.com/us/app/ispace-defender/id663317268?ls=1&mt=8">https://itunes.apple.com/us/app/ispace-defender/id663317268?ls=1&mt=8</a></div>
<div>
<br /></div>
<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=AD6v5dxlPJW_IyY0EbYPXOOB7hJO6v8a22pW7DhsTzrTekb48dsRKoKpDfdp8e8JKShYA0BldNKKSFyjZ20lkiTASQ' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<div>
<span style="font-size: x-large;"><br /></span></div>
</div>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-78661739854808192892012-05-03T05:30:00.003-07:002012-07-21T05:31:34.862-07:00Simply Compute Shader Example<div dir="ltr" style="text-align: left;" trbidi="on">
Одной из главных особенностей DirectX 11 является наличие Compute Shader. У меня появилась идея написать простой пример на эту тему. Прежде всего позвольте мне сказать, что вычислительные шейдеры являются чем-то потрясающим! Они открывают так много возможностей, что сразу теряешься в идеях, где бы их опробовать в действии. В этом примере будет показано, как создать Compute Shader и затем использовать его для запуска потоков, которые просто выведут идентификатор потока в текстуру.<br />
Device device = new Device(DriverType.Hardware, DeviceCreationFlags.Debug, FeatureLevel.Level_11_0); <br />
ComputeShader compute = Helper.LoadComputeShader(device, "SimpleCompute.hlsl", "main"); Texture2D uavTexture;<br />
UnorderedAccessView computeResult = Helper.CreateUnorderedAccessView(device, 1024, 1024, Format.R8G8B8A8_UNorm, out uavTexture); <br />
device.ImmediateContext.ComputeShader.Set(compute);<br />
device.ImmediateContext.ComputeShader.SetUnorderedAccessView(computeResult, 0);device.ImmediateContext.Dispatch(32, 32, 1);<br />
Texture2D.ToFile(device.ImmediateContext, uavTexture, ImageFileFormat.Png, "uav.png");<br />
<br />
Верьте или нет, но это весь код обработчика. <br />
<br />
Вот что именно нужно сделать в коде:<br />
1) Создать графическое устройство DX11, с тем чтобы использовать Compute Shaders 5.0<br />
2) Загрузить/скомпилить код HLSL в объект ComputeShader.<br />
3) Создать объект UnorderedAccesdView 1024 x 1024, который будет использоваться для хранения данных.<br />
4) Передать ComputeShader и UnorderedAccesdView на графическое устройство.<br />
5) Запустить ComputeShader путем вызова метода Dispatch (32 x 32 x 1).<br />
6) Сохранить текстуру на жесткий диск.<br />
<br />
Код HLSL еще проще:<br />
<br />
RWTexture2D<float4> Output;<br />
<br />
[numthreads(32, 32, 1)]<br />
void main( uint3 threadID : SV_DispatchThreadID )<br />
{<br />
Output[threadID.xy] = float4(threadID.xy / 1024.0f, 0, 1);<br />
}<br />
<br />
Как вы можете видеть объект RWTexture2D используется для хранения данных. Шейдер -настроен для запуска 32 x 32 x 1 потоков в группе потоков. Это означает, что процессор запустит 32 x 32 x 1 группу потоков, то есть (32x32) x (32x32) x 1 потоки запустятся по отдельности. Это равняется одному потоку на пиксел в выходных данных UAV. Так в UAV, цвет устанавливается равным идентификатору потока - результат работы этого кода приводится на следующем изображении :<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEPG358wAvcNzy9xU2WOHJhxYE9x-S0HP9rR4_5ZWJdBjpwYb1Ya1Q0g3NUWdDnVdJr_hx7hI66gqbG33mWXBcDw7068cJr4k0hSk9W2LAH4LIcCyPdAyaZNNrl1FK_J7CeSGwJqQT0V_f/s1600/uav.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" mea="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEPG358wAvcNzy9xU2WOHJhxYE9x-S0HP9rR4_5ZWJdBjpwYb1Ya1Q0g3NUWdDnVdJr_hx7hI66gqbG33mWXBcDw7068cJr4k0hSk9W2LAH4LIcCyPdAyaZNNrl1FK_J7CeSGwJqQT0V_f/s320/uav.png" width="320" /></a></div>
<br />
Довольно просто, да? Но не очень интересно. Мы могли бы легко сделать что-то подобное с pixel Shader (хотя нам пришлось бы растрировать полноэкранный квад для этого).<br />
<br />
Мы должны попытаться сделать то, что показывает силу вычислительного шейдера. Что-то, что нельзя было сделать в шейдерах раньше. Как насчет рисования некоторых примитивов, таких как линии или круги? Для рисования линий, давайте использовать алгоритм <a href="http://en.wikipedia.org/wiki/Digital_Differential_Analyzer_(graphics_algorithm)" target="_blank"><span style="color: #e69138;">ЦДА</span></a>. Он переводится на HLSL очень легко. <br />
<br />
void Plot(int x, int y)<br />
{<br />
Output[uint2(x, y)] = float4(0, 0, 1, 1);<br />
}<br />
<br />
void DrawLine(float2 start, float2 end)<br />
{<br />
float dydx = (end.y - start.y) / (end.x - start.x);<br />
float y = start.y;<br />
for (int x = start.x; x <= end.x; x++) <br />
{<br />
Plot(x, round(y)); y = y + dydx; <br />
}<br />
}<br />
<br />
Для рисования кругов давайте использовать <a href="http://en.wikipedia.org/wiki/Midpoint_circle_algorithm"><span style="color: #de7008;">Midpoint Circle</span></a> алгоритм. Для краткости я не буду обьяснять его сейчас. Для этого в моей главной функции CS, я просто добавлю этот код:<br />
<br />
if (threadID.x == 1023 && threadID.y == 1023)<br />
{<br />
DrawLine(float2(0, 0), float2(1024, 1024)); <br />
DrawLine(float2(0, 1023), float2(1023, 0));<br />
DrawCircle(512, 512, 250);<br />
DrawCircle(0, 512, 250);<br />
}<br />
<br />
Результат этого кода приводится на следующем рисунке:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9nFbeXSp5TaQxMvZoL9u2alFgzVJ5keAPmuUOg42zu52UrZnTb67tNXNJwIBVsa4pmPoJ4_gjNoAdpxdMfxqpw1yyIeRWm3fcrAqo_I4iRouiKSshmc2XU-0xcyfFaE-47xwAsswp86nc/s1600/uav2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" mea="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9nFbeXSp5TaQxMvZoL9u2alFgzVJ5keAPmuUOg42zu52UrZnTb67tNXNJwIBVsa4pmPoJ4_gjNoAdpxdMfxqpw1yyIeRWm3fcrAqo_I4iRouiKSshmc2XU-0xcyfFaE-47xwAsswp86nc/s320/uav2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Я подозреваю, что вам, возможно покажется довольно странным, написание шейдера, который рисует примитивы. Но этот пример определенно помогает проиллюстрировать мощь Compute Shader.</div>
<div style="text-align: justify;">
<br />
<a href="http://recreationstudios.blogspot.com/2010/04/simple-compute-shader-example.html" target="_blank"><span style="color: red;">Оригинал статьи...</span></a></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-2049533387191215652012-05-03T02:40:00.002-07:002012-05-03T04:59:28.784-07:00Effects in DirectX 11<div dir="ltr" style="text-align: left;" trbidi="on">
Я никогда не поверю, что Microsoft сделали доброе дело, так усложнив DirectX11. Effects framework - API которое поддерживало загрузку и использование файлов эффектов сгруппированных в HLSL код, рендерринг через passes и techniques – больше не являются неотъемлемой частью D3DX. Вместо этого они предоставили исходный код этой библиотеки, и это означает, что вы можете скомпилировать его самостоятельно!<br />
<br />
Итак сделаем это: Зайдите в DX SDK подпапку "Samples\C++\Effects11", откройте "Effects11_*.sln".<br />
<br />
Чтобы использовать эффекты API в своем проекте вы должны включить этот заголовок: "YOUR_DX_SDK_PATH\Samples\C++\Effects11\Inc\D3dx11effect.h" and link with this lib: "YOUR_DX_SDK_PATH\Samples\C++\Effects11\Debug\D3DX11EffectsD.lib (Debug) or "YOUR_DX_SDK_PATH\Samples\C++\Effects11\Release\D3DX11Effects.lib" (Release), as well as with "d3dcompiler.lib" (в обеих конфигурациях).<br />
<br />
Вот пример того, как загружается эффект из файла. Вы должны сначала скомпилировать исходный код из файла или из памяти в бинарный эффект (ID3D10Blob), а затем создать реальный объект эффекта из него.<br />
<br />
// Compile effect from HLSL file into binary Blob in memory<br />
ID3D10Blob *effectBlob = 0, *errorsBlob = 0;<br />
HRESULT hr = D3DX11CompileFromFile("Effect1.fx", 0, 0, 0, "fx_5_0", 0, 0, 0, &effectBlob, &errorsBlob, 0);<br />
assert(SUCCEEDED(hr) && effectBlob);<br />
if (errorsBlob) errorsBlob->Release();<br />
// Create D3DX11 effect from compiled binary memory block<br />
ID3DX11Effect *g_Effect;<br />
hr = D3DX11CreateEffectFromMemory(effectBlob->GetBufferPointer(), effectBlob->GetBufferSize(), 0, g_Dev, &g_Effect);<br />
assert(SUCCEEDED(hr));<br />
effectBlob->Release();<br />
<br />
Одного эффекта не достаточно. Вам нужно получить объект, который представляет "pass" чтобы использовать его. Если вы получите технику с эффектом (по индексу или по имени), то от нее получите и "pass".<br />
<br />
ID3DX11EffectTechnique *g_EffectTechnique; // No need to be Release()-d.<br />
g_EffectTechnique = g_Effect->GetTechniqueByIndex(0);<br />
assert(g_EffectTechnique && g_EffectTechnique->IsValid());<br />
ID3DX11EffectPass *g_EffectPass; // No need to be Release()-d.<br />
g_EffectPass = g_EffectTechnique->GetPassByIndex(0);<br />
assert(g_EffectPass && g_EffectPass->IsValid());<br />
<br />
Теперь у вас есть этот объект, и вы можете применить настройки этого пасса к deviceContext во время рендеринга:<br />
<br />
g_EffectPass->Apply(0, g_Ctx);<br />
g_Ctx->Draw(3, 0);<br />
<br />
Но все-таки одна проблема остается. В DirectX 11 вам нужно передать указатель на байт-код вместе со скомпилированным шейдером при создании input layout - шаг, который вы, вероятно, не можете пропустить. К счастью, есть способ получить доступ к этому указателю. Он хранится внутри загруженного эффекта. Вам просто нужно пройти через два дескриптора, как здесь:<br />
<br />
D3DX11_PASS_SHADER_DESC effectVsDesc;<br />
g_EffectPass->GetVertexShaderDesc(&effectVsDesc);<br />
D3DX11_EFFECT_SHADER_DESC effectVsDesc2;<br />
effectVsDesc.pShaderVariable->GetShaderDesc(effectVsDesc.ShaderIndex, &effectVsDesc2);<br />
const void *vsCodePtr = effectVsDesc2.pBytecode;<br />
unsigned vsCodeLen = effectVsDesc2.BytecodeLength;<br />
ID3D11InputLayout *g_InputLayout;<br />
D3D11_INPUT_ELEMENT_DESC inputDesc[] = { /* ... */ };<br />
hr = g_Dev->CreateInputLayout( inputDesc, _countof(inputDesc), vsCodePtr, vsCodeLen, &g_InputLayout);<br />
<br />
К счастью, дела обстоят так, что effect framework не добавляет больше функциональности чем HLSL шейдеры поддерживающиеся в D3D11, поэтому вы можете его не использовать. Определение этих методов и passess не столь важно, в конце концов ...</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-81666341671049881892012-04-27T02:19:00.001-07:002012-05-03T03:29:41.538-07:00SSAO - Простой и практический подход.<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: left;">
Глобальное освещение (GI) — термин, используемый в компьютерной графике для вычисления освещения, вызванного неким взаимодействием между ближайшими поверхностями. Довольно часто термин GI используют только при окрашивании поверхности обьектов лучом отраженным от обьектов окружающей среды. Прямое освещение непосредственно от источника света – легко вычисляется в режиме реального времени на современном оборудовании, но мы не можем сказать то же самое о GI потому, что нам нужно обработать информацию о ближайших поверхностях для каждой точки в сцене, а управлять этим процессом еще довольно сложно. Однако есть определенные методы вычислений GI, которыми управлять не так сложно. Когда свет падает на сцену,или отражается от поверхности, то в сцене могут иметь место некоторые точки, которые имеют меньше шансов получить порцию света: уголки, трещиты между объектами, складки, и др. Это приводит к появлению тех областей, которые в результате будут темнее, чем окружающие их обьекты. </div>
<div style="text-align: justify;">
<br /></div>
<h4 style="text-align: left;">
Предпосылки</h4>
<div style="text-align: justify;">
Оригинальная реализация Crytek базируется на буфере глубины и работает примерно следующим образом: для каждого пикселя в буфере глубины опрашиваются несколько точек в 3D пространстве вокруг него, проецируются обратно в пространство экрана для сравнения глубин этих пикселей с глубиной текущего пикселя, с целью понять - выбранные пиксели находится впереди (не загажденный) или за (загражденный) текущим пикселем. </div>
<div style="text-align: left;">
Буфер заграждений создается из усредненных значений дистанций между выборками. Однако у этого метода есть некоторые проблемы (такие, как самозатенение, появление хало), о них я расскажу позднее.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Алгоритм, который я описываю здесь делает все расчеты в 2D, и не использует проекцию. Он использует попиксельную позицию и буфер нормалей, так что если ваш движок использует модель отложенного освещения, то считайте, что пол дела вы уже сделали. Если вы не можете восстановить позицию из буфера глубины, вы можете хранить позицию в буфере позиций с точностью числа с плавающей точкой. Я рекомендую второй способ, так как я не буду обсуждать способы восстановления позиции из буфера глубины в этом посте. В любом случае, для оставшейся части статьи предпологается, что буфер нормалей и буфер позиций у вас уже есть.</div>
<div style="text-align: left;">
Все, что мы сделаем в этой статье - это получим буферы нормалей и позиций, и сгенерируем однокомпонентный попиксельный буффер заграждений. Вы сможете использовать эту информацию как угодно. Обычно она вычитается из окружающего освещения в сцене, но можно также использовать этот буфер в более замысловатых или странных применениях, например если захотите получить не фотореалистичные результаты.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ6cJcbCXXM-LFnnhyphenhyphenDYTJllPpBAEYd8S4a5_hNHidr9h5_9a-UCWYNyAvf4zDpZQE7V-kvDFNlKhgUUB_SQA_ezYTOpA6IV84r9FHbHgIGWaYGIUnNBLSdULYsH4BRDg94nPiRoozQSVx/s1600/ssao.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ6cJcbCXXM-LFnnhyphenhyphenDYTJllPpBAEYd8S4a5_hNHidr9h5_9a-UCWYNyAvf4zDpZQE7V-kvDFNlKhgUUB_SQA_ezYTOpA6IV84r9FHbHgIGWaYGIUnNBLSdULYsH4BRDg94nPiRoozQSVx/s320/ssao.jpg" width="320" /></a></div>
<h4 style="text-align: left;">
Алгоритм</h4>
<div style="text-align: left;">
Имея любой пиксель в сцене, можно вычислить его загражденность, если рассматривать </div>
<div style="text-align: left;">
все соседние пикселы, как маленькие сферы и складывать их совместное влияние на затенение.</div>
<div style="text-align: left;">
Для понятности мы будем работать с точками вместо сфер: occluders - это точки без ориентации, а occludee(пиксель, который принемает затенение) будет иметь пару. Вклад в затенение каждого окклюдера зависит от двух факторов: </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Дистанция "d" до окклюдера</div>
<div style="text-align: left;">
Угол между нормалью "N" и векторм между occluder и occludee "V".</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
С учетом этих двух факторов выводится простая формула для вычисления затенения:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<strong class="bbc">Occlusion = max( 0.0, dot( N, V) ) * ( 1.0 / ( 1.0 + d ) )</strong></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Первое выражение <strong>max( 0.0, dot( N,V ) )</strong>, основывается на интуитивном предположении, что точки над вершиной вносят больший вклад в затенение, чем точки находящиеся левее или правее. Второе выражение <strong>( 1.0 / ( 1.0 + d ) )</strong> смягчает линейность эффекта зафисимую от расстояния. Можно использовать квадратичное затухания или воспользоваться любой другой функцией, это просто дело вкуса. Алгоритм очень прост: выбираем несколько соседей вокруг текущего пиксела и накапливаем их вклад в затенение с использованием формулы выше.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Чтобы выбрать окклюдеры, я использую 4 выборки <strong>(<1,0>, <-1,0>, <0,1>, <0,-1>)</strong> которые повернуты на 45 ° и 90 ° и отражаются в последствии случайным образом используя текстуру случайных нормалей.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Это код шейдеров HLSL для эффекта, который должен применяться к полноэкранному квадрату:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
sampler g_buffer_norm; </div>
<div style="text-align: left;">
sampler g_buffer_pos; </div>
<div style="text-align: left;">
sampler g_random; </div>
<div style="text-align: left;">
float random_size; </div>
<div style="text-align: left;">
float g_sample_rad; </div>
<div style="text-align: left;">
float g_intensity; </div>
<div style="text-align: left;">
float g_scale; </div>
<div style="text-align: left;">
float g_bias; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
struct PS_INPUT </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
float2 uv : TEXCOORD0; </div>
<div style="text-align: left;">
}; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
struct PS_OUTPUT </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
float4 color : COLOR0; </div>
<div style="text-align: left;">
}; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float3 getPosition(in float2 uv) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
return tex2D(g_buffer_pos,uv).xyz; </div>
<div style="text-align: left;">
} </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float3 getNormal(in float2 uv) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
return normalize(tex2D(g_buffer_norm, uv).xyz * 2.0f - 1.0f); </div>
<div style="text-align: left;">
} </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float2 getRandom(in float2 uv) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
return normalize(tex2D(g_random, g_screen_size * uv / random_size).xy * 2.0f - 1.0f); </div>
<div style="text-align: left;">
} </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float doAmbientOcclusion(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
float3 diff = getPosition(tcoord + uv) - p; </div>
<div style="text-align: left;">
const float3 v = normalize(diff); </div>
<div style="text-align: left;">
const float d = length(diff)*g_scale; </div>
<div style="text-align: left;">
return max(0.0,dot(cnorm,v)-g_bias)*(1.0/(1.0+d))*g_intensity; </div>
<div style="text-align: left;">
} </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
PS_OUTPUT main(PS_INPUT i) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
PS_OUTPUT o = (PS_OUTPUT)0; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
o.color.rgb = 1.0f; </div>
<div style="text-align: left;">
const float2 vec[4] = {float2(1,0),float2(-1,0), </div>
<div style="text-align: left;">
float2(0,1),float2(0,-1)}; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float3 p = getPosition(i.uv); </div>
<div style="text-align: left;">
float3 n = getNormal(i.uv); </div>
<div style="text-align: left;">
float2 rand = getRandom(i.uv); </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
float ao = 0.0f; </div>
<div style="text-align: left;">
float rad = g_sample_rad/p.z; </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
//**SSAO Calculation**// </div>
<div style="text-align: left;">
int iterations = 4; </div>
<div style="text-align: left;">
for (int j = 0; j < iterations; ++j) </div>
<div style="text-align: left;">
{ </div>
<div style="text-align: left;">
float2 coord1 = reflect(vec[j],rand)*rad; </div>
<div style="text-align: left;">
float2 coord2 = float2(coord1.x*0.707 - coord1.y*0.707, </div>
<div style="text-align: left;">
coord1.x*0.707 + coord1.y*0.707); </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
ao += doAmbientOcclusion(i.uv,coord1*0.25, p, n); </div>
<div style="text-align: left;">
ao += doAmbientOcclusion(i.uv,coord2*0.5, p, n); </div>
<div style="text-align: left;">
ao += doAmbientOcclusion(i.uv,coord1*0.75, p, n); </div>
<div style="text-align: left;">
ao += doAmbientOcclusion(i.uv,coord2, p, n); </div>
<div style="text-align: left;">
} </div>
<div style="text-align: left;">
ao/=(float)iterations*4.0; </div>
<div style="text-align: left;">
//**END**// </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
//Do stuff here with your occlusion value “ao”: modulate ambient lighting, write it to a buffer for later //use, etc. </div>
<div style="text-align: left;">
return o; </div>
<div style="text-align: left;">
}</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Эта техника очень напоминает технику описанную в "Hardware Accelerated Ambient Occlusion Techniques on GPUs". Основные различия в структуре выборки и функции AO. Он также может рассматриваться как Image-Space версия "Hardware Accelerated Ambient Occlusion Techniques on GPUs". Некоторые пояснения по коду:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Радиус делится на p.z, для изменения масштаба в зависимости от расстояния до камеры. Если пропустить этот раздел, все точки на экране будут использовать же радиус выборки, и результат не будет учитывать перспективу.</div>
<div style="text-align: left;">
В цикле for, </div>
<div style="text-align: left;">
coord1 это первоначальная выборка координат на 90 °. </div>
<div style="text-align: left;">
coord2 является те же координаты, повернутые на 45 °.</div>
<div style="text-align: left;">
<br /></div>
<h4 style="text-align: left;">
Результаты</h4>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcmbap8CEV5aiyiuTDHH5QtC3a4eOXGGXKLyRA0UGDH1kO8GZ8a7hh-2xEYLrqgwqdvSxwvPc_RFXKppik_K6gEuHhtCJZ2KKZOs1LVOaVy6LTMhuJXePwkeGvuJqHMa0Hh-RRx_7wtQZb/s1600/ssao2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="173" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcmbap8CEV5aiyiuTDHH5QtC3a4eOXGGXKLyRA0UGDH1kO8GZ8a7hh-2xEYLrqgwqdvSxwvPc_RFXKppik_K6gEuHhtCJZ2KKZOs1LVOaVy6LTMhuJXePwkeGvuJqHMa0Hh-RRx_7wtQZb/s320/ssao2.jpg" width="320" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Как видите, код не сложный и не большой. Результаты не самозатеняются и хало почти нет. </div>
<div style="text-align: left;">
Это две основные проблемы всех алгоритмов SSAO, которые используют только буфер глубины в качестве исходных данных, вы можете увидеть их в этих изображениях:</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4E4IaxfIhleVEQS-ixhs7XHJhfNj7T1kZ8iFwgkNLwekqbZMNgpejWr3pPoXJhfuKEwSk2LTdhob0aNh54BiUsuvQ_r3WbNq2CR1KH4v7RfnWbBHCVde7-WE5Y-Bcpz5netmkRhSMQ5gs/s1600/ssao3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4E4IaxfIhleVEQS-ixhs7XHJhfNj7T1kZ8iFwgkNLwekqbZMNgpejWr3pPoXJhfuKEwSk2LTdhob0aNh54BiUsuvQ_r3WbNq2CR1KH4v7RfnWbBHCVde7-WE5Y-Bcpz5netmkRhSMQ5gs/s1600/ssao3.jpg" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB9BiVNZAb6PgmD5-ZbN0OucoRgRztSTQpqVxp7Ik4JY2kyf9Tw9UCR76cQCmw50F4GTL9AfVSvMmIz9goZYnexTU0K1H4ngrBDkgJjFffCuFNjmdNCvvtWvIKhHxUY2UrbLuiIOktxcbe/s1600/ssao4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB9BiVNZAb6PgmD5-ZbN0OucoRgRztSTQpqVxp7Ik4JY2kyf9Tw9UCR76cQCmw50F4GTL9AfVSvMmIz9goZYnexTU0K1H4ngrBDkgJjFffCuFNjmdNCvvtWvIKhHxUY2UrbLuiIOktxcbe/s1600/ssao4.jpg" /></a></div>
Self-occlusion появляется, потому что традиционные алгоритмы формирования выборок внутри области вокруг каждого пикселя, поэтому на плоских поверхностях которые не «occluded», по меньшей мере, половина из выборок помечены как «occluded». Из за этого получается сероватый цвет для общей окклюзии. Белое хало вокруг объектов возникает, потому что в этих областях self-occlusion не выполняется. Таким образом избавление от self-occlusion на самом деле хорошо помогает избавиться от хало. При перемещении камеры результат окклюзии изменяется, если следовать этому методу. Если вы склоняетесь к качеству вместо скорости, можно использовать два или несколько проходов алгоритма (повторяющиеся цикл for в коде) с различными радиусами, один для захвата более глобальных AO, а другие для детализации небольших трещин.<br />
<h4 style="text-align: left;">
<br />Продолжаем</h4>
<div style="text-align: left;">
Я описал упрощенную реализацию SSAO, которая очень хорошо подходит для игр.</div>
<div style="text-align: left;">
Однако ее легко улучшить если принимать во внимание скрытые от камеры поверхности, для получения более высокого качества. Это потребует три буфера: две позиции/глубина буферов (front/back faces) и один буфер нормалей. Но вы можете сделать это только с двумя буферами: хранить глубину front/back faces в красном и зеленом каналах буфера, соответственно, а затем восстановить позицию каждого из них. Таким образом у вас будет один буфер для позиций и второй буфер для нормалей. Таковы результаты при использовании 16 выборок для каждой позиции буфера:</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLJlIH5X3l82DkwvRkLFW9PHlA1m24IJhBeL7Gvy0AwxrtqMoM6qu_rr3CsZwVvQ5YEzzPwI4qFJ4HznMnpikUfnbGje463sQyLzJXFUmQEN3r66B71D3GMsgPZJLZ9skHkKsdQCyjjuC0/s1600/ssao4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="145" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLJlIH5X3l82DkwvRkLFW9PHlA1m24IJhBeL7Gvy0AwxrtqMoM6qu_rr3CsZwVvQ5YEzzPwI4qFJ4HznMnpikUfnbGje463sQyLzJXFUmQEN3r66B71D3GMsgPZJLZ9skHkKsdQCyjjuC0/s320/ssao4.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: center;">
<em>left: front faces occlusion, right: back faces occlusion</em> </div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
Что бы применить это, нужно просто добавить вызова функции doAmbientOcclusion()</div>
<div style="text-align: center;">
</div>
<div style="text-align: left;">
Это дополнительный код, который необходимо добавить:</div>
<div style="text-align: left;">
внутри цикла for, добавьте эти вызовы:</div>
<div style="text-align: left;">
</div>
<div style="text-align: left;">
ao += doAmbientOcclusionBack(i.uv,coord1*(0.25+0.125), p, n); <br />
ao += doAmbientOcclusionBack(i.uv,coord2*(0.5+0.125), p, n); <br />
ao += doAmbientOcclusionBack(i.uv,coord1*(0.75+0.125), p, n); <br />
ao += doAmbientOcclusionBack(i.uv,coord2*1.125, p, n);</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Добавьте эти две функции шейдер: </div>
<div style="text-align: left;">
<br />
float3 getPositionBack(in float2 uv) <br />
{ <br />
return tex2D(g_buffer_posb,uv).xyz; <br />
} </div>
<div style="text-align: left;">
<br />
float doAmbientOcclusionBack(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm) <br />
{ <br />
float3 diff = getPositionBack(tcoord + uv) - p; <br />
const float3 v = normalize(diff); <br />
const float d = length(diff)*g_scale; <br />
return max(0.0,dot(cnorm,v)-g_bias)*(1.0/(1.0+d)); <br />
}</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Добавьте семлер с именем «g_buffer_posb», содержащий позицию backFaces (для его создания вам нужно рисовать сцену с передних граней). Еще небольшое изменение, которое может помочь, на этот раз улучшить скорость, является добавление простой системы LOD (уровень детализации) в наш шейдер. Измените фиксированное количества итераций в соответствии с:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
int iterations = lerp(6.0,2.0,p.z/g_far_clip); </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Переменная «g_far_clip» - это расстояние до дальней плоскости отсечения, которое должно быть передано в шейдер. Теперь количество итераций, применяемое к каждой точке зависит от расстояния до камеры. Таким образом для дальних точек выполнется меньше выборок, это позволяет повысить производительность без заметного ухучшения качества. Я не использовал это при измерении производительности (см. ниже), но все-же:</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho0yP2yF_Q5GZEeo-EBDiMeBXqbdq_ysL1-PG3NRpxTM0sbgUuAIwQxSLTZm1y-L3DgPfHogjjtInUvadaS-gYhigiyH_mxcOnpb6Ipz8NB5tBErLMdJ2H8k2JLTfxw808edWHPED1DnU6/s1600/ssao3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho0yP2yF_Q5GZEeo-EBDiMeBXqbdq_ysL1-PG3NRpxTM0sbgUuAIwQxSLTZm1y-L3DgPfHogjjtInUvadaS-gYhigiyH_mxcOnpb6Ipz8NB5tBErLMdJ2H8k2JLTfxw808edWHPED1DnU6/s320/ssao3.jpg" width="300" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaYcIxrqNoPHE2CaUHkfuuXVN1ZjqfrvXXzKgzfzDwrAnUDH36Eq6TPaVofeIFg-e7P3iOTpM_rQkoxcf0IYc3pRvuqYPNiFp9sC6ZCfPojTndAPGniLFKHXLwjbr7HfmD3u1UF2Nv0bVX/s1600/ssao4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaYcIxrqNoPHE2CaUHkfuuXVN1ZjqfrvXXzKgzfzDwrAnUDH36Eq6TPaVofeIFg-e7P3iOTpM_rQkoxcf0IYc3pRvuqYPNiFp9sC6ZCfPojTndAPGniLFKHXLwjbr7HfmD3u1UF2Nv0bVX/s320/ssao4.jpg" width="280" /></a></div>
<br />
Так-же полезно рассмотреть, как этот метод сравнивается с трассировкой лучей AO. Цель этого сравнения заключается в том, чтобы увидеть, будет ли этот метод приравниваться к реальному AO при использовании достаточного количества выборок.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhorwA89O3mCJfUhXrCBYAWYyjGTqToQIg4DVykGhDUZGYabNARpl_W2ps4eb9aA_7dvkWmBXoGBf-6ovK7eALh66vh8iXkiclgLheVDhq_nGVWyS9FMWcED7mxlTqv3amEhJHm6rX_ON1x/s1600/ssao4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhorwA89O3mCJfUhXrCBYAWYyjGTqToQIg4DVykGhDUZGYabNARpl_W2ps4eb9aA_7dvkWmBXoGBf-6ovK7eALh66vh8iXkiclgLheVDhq_nGVWyS9FMWcED7mxlTqv3amEhJHm6rX_ON1x/s320/ssao4.jpg" width="247" /></a></div>
<div style="text-align: center;">
<em>Left: the SSAO presented here, 48 samples per pixel (32 for front faces and 16 for back faces), no blur. Right: Ray traced AO in Mental Ray. 32 samples, spread = 2.0, maxdistance = 1.0; falloff = 1.0.</em> </div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
И совет на последок: не планируйте подключить шейдер в свой конвейер и автоматически получить реалистичный результат. Несмотря на эту реализацию, что-бы получить соотношение хорошей производительности и качества SSAO, вы должны настроить его как можно тщательнее для своих нужд и добиться максимальной производительности.</div>
<div style="text-align: left;">
Добавление или удаление выборок и размытия, изменяя интенсивность и т.д. Вам следует также понять, подходит ли такой алгоритм SSAO для вас. Если у вас есть много динамических объектов в сцене, то возможно нет необходимости в SSAO вообще, может быть использование LightMap-ов достаточно для ваших целей, так- как они могут обеспечить лучшее качество для статических сцен. Я надеюсь, что вы извлекли пользу от этого метода. Весь код, представленный в этой статье доступен по <a href="http://www.opensource.org/licenses/mit-license.php" target="_blank"><span style="color: red;">mit-license</span></a>.</div>
<div style="text-align: left;">
Оригинал статьи можно найти здесь <a href="http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-simple-and-practical-approach-to-ssao-r2753" target="_blank"><span style="color: red;">a-simple-and-practical-approach-to-ssao</span></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<br /></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-91336202472017780532012-04-26T03:21:00.002-07:002012-04-27T23:24:41.423-07:00DBF TO ANY<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
<strong>DBF TO ANY</strong> - это удобная программа-конвертер данных формата (.dbf) в данные MSSQL, PostgreSQL, Oracle и MySQL. Продукт ориентирован на профессионалов работающих с базами данных.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGgTaBc8-3ZlmUflVIb8ShPtQkgLoWlUkmzzJhuSJDQxtjRXStZ8gfmMPbzIMojgZW6NEGDqK_XN0BDCOrIJHLtA98CytQL9Grz06hy4SZXyu1U3ZW99WmUkCEOaIV8WACjiyX_B_4GDIr/s1600/DBF1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGgTaBc8-3ZlmUflVIb8ShPtQkgLoWlUkmzzJhuSJDQxtjRXStZ8gfmMPbzIMojgZW6NEGDqK_XN0BDCOrIJHLtA98CytQL9Grz06hy4SZXyu1U3ZW99WmUkCEOaIV8WACjiyX_B_4GDIr/s320/DBF1.gif" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRAGyRGmAqLXqxCo_wF6uqt4fMXD_GZGcGPXG5YkLfhqjdhMlYxocQvqK3C_J9V6SXzPVklp3keUPkGurfVNHOHYUVudYJmLh7gJu7_toAJcw_cvGzbGMPIWxekd2-NhJP91UwcMREhPj/s1600/Untitled-2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRAGyRGmAqLXqxCo_wF6uqt4fMXD_GZGcGPXG5YkLfhqjdhMlYxocQvqK3C_J9V6SXzPVklp3keUPkGurfVNHOHYUVudYJmLh7gJu7_toAJcw_cvGzbGMPIWxekd2-NhJP91UwcMREhPj/s320/Untitled-2.gif" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEw0opys4pf3vSARbq3C7J58mfsX8xd3ThPAcVlsT7i3WfLhTUZyNMHx0KD9FNzepXbuS6hJ2Bg7OoWex5NY9zUeFuilQp8PD3djoQNSfekDAl5sm9Z0Yu8nQ8GbB2NUoFidNOJ9XwZqyw/s1600/Untitled-1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="215" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEw0opys4pf3vSARbq3C7J58mfsX8xd3ThPAcVlsT7i3WfLhTUZyNMHx0KD9FNzepXbuS6hJ2Bg7OoWex5NY9zUeFuilQp8PD3djoQNSfekDAl5sm9Z0Yu8nQ8GbB2NUoFidNOJ9XwZqyw/s320/Untitled-1.gif" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div align="left">
<a href="http://programmingwithnoproblem.blogspot.com/2012/04/migrating-from-visual-fox-pro-to-sql.html" target="_blank">Подробнее...</a></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-87568312938995606162012-04-24T23:48:00.002-07:002012-04-25T00:18:58.601-07:00[PARADIGMA] - Engine<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
Это графический движок, который сравнительно недавно был запущен в разработку. Его особенностью является то, что он использует Microsoft XNA. Имеется две системы рендерринга, Forward и Deffered. Так-же имеется собственный редактор уровней, набор вспомагательных утилит и конверторов, которые будут полезны любому игроделу. </div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5w9Tr6habUaBMxLSmCqPKBNoBk25OMtIX0sjKqt-61MAsqj4lpjdX5zkTNOztKCrY9om02NET4GDeIdoCWvawM4Yy83hhKmdX1I3PzPMxCCyiZP8XDGLQhK4OzkPwGKbIpxX-PnVQzEMa/s1600/Paradigma1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5w9Tr6habUaBMxLSmCqPKBNoBk25OMtIX0sjKqt-61MAsqj4lpjdX5zkTNOztKCrY9om02NET4GDeIdoCWvawM4Yy83hhKmdX1I3PzPMxCCyiZP8XDGLQhK4OzkPwGKbIpxX-PnVQzEMa/s320/Paradigma1.gif" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: justify;">
Landscape + instancing + normal + specular </div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi90kp8kzL47ioc1nmR95k2Rcv_Fygfizg42els3Mr8eJUoPawwWxRW4Vjs1yvWn9aNh60oF_uqwo4yhnhGjGN8lzddHbkT57s5wJRnOUlOtqocqlU4d2StjhyphenhyphenOldYRyg9ckovqkoBYyaI4/s1600/Paradigma2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="177" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi90kp8kzL47ioc1nmR95k2Rcv_Fygfizg42els3Mr8eJUoPawwWxRW4Vjs1yvWn9aNh60oF_uqwo4yhnhGjGN8lzddHbkT57s5wJRnOUlOtqocqlU4d2StjhyphenhyphenOldYRyg9ckovqkoBYyaI4/s320/Paradigma2.gif" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Поддержка бльшого числа источников света (Deferred Shading)</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXbnCePucz_rGifDsACzTlVw98mbrnwfebZYTw8c_4S4c1xU0KmUVe2kKeat5sYPS4r6W7LmDJWksXFD7r9zlADUKwAX0w5Jq3p6Uh-emQ7zyEy8yW0kMrj8TSZ5FsgazuHlHdFpLT2vQh/s1600/Paradigma3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="177" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXbnCePucz_rGifDsACzTlVw98mbrnwfebZYTw8c_4S4c1xU0KmUVe2kKeat5sYPS4r6W7LmDJWksXFD7r9zlADUKwAX0w5Jq3p6Uh-emQ7zyEy8yW0kMrj8TSZ5FsgazuHlHdFpLT2vQh/s320/Paradigma3.gif" width="320" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Редактор уровней</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4b7wzRs4pZThvbRrb3t_z9SOsLFhjyLUhkNF7FIPsFli4l661m395WugArUmdexAfj9Yn0nRQ2P7Ut-MnRtCPMjBDi6h-cD8iLaM6yE5fSMWa6ssSa3vDnNkPwTRj1AFcXhPAGqJ_VWvY/s1600/Paradigma4.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4b7wzRs4pZThvbRrb3t_z9SOsLFhjyLUhkNF7FIPsFli4l661m395WugArUmdexAfj9Yn0nRQ2P7Ut-MnRtCPMjBDi6h-cD8iLaM6yE5fSMWa6ssSa3vDnNkPwTRj1AFcXhPAGqJ_VWvY/s320/Paradigma4.gif" width="320" /></a></div>
<div style="text-align: left;">
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAvx0E7atF1SXmLLrIALpcMgbjEKkKrvxg2B8JIPRBzvvnIG4PC6sUPZRRV5UsWI-bCv3LH1dA2Emug0sBeNQ854LbllU8LwjzeZ4sAPaD9_z3Hfp2BeF3hvhdlQSNrhhli04vAIs1GCje/s1600/Paradigma5.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" oda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAvx0E7atF1SXmLLrIALpcMgbjEKkKrvxg2B8JIPRBzvvnIG4PC6sUPZRRV5UsWI-bCv3LH1dA2Emug0sBeNQ854LbllU8LwjzeZ4sAPaD9_z3Hfp2BeF3hvhdlQSNrhhli04vAIs1GCje/s320/Paradigma5.gif" width="320" /></a></div>
<br />
</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-11134579504872216002012-04-20T04:06:00.001-07:002014-07-10T10:57:45.909-07:00SGS - Synthetic Engine.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-size: x-large;">SGS Project.</span></h2>
<br />
<div style="text-align: justify;">
С недавних пор началась разработка проекта SGS. Рабочее название проекта "Synthetic Engine". Вообще SGS - это аббревиатура расшифровующаяся как <span style="color: red;">S</span>ynthetic <span style="color: red;">G</span>raphics <span style="color: red;">S</span>ystem, или же <span style="color: red;">S</span>ynthetic <span style="color: red;">G</span>ame <span style="color: red;">S</span>tudio. В основе программного комплекса лежит Microsoft DirectX11 API. В силу того, что проект создавался одним человеком, да еще на чистом энтузиазме, процесс разработки продвигается не очень быстро, однако кое - какие результаты уже получены. В этой статье я буду стараться периодически освещать состояние проекта, делиться получеными результатами. Приглашаются так-же все желающие поучавствовать в обсуждении вопросов связяных с творческим процессом создания графического движка. И так, на сегодняшний день уже реализовано не мало. Архитектура программы изначально затачивается для реализации в дальнейшем трех систем рендерринга, Forward, Deffered, Raytracing. Физика - Nvidia PhysX библиотеки. В качестве неба было принято решение пока использовать SkyBox, ввиду его относительной простоты и понятности.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPYlm9TznyqkkUOvntQR-iSMFsDFdfvMpfO4aZJZlWETAlIaTuftFqHlAgJQNPUvLb5PmOPdkANdv7Rj8ULQe_jKAUc8ORxkMLdoNR7LdrMiU5He5O9Qb3OM-ATcA2hxcZQAWIVtVaX2eP/s1600/screen1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPYlm9TznyqkkUOvntQR-iSMFsDFdfvMpfO4aZJZlWETAlIaTuftFqHlAgJQNPUvLb5PmOPdkANdv7Rj8ULQe_jKAUc8ORxkMLdoNR7LdrMiU5He5O9Qb3OM-ATcA2hxcZQAWIVtVaX2eP/s320/screen1.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Реализована система материалов, позволяющая создавать базовые материалы такие как Lambert:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBoQ32si6MlqaKDy9YA1mzcLQ3V_mksAxuI9d1VFcuOoXQ-8CYmC8sOgTfO4P6Ku1Oa4RY8TslcBUFMl_QunGGLkHMwWfEuCXLIMiR93_p4ZFxNksWf4HXEqrPdg4J_kBUb88gU1n9jMMz/s1600/screen2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBoQ32si6MlqaKDy9YA1mzcLQ3V_mksAxuI9d1VFcuOoXQ-8CYmC8sOgTfO4P6Ku1Oa4RY8TslcBUFMl_QunGGLkHMwWfEuCXLIMiR93_p4ZFxNksWf4HXEqrPdg4J_kBUb88gU1n9jMMz/s320/screen2.gif" height="179" qda="true" width="320" /></a></div>
Blinn + Phong: <br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtHWu62oTz0waaPT4Hor4zLcfHj_rmp2vIp7vq5YuVuIydZp7o5S9HiDnDfPu6gUWrRAvKlslsPGibXq-h1FDIIye87N5Q_DSGP2FCyGGVuM9WagVkpVLfDzrPvu2AF4y8uBWVxYRFM-3A/s1600/screen3.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtHWu62oTz0waaPT4Hor4zLcfHj_rmp2vIp7vq5YuVuIydZp7o5S9HiDnDfPu6gUWrRAvKlslsPGibXq-h1FDIIye87N5Q_DSGP2FCyGGVuM9WagVkpVLfDzrPvu2AF4y8uBWVxYRFM-3A/s320/screen3.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Normal + Specular + Masked <br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZYP_5rE0l8nQDaBpvIqO9_kjmLZM-lHPu5VxcF9NVQYwI_kOQ5GipDrlcCxMVmh83NLyxHIW169wcRj7iZlMlpE5bzcankJTsnJkEoMKCaHiK8d63LeL1yMqarnGD9jxB5iRQ24zvYb7Q/s1600/screen4.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZYP_5rE0l8nQDaBpvIqO9_kjmLZM-lHPu5VxcF9NVQYwI_kOQ5GipDrlcCxMVmh83NLyxHIW169wcRj7iZlMlpE5bzcankJTsnJkEoMKCaHiK8d63LeL1yMqarnGD9jxB5iRQ24zvYb7Q/s320/screen4.gif" height="179" qda="true" width="320" /></a></div>
<br />
Metall<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPowZ6FUu3oa1wvgOFJGWpuqqi-Z2mTBUW9BwmXRRxpvLU34eySJEFj3Vz7NlR-Ml022GdNco9PfO8sOUiAn7ZUUbs5h8dcWe8yeI2ItapjUkoF-7j4WauyRUzyTC8lAgg69QgQ-1yQlpD/s1600/screen5.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPowZ6FUu3oa1wvgOFJGWpuqqi-Z2mTBUW9BwmXRRxpvLU34eySJEFj3Vz7NlR-Ml022GdNco9PfO8sOUiAn7ZUUbs5h8dcWe8yeI2ItapjUkoF-7j4WauyRUzyTC8lAgg69QgQ-1yQlpD/s320/screen5.gif" height="179" qda="true" width="320" /></a></div>
<br />
Metall + Normal<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8XVFbHnbQqoMAef9i0eM3GXhtn_ntiHRlUIN13WS0tAqK449T9U6WyrrHIyeY60sAr3lqdmKChRd048FAAUWXxvHIfeMQq91WQlwfABae4i64ykCDLHN2LzizaOd9liaibmFsLfuyGpdA/s1600/screen6.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8XVFbHnbQqoMAef9i0eM3GXhtn_ntiHRlUIN13WS0tAqK449T9U6WyrrHIyeY60sAr3lqdmKChRd048FAAUWXxvHIfeMQq91WQlwfABae4i64ykCDLHN2LzizaOd9liaibmFsLfuyGpdA/s320/screen6.gif" height="179" qda="true" width="320" /></a></div>
<br />
Self Shadowed Parallax<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO0RfUkKWBHGgw4tMdcT-k-ibXs9gnL0jpIbqhy55aschXWvqOPcSxcYNvAUTflNIm1uNq4WQQdcd0LtaBgWTw39573CHlMZVYD13VxM-HuSpv4nRSDYBbaon_OjoJ2Asfvk2FopDY69Zh/s1600/screen7.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO0RfUkKWBHGgw4tMdcT-k-ibXs9gnL0jpIbqhy55aschXWvqOPcSxcYNvAUTflNIm1uNq4WQQdcd0LtaBgWTw39573CHlMZVYD13VxM-HuSpv4nRSDYBbaon_OjoJ2Asfvk2FopDY69Zh/s320/screen7.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Рендерринг текста и Terrain generator.<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh73uUkiVVVlUn1R43OWjegZIOBfseKFlLZHLvXMpEzD-BBAzhFg9thfCPJNo2c3bhR8jTDsZr-ryHltbXhE-ORhBBL4Gv2_SxKW59n2Th0GrMqHOblwFnrqa_XireVyNDiPjJyVg04c6rw/s1600/screen8.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh73uUkiVVVlUn1R43OWjegZIOBfseKFlLZHLvXMpEzD-BBAzhFg9thfCPJNo2c3bhR8jTDsZr-ryHltbXhE-ORhBBL4Gv2_SxKW59n2Th0GrMqHOblwFnrqa_XireVyNDiPjJyVg04c6rw/s320/screen8.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Недавно были добавлены Parallel Split Shadow Maps, основаные на алгоритме описаном разработчиками компьютерной игры Battlefield3.<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4dG84QUT5ORZFq9nU6j3yXY_-1p3Hjhi8tKVBx4zLO2Sq8Emk5a9e46wkM8WFNrhHBfF-mI9IZ28laKO1CJmHRsmxd_jYJEpyDR-hO6tg7rNt1-mjsV2WshbfQpe2DqjfvhXdpNErbnsL/s1600/screen9.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4dG84QUT5ORZFq9nU6j3yXY_-1p3Hjhi8tKVBx4zLO2Sq8Emk5a9e46wkM8WFNrhHBfF-mI9IZ28laKO1CJmHRsmxd_jYJEpyDR-hO6tg7rNt1-mjsV2WshbfQpe2DqjfvhXdpNErbnsL/s320/screen9.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Так-же была разработана система постпроцессов. Пока реализовано следующее:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Bloom:</div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC4herWFPDnuZBBQUn5f3V2y24IhLVKZGwopl6FL4NTXy0RAZy5M5eoAvjcH2jyj5bPaCFZK0TcRbxD78cddC60uhSWyHUP_S7Temk1EKvJZXQ8MVfkK5GT5TVQSaERhaXSc2tjjW-IBbz/s1600/screen10.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiC4herWFPDnuZBBQUn5f3V2y24IhLVKZGwopl6FL4NTXy0RAZy5M5eoAvjcH2jyj5bPaCFZK0TcRbxD78cddC60uhSWyHUP_S7Temk1EKvJZXQ8MVfkK5GT5TVQSaERhaXSc2tjjW-IBbz/s320/screen10.gif" height="179" qda="true" width="320" /></a></div>
<br />
DOF:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgW3l4Pu87S5WTe0PLA2zfI3sbKqSvu2cEqp_0fxssa29W8sQ-mSRi-F9oqlX_j3zxfNXkK-Wa1waI3-X2tHY8oqJuWbhlEuIlckpXQ0zQTTyzUyxzR7DLKBpjOCQfYvSRWVRKZLafvASZQ/s1600/screen11.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgW3l4Pu87S5WTe0PLA2zfI3sbKqSvu2cEqp_0fxssa29W8sQ-mSRi-F9oqlX_j3zxfNXkK-Wa1waI3-X2tHY8oqJuWbhlEuIlckpXQ0zQTTyzUyxzR7DLKBpjOCQfYvSRWVRKZLafvASZQ/s320/screen11.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
SSAO:<br />
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYuX7kH5KfWOCTq6yutELuceBC6rZw7R0vrgVKX4PwMfEO2uBORmOB5pYrPLR27l2488wHKPQZPz-ifL3GsCOlh5Dowhvkf16LuC9HHoO3QreN6feoC3-kKuXffW-WV_FLPOjNdx9WPPoC/s1600/screen12.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYuX7kH5KfWOCTq6yutELuceBC6rZw7R0vrgVKX4PwMfEO2uBORmOB5pYrPLR27l2488wHKPQZPz-ifL3GsCOlh5Dowhvkf16LuC9HHoO3QreN6feoC3-kKuXffW-WV_FLPOjNdx9WPPoC/s320/screen12.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVnsuGneebupi65nFKJiwKAqHklJr8NOlitSVL06BSRgIKI97vk3Mxf6ixp-2ypnb5IE16jVKaTJLT5S_IJnOFa9yA5MH4m9W0YMvZRJm5mCGZQGUcx95pyg81sC4rvNo_xowBzFK5L4QK/s1600/screen18.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVnsuGneebupi65nFKJiwKAqHklJr8NOlitSVL06BSRgIKI97vk3Mxf6ixp-2ypnb5IE16jVKaTJLT5S_IJnOFa9yA5MH4m9W0YMvZRJm5mCGZQGUcx95pyg81sC4rvNo_xowBzFK5L4QK/s320/screen18.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh065cFFSpxGK2pVedZPZDvI1yhgbbpcBVop6m9FC1eTPHATZ4i-Ym18tU_q59WFDdJNWCkE-U0dAjFz_T8NPV3hwmWRsCtMq9o1grQfLHrUzX34jL_G5AY-bUbBd_EjFC81ABYxxltDtns/s1600/ceh---normals.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh065cFFSpxGK2pVedZPZDvI1yhgbbpcBVop6m9FC1eTPHATZ4i-Ym18tU_q59WFDdJNWCkE-U0dAjFz_T8NPV3hwmWRsCtMq9o1grQfLHrUzX34jL_G5AY-bUbBd_EjFC81ABYxxltDtns/s320/ceh---normals.gif" height="179" oda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: justify;">
<br /></div>
Хотя SSAO пока еще требует хорошей доработки, это планируется в ближайшее время.<br />
Так-же планируется написать свой редактор уровней, но он пока еще находится в зародыше.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbtThQ6lTf5Txx0b-ZR8XtAIzADLbLMimfXOUtWbFTnY-f83ozQpKDsSs9xwlSBsWfhgbxS5clg-iXF9q7Kq29AkBtpIVPQWwr9aCU6BbjhyYWkxjc6LVLesm1_hL55HWJLDG2OMIMFrTa/s1600/screen13.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbtThQ6lTf5Txx0b-ZR8XtAIzADLbLMimfXOUtWbFTnY-f83ozQpKDsSs9xwlSBsWfhgbxS5clg-iXF9q7Kq29AkBtpIVPQWwr9aCU6BbjhyYWkxjc6LVLesm1_hL55HWJLDG2OMIMFrTa/s320/screen13.gif" height="172" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_aeGtW3l4TZEvj1LUIskn1SRFpI8nNv8a5l4dfhiPU8ILje88qTrF5jkLLLgUP0kD-dM17iwPcVlA-PXEwCJjLPgbsbnXBmAflsPwdryRZ03ofvG5AsdK5e5RKwkUnHFYuqZxRpnjfA9Y/s1600/Untitled-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_aeGtW3l4TZEvj1LUIskn1SRFpI8nNv8a5l4dfhiPU8ILje88qTrF5jkLLLgUP0kD-dM17iwPcVlA-PXEwCJjLPgbsbnXBmAflsPwdryRZ03ofvG5AsdK5e5RKwkUnHFYuqZxRpnjfA9Y/s1600/Untitled-1.png" height="179" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Это заготовка, в которой пока доступны только элементарные функции по редактированию моделей. Как только ядро движка будет более-менее готово, работа над редактором возобновится. В качестве задатков на тулсеты уже поддерживаются некие функции по вспомагательной визуализации, такие как <br />
<br />
визуализация тангенциального пространства:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYjhdW5YZl9qAYLE_dCtu20Nxy3ihP_9e7nRkrLVjIJTfea3x7QvBUDB1zZF1iOgPXpo8KvIF6jdPuza1OLJFEUY9WbuEbbQfZqemo6utiZckmxdF4au9ge1xcBs362glbZCRCHY4MrwYp/s1600/screen14.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYjhdW5YZl9qAYLE_dCtu20Nxy3ihP_9e7nRkrLVjIJTfea3x7QvBUDB1zZF1iOgPXpo8KvIF6jdPuza1OLJFEUY9WbuEbbQfZqemo6utiZckmxdF4au9ge1xcBs362glbZCRCHY4MrwYp/s320/screen14.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Bounding Sphere & Bounding Box</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaI0N6zAJnczwTNjFs3BHBhCewKMkpYvcjzuomx-MBuUzgrt1vgMtJL6S3mx2KP6o1NQjbYlmrXAectRZBtBqe7SiavV_9yD471TE9YaCK2zqo8pKaThSU5BnHBYROAxazmk0Ps9SWTDJn/s1600/screen15.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaI0N6zAJnczwTNjFs3BHBhCewKMkpYvcjzuomx-MBuUzgrt1vgMtJL6S3mx2KP6o1NQjbYlmrXAectRZBtBqe7SiavV_9yD471TE9YaCK2zqo8pKaThSU5BnHBYROAxazmk0Ps9SWTDJn/s320/screen15.gif" height="179" qda="true" width="320" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_qi-7ouzJdOEqhk02LGAEvu4Rv6XSHfRhQibRZoi0gSMvEB7HBAUCOTNazXAe1dOhCIw6fEhbmhJK2AKRo0fRzX2YOe247nsPicEMuq6fsECSNrDLBPSD5cpTA57FJCMN4gvrniR7GQyg/s1600/screen16.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_qi-7ouzJdOEqhk02LGAEvu4Rv6XSHfRhQibRZoi0gSMvEB7HBAUCOTNazXAe1dOhCIw6fEhbmhJK2AKRo0fRzX2YOe247nsPicEMuq6fsECSNrDLBPSD5cpTA57FJCMN4gvrniR7GQyg/s320/screen16.gif" height="179" qda="true" width="320" /></a></div>
<br />
Axis:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVy5uBoE3IK6GD8Y82_U72jluNdhJpCfZbAq8mWsWjIz8K87gG84Jjw73Xotya5tChnTrv140whO2lr7fMYwGHxd_rkfLHkSaxKz0DMBW1njNCQGKkkHynuQvlRUYUhPbmgD6eio5xJxJT/s1600/screen17.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVy5uBoE3IK6GD8Y82_U72jluNdhJpCfZbAq8mWsWjIz8K87gG84Jjw73Xotya5tChnTrv140whO2lr7fMYwGHxd_rkfLHkSaxKz0DMBW1njNCQGKkkHynuQvlRUYUhPbmgD6eio5xJxJT/s320/screen17.gif" height="179" qda="true" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
</div>
Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-5514974705425561744.post-81112458910468466452012-04-20T02:30:00.001-07:002012-04-20T03:10:04.226-07:00Cubemapper<div dir="ltr" style="text-align: left;" trbidi="on">
<div>
<strong><span style="font-family: Impact; font-size: 24pt;"><em>Synthetic Cubemapper</em></span></strong></div>
<div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh70vL8q1yQd6-E4m-bLCawtgb0vlhlbbER5LE5ajhM6iQ0RORDF2fMgF7T0zN1DKRo3UyfOCLk1kTf9hwdQ3V6aDLI_tczkuN_VszaRYMDWw4Aj6SMbzlXNJAnLi-lF56444a0VC03Yezg/s1600/SyntheticCubemapper1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="228" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh70vL8q1yQd6-E4m-bLCawtgb0vlhlbbER5LE5ajhM6iQ0RORDF2fMgF7T0zN1DKRo3UyfOCLk1kTf9hwdQ3V6aDLI_tczkuN_VszaRYMDWw4Aj6SMbzlXNJAnLi-lF56444a0VC03Yezg/s320/SyntheticCubemapper1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<span style="font-family: Impact; font-size: 24pt;"><span style="font-family: Times New Roman; font-size: small;">Это абсолютно бесплатная программа, которая позволяет создавать кубические текстуры из шести (или менее) 2D текстур, и сохранять их в dds формате. Такие текстуры могут использоваться для создания неба в компьютерных играх, а так же для всевозможных "environment" эффектов, таких как отражение/преломление света и т.д.</span></span></div>
<div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkb4O88LyRO8WXt10ma1Vn_EBXha6OG-BB-FXtvPcldUBrXXo9UPGUy1KYwvQEOqJ5_X9Ght4ncNg0fvGMo4w9crYMau6uVEH5YS4Cg8HAH62RnlraYdtl8JaS10pFVrQ5gPpzGSSr0bM5/s1600/SyntheticCubemapper2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="221" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkb4O88LyRO8WXt10ma1Vn_EBXha6OG-BB-FXtvPcldUBrXXo9UPGUy1KYwvQEOqJ5_X9Ght4ncNg0fvGMo4w9crYMau6uVEH5YS4Cg8HAH62RnlraYdtl8JaS10pFVrQ5gPpzGSSr0bM5/s320/SyntheticCubemapper2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div align="justify" class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn7eju9SKJdsRZbLTkXy2X9k-xd6H_GQL2eOAbcG-xTUzS-NzgMtib0edybrOi5QxaafUa2SMfbODre0yQOpJXW3eebAaFTRQOeAvDnfEF0UOJzudQ1QUAOVhJ8PR4dYDOpTl-SH8RS2n0/s1600/SyntheticCubemapper3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="272" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgn7eju9SKJdsRZbLTkXy2X9k-xd6H_GQL2eOAbcG-xTUzS-NzgMtib0edybrOi5QxaafUa2SMfbODre0yQOpJXW3eebAaFTRQOeAvDnfEF0UOJzudQ1QUAOVhJ8PR4dYDOpTl-SH8RS2n0/s320/SyntheticCubemapper3.png" width="320" /></a></div>
<div align="justify" class="separator" style="clear: both; text-align: center;">
<br /></div>
Программа требует OS Windows7 и установленный XNA framework 3.1<br />
<br />
<a href="http://baybaksoft.ucoz.com/index/0-11" target="_blank"><span style="color: red;">Скачать бесплатно ...</span></a><br />
<div align="justify">
<br /></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-75032035006168264192012-04-19T00:40:00.000-07:002012-04-19T00:44:24.162-07:00PDF Book Printer<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<strong><span style="font-family: Impact; font-size: 24pt;"><em>PDF Book Printer <span style="font-size: 12pt;">(beta)</span></em></span></strong></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
</div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgs-u1cdpBsXY4NTldqz5Aemnogf89Ko4vZzr87dnFFaDqvvWZ0SHDiB1uZBaZPJOhZJm_ktXk2RfJnEotXGis6YLyHdAraKnGzi10joDEuEQrZ1Pv5Zq65XZAvov-as3BLeuhyphenhyphenEEAlPOOH/s1600/BookGirl.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgs-u1cdpBsXY4NTldqz5Aemnogf89Ko4vZzr87dnFFaDqvvWZ0SHDiB1uZBaZPJOhZJm_ktXk2RfJnEotXGis6YLyHdAraKnGzi10joDEuEQrZ1Pv5Zq65XZAvov-as3BLeuhyphenhyphenEEAlPOOH/s1600/BookGirl.bmp" /></a></div>
<div align="justify" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Эта бесплатная утилита, двумя кликами мыши, позволяет распечатывать электронные книги (.pdf), из расчета - четыре страницы на один стандартный лист формата А4. Страницы книги группируются в буклеты по 10 листов (40 страниц) для дальнейшего скрепления между собой. Готовые буклеты пронумерованые и приготовленные для печати сохраняются в папке с программой, и могут быть переданы третьим лицам для самостоятельной печати или хранения. Для печати одного буклета вам потребуется положить в лоток принтера 10 листов, а после того как они отпечатаются, перевернуть всю пачку и положить ее снова в лоток вашего принтера и напечатать вторую сторону буклета. После второй процедуры печати, листы лягут в нужном порядке и вам останется только скрепить их посередине скрепками. Буклет готов. После этого можно приступать к печати следующего буклета. Если в распечатываемой книге число страниц не кратно 40, то оставшиеся страницы упаковываются в буклет с названием "Appendix", и на экран выводится сообщение о том, сколько листов потребуется положить в принтер для печати заключительного буклета.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnqpooQD_lmNb8TXzg4eOFbCOnK9xLaHb0w9-1k0kWkP74e-ibdyyvgyOaUSmixv1qGieQ8A43IIMSP9k6HY9BdRw8fqviREqQCYZ0eGL-bdLDFLYcPPhHO-aBQH3iGEXVfIpOCdKWZcyW/s1600/PdfPrint.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnqpooQD_lmNb8TXzg4eOFbCOnK9xLaHb0w9-1k0kWkP74e-ibdyyvgyOaUSmixv1qGieQ8A43IIMSP9k6HY9BdRw8fqviREqQCYZ0eGL-bdLDFLYcPPhHO-aBQH3iGEXVfIpOCdKWZcyW/s1600/PdfPrint.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJDyIJTN3d6gGaMiqPc4bzGSxUPbASk6Xk5hejz76cjWi46AfYGnO3i-XRKwmC0pV4Y9-kKDUC9PONtVhRedBcDX29eTn2Rr_gCxSunHP_bKPJUCAatSa1sDLdaFogY31HR7pVd-69zBvZ/s1600/Booklets.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJDyIJTN3d6gGaMiqPc4bzGSxUPbASk6Xk5hejz76cjWi46AfYGnO3i-XRKwmC0pV4Y9-kKDUC9PONtVhRedBcDX29eTn2Rr_gCxSunHP_bKPJUCAatSa1sDLdaFogY31HR7pVd-69zBvZ/s320/Booklets.png" width="320" /></a></div>
<br /></div>
<div align="justify" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
</div>
<div align="justify">
<span style="color: red;"> </span><a href="http://baybaksoft.ucoz.com/index/0-11" target="_blank"><span style="color: red;">Скачать бесплатно можно здесь...</span></a></div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-73247357473746422212012-04-17T05:58:00.001-07:002012-04-18T01:53:17.499-07:00Практическое применение XNA. Часть 16.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-size: x-large;">Skybox</span></h2>
<div style="text-align: justify;">
Одним из немаловажных вопросов при написании игры является создание неба. В этой статье я рассмотрю один из наиболее распространенных способов. Небо в играх представляет собой куб (или сферу, но сейчас о кубе), с наложенной на него текстурой. Камера наблюдения должна всегда находиться в середине этого куба. При перемещении камеры, позиция куба изменяется в соответствии с позицией камеры. Буфер глубины можно отключить, и порядок обхода треугольников (CullMode) нужно установить в обратную сторону, (это позволит видеть куб изнутри). Вся хитрость заключается здесь в использовании специальной кубической текстуры, при наложении которой на куб, грани куба сливаются, и перестают быть видимыми. Как генерировать куб и использовать камеру, я рассказывал ранее. По этому ничего принципиально нового для вас здесь быть не должно, кроме особенностей применения кубических текстур в шейдере.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwjKB-2ZFKrUHOFZcFCwrTnzeyblspH36oiBhjXIb-_TyI22eYLUU3mISgkEpXD3HoKbdB71pnYpN1P4pkLFtUdvifCxRoEr2h7sMUX4TeEjJrmu8v_U5N4IxvC6K-dzLN7vsmGepdSsAc/s1600/Terragen-Skybox.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwjKB-2ZFKrUHOFZcFCwrTnzeyblspH36oiBhjXIb-_TyI22eYLUU3mISgkEpXD3HoKbdB71pnYpN1P4pkLFtUdvifCxRoEr2h7sMUX4TeEjJrmu8v_U5N4IxvC6K-dzLN7vsmGepdSsAc/s320/Terragen-Skybox.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Шейдер для скайбокса:<br />
<br />
TextureCube shaderTexture;<br />
sampler SampleType;<br />
cbuffer MatrixBuffer<br />
{<br />
matrix worldMatrix;<br />
matrix viewMatrix;<br />
matrix projectionMatrix;<br />
};<br />
<br />
struct VS_INPUT<br />
{<br />
float4 position : POSITION;<br />
float2 texcoord : TEXCOORD0;<br />
};<br />
<br />
struct PS_INPUT<br />
{<br />
float4 position : SV_POSITION;<br />
float3 texcoord : TEXCOORD0;<br />
};<br />
<br />
PS_INPUT VS(VS_INPUT input)<br />
{<br />
PS_INPUT output = (PS_INPUT)0;<br />
<br />
output.position = mul(mul(mul(input.position, worldMatrix), viewMatrix), projectionMatrix);<br />
output.texcoord = input.position;<br />
return output;<br />
}<br />
<br />
float4 PS(PS_INPUT input) : SV_TARGET<br />
{<br />
return shaderTexture.Sample(SampleType, input.texcoord);<br />
}</div>
<div style="text-align: justify;">
<br />
В результате у нас получится подобная картинка:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3H2AZ4dcQciDttNX2GWyosf2sGVhb4tBQdzFo0_e3XvgzIpg-5ky5Rhp-gAcTpvPGponvTe2aPw7ygtK0gdUWff7QbNOlFt6k1Yscc-JLzIZc5faXl0jfgYjr6cG26rzY4N6Ke3pJM6Cm/s1600/Sky.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="227" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3H2AZ4dcQciDttNX2GWyosf2sGVhb4tBQdzFo0_e3XvgzIpg-5ky5Rhp-gAcTpvPGponvTe2aPw7ygtK0gdUWff7QbNOlFt6k1Yscc-JLzIZc5faXl0jfgYjr6cG26rzY4N6Ke3pJM6Cm/s320/Sky.png" width="320" /></a></div>
Вопросы?</div>
<div style="text-align: justify;">
</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-37401476541467657302012-04-12T05:53:00.003-07:002012-04-12T05:56:10.615-07:00Практическое применение XNA. Часть 15.<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: x-large;"><strong>Сериализация данных в игровых приложениях. (Serialization XNA)</strong></span><br />
<br />
<br />
По мере продвижения в изучении этой серии статей вы уже освоили некоторые азы создания игр. Теперь я хочу рассказать вам о сериализации данных. Что же это такое и для чего это нам нужно? Вообще пугающее новичков понятие сериализация используется не только в играх, а и во многих не игровых приложениях. Суть процесса сериализации заключается в следующем. Во время выполнения приложения, например игры, ваша программа использует некоторые переменные содержащие некоторые значения. Эти значения постоянно меняются, изменяются и поведения целых классов в зависимости от значений переменных. Например количество патронов, жизней, енергии или очков игрового персонажа. Понятное дело, чтобы насобирать максимум, скажем, жизней - игроку пришлось просидеть за игрой несколько часов. Игра в момент своего старта конечно же, присваевает игроку некоторое число жизней, которое вы укажите в своей программе, но в процессе тяжелых игровых событий персонаж это число ,допустим, удвоил. Если теперь он выйдет из игры, то потом начнет играть снова, он обнаружит что все что нажито честным и непосильным трудом сгорело, количество заветных жизней такое как было сначала. Все кто когда либо играл в компьютеоные игры, наверняка слышал термин сохранение игры. В любой момент времени, нажав на определенную клавишу, игра "сохраняется" в некий файл сохранения, имеющий как правило расширение .sav. После чего во время повторного старта игры, персонаж оказывается в том игровом месте и с такими же регалиями, которые он сохранял. Вопрос - откуда программа загрузила данные, которые разработчики нигде не определили? Ответ напрашивается сам собой. Конечно же из файла сохранения. Пока вроде все просто. Мы теоретически можем себе представить как из текстового файла подгрузить например имя игрока. Но как подгрузить n-мерный массив, или готовый класс? Несомненно найдутся люди, которые скажут, что способ такой есть, и написав специальную логику, которая на лету будет разбирать текст, конвертировать всевозможные значения и типы, в итоге все таки справится с этой задачей. Но мы пойдем другим путем. И путь этот назывется сериализация. Иными словами сериализация - это способ сохранения в бинарный файл целых классов, структур, перечислений, делегатов. Антипод сереиализации - десериализация. Это как вы наверное уже догадались обратный процесс, то есть извлечение из бинарного файла тех же классов, структур, делегатов и перечислений.<br />
На практике все выше сказанное можно реализовать например вот так. Заведем новый пример с названием myTerrainSerialize. Это будет слегка модернизированый вариант примера myTerrain. Если помните в нем мы строили ландшафт на основе карты высот. Значения высоты вершины считывалось из нее же. В этом примере мы отделим код заполнения данными массива float[,] heightData в новый класс с названием Configurator. Выглядеть он будет так.<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using System.Text;<br />
using Microsoft.Xna.Framework.Graphics;<br />
namespace myTerrainSerialize<br />
{<br />
[Serializable]<br />
public class Configurator<br />
{<br />
public int WIDTH;<br />
public int HEIGHT;<br />
public float[,] heightData;<br />
public Configurator(Game1 game)<br />
{<br />
LoadHeightData(game.Content.Load<Texture2D>("HeightMap"));<br />
}<br />
public void LoadHeightData(Texture2D heightMap)<br />
{<br />
// установили значение <br />
// сторон карты высот в<br />
// глобальные переменные<br />
WIDTH = heightMap.Width;<br />
HEIGHT = heightMap.Height;<br />
// создали массив цветов для хранения<br />
// данных карты высот с размерностью<br />
// высоты умноженной на ширину<br />
Color[] heightMapColors = new Color[WIDTH * HEIGHT];<br />
// загружаем данные из карты высот <br />
// в массив цветов<br />
heightMap.GetData(heightMapColors);<br />
// создали массив значений высот<br />
heightData = new float[WIDTH, HEIGHT];<br />
// в цикле<br />
for (int x = 0; x < WIDTH; x++)<br />
{<br />
for (int y = 0; y < HEIGHT; y++)<br />
{<br />
// если карта высот ч/б то не важно из какого канала<br />
// брать значение. Мы возьмем из красного<br />
heightData[x, y] = heightMapColors[x + y * WIDTH].R;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
Перед тем как обьявить новый класс мы написали строчку [Serializable].<br />
Это означает, что класс готов к сериализации. Он имеет массив<br />
<br />
public float[,] heightData; <br />
<br />
который нам понадобится в дальнейшем при создании вершин. При первом запуске программы этот массив заполнится данными, после чего весь класс будет сохранен в файл savegame.sav ,который будет создан по пути <br />
C:\Users\имя пользователя\Documents\SavedGames\XNAGame\Player1. <br />
Помимо этого массива, класс конфигуратора игры может хранить множество необходимых переменных, с их количеством и назначением вы можете поэксперриментировать самостоятельно. Продолжим далее. Теперь у нас есть то, что подлежит сохранению, но пока еще нет самого механизма сохранения и извлечения данных. Для этих целей создадим класс с названием Serializator.<br />
class Serializator<br />
{<br />
Game1 game;<br />
static StorageDevice HardDiskDevice;<br />
Configurator ourObj;<br />
public Serializator(Game1 game)<br />
{<br />
this.game = game;<br />
HardDiskDevice = Guide.EndShowStorageDeviceSelector(Guide.BeginShowStorageDeviceSelector(PlayerIndex.One, null, null));<br />
ourObj = new Configurator(game);<br />
//serialize();<br />
}<br />
//Процедура сериализации объекта<br />
void serialize()<br />
{<br />
//Контейнер для хранения данных<br />
StorageContainer container = HardDiskDevice.OpenContainer("XNAGame");<br />
//Полное имя файла - комбинация адреса контейнера и имени<br />
string filename = Path.Combine(container.Path, "savegame.sav");<br />
//Объект, предназначенный для сериализации и десериализации других объектов<br />
IFormatter formatter = new BinaryFormatter();<br />
//Создаем новый поток для записи файла<br />
Stream stream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None);<br />
//Сериализуем объект ourObj в поток stream<br />
formatter.Serialize(stream, ourObj);<br />
//Закрываем поток<br />
stream.Close();<br />
//Уничтожаем контейнер<br />
container.Dispose();<br />
}<br />
<br />
//метод возвращающий массив высот из файла<br />
public static float[,] getdata()<br />
{<br />
StorageContainer container = HardDiskDevice.OpenContainer("XNAGame");<br />
IFormatter formatter = new BinaryFormatter();<br />
//Новый поток для чтения файла<br />
Stream stream = new FileStream(Path.Combine(container.Path, "savegame.sav"), FileMode.Open, FileAccess.Read, FileShare.Read);<br />
//Получаем данные из потока и приводим их к типу MyObj<br />
Configurator MyObj = (Configurator)formatter.Deserialize(stream);<br />
stream.Close();<br />
container.Dispose();<br />
return MyObj.heightData;<br />
}<br />
}<br />
При первом запуске приложения мы не будем комментировать вызов метода serialize();<br />
Иначе произойдет ошибка свидетельствующая о том, что в указанной директории еще нет искомого файла. Но в последующих запусках приложения, этот вызов комментируется. И когда класс Terrain потребует от Serializatora массив высот<br />
heightData = Serializator.getdata();<br />
то метод getdata() вернет ему необходимый массив взятый из класса Configurator, который был извлечен именно из файла savegame.sav.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuU-8OhJz9KqiuYZD2Oh2RZBfn5P5vYaAurDgBIh9SPCV4dnQMojfqbmzc9o4TE7BE11xtu8sJnHPQj4VYnCc9upocaJEiBFhisysCz8y133GvUnO_P07gB0l1pclaRVctWk7JaAQODN67/s1600/serialize.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuU-8OhJz9KqiuYZD2Oh2RZBfn5P5vYaAurDgBIh9SPCV4dnQMojfqbmzc9o4TE7BE11xtu8sJnHPQj4VYnCc9upocaJEiBFhisysCz8y133GvUnO_P07gB0l1pclaRVctWk7JaAQODN67/s320/serialize.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: justify;">
Как вы сами убедились, сериализация - мощнейший инструмент который наверняка вам не раз пригодится.</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-76820962793087211312012-04-12T03:59:00.001-07:002012-04-12T05:11:07.518-07:00Стратегия и тактика. (Как я это понимаю)<div dir="ltr" style="text-align: left;" trbidi="on">
<div style="text-align: justify;">
Сегодня я хотел бы поделиться размышлениями о таких понятиях, как стратегия и тактика, и о том, как эту информацию можно использовать для написания игрового искусственного интеллекта. Эти термины были придуманы людьми уже очень давно, и по сей день используются во многих отраслях человеческой деятельности, начиная от военных дисциплин, бизнеса и заканчивая жизненными позициями многих членов общества или играми. Вообще, понятие "Стратегия" можно приравнять к понятию "Пошаговое планирование". Это может быть некое долгосрочное, планирование строительства какого нибудь комбината, или-же наоборот, планирование заданий на один день у очень занятого человека. Так или иначе, стратегия - это, некое общее, абстрактное планирование, которое не учитывает многие факторы, которые может внести реальность. Предположим, генерал некой армии решил захватить город N. Он составил пошаговый стратегический план захвата.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: left;">
Первый пункт - высадить десант где то на перифирии города.</div>
<div style="text-align: left;">
Второй пункт - подобраться к вокзалу и захватить его лишив город транспортной системы.</div>
<div style="text-align: left;">
Третий пункт - захватив вокзал, перебраться через единственный мост ведущий к городу к телеграфу и захватить его, лишив город связи.</div>
<div style="text-align: left;">
Четвертый пункт - двинуться к главному зданию в городе, и захватить его правительство.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: justify;">
Стратегически все верно. Но каковы шансы на успех такой операции? Вполне возможно, что подойдя к мосту, солдаты обнаружат, что мост по каким-то причинам взорван, и дальше двигаться нельзя. План сорван, и битва проиграна? Но тут в дело должна вступить тактика.</div>
<div style="text-align: justify;">
Что же такое тактика? Тактика - это как-бы реализация (воплощение в жизнь) стратегии.</div>
<div style="text-align: justify;">
Иными словами обнаружив, что план не состоятелен по каким-либо причинам, его необходимо пересмотреть и скорректировать. Решить какие именно действия необходимо предпринять для составления нового плана и прожолжения битвы, можно только наткнувшись на конкретную проблему, которая появилась здесь и сейчас, и о возможном существовании которой составитель плана даже не подозревал. Таким образом, Стратегия ПРЕДПОЛАГАЕТ возможное развитие событий, а тактика РАСПОЛАГАЕТ конкретными фактами, и на их основании позволяет принимать по ситуации те или иные решения.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
На основании всего высшесказанного, можно попытаться разработать алгоритм принятия решений для ИИ. Для простого примера возьмем игровое поле 4х3 клетки, и в нижний левый угол поставим синюю фишку. По правилам эта фишка может передвигаться по трем направлениям. Вперед, влево и вправо на одну клетку.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4tmpAsN-kkKKCrF62PVz1KvFBO-m34pFx1dRJkl2BpZEkmQuDqlrDMr1nptn7ncpIrDuDaRJTLKF8s3HSOEvnB0BIp_AHbQ_XlD6jniDr9MP8uGPfjUScZIQMR8-A2fnBVd1ptJuUGVq6/s1600/strategy1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4tmpAsN-kkKKCrF62PVz1KvFBO-m34pFx1dRJkl2BpZEkmQuDqlrDMr1nptn7ncpIrDuDaRJTLKF8s3HSOEvnB0BIp_AHbQ_XlD6jniDr9MP8uGPfjUScZIQMR8-A2fnBVd1ptJuUGVq6/s1600/strategy1.png" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Цель игры - как можно быстрее дойти до зеленой фишки. Красный квадратик - это преграда о которой фишка пока не подозревает, потому что она находится дальше одной клетки от нее.</div>
<div style="text-align: justify;">
И вот синяя фишка строит стратегический план для достижения цели. Она начинает рекурсивно перебирать все возможные последовательности ходов. В результате этого перебора появится древовидная структура которая будет начинаться в точке H1W1 а заканчиваться в точке H1W4. Очевидно, что следует выбирать самую короткую ветку графа, для достижения цели в кратчайшие сроки. Ей будет являться такая последовательность: </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
H1W1>>H1W2>>H1W3>>H1W4.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Однако перейдя на клетку H1W2 фишка обнаружит, что впереди преграда (взорванный мост).</div>
<div style="text-align: justify;">
Для решения проблемы, необходимо снова расчитать все возможные пути достижения цели (скорректировать план) и выбрать кратчайший, используя его до тех пор, пока не появится еще какая-либо преграда, о существовании которой мы не подозревали. Таким образом, расчет пути в данном случае - это стратегия, а повторный расчет пути из-за внезапно появившейся преграды, это уже тактика. На примере такого простого алгоритма, уже можно написать простейшего "бота" использующего стратегию и тактику, который сможет находить путь к чему либо. Постепенно усложняя алгоритм, вводя в него дополнительные параметры можно добиться решения более сложных задач, но в основе все равно будет лежать стратегия и тактика.</div>
<br />
<br />
<br /></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-9269135932059782352012-04-10T05:01:00.002-07:002012-04-10T05:05:23.059-07:00Практическое применение XNA. Часть 14.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-size: x-large;">Теневые карты (Shadow Maps)</span></h2>
Суть теневых карт заключается в следующем. Сцену рендерят в несколько проходов как и впредыдущем примере. Количество проходов может быть различным. Это зависит от многих факторов, смотря какое качество теней вы желаете получить. Но сейчас это не главное, я постараюсь объяснить сам принцип этого подхода. Первым делом мы рендерим сцену с позиции источника освещения. И делаем это хитрым способом, так как будто это не просто координата источника света, а камера. Иными словами источник света может видеть сцену. То что видет источник можно сохранить в текстуру. Сперва увиденное записывается в цель визуализации или RenderTarget2D, а после из нее в текстуру. Более того, эта текстура должна в себе хранить не просто вид сцены как мы привыкли его видеть обычно, с цветными обьектами, а информацию о глубине сцены. Иначе говоря записываем в текстуру, своего рода, буфер глубины. Делается это таким образом, что бы цвет (значение) пикселя соответствовал расстоянию от источника света до каждого действительного пикселя сцены. Что бы вам легче было представить вышесказанное рисунок ниже показывает пример такой текстуры глубины.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUHfNWSzl4zFr0Ns8Qat7dGGGELpW5EsPPWO54CV7zIC32BXCQs6wHMHQhlHBw0LHP8U5ijUyTXtMePtIe3OIcM1WpWlusPpLxZu6wycx3-Hi6AsB_gbO0ahTur4mH6sTtlzu_0AEu-CBi/s1600/SM1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="194" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUHfNWSzl4zFr0Ns8Qat7dGGGELpW5EsPPWO54CV7zIC32BXCQs6wHMHQhlHBw0LHP8U5ijUyTXtMePtIe3OIcM1WpWlusPpLxZu6wycx3-Hi6AsB_gbO0ahTur4mH6sTtlzu_0AEu-CBi/s320/SM1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Зачем нам нужна такая текстура я скажу чуть позднее. Во втором проходе мы рендерим сцену уже непосреддственно из позиции нашей камеры. Здесь мы будем сравнивать действительное расстояние от позиции источника света с преобразованной информацией из нашей первой текстуры которая хранит глубину (расстояния). Надо сказать что текстура глубины хранит в себе информацию только о видимых источнику освещения поверхностях. Соответственно все данные о поверхностях которые свет не видит, а это касается не только обратных поверхностей моделей, но и поверхностей к которым прегражден путь световых лучей, будут отсутствовать. А значит при сравнении данных о расстояниях полученых во втором проходе с данными из текстуры глубины становится ясно какие поверхности находятся в тени.</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Z-jNsjv7VgCYIZGNPCouUY4XcHqm6VP6rO11S3y1rOgFUmLoEW11Sph0aJEzZLuul26Nl1f_xqmnQhy0wDoZ3QdkqrLDkFXQdHn-auHAp-MKaom3UHhVQCtHo2lZYLAnJrWAOwpeGh_B/s1600/SM2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="259" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2Z-jNsjv7VgCYIZGNPCouUY4XcHqm6VP6rO11S3y1rOgFUmLoEW11Sph0aJEzZLuul26Nl1f_xqmnQhy0wDoZ3QdkqrLDkFXQdHn-auHAp-MKaom3UHhVQCtHo2lZYLAnJrWAOwpeGh_B/s320/SM2.png" width="320" /></a></div>
На рисунке точкой A показана поверхность видимая источнику света. Точкой B поверхность не видимая. Теперь надеюсь, что вам общая теоретическая часть понятна и для более конкретного изучения можно переходить к практике. Давайте создадим новый проект и назовем его myShadowWorld. Сразу же добавим в него компонент Camera. Он нам уже знаком по предыдущим примерам. Добавим так же и класс Terrain. Но в этот раз это уже будет не компонент с внешним вызовом функций, а обычный класс. Это нужно для того, что бы мы имели возможность вызывать метод отрисовки в любой момент когда нам это будет нужно, ведь сцену придется рендерить дважды.<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using Microsoft.Xna.Framework;<br />
using Microsoft.Xna.Framework.Audio;<br />
using Microsoft.Xna.Framework.Content;<br />
using Microsoft.Xna.Framework.GamerServices;<br />
using Microsoft.Xna.Framework.Graphics;<br />
using Microsoft.Xna.Framework.Input;<br />
using Microsoft.Xna.Framework.Media;<br />
using Microsoft.Xna.Framework.Net;<br />
using Microsoft.Xna.Framework.Storage;<br />
<br />
namespace myShadowWorld<br />
{<br />
public class Terrain<br />
{<br />
private int WIDTH;<br />
private int HEIGHT;<br />
private VertexBuffer vertexBuffer;<br />
private IndexBuffer indexBuffer;<br />
private VertexDeclaration vertexDecalaration;<br />
private float[,] heightData;<br />
Texture2D texture;<br />
Game game;<br />
public Terrain(Game game)<br />
{<br />
this.game = game;<br />
}<br />
public void Initialize()<br />
{<br />
texture = game.Content.Load<Texture2D>("Terrain/grass");<br />
LoadHeightData(game.Content.Load<Texture2D>("Terrain/HeightMap"));<br />
SetUpTerrainVertices(game.GraphicsDevice);<br />
SetUpTerrainIndices(game.GraphicsDevice);<br />
vertexDecalaration = new VertexDeclaration(game.GraphicsDevice, VertexPositionNormalTexture.VertexElements);<br />
}<br />
private void LoadHeightData(Texture2D heightMap)<br />
{<br />
// установили высоту и<br />
// ширину карты высот в<br />
// глобальные переменные<br />
WIDTH = heightMap.Width;<br />
HEIGHT = heightMap.Height;<br />
// создали массив цветов для хранения<br />
// данных карты высот с размерностью<br />
// высоты умноженной на ширину<br />
Color[] heightMapColors = new Color[WIDTH * HEIGHT];<br />
// загружаем данные из карты высот<br />
// в массив цветов<br />
heightMap.GetData(heightMapColors);<br />
// создали массив значений высот<br />
heightData = new float[WIDTH, HEIGHT];<br />
// в цикле<br />
for (int x = 0; x < WIDTH; x++)<br />
{<br />
for (int y = 0; y < HEIGHT; y++)<br />
{<br />
// если карта высот ч/б то не важно из какого канала<br />
// брать значение. Мы возьмем из красного<br />
heightData[x, y] = heightMapColors[x + y * WIDTH].R;<br />
}<br />
}<br />
}<br />
public void SetUpTerrainVertices(GraphicsDevice device)<br />
{<br />
// создаем массив вершин с размерностью<br />
// количества всех точек изображения<br />
VertexPositionNormalTexture[] vertices =<br />
new VertexPositionNormalTexture[WIDTH * HEIGHT];<br />
for (int x = 0; x < WIDTH; x++)<br />
for (int y = 0; y < HEIGHT; y++)<br />
{<br />
// устанавливаем значение высоты в Y<br />
// каждого вектора позиции вершины<br />
// делением на 5 регулируем амплитуду<br />
// высоты<br />
vertices[x + y * WIDTH].Position = new Vector3(x, heightData[x, y] / 5, y);<br />
vertices[x + y * WIDTH].Normal = new Vector3(0, 0, 1);<br />
// устанавливаем координаты текстуры делением<br />
// на 50 определяем мозаичность (tiling) текстуры<br />
vertices[x + y * WIDTH].TextureCoordinate.X = (float)x / 50.0f;<br />
vertices[x + y * WIDTH].TextureCoordinate.Y = (float)y / 50.0f;<br />
}<br />
// создаем вершинный буффер<br />
// на основе полученного массива<br />
vertexBuffer =<br />
new VertexBuffer(device, typeof(VertexPositionNormalTexture),<br />
VertexPositionNormalTexture.SizeInBytes * WIDTH * HEIGHT, BufferUsage.Points);<br />
// записываем в него данные<br />
vertexBuffer.SetData(vertices);<br />
}<br />
private void SetUpTerrainIndices(GraphicsDevice device)<br />
{<br />
short[] indices = new short[(WIDTH - 1) * (HEIGHT - 1) * 6];<br />
for (int x = 0; x < WIDTH - 1; x++)<br />
{<br />
for (int y = 0; y < HEIGHT - 1; y++)<br />
{<br />
indices[(x + y * (WIDTH - 1)) * 6] = (short)((x + 1) + (y + 1) * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 1] = (short)((x + 1) + y * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 2] = (short)(x + y * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 3] = (short)((x + 1) + (y + 1) * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 4] = (short)(x + y * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 5] = (short)(x + (y + 1) * WIDTH);<br />
}<br />
}<br />
indexBuffer = new IndexBuffer(device, sizeof(short) * indices.Length, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);<br />
indexBuffer.SetData(indices);<br />
}<br />
public void Update()<br />
{<br />
}<br />
public void Draw(Effect eff, string tech)<br />
{<br />
eff.Parameters["World"].SetValue(Matrix.CreateTranslation(0, 0, 0));<br />
eff.Parameters["MeshTexture"].SetValue(game.Content.Load<Texture2D>("Terrain/grass"));<br />
eff.CurrentTechnique = eff.Techniques[tech];<br />
eff.Begin();<br />
foreach (EffectPass pass in eff.CurrentTechnique.Passes)<br />
{<br />
pass.Begin();<br />
game.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);<br />
game.GraphicsDevice.Indices = indexBuffer;<br />
game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(game.GraphicsDevice, VertexPositionNormalTexture.VertexElements);<br />
game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, (WIDTH - 1) * (HEIGHT - 1) * 2);<br />
pass.End();<br />
}<br />
eff.End();<br />
}<br />
}<br />
}<br />
Так же добавим класс Scene. В нем нет ничего нового для нас. Этот класс нарисует нам несколько моделей но не спомощью BasicEffect а с нашим эффектом.<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using Microsoft.Xna.Framework;<br />
using Microsoft.Xna.Framework.Audio;<br />
using Microsoft.Xna.Framework.Content;<br />
using Microsoft.Xna.Framework.GamerServices;<br />
using Microsoft.Xna.Framework.Graphics;<br />
using Microsoft.Xna.Framework.Input;<br />
using Microsoft.Xna.Framework.Media;<br />
using Microsoft.Xna.Framework.Net;<br />
using Microsoft.Xna.Framework.Storage;<br />
namespace myShadowWorld<br />
{<br />
class Scene<br />
{<br />
Model tractor;<br />
Matrix tractorWorld;<br />
Model uaz;<br />
Matrix uazWorld;<br />
Model mi24;<br />
Matrix mi24World;<br />
Game game;<br />
Matrix[] transforms;<br />
<br />
public Scene(Game game)<br />
{<br />
this.game = game;<br />
}<br />
public void Load(Effect sEffect)<br />
{ <br />
tractor = game.Content.Load<Model>("Models/tractor");<br />
transforms = new Matrix[tractor.Bones.Count];<br />
tractor.CopyAbsoluteBoneTransformsTo(transforms);<br />
tractorWorld = transforms[0] *<br />
Matrix.CreateScale(2) *<br />
Matrix.CreateRotationY(0.5f) *<br />
Matrix.CreateTranslation(new Vector3(150, 0, 100));<br />
RemapModel(tractor, sEffect);<br />
mi24 = game.Content.Load<Model>("Models/mi24");<br />
transforms = new Matrix[mi24.Bones.Count];<br />
mi24.CopyAbsoluteBoneTransformsTo(transforms);<br />
mi24World = transforms[0] *<br />
Matrix.CreateScale(2) *<br />
Matrix.CreateRotationY(-0.3f) *<br />
Matrix.CreateTranslation(new Vector3(120, 0, 140));<br />
RemapModel(mi24, sEffect);<br />
uaz = game.Content.Load<Model>("Models/uaz");<br />
transforms = new Matrix[uaz.Bones.Count];<br />
uaz.CopyAbsoluteBoneTransformsTo(transforms);<br />
uazWorld = transforms[0] *<br />
Matrix.CreateScale(2) *<br />
Matrix.CreateRotationY(0.3f) *<br />
Matrix.CreateTranslation(new Vector3(140, 0, 170));<br />
RemapModel(uaz, sEffect);<br />
}<br />
public static void RemapModel(Model model, Effect effect)<br />
{<br />
foreach (ModelMesh mesh in model.Meshes)<br />
{<br />
foreach (ModelMeshPart part in mesh.MeshParts)<br />
{<br />
part.Effect = effect;<br />
}<br />
}<br />
}<br />
public void Update()<br />
{<br />
<br />
}<br />
public void Draw(string tech)<br />
{<br />
foreach (ModelMesh mesh in tractor.Meshes)<br />
{<br />
foreach (Effect effect in mesh.Effects)<br />
{<br />
effect.Parameters["World"].SetValue(tractorWorld);<br />
effect.Parameters["MeshTexture"].SetValue(game.Content.Load<Texture2D>("Models/traktor_color"));<br />
effect.CurrentTechnique = effect.Techniques[tech];<br />
}<br />
mesh.Draw();<br />
}<br />
<br />
foreach (ModelMesh mesh in mi24.Meshes)<br />
{<br />
foreach (Effect effect in mesh.Effects)<br />
{<br />
effect.Parameters["World"].SetValue(mi24World);<br />
effect.Parameters["MeshTexture"].SetValue(game.Content.Load<Texture2D>("Models/mi24_color"));<br />
effect.CurrentTechnique = effect.Techniques[tech];<br />
}<br />
mesh.Draw();<br />
}<br />
<br />
foreach (ModelMesh mesh in uaz.Meshes)<br />
{<br />
foreach (Effect effect in mesh.Effects)<br />
{<br />
effect.Parameters["World"].SetValue(uazWorld);<br />
effect.Parameters["MeshTexture"].SetValue(game.Content.Load<Texture2D>("Models/uazik_color"));<br />
effect.CurrentTechnique = effect.Techniques[tech];<br />
}<br />
mesh.Draw();<br />
}<br />
<br />
}<br />
}<br />
}<br />
<br />
Напомню что функция RemapModel служит для замены эффекта который присвоен модели по умолчанию импортером среды разработки, на наш эффект. Ну и наконец наш главный класс.<br />
GraphicsDeviceManager graphics;<br />
SpriteBatch spriteBatch;<br />
Camera CAM;<br />
Terrain terrain;<br />
Scene scene;<br />
Effect sEffect;<br />
Vector3 LightPos; <br />
<br />
Эти переменные вам уже знакомы.<br />
RenderTarget2D shadowRT;<br />
DepthStencilBuffer shadowDB;<br />
Цель визуализации для хранения данных о глубине сцены с позиции источника света. И буфер глубины который мы использовали в предыдущем примере. <br />
protected override void LoadContent()<br />
{<br />
spriteBatch = new SpriteBatch(GraphicsDevice);<br />
shadowRT = new RenderTarget2D(GraphicsDevice, 1024 * 4, 1024 * 4, 0, SurfaceFormat.Rgb32);<br />
shadowDB = new DepthStencilBuffer(GraphicsDevice, 1024 * 4, 1024 * 4, DepthFormat.Depth24);<br />
LightPos = new Vector3(128, 100, 0);<br />
sEffect = Content.Load<Effect>("Effects/ShadowMap");<br />
terrain.Initialize();<br />
scene.Load(sEffect);<br />
}<br />
Здесь наиболее важным является создоние shadowRT и shadowDB. Чем большие величины будут установлены, тем точнее будут результаты ваших вычислений. Однако тем сильнее станет нагрузка на видеопроцессор. В зависимости от конфигурации вашего компьютера вам, возможно, придется искать компромис между производительностью и качеством (это нормально). <br />
protected override void Update(GameTime gameTime)<br />
{<br />
if (Keyboard.GetState().IsKeyDown(Keys.Escape))<br />
this.Exit();<br />
sEffect.Parameters["CameraPos"].SetValue(Camera.Position);<br />
sEffect.Parameters["CameraView"].SetValue(Camera.viewMatrix);<br />
sEffect.Parameters["CameraProj"].SetValue(Camera.projectionMatrix);<br />
sEffect.Parameters["LightPos"].SetValue(LightPos);<br />
sEffect.Parameters["LightView"].SetValue((Matrix.CreateLookAt(LightPos, new Vector3(128, 0, 128), Vector3.Up)));<br />
sEffect.Parameters["LightProj"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 1.45f, 90, 300));<br />
<br />
terrain.Update();<br />
scene.Update();<br />
base.Update(gameTime);<br />
}<br />
В методе Update() мы присваеваем нашему эффекту те значения параметров которые будут использоваться и в первом и во втором проходах рендерринга. Далее метод Draw()<br />
protected override void Draw(GameTime gameTime)<br />
{<br />
//========= первый проход =============<br />
GraphicsDevice.RenderState.DepthBufferFunction = CompareFunction.LessEqual;<br />
// Устанавливаем Render Target для shadow map<br />
GraphicsDevice.SetRenderTarget(0, shadowRT);<br />
// Сохраняем текущий буфер глубины<br />
DepthStencilBuffer old = GraphicsDevice.DepthStencilBuffer;<br />
// Устанавливаем наш самодельный буфер глубины<br />
GraphicsDevice.DepthStencilBuffer = shadowDB;<br />
GraphicsDevice.Clear(Color.Black);<br />
terrain.Draw(sEffect, "ShadowMapRender");<br />
scene.Draw("ShadowMapRender");<br />
GraphicsDevice.SetRenderTarget(0, null);<br />
// Возвращаем предыдущий буфер глубины<br />
GraphicsDevice.DepthStencilBuffer = old;<br />
В первом проходе мы подготавливаем графическое устройство таким образом, что бы рендерринг сцены с позиции источника освещения происходил в shadowRT. если из него взять текстуру и визуализировать ее с помощью SpriteBatch то мы увидим глубину сцены. <br />
spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);<br />
spriteBatch.Draw(shadowRT.GetTexture(), new Rectangle(0, 0, 300, 200), Color.White);<br />
spriteBatch.End();<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRwIeGABuCWDsuZ8gJNyQ8pA4i6-VE2QWhtTq04Pg8IDItYkWfRJ2Ck-4NsszH16GjEHVQrUTkGcPYJ97EYHwOcQP3pLnlF4c7zEqK0D2VOSB0_xxyW8BPhq-j8gnwh92lm-IxdHe1oKTh/s1600/SM3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="210" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRwIeGABuCWDsuZ8gJNyQ8pA4i6-VE2QWhtTq04Pg8IDItYkWfRJ2Ck-4NsszH16GjEHVQrUTkGcPYJ97EYHwOcQP3pLnlF4c7zEqK0D2VOSB0_xxyW8BPhq-j8gnwh92lm-IxdHe1oKTh/s320/SM3.png" width="320" /></a></div>
<br />
<br />
Во втором проходе рисуется сама сцена с позиции камеры<br />
//========== второй проход ============<br />
sEffect.Parameters["ShadowMapTexture"].SetValue(shadowRT.GetTexture());<br />
GraphicsDevice.RenderState.CullMode = CullMode.None;<br />
GraphicsDevice.RenderState.AlphaBlendEnable = false;<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
terrain.Draw(sEffect, "ShadowRender");<br />
scene.Draw("ShadowRender");<br />
При этом в эффект передается текстура глубины. Зачем она нужна я говорил в первой части этой главы. И в результате сравнения необходимых данных получаем такое изображение<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGx8tLMNyCQ0kPNZpTAifXhrVwTeMuLj5Q3l-y_x-D51js1AXRni4KbovdaZ4tyWgN2FKhKn3QOXV_GUsxK_Z8hM1NS8e0QgX7QtibcGVaHnNJhOOy6P6wa2o5ULbSZk-KnkOVbpCJE0UN/s1600/SM4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGx8tLMNyCQ0kPNZpTAifXhrVwTeMuLj5Q3l-y_x-D51js1AXRni4KbovdaZ4tyWgN2FKhKn3QOXV_GUsxK_Z8hM1NS8e0QgX7QtibcGVaHnNJhOOy6P6wa2o5ULbSZk-KnkOVbpCJE0UN/s320/SM4.png" width="320" /></a></div>
<br />
Как видите теперь наши тени от моделей отбрасываются не только на землю но и на самих себя, и на другие модели. Само собой разумеется, что такие результаты можно было получить только с помощью специального эффекта. Мы в начале книги касались темы шейдеров. Тема эта очень не простая и требует глубокого разбирательства. Вторая часть этой книги, как раз будет посвящена высокоуровневому языку шейдеров. В заключении этой ститьи приведу код этого эффекта, попутно его коментируя. Глобальные переменные которые будут использоваться в эффекте это:<br />
float4 MaterialAmbientColor; <br />
float4 MaterialDiffuseColor;<br />
две переменные хранящие параметры цвета.<br />
float3 LightPos;<br />
float3 LightDir;<br />
float4x4 LightView;<br />
float4x4 LightProj;<br />
переменные хранящие все данные о источнике света, а именно его позиция, направление, и матрицы 4х4 вида и проекции. <br />
texture MeshTexture; <br />
texture ShadowMapTexture;<br />
sampler MeshTextureSampler = sampler_state<br />
{<br />
Texture = <MeshTexture>;<br />
MipFilter = LINEAR;<br />
MinFilter = LINEAR;<br />
MagFilter = LINEAR;<br />
};<br />
sampler ShadowMapSampler = sampler_state<br />
{<br />
Texture = <ShadowMapTexture>;<br />
MinFilter = POINT;<br />
MagFilter = POINT;<br />
MipFilter = POINT;<br />
AddressU = Clamp;<br />
AddressV = Clamp;<br />
};<br />
две текстуры , одна для текстурирования моделей, другая для хранения дпнных глубины.<br />
float4x4 World; <br />
float3 CameraPos;<br />
float4x4 CameraView; <br />
float4x4 CameraProj;<br />
переменные хранящие данные о камере<br />
float4 GetPositionLight(float4 position)<br />
{<br />
float4x4 WorldViewProjection = mul(mul(World, LightView), LightProj);<br />
return mul(position, WorldViewProjection);<br />
}<br />
Функция помогающая получить позицию света.<br />
struct VS_SHADOW_OUTPUT<br />
{<br />
float4 Position : POSITION;<br />
float Depth : TEXCOORD0;<br />
};<br />
<br />
VS_SHADOW_OUTPUT RenderShadowMapVS(float4 vPos: POSITION)<br />
{<br />
VS_SHADOW_OUTPUT Out;<br />
Out.Position = GetPositionLight(vPos); <br />
// Глубина это Z/W. Это значение будет возвращено пиксельному шейдеру.<br />
// Вычитание из еденицы даёт нам большую точность в floating-point данных<br />
Out.Depth.x = 1-(Out.Position.z/Out.Position.w);<br />
return Out;<br />
}<br />
float4 RenderShadowMapPS( VS_SHADOW_OUTPUT In ) : COLOR<br />
{ <br />
// Глубина это Z деленный на W. Мы возвращаем<br />
// это значение полностью в 32 битном красном канале<br />
// используя SurfaceFormat.Single. Это сохраняет данные<br />
// floating-point с наибольшнй точностью.<br />
return float4(In.Depth.x, 0, 0, 1);<br />
}<br />
Это код шейдера получающего значение глубины для каждого пикселя.<br />
struct VS_OUTPUT<br />
{<br />
float4 Position : POSITION0;<br />
float2 TextureUV : TEXCOORD0;<br />
float3 vNormal : TEXCOORD1;<br />
float4 vPos : TEXCOORD2; <br />
};<br />
<br />
VS_OUTPUT RenderSceneVS( float3 position : POSITION, <br />
float3 normal : NORMAL, <br />
float2 vTexCoord0 : TEXCOORD0 )<br />
{<br />
VS_OUTPUT Output;<br />
float4x4 wvp = mul(mul(World, CameraView), CameraProj); <br />
Output.Position = mul(float4(position, 1.0), wvp);<br />
Output.vNormal = mul(normal, World); <br />
Output.vPos = float4(position,1.0); <br />
Output.TextureUV = vTexCoord0;<br />
<br />
return Output;<br />
}<br />
struct PS_INPUT<br />
{<br />
float2 TextureUV : TEXCOORD0; <br />
float3 vNormal : TEXCOORD1;<br />
float4 vPos : TEXCOORD2; <br />
};<br />
struct PS_OUTPUT<br />
{<br />
float4 RGBColor : COLOR0; <br />
};<br />
PS_OUTPUT RenderScenePS( PS_INPUT In ) <br />
{ <br />
PS_OUTPUT Output; <br />
<br />
// Стандартное выравнивание освещения<br />
float4 vTotalLightDiffuse = float4(1, 1, 1, 1);<br />
float3 lightDir = normalize(LightPos-In.vPos); // направление света<br />
vTotalLightDiffuse += LightDiffuse * max(0,dot(In.vNormal, lightDir)); <br />
vTotalLightDiffuse.a = 1.0f;<br />
// Сейчас, принимая во внимание ShadowMap смотрим если мы в тени<br />
float4 lightingPosition = GetPositionLight(In.vPos);// Полючаем нашу позицию на shadow map <br />
// Получаем значение глубины ShadowMap для этого пикселя<br />
float2 ShadowTexC = 0.5 * lightingPosition.xy / lightingPosition.w + float2(0.5, 0.5);<br />
ShadowTexC.y = 1.0f - ShadowTexC.y;<br />
float shadowdepth = tex2D(ShadowMapSampler, ShadowTexC).r; <br />
// Проверяем наше значение по отношению к значению глубины<br />
float ourdepth = 0.98 - (lightingPosition.z / lightingPosition.w); <br />
// Проверяем shadowdepth по отношению к глубине этого пикселя (фактор неточности добавлен из расчета ошибки плавающей точки)<br />
if (shadowdepth - 0.03 > ourdepth)<br />
{ <br />
// если мы в тени, выключаем свет<br />
vTotalLightDiffuse = float4(0.3, 0.3, 0.3, 1);<br />
};<br />
Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV) * (vTotalLightDiffuse + LightAmbient); <br />
return Output; <br />
}<br />
Финальный шейдер в котором происходит сравнение данных глубины с данными камеры и на основании результатов сравнения выполняется затенение. Как я уже говорил ранее, для новичков этот код достаточно сложен и именно по этому моя следующая книга будет посвящена языку шейдеров, в которой мы шаг за шагом изучим все особенности и способы работы с HLSL.</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-10875481143417292132012-04-10T04:39:00.001-07:002012-04-12T05:27:42.610-07:00Практическое применение XNA. Часть 13.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
<span style="font-size: x-large;">Трафаретные тени (Stencil Shadows)</span></h2>
<div style="text-align: justify;">
Большинство разработчиков компьютерных игр стремятся к фотореалистичности. Ведь чем сильнее сходство игрового изображентя и реальности, тем захватывающе становится пребывание в игровом мире. И как следствие, игра имеет большее количество поклонников. На реалистичность картинки влияет множество факторов. В идеале если разработчик математически опишет все процессы происходящие в окружающем его мире, то для реализации всех этих вычислений потребуется очень большие, по сегодняшним меркам, вычислительные ресурсы. Хотя бы потому, что все они должны происходить в реальном времени. Это потребует использования очень мощных микропроцессоров которых на данный момент не существует. Понятное дело, что наука не стоит на одном месте, специалисты в области нанотехнологий и микроэлектроники ищут все новые и новые пути решения тех или иных задач. Создаются новейшие центральные и видео процессоры, растет количество их ядер, совершенствуется внутренняя архитектура. Вспоминая революционные события прошлых лет, когда только происходил переход от первой шейдерной модели и фиксированного графического конвеера, к современным технологиям, казалось что еще немного, и компьютерное железо выйдет на такой уровень, когда все вопросы по производительности исчезнут. Действительно многое изменилось в лучшую сторону, но вычислительной мощности как и раньше, попрежнему, не хватает. Одним из необходимых свойств, которыми должен обладать ваш рендер, является умение рисовать тени от света. Без теней о какой бы то нибыло реалистичности не может быть и речи. Вообще существует много различных способов создания тени. В этой главе мы рассмотрим создание и использование теней с помощью так называемого Stencil Buffer (буфер трафаретов). Что же такое буфер трафаретов? Я не непрасно начал разговор о новых технологиях в процессорном мире, и вот почему. Буфер трафаретов - это новшество которым стали обладать современные видеочипы. Он имеется у многих видеокарт и может отличаться друг от друга размерами.</div>
<div style="text-align: justify;">
Благодаря ему у нас появилась возможность быстро и качественно нарисовать тени от обьектов. Переходим к практической части. Создадим новый проект под названием myFirstWorld. Это будет не просто очередной пример, а совокупность рассмотренных ранее техник и примеров этой книги. Здесь мы будем использовать уже пройденый нами класс динамического террайна. Подключим класс летающей камеры, что бы полетать над сценой. Ну и конечно же разберемся с созданием и использованием теней. Первое что нам необходимо будет сделать - это проверить нашу видеокарту на наличие буфера трафаретов и выбрать его подходящий режим.</div>
<div style="text-align: justify;">
private static DepthFormat SelectStencilMode()<br />
{<br />
GraphicsAdapter adapter = GraphicsAdapter.DefaultAdapter;<br />
SurfaceFormat format = adapter.CurrentDisplayMode.Format;</div>
<div style="text-align: justify;">
if (adapter.CheckDepthStencilMatch(DeviceType.Hardware,<br />
format,<br />
format,<br />
DepthFormat.Depth24Stencil8))<br />
return DepthFormat.Depth24Stencil8;<br />
else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware,<br />
format,<br />
format,<br />
DepthFormat.Depth24Stencil8Single))<br />
return DepthFormat.Depth24Stencil8Single;<br />
else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware,<br />
format,<br />
format,<br />
DepthFormat.Depth24Stencil4))<br />
return DepthFormat.Depth24Stencil4;<br />
else if (adapter.CheckDepthStencilMatch(DeviceType.Hardware,<br />
format,<br />
format, <br />
DepthFormat.Depth15Stencil1))<br />
return DepthFormat.Depth15Stencil1;<br />
else<br />
throw new InvalidOperationException("Could Not Find Stencil Buffer for Default Adapter");<br />
}</div>
<div style="text-align: justify;">
Далее инициализируем графическое устройство и подключаем два игровых компонента Terrain и Camera. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
public Game1()<br />
{<br />
graphics = new GraphicsDeviceManager(this);<br />
graphics.PreferredBackBufferWidth =<br />
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;<br />
graphics.PreferredBackBufferHeight =<br />
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;<br />
Aspect =<br />
GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.AspectRatio;<br />
Content.RootDirectory = "Content";<br />
graphics.PreferredDepthStencilFormat = SelectStencilMode();</div>
<div style="text-align: justify;">
Terrain t = new Terrain(this);<br />
Components.Add(t);<br />
Camera c = new Camera(this);<br />
Components.Add(c);<br />
Scene s = new Scene(this);<br />
Components.Add(s);<br />
}</div>
<div style="text-align: justify;">
Далее добавляем новый игровой компонент который будет называться Scene. Выглядит он так.<br />
Model tractor;<br />
Matrix TractorWorld;<br />
Model mi24;<br />
Matrix mi24World;<br />
Model UAZ;<br />
Matrix UazWorld;<br />
Matrix[] transforms;</div>
<div style="text-align: justify;">
Переменные которые будут хранить наши модели сцены и их матрицы мира. Массив матриц трансформаций transforms для того чтобы модели правильно расположились в трехмерном пространстве с учетом внутренней иерархии самих моделей.</div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
protected override void LoadContent()<br />
{<br />
tractor = Game.Content.Load<Model>("Models/tractor");<br />
transforms = new Matrix[tractor.Bones.Count];<br />
tractor.CopyAbsoluteBoneTransformsTo(transforms);<br />
TractorWorld = transforms[0] * <br />
Matrix.CreateRotationY(0.5f) *<br />
Matrix.CreateTranslation(new Vector3(150, 0, 100));<br />
mi24 = Game.Content.Load<Model>("Models/mi24");<br />
transforms = new Matrix[mi24.Bones.Count];<br />
mi24.CopyAbsoluteBoneTransformsTo(transforms);<br />
mi24World = transforms[0] * <br />
Matrix.CreateRotationY(-0.3f) *<br />
Matrix.CreateTranslation(new Vector3(120, 0, 150));<br />
UAZ = Game.Content.Load<Model>("Models/uaz");<br />
transforms = new Matrix[UAZ.Bones.Count];<br />
UAZ.CopyAbsoluteBoneTransformsTo(transforms);<br />
UazWorld = transforms[0] *<br />
Matrix.CreateRotationY(0.3f) *<br />
Matrix.CreateTranslation(new Vector3(140, 0, 150));<br />
<br />
base.LoadContent();<br />
}</div>
<div style="text-align: justify;">
В методе Load() мы загружаем модель и создаем позицию моделей с учетом трансформаций. Эти данные нам потребуются как для отрисовки самих моделей, так и их теней. </div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
public override void Draw(GameTime gameTime)<br />
{<br />
DrawScene();<br />
DrawShadow();</div>
<div style="text-align: justify;">
base.Draw(gameTime);<br />
}</div>
<div style="text-align: justify;">
Метод Draw() разбит на два внутренних метода. Один будет только рисовать модели другой рисовать их тени. Всё это можно делать и в одном методе но для понятности я эти действия разделил.</div>
<div style="text-align: justify;">
public void DrawScene()<br />
{ <br />
Game.GraphicsDevice.RenderState.CullMode = CullMode.None;</div>
<div style="text-align: justify;">
foreach (ModelMesh mesh in tractor.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.World = TractorWorld;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;</div>
<div style="text-align: justify;">
effect.EnableDefaultLighting();<br />
}<br />
mesh.Draw();<br />
}</div>
<div style="text-align: justify;">
</div>
<div style="text-align: justify;">
foreach (ModelMesh mesh in mi24.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.World = mi24World;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;</div>
<div style="text-align: justify;">
effect.EnableDefaultLighting();<br />
}<br />
mesh.Draw();<br />
}</div>
<div style="text-align: justify;">
<br />
foreach (ModelMesh mesh in UAZ.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.World = UazWorld;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;</div>
<div style="text-align: justify;">
effect.EnableDefaultLighting();<br />
}<br />
mesh.Draw();<br />
}<br />
}</div>
<div style="text-align: justify;">
Как видите ничего нового тут нет. Стандартный способ отрисовки моделей с помощью BasicEffect. Запускаем приложение и видим нашу сцену пока что без теней.</div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf8WO2Sxmkp6DqtbE2qNjHL0tD56kM-CcsxqYUFirwBKsCmKrVROG4J8KVNPocw3VKnO7YcoEAW_-392q-pPpdmx6H_ty_gVQZP5CyIGKCwGBPJixXkMctZUoP10AY6Eu0pgBM1JMDu-RD/s1600/stencil1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="188" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf8WO2Sxmkp6DqtbE2qNjHL0tD56kM-CcsxqYUFirwBKsCmKrVROG4J8KVNPocw3VKnO7YcoEAW_-392q-pPpdmx6H_ty_gVQZP5CyIGKCwGBPJixXkMctZUoP10AY6Eu0pgBM1JMDu-RD/s320/stencil1.png" width="320" /></a></div>
<div style="text-align: justify;">
<br /></div>
<div style="text-align: justify;">
Сразу бросается в глаза какая-то неестественность пейзажа. Иными словами не очень реалистично. Движемся далее. </div>
<div style="text-align: justify;">
void DrawShadow()<br />
{<br />
//Создаем матрицу тени<br />
Matrix shadow = Matrix.CreateShadow(new Vector3(-50, -100, 40),<br />
new Plane(new Vector3(0, 0.01f, 0),<br />
new Vector3(500, 0.01f, 0),<br />
new Vector3(0, 0.01f, 500)));<br />
//Очищаем буфер трафаретов<br />
Game.GraphicsDevice.Clear(ClearOptions.Stencil, Color.Black, 0, 0);<br />
//Включаем буфер трафаретов<br />
Game.GraphicsDevice.RenderState.StencilEnable = true;<br />
// Вывод на экран элемента буфера в том случае, если он установлен в 0<br />
Game.GraphicsDevice.RenderState.ReferenceStencil = 0;<br />
Game.GraphicsDevice.RenderState.StencilFunction = CompareFunction.Equal;<br />
//При выводе применяем операцию увеличения<br />
Game.GraphicsDevice.RenderState.StencilPass = StencilOperation.Increment;<br />
//Включаем альфа-смешивание для того, чтобы сделать тень полупрозрачной<br />
//Выводим тень</div>
<div style="text-align: justify;">
foreach (ModelMesh mesh in tractor.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.AmbientLightColor = Vector3.Zero;<br />
effect.Alpha = 0.5f;<br />
effect.DirectionalLight0.Enabled = false;<br />
effect.DirectionalLight1.Enabled = false;<br />
effect.DirectionalLight2.Enabled = false;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;<br />
//При выводе тени умножаем мировую матрицу<br />
//на матрицу вывода тени<br />
effect.World = TractorWorld * shadow;<br />
}<br />
mesh.Draw();<br />
}</div>
<div style="text-align: justify;">
foreach (ModelMesh mesh in mi24.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.AmbientLightColor = Vector3.Zero;<br />
effect.Alpha = 0.5f;<br />
effect.DirectionalLight0.Enabled = false;<br />
effect.DirectionalLight1.Enabled = false;<br />
effect.DirectionalLight2.Enabled = false;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;<br />
//При выводе тени умножаем мировую матрицу<br />
//на матрицу вывода тени<br />
effect.World = mi24World * shadow;<br />
}<br />
mesh.Draw();<br />
}</div>
<div style="text-align: justify;">
<br />
foreach (ModelMesh mesh in UAZ.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.AmbientLightColor = Vector3.Zero;<br />
effect.Alpha = 0.5f;<br />
effect.DirectionalLight0.Enabled = false;<br />
effect.DirectionalLight1.Enabled = false;<br />
effect.DirectionalLight2.Enabled = false;<br />
effect.View = Camera.viewMatrix;<br />
effect.Projection = Camera.projectionMatrix;<br />
//При выводе тени умножаем мировую матрицу<br />
//на матрицу вывода тени<br />
effect.World = UazWorld * shadow;<br />
}<br />
mesh.Draw();<br />
}</div>
<div style="text-align: justify;">
//Отключаем буфер<br />
Game.GraphicsDevice.RenderState.StencilEnable = false;<br />
//Отключаем альфа-смешивание<br />
Game.GraphicsDevice.RenderState.AlphaBlendEnable = false;<br />
}</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQdrkCAvWNRlbgTBpj2em6GuNyev6fzbp-5DywYIfKayuJqo-7gLyCvpfncdmFCrFZb5WO6s9i0TA1RcoICINSgAY4qh2ZbNaQP9_JVjxRj6Q7HLL8m6KIvl5QrOqlG9C0QAYcYaEyY3cw/s1600/stencil2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQdrkCAvWNRlbgTBpj2em6GuNyev6fzbp-5DywYIfKayuJqo-7gLyCvpfncdmFCrFZb5WO6s9i0TA1RcoICINSgAY4qh2ZbNaQP9_JVjxRj6Q7HLL8m6KIvl5QrOqlG9C0QAYcYaEyY3cw/s320/stencil2.png" width="320" /></a></div>
<br />
Вот результат с применением буфера трафаретов. Уже намного лучше. Только сейчас наши тени слишком черные. Если бы действия игры происходили где нибудь на луне, где нет атмосферы - то такие тени нам бы вполне подошли. Однако нам хотелось бы сделать тени полупрозрачными. Для этого воспользуемся настройками альфасмешивания.<br />
Game.GraphicsDevice.RenderState.AlphaBlendEnable = true;<br />
Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;<br />
Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;<br />
В результате чего мы получили полупрозрачные тени.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVsfDcq1ZTjGxwtqry2e0I-pVHWTr0IywSpdoQikd5P1KoqjvKgk-DwJ4xu_3371VnwRru3BJ5YsxoDEtSLJfPWxrYoZdP-Ypkqv8vrIX-HHrX7aJpGHciathp3L3z8dtGfIaeDh4FUoS3/s1600/stencil3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVsfDcq1ZTjGxwtqry2e0I-pVHWTr0IywSpdoQikd5P1KoqjvKgk-DwJ4xu_3371VnwRru3BJ5YsxoDEtSLJfPWxrYoZdP-Ypkqv8vrIX-HHrX7aJpGHciathp3L3z8dtGfIaeDh4FUoS3/s320/stencil3.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: justify;">
Для большей понятности сути происходящего поясню. Stencil buffer это так называемая, проекция сцены на геометрическую фигуру Plane (прямоугольная плоскость не имеющая толщины). Вы заметили, что во время создания матрицы теней мы передаем параметрами позицию тени и создаем Plane на который эта тень будет спроецирована. Из плюсов такого подхода можно выделить простоту использования матрицы теней. Из минусов это то, что наши тени могут быть отброшены только на Plane. То есть тень не может быть отброшена саму модель. Для таких теней нам потребуется техника ShadowMapping. О ней я расскажу в следующих главах.</div>
<div style="text-align: justify;">
<br /></div>
</div>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-5514974705425561744.post-83850684448129774052012-04-10T03:15:00.003-07:002012-04-10T03:16:59.960-07:00Практическое применение XNA. Часть 12.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
Создание Terrain.</h2>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br />
Terrain в переводе с английского означает - местность, территория, ландшафт. В контексте программирования игр этот термин применяется по отношению к земле, но естесственно имеется ввиду не планета, а тот грунт по которому мы ходим ногами. В этой главе мы узнаем, как самим создавать terrain. Вообще ландшафт это неотьемлемая часть множества игр, и понять принципы его создани всем будет полезно.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDbynosqxgAimzXVcUP5AL6SSUZqlSTU0weq4HeU7MYKC-CN6RFlOzLD8SYlfTVOtYltOULG2arVyG-MqJyYNUhrIeun9dN82lcetO27Us0Bp0hyu7LmMQcmXsk1C852naMOXd8gq7AQD4/s1600/Terrain1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="107" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDbynosqxgAimzXVcUP5AL6SSUZqlSTU0weq4HeU7MYKC-CN6RFlOzLD8SYlfTVOtYltOULG2arVyG-MqJyYNUhrIeun9dN82lcetO27Us0Bp0hyu7LmMQcmXsk1C852naMOXd8gq7AQD4/s320/Terrain1.png" width="320" /></a></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<br /></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2p-b2NIkXZw7wYUxLJQEx3b7yBarZCdCt_npaqKIQHsrfU1M4giU3J3o4wVTFbLDvMSvZYsZh69fw3EDbkmzo9wlSqq52BGW_jnAZ0N_T3mxhJSSOSaYJrv7si3D2j3wzPL5pu4-WV2GT/s1600/Terrain2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="107" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2p-b2NIkXZw7wYUxLJQEx3b7yBarZCdCt_npaqKIQHsrfU1M4giU3J3o4wVTFbLDvMSvZYsZh69fw3EDbkmzo9wlSqq52BGW_jnAZ0N_T3mxhJSSOSaYJrv7si3D2j3wzPL5pu4-WV2GT/s320/Terrain2.png" width="320" /></a></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ3ynnJx3nOgsUGlOLsOIrVRtngagNl4kIY7ydHqplgDjPQo-AM_A0GKxFwe-db3bGynt1ytp9Fwqr9ULp_ZNMVHSSQ6oACVvl0oBkz_Tpg-cefOmRa3leM5uYVquqzDOiPHJ_5dm5pIbr/s1600/Terrain3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="107" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ3ynnJx3nOgsUGlOLsOIrVRtngagNl4kIY7ydHqplgDjPQo-AM_A0GKxFwe-db3bGynt1ytp9Fwqr9ULp_ZNMVHSSQ6oACVvl0oBkz_Tpg-cefOmRa3leM5uYVquqzDOiPHJ_5dm5pIbr/s320/Terrain3.png" width="320" /></a></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<br /></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: justify;">
Для начала немного теории. Многим из начинающих программистов игр первым делом приходит на ум использовать в качестве ландшафта готовую модель. Не скрою, сам таким был. Вообще создание и использование terraina задача не тривиальная и однозначно сказать какой способ наилучший нельзя. Всё зависит от задач которые ставятся перед разработчиком, и от свойств котрыми должен обладать ландшафт. Ну к примеру если вы будете созерцать землю из окна высокой башни, то действительно обойтись моделью да еще с низким содержанием полигонов, было бы разумно. Но если вы решили создать игру от первого лица, в которой вам предстоит перемещаться во всех направлениях по ландшафту, видеть его во всей своей красе (высокая детализация) да еще в придачу площадь этого самого ландшафта 100 квадратных километров, и переодически требуется получить высоту (координату) любой точки на поверхности земли хотя бы для того, что бы что то на землю поставить, тогда без динамического создания земли нам не обойтись. Вот об этом дальше речь и пойдет. Что вообще собой представляет terrain? Я бы его охарактеризовал так - это геометрическая фигура Plane (плоский квадрат или прямоугольник), имеющий сетчатую структуру с размером n х n,</div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHstMsM5LXkrIjyRp5pGP16aNEZU07hFUsIxxpie7ET8z1Th5TiDhUTxinp7RilW66r2g61Ot3-sMQZGAzBkzMv_IlEaJRE87wjGcM8oAOfFla7Fjqe10fYQv6DJGEZ4X5ElU8JQNBjGsH/s1600/Terrain4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHstMsM5LXkrIjyRp5pGP16aNEZU07hFUsIxxpie7ET8z1Th5TiDhUTxinp7RilW66r2g61Ot3-sMQZGAzBkzMv_IlEaJRE87wjGcM8oAOfFla7Fjqe10fYQv6DJGEZ4X5ElU8JQNBjGsH/s1600/Terrain4.png" /></a></div>
<br />
с различными высотами вершин. Это как раз и имитирует неровности грунта.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhthvX9R1NBkvHGw42uFsCTKujPPttUaE-6T7l9jKplE7VW_lB0VuJQDF4J19Al25FpaDhpB9JwXhJAGLU_LxoXN6-Pbn79vVpyJfamFuRaHvnH3Q3oPmj7gzmqc0Yx2-ugIkJwxXFMfJL5/s1600/Terrain5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhthvX9R1NBkvHGw42uFsCTKujPPttUaE-6T7l9jKplE7VW_lB0VuJQDF4J19Al25FpaDhpB9JwXhJAGLU_LxoXN6-Pbn79vVpyJfamFuRaHvnH3Q3oPmj7gzmqc0Yx2-ugIkJwxXFMfJL5/s320/Terrain5.png" width="320" /></a></div>
<br />
Как создать такую сетку, теоретически, вам уже должно быть понятно. Так же как и любой примитив. К примеру вы решили создать terrain с размерами 100 х 100 вершин. Заметьте, я указал размер именно в вершинах, а не метрах или километрах, потому что генерировать я буду вершины, а так сказать условные километры будут зависеть от расстояния между вершинами. Для этого нам понадобится обычный цикл который выглядит примерно так<br />
for(int x = 0; x < 100; x ++)<br />
{<br />
for(int z = 0; z < 100; z ++)<br />
{<br />
...new Vector3(x, ..., z);<br />
}<br />
}<br />
где переменные x и z соответствуют координатам по оси X и Z. Пока, что это очень общий принцип создания сетки, но он необходим для понимания азов. Теперь если ширину и глубину мы получили, то как нам быть с самой интересной величиной - высотой. Для реализации этой задачи существует множество различных способов. Можно например, написать генератор случайных велечин в неком диапазоне высот, и поставить каждой вершине случайную высоту, но предсказать итоговую геометрию будет не просто. Вероятнее всего это будет некий ёжик, которого после десятка интерполяций удастся сгладить, но управлять формой ландшафта будет не так то просто. Я предлагаю пойти иным путем. Наверное многим бы пришелся по душе способ, в котором бы вы просто нарисовали на экране какого нибудь графического редактора желаемую местность, а именно вид сверху, а в игре получили задуменный рельеф. Что захотели то и получили. Или еще лучше, взяли фотографию с самлёта или спутника какой то понравившейся части земли, и довольно достоверно ее повторили у себя в игре. Принцип такого волшебного способа весьма прост. Это использование так называемой карты высот, ее роль играет обыкновенный графический файл в .bmp формате.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7pWT5sDACNcpjKsMlK2aOjs2iR7hYvCJ7KG02d8VGNtx3ATU6xrGdvLvmugf9qu_AJntvlUtZA5WHWBGVyWPSS6CQYPYq4ClkUXz-NQa7dkHBlTyMqmQCEr60yikwaQG0ykTXjqjxeDab/s1600/Terrain6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7pWT5sDACNcpjKsMlK2aOjs2iR7hYvCJ7KG02d8VGNtx3ATU6xrGdvLvmugf9qu_AJntvlUtZA5WHWBGVyWPSS6CQYPYq4ClkUXz-NQa7dkHBlTyMqmQCEr60yikwaQG0ykTXjqjxeDab/s1600/Terrain6.png" /></a></div>
<br />
<br />
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Суть этого принципа заключается в том, чтобы использовать в качестве значения высоты цветовое значение точки изображения. Как вы наверняка уже знаете, цвет в компьютере представлен структурой состоящей из четырех переменных.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Color = {R, G, B, A};</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
R - хранит информацию о красной составляющей цвета, или правильней будет сказать это красный канал.<br />
G - канал зеленой составляющей.<br />
B - канал синей составляющей.<br />
A - это канал прозрачности или альфа канал, о котором мы упоминали в предыдущих главах. Любой из этих каналов хранит значение в диапазоне от 0 до 255. Для сравнения истинно черный и истинно белый цвета выглядят так:</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Color.Black = {0, 0, 0, 0};<br />
Color.White = {255, 255, 255, 0};</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
В нашем подходе мы можем использовать любой из цветовых каналов при условии что карта высот чернобелая.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Как видите все эти цвета ничто иное как оттенки серого, а значит что любая цветовая точка или пиксел взятая из чернобелого изображения всегда определяется выражением R=G=B, а это и означает то, что изображение должно быть черно-белое. Движемся далее. Для большей наглядности принято считать темные участки карты высот впадинами и низинами ассоциирующиеся с плохой освещенностью, а возвышенности закрашиваются светлыми тонами и ассоциируются с хорошим освещением или заснеженными горными вершинами. Так и для восприятия понятней, да и значение точки чем светлее тем выше, то есть в нашем случае данные именно в том формате в каком нам и требуется. Теперь нам необходим механизм, который считает данные с нашего битмапа и заполнит ими значения в массиве позиций вершин будущего terraina. Плавно переходим от теории к практике. Создадим новый проект под названием myTerrain. Но в отличии от всех предыдущих примеров, для создания ландшафта мы используем оттдельный класс, а точнее Microsoft.Xna.Framework.DrawableGameComponent<br />
Теперь подробнее что это такое и зачем нам все это нужно. Во первых GameComponent и DrawableGameComponent это два игровых класса (компонента) которые имеют такие же как и главный класс методы, а именно Load(), Update() но метод Draw() имеет только DrawableGameComponent. Вызов этих методов происходит из екстернального кода, можно сказать автоматически без вашего участия, вам лишь остаётся в этих методах написать свой код. Для того чтобы создать новый игровой компонент вам надо в вновь созданом проекте, правой кнопкой мышки сделать следующие манипуляции</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirLHLynFrj5goPRJ911evqpHboSa9cQ83VNOxbsZIcy7ygfcxTtbsCxytW952ufo1hznnY-Itv9Rn7_i_F8bg0Em9VIPD7P7vJWYmSGymxC2Vgn9w8cQkU1nOre5kJrk8-79EtgazZQaSv/s1600/Terrain7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="312" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirLHLynFrj5goPRJ911evqpHboSa9cQ83VNOxbsZIcy7ygfcxTtbsCxytW952ufo1hznnY-Itv9Rn7_i_F8bg0Em9VIPD7P7vJWYmSGymxC2Vgn9w8cQkU1nOre5kJrk8-79EtgazZQaSv/s320/Terrain7.png" width="320" /></a></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
myTerrain > Добавить > Компонент. Далее в появившемся окне выбрать категорию XNA GameStudio 3.1 в левой части формы, а в правой части выбрать шаблон Game Component. Указать название игровому компоненту и нажать кнопку [Добавить]. После чего в ваш проект будет добавлен класс игрового компонента. Важно сказать, что игровой компонент это самостоятельный модуль, который вы сможете потом использовать в любых других проектах XNA Game Studio, если добавите его в проект.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmKQMLNJ9GfhOoCVkGAcfOI4pqY8BC8mfCAhmxSyl0PLcFjD8YnMNmts6LcN1tyATuqWJvL03N3f8E08zjcMxaYzUrm_Aus-0JB7mSobHH9cvEbOnMi_EUV8q9hYThU81Wur8qL4ytpVe9/s1600/Terrain8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="191" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmKQMLNJ9GfhOoCVkGAcfOI4pqY8BC8mfCAhmxSyl0PLcFjD8YnMNmts6LcN1tyATuqWJvL03N3f8E08zjcMxaYzUrm_Aus-0JB7mSobHH9cvEbOnMi_EUV8q9hYThU81Wur8qL4ytpVe9/s320/Terrain8.png" width="320" /></a></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
После проделаных операций в вашем проекте должен появиться класс Terrain. Он уже будет иметь набор необходимых методов, но эти методы пока еще ни откуда не вызываются. Для того что бы это исправить, мы должны вновь созданный компонент добавить в коллекцию компонентов нашей игры, после чего они непременно начнут вызываться. Делается это так. В методе Initialize() мы создадим экземпляр игрового компонента, а затем добавим в коллекцию компонентов.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
protected override void Initialize()<br />
{<br />
Terrain terrain = new Terrain(this);<br />
Components.Add(terrain);<br />
base.Initialize();<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Вот после этого все начнет работать как надо. Теперь переходим собственно к самому изготовления terraina. Для начала явно добавляем в проект файл HeightMap.bmp по традиции в папку Content. Это наша карта высот. Еще из ресурсов нам понадобится текстура травы которой мы раскрасим нашу землю. Для этого добавим в папку Content файл grass.dds. Далее код который будет находться именно в компоненте Terrain.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
private int WIDTH;<br />
private int HEIGHT;<br />
private VertexBuffer vertexBuffer;<br />
private IndexBuffer indexBuffer;<br />
private VertexDeclaration vertexDecalaration;<br />
private float[,] heightData;<br />
private BasicEffect effect;<br />
Texture2D texture;</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Здесь вы видите уже знакомые вам классы по урокам создания примитивов. Имеются также переменные которые будут хранить ширину и высоту карты высот, и двухмерный массив heightData[,] в котором как видно из названия будут храниться все высоты нашего ландшафта. Создаваться terrain будет в три этапа. Первым делом мы считаем данные из карты высот, и запишем их в массив heightData[,]. Вторым этапом будет создание вершин на основе данных из heightData[,],и записывания их в вершинный буфер. Третьим по счету, будет создание всех индексов и записываниие их в буфер индексов. Для этого мы используем три отдельных метода, и вызовим их в той последовательности, что оговорили.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
public override void Initialize()<br />
{<br />
effect = new BasicEffect(Game.GraphicsDevice, null); <br />
texture = Game.Content.Load<Texture2D>("grass");</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
LoadHeightData(Game.Content.Load<Texture2D>("HeightMap"));<br />
SetUpTerrainVertices(Game.GraphicsDevice);<br />
SetUpTerrainIndices(Game.GraphicsDevice);</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
vertexDecalaration = new VertexDeclaration(Game.GraphicsDevice, VertexPositionNormalTexture.VertexElements);</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
base.Initialize();<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Теперь рассмотрим по отдельности каждый метод. Начнем с загрузки данных о высотах.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
private void LoadHeightData(Texture2D heightMap)<br />
{<br />
// установили высоту и <br />
// ширину карты высот в<br />
// глобальные переменные</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
WIDTH = heightMap.Width;<br />
HEIGHT = heightMap.Height;<br />
// создали массив цветов для хранения<br />
// данных карты высот с размерностью<br />
// высоты умноженной на ширину<br />
Color[] heightMapColors = new Color[WIDTH * HEIGHT];<br />
// загружаем данные из карты высот <br />
// в массив цветов<br />
heightMap.GetData(heightMapColors);<br />
// создали массив значений высот<br />
heightData = new float[WIDTH, HEIGHT];<br />
// в цикле<br />
for (int x = 0; x < WIDTH; x++)<br />
{<br />
for (int y = 0; y < HEIGHT; y++)<br />
{<br />
// если карта высот ч/б то не важно из какого канала<br />
// брать значение. Мы возьмем из красного<br />
heightData[x, y] = heightMapColors[x + y * WIDTH].R;<br />
}<br />
}<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Метод создания вершинного буффера.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
public void SetUpTerrainVertices(GraphicsDevice device)<br />
{<br />
// создаем массив вершин с размерностью<br />
// количества всех точек изображения<br />
VertexPositionNormalTexture[] vertices = <br />
new VertexPositionNormalTexture[WIDTH * HEIGHT];</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
for (int x = 0; x < WIDTH; x++)<br />
for (int y = 0; y < HEIGHT; y++)<br />
{<br />
// устанавливаем значение высоты в Y <br />
// каждого вектора позиции вершины<br />
// делением на 5 регулируем амплитуду<br />
// высоты<br />
vertices[x + y * WIDTH].Position = new Vector3(x, -heightData[x, y] / 5, y);<br />
vertices[x + y * WIDTH].Normal = new Vector3(0, 0, 1);<br />
// устанавливаем координаты текстуры делением<br />
// на 50 определяем мозаичность (tiling) текстуры<br />
vertices[x + y * WIDTH].TextureCoordinate.X = (float)x / 50.0f;<br />
vertices[x + y * WIDTH].TextureCoordinate.Y = (float)y / 50.0f;<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
// создаем вершинный буффер <br />
// на основе полученного массива<br />
vertexBuffer =<br />
new VertexBuffer(device, typeof(VertexPositionNormalTexture),<br />
VertexPositionNormalTexture.SizeInBytes * WIDTH * HEIGHT, BufferUsage.Points);<br />
// записываем в него данные<br />
vertexBuffer.SetData(vertices);<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br />
Метод создания индексного буффера.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
private void SetUpTerrainIndices(GraphicsDevice device)<br />
{<br />
short[] indices = new short[(WIDTH - 1) * (HEIGHT - 1) * 6];<br />
for (int x = 0; x < WIDTH - 1; x++)<br />
{<br />
for (int y = 0; y < HEIGHT - 1; y++)<br />
{<br />
indices[(x + y * (WIDTH - 1)) * 6] = (short)((x + 1) + (y + 1) * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 1] = (short)((x + 1) + y * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 2] = (short)(x + y * WIDTH);</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
indices[(x + y * (WIDTH - 1)) * 6 + 3] = (short)((x + 1) + (y + 1) * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 4] = (short)(x + y * WIDTH);<br />
indices[(x + y * (WIDTH - 1)) * 6 + 5] = (short)(x + (y + 1) * WIDTH);<br />
}<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
indexBuffer = new IndexBuffer(device, sizeof(short) * indices.Length, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);<br />
indexBuffer.SetData(indices);<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Ну и наконец метод отрисовки всего этого с использованием BasicEffect.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
public override void Draw(GameTime gameTime)<br />
{<br />
effect.World = Matrix.CreateTranslation(0, 0, 0);<br />
effect.View = Matrix.CreateLookAt(new Vector3(100, 400, -250), new Vector3(100, 0, 100), Vector3.Up);<br />
effect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(18), 1.3f, 0.1f, 1000);</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
effect.TextureEnabled = true;<br />
effect.Texture = texture;</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
effect.Begin();<br />
foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br />
{<br />
pass.Begin();<br />
Game.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionNormalTexture.SizeInBytes);<br />
Game.GraphicsDevice.Indices = indexBuffer;<br />
Game.GraphicsDevice.VertexDeclaration = new VertexDeclaration(Game.GraphicsDevice, VertexPositionNormalTexture.VertexElements);<br />
Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, WIDTH * HEIGHT, 0, (WIDTH - 1) * (HEIGHT - 1) * 2);<br />
pass.End();<br />
}<br />
effect.End();</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
base.Draw(gameTime);<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
И в главном классе в методе Draw() добавим следующие строки</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br />
protected override void Draw(GameTime gameTime)<br />
{<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
GraphicsDevice.RenderState.CullMode = CullMode.None;</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
base.Draw(gameTime);<br />
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br />
Здесь вам должно быть всё знакомо по нашим предидущим примерам. В результате всей проделанной работы мы теперь можем посмотреть на на обьемную модель ландшафта созданную на основе плоской карты высот. Ниже для сравнения приводятся две иллюстрации.</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2uzXxjSLTKHI7PnI8rcZotiNaHztBDbt2fG_iRvPdSJ_TIrWbFIDyOIS8-EVth4UsKW8S62m4UmvFyhDzbpZ7yCPHHwlrDH0VavpXN0CVjnH2U3W4SC5b5qTXcnzFoZZBsvwkBv7ys5qv/s1600/Terrain9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2uzXxjSLTKHI7PnI8rcZotiNaHztBDbt2fG_iRvPdSJ_TIrWbFIDyOIS8-EVth4UsKW8S62m4UmvFyhDzbpZ7yCPHHwlrDH0VavpXN0CVjnH2U3W4SC5b5qTXcnzFoZZBsvwkBv7ys5qv/s320/Terrain9.png" width="320" /></a></div>
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5mZAynBZ9KXba6d7ZAT5ztAunH9htbVW4_HCC7fZzsOW9Nmb2x0Hc_EN3vYE2OnAEm9NUxbFfTXveWV2CirvLDlxgbgJ11yW9UrEb3xwVOoXuhtbj-lq3Df4Q9YwMyXzBpUpk2kRExrsK/s1600/Terrain10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="257" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5mZAynBZ9KXba6d7ZAT5ztAunH9htbVW4_HCC7fZzsOW9Nmb2x0Hc_EN3vYE2OnAEm9NUxbFfTXveWV2CirvLDlxgbgJ11yW9UrEb3xwVOoXuhtbj-lq3Df4Q9YwMyXzBpUpk2kRExrsK/s320/Terrain10.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
и напоследок установим нашу камеру так, что бы она находилась немного выше уровня земли, добавим уже известный вам эффект тумана и наложим текстуру травы. <br />
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbWRJ2r5d9LGZKEUNufBUbDjdyvbrpk5PfMTI_z-E8-pKUICRIXRlVfHw9fqmnldrzj0KZaSSSUrAiY0cczCOz_tSzv844jP-TPGoKzbHx4EBzGZ3mjArEM0fOmDuCQqu_7Do0AwreYXDg/s1600/Terrain11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbWRJ2r5d9LGZKEUNufBUbDjdyvbrpk5PfMTI_z-E8-pKUICRIXRlVfHw9fqmnldrzj0KZaSSSUrAiY0cczCOz_tSzv844jP-TPGoKzbHx4EBzGZ3mjArEM0fOmDuCQqu_7Do0AwreYXDg/s320/Terrain11.png" width="320" /></a></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Вот таким симпатичным снимком мы и заканчиваем знакомство с понятием terraina. Теперь, я надеюсь, вам понятны базовые принципы динамического создания ландшафтов. </div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br />
</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-69048943450571484292012-04-10T02:42:00.001-07:002012-04-10T03:16:50.820-07:00Практическое применение XNA. Часть 11.<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
Instancing.</h2>
<br />
В этой главе, мы с вами рассмотрим технологию отображения объектов имеющую название instancing, что в переводе с английского означает "отдельный". Суть этой техники такова, что за один вызов метода Draw(), мы отрисовуем не один обьект, как например наш куб, а множество таких же кубов или чего либо еще. Вот здесь важно сразу понять разницу и преимущество техники instancing. Pечь идет о том, что бы за единственный вызов метода Draw() получить к примеру 10000 моделей на экране. Как же такое возможно, спросите вы? А возможно такое стало с приходом видеокарт, которые поддерживают третью шейдерную модель, именно в ней за один вызов драйвера. Т.е., нам достаточно один раз вызвать DrawIndexedPrimitives(…) и получить на экране не однин, а несколько mesh’ей, расположенных по разным координатам и повернутых под разными углами.<br />
Многие современные игры с помощью этой техники рисуют такие объекты как трава, астероиды и прочие множественные модели, счет которых может переваливать за тысячи экземпляров, и сами понимаете, если каждый раз вызывать Draw(), то нарисовав одну лишь траву, количество кустов которой например 10000, FPS (количество кадров в секунду) вашей игры может сильно пострадать. Что бы избежать подобных проблем, на помощь придет техника instancing.<br />
Для более глубокого изучения этой замечательной техники заведем новый проект под названием instancing. В нем мы создадим уже знакомый нам примитив, куб с помощью VertexPositionColored вершин и нарисуем его 10000 раз, и так начнем.<br />
Глобальными переменными у нас будут выступать:<br />
<br />
<br />
private VertexDeclaration MyVertexDeclaration;<br />
private VertexPositionColor[] myVertices;<br />
private int[] myIndices;<br />
private VertexBuffer myVertexBuffer;<br />
private IndexBuffer myIndexBuffer;<br />
private Effect myEffect;<br />
private Matrix[] myInstansedWorld;<br />
private const int sizeofMatrix = sizeof(float) * 16;<br />
private int instanceDataSize;<br />
<br />
<br />
Их назначение понятно из названия если вы внимательно ознакомились с предыдущими примерами, тем более что они уже использовались нами ранее. Далее создадим наш куб:<br />
<br />
<br />
protected override void Initialize()<br />
{<br />
myEffect = Content.Load<Effect>("Instancing");<br />
//Создаем свой VertexDeclaration <br />
VertexElement[] elements = GetVertexElementVertexPositionColorMatrix();<br />
MyVertexDeclaration = new VertexDeclaration(graphics.GraphicsDevice, elements);<br />
// Строим треугольник и создаем вершинный и индексный буфер<br />
myVertices = new VertexPositionColor[8];<br />
myIndices = new int[36];<br />
<br />
В методе Update() для большей наглядности мы пересчитываем наши матрицы мира так, что бы кубики вращались.<br />
<br />
protected override void Update(GameTime gameTime)<br />
{<br />
if(Keyboard.GetState().IsKeyDown(Keys.Escape))<br />
{<br />
this.Exit();<br />
}<br />
float ang = 0.001f;<br />
Matrix matrRot = Matrix.CreateRotationX(ang * (float)gameTime.ElapsedGameTime.TotalMilliseconds) *<br />
Matrix.CreateRotationY(ang * (float)gameTime.ElapsedGameTime.TotalMilliseconds) *<br />
Matrix.CreateRotationZ(ang * (float)gameTime.ElapsedGameTime.TotalMilliseconds);<br />
for (int i = 0; i < myInstansedWorld.Length; i++)<br />
{<br />
myInstansedWorld[i] = matrRot * myInstansedWorld[i];<br />
}<br />
base.Update(gameTime);<br />
}<br />
<br />
Далее следует самый главный метод Draw(). В нем следует особое внимание обратить на две новые сущности<br />
<br />
DynamicVertexBuffer instanceDataStream<br />
VertexStreamCollection vertexCollection<br />
<br />
Благодаря этим классам и реализуется наша техника.<br />
<br />
protected override void Draw(GameTime gameTime)<br />
{<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
// настримваем вывод<br />
graphics.GraphicsDevice.VertexDeclaration = MyVertexDeclaration;<br />
graphics.GraphicsDevice.Indices = myIndexBuffer;<br />
//graphics.GraphicsDevice.RenderState.DepthBufferEnable = true;<br />
// инициируем динамический вершинный буфер - поток с данными о матрицах<br />
DynamicVertexBuffer instanceDataStream = new DynamicVertexBuffer(graphics.GraphicsDevice, instanceDataSize, BufferUsage.WriteOnly);<br />
instanceDataStream.SetData(myInstansedWorld, 0, myInstansedWorld.Length, SetDataOptions.Discard);<br />
// назначаем вершинные буферы нашего GraphicsDevice<br />
VertexStreamCollection vertexCollection = graphics.GraphicsDevice.Vertices;<br />
vertexCollection[0].SetSource(myVertexBuffer, 0, VertexPositionColor.SizeInBytes);<br />
vertexCollection[0].SetFrequencyOfIndexData(myInstansedWorld.Length);<br />
vertexCollection[1].SetSource(instanceDataStream, 0, sizeofMatrix);<br />
vertexCollection[1].SetFrequencyOfInstanceData(1);<br />
// настраиваем effect<br />
myEffect.Parameters["matVP"].SetValue(Matrix.CreateLookAt(new Vector3(-40f, 40f, 40f), new Vector3(0f, 15f, 0f), new Vector3(0f, 1f, 0f)) *<br />
Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(30), 1.5f, 0.01f, 2000.0f)); <br />
// стандартный вывод<br />
myEffect.Begin();<br />
foreach (EffectPass pass in myEffect.CurrentTechnique.Passes)<br />
{<br />
pass.Begin();<br />
graphics.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, myVertices.Length, 0, myIndices.Length);<br />
pass.End();<br />
}<br />
myEffect.End();<br />
// освобождаем поток с данными о матрицах<br />
instanceDataStream.Dispose();<br />
// очищаем вершинные буферы нашего GraphicsDevice<br />
vertexCollection[0].SetSource(null, 0, 0);<br />
vertexCollection[1].SetSource(null, 0, 0);<br />
base.Draw(gameTime);<br />
}<br />
Запускаем программу и видим желаемый результат<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilCW6Swg3RUJ6L4Akb6CBCNsqbyNpJCspBqA005L9EWcBNJi8PVDorv5eQVQxCK3C5YoioozdD7zVChvLvxwEu2M9pLI9XpNKvJ-DnBt0QUoPGqO749yUY21S4BO5kumX0aHMx9OVfgfnM/s1600/Instansing.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilCW6Swg3RUJ6L4Akb6CBCNsqbyNpJCspBqA005L9EWcBNJi8PVDorv5eQVQxCK3C5YoioozdD7zVChvLvxwEu2M9pLI9XpNKvJ-DnBt0QUoPGqO749yUY21S4BO5kumX0aHMx9OVfgfnM/s320/Instansing.png" width="320" /></a><br />
<br />
Еще нужно упомянуть и о шейдере, с помощью которого мы осуществили вывод изображение<br />
float4x4 matVP : VIEWPROGECTION;<br />
struct VS_INPUT<br />
{<br />
float4 Position : POSITION0;<br />
float4 Color : COLOR0;<br />
float4x4 InstanceWorld : TEXCOORD0;<br />
};<br />
struct VS_OUTPUT<br />
{<br />
float4 Position : POSITION0;<br />
float4 Color : COLOR0;<br />
};<br />
VS_OUTPUT VS(VS_INPUT input)<br />
{<br />
VS_OUTPUT output = (VS_OUTPUT)0;<br />
output.Position = mul(input.Position, mul(transpose(input.InstanceWorld), matVP));<br />
output.Color = input.Color;<br />
return output;<br />
}<br />
struct PS_INPUT <br />
{<br />
float4 Position : POSITION0;<br />
float4 Color : COLOR0;<br />
};<br />
float4 PS(PS_INPUT input) : COLOR0<br />
{<br />
return input.Color;<br />
}<br />
technique Instancing<br />
{<br />
pass Pass1<br />
{<br />
VertexShader = compile vs_3_0 VS();<br />
PixelShader = compile ps_3_0 PS();<br />
}<br />
}<br />
<br />
как видите он достаточно прост. Здесь вам все должно быть знакомо, за исключением одного момента. Обратите внимание на стуруктуру VS_INPUT, а конкретно на поле: <br />
<br />
{… float4x4 InstanceWorld:TEXCOORD0;} <br />
<br />
Данное поле и будет у нас отвечать за получение матрицы мира для конкретной вершины. <br />
Для того, что бы определить выигрыш по производительности этого подхода, нам понадобится сравнение FPS двух сцен с использованием instancing и без него. Для того чтобы измерить FPS нам понадобится слегка дополнить наш код следующими строками:<br />
<br />
int frameRate = 0;<br />
int frameCounter = 0;<br />
TimeSpan elapsedTime = TimeSpan.Zero;<br />
string gameName;<br />
Добавили глобальные переменные с которыми будем проводить операции. Далее в методе Update()<br />
elapsedTime += gameTime.ElapsedGameTime;<br />
if (elapsedTime > TimeSpan.FromSeconds(1))<br />
{<br />
elapsedTime -= TimeSpan.FromSeconds(1);<br />
frameRate = frameCounter;<br />
frameCounter = 0;<br />
}<br />
<br />
<br />
щитаем количество кадров в секунду и в методе Draw() выводим это значение в Title нашего окна.<br />
frameCounter++;<br />
string fps = frameRate.ToString();<br />
this.Window.Title = gameName + " " + fps + "fps";<br />
в результате частота обновления буффера составила около 430 кадров в секунду.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Неплохой результат. Но и это еще не предел для нашего примера. Если вспомнить, что в методе Update() мы производим пересчет всех матриц мира для вращения, то становится понятно что перемножение 10000 матриц не может не украсть драгоценный FPS. Если это убрать, что же теперь получится? К стати показатели из моей статьи могут резко отличаться от ваших. И происходить это будет неизбежно, в зависимости от конфигурации вашего компьютера. Само собой разумеется, чем мощнее видеокарта, тем выше будут ваши значения. Важно знать, что в среде разработки XNA, создатели платформы зафиксиовали FPS по умолчанию равной около 60 кадров в секунду. Этого значения хватает для комфортного созерцания картинки на экране. Переопределенный метод <br />
<br />
<br />
protected override void Draw(GameTime gameTime)<br />
<br />
<br />
не вызывается чаще установленного значения, экономя тем самым ресурсы компьютера, ведь они как воздух нужны для обработки игровых данных подобных физике или искусственному интеллекту. К тому же XNA если помните, ориентирована на консольную приставку XBOX 360, а в ней есть ряд обязательных требований по графике. Что бы снять ограничения по FPS и увидеть максимально возможное значение надо написать две строчки в конструкторе класса:<br />
<br />
<br />
IsFixedTimeStep = false;<br />
<br />
<br />
говорит о том, что мы хотим фиксированый временной шаг. И<br />
<br />
<br />
graphics.SynchronizeWithVerticalRetrace = false;<br />
<br />
<br />
говорит о том, что мы выключили вертикальную синхронизацию.<br />
Всё это необходимо сделать, иначе FPS, будет держаться на отметке 60.<br />
После всех изменений нашем случае получился прирост FPS около 780 кадров в секунду.<br />
Как видите вообще отлично, на таком количестве обьектов получить такую FPS. И на конец самое интересное. Изменим наш код так, что бы модели рисовались в цикле, так же как в предидущих примерах. Закомментарим содержимое метода Draw() кроме кода отвечающего за вывод значения FPS, и напишем следующее:<br />
<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
GraphicsDevice.VertexDeclaration = MyVertexDeclaration;<br />
for (int i = 0; i < 10000; i++)<br />
{<br />
eff.World = myInstansedWorld[i];<br />
eff.View = Matrix.CreateLookAt(new Vector3(-40f, 40f, 40f), new Vector3(0f, 15f, 0f), new Vector3(0f, 1f, 0f));<br />
eff.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(30), 1.5f, 0.01f, 2000.0f);<br />
eff.VertexColorEnabled = true;<br />
eff.Begin();<br />
foreach (EffectPass pass in eff.CurrentTechnique.Passes)<br />
{<br />
pass.Begin();<br />
graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, myVertices, 0, 8, myIndices, 0, 12);<br />
pass.End();<br />
}<br />
eff.End();<br />
}<br />
<br />
<br />
Стартуем и смотрим на результат = 130;<br />
Комментарии, как говорится, излишни. Где можно использовать instancing ( в нашем случае правильнее будет сказать hardware instancing ), потому что для реализации данной техники использовались исключительно ресурсы видеокарты! <br />
<br />
Наверняка, помимо отрисовки примитивов с помощью instancinga, вам понадобится умение рисовать и готовые модели. Об этом вторая половина этой главы. Как всегда создадим новый проект с названием myInstancedModels. Нам понадобятся такие переменные<br />
<br />
public List<InstancedModelPart> ModelParts = new List<InstancedModelPart>();<br />
private List<Matrix> instanceMatrices;<br />
private Model myModel;<br />
private int myModelCount = 10000;<br />
private Effect instancedEffect;<br />
private Texture2D myTexture;<br />
<br />
Метод Load() выглядит таким образом<br />
<br />
protected override void LoadContent()<br />
{<br />
spriteBatch = new SpriteBatch(GraphicsDevice);<br />
instancedEffect = Content.Load<Effect>("InstancedModel");<br />
instanceMatrices = new List<Matrix>();<br />
myTexture = Content.Load<Texture2D>("10");<br />
myModel = Content.Load<Model>("box");<br />
foreach (ModelMesh mesh in myModel.Meshes)<br />
{<br />
foreach (ModelMeshPart part in mesh.MeshParts)<br />
{<br />
ModelParts.Add(new InstancedModelPart(this, part, mesh.VertexBuffer, mesh.IndexBuffer));<br />
}<br />
}<br />
for (int X = 0; X < Math.Sqrt((double)myModelCount); X++)<br />
{<br />
for (int Z = 0; Z < Math.Sqrt((double)myModelCount); Z++)<br />
{<br />
instanceMatrices.Add(Matrix.CreateScale(0.1f) * Matrix.CreateTranslation(new Vector3(X * 2, 0, Z * 2)));<br />
}<br />
}<br />
}<br />
здесь мы последовательно загрузили наш эффект, с помощью которого непосредственно будет происходить отрисовка моделей. Далее создается список мировых матриц, ктотрый будет хранить данные для каждой из множества моделей. Далее идет загрузка текстуры, которую мы хотим видеть на нашей модели, и сама модель. После этого запускается цикл в котором мы заполняем список ModelParts данными из модели. Как вы уже убедились ранее, каждая модель состоит из сетки, каждая сетка из треугольников (шестиугольные и восьмиугольные сетки мы пока не рассматриваем). Для того, что бы видеокарта которая как мы выяснили кроме точек, линий, и как результат того и другого - треугольников, в принципе рисовать ничего не умеет, смогла нарисовать модель, ей как и вслучае с примитивами необходим тот же вершинный буфер и буфер индексов. Где же их взять для нашей модели, ведь руками написать их задача не для слабонервных,тем более, если модель имеет сложную форму не такую как куб, а например, как тело человека. Ответ заключается в том, что все эти данные хранятся в самой модели, а попадают они туда в процессе создания во всевозможных генераторах геометрии таких как 3DS Max и тд. Наша задача их оттуда получить. Для этого мы создадим отдельный класс InstancedModelPart ,да и так будет нагляднее показан процесс изьятия данных.<br />
<br />
public InstancedModelPart(Game game, ModelMeshPart part, VertexBuffer vertexBuffer, IndexBuffer indexBuffer)<br />
{<br />
this.game = game;<br />
//передаем данные из параметров в переменые<br />
_primitiveCount = part.PrimitiveCount;<br />
_vertexCount = part.NumVertices;<br />
_vertexStride = part.VertexStride;<br />
_vertexDeclaration = part.VertexDeclaration;<br />
_vertexBuffer = vertexBuffer;<br />
_indexBuffer = indexBuffer;<br />
originalVertexDeclaration = part.VertexDeclaration.GetVertexElements();<br />
VertexElement[] extraElements = new VertexElement[4];<br />
short offset = 0;<br />
byte usageIndex = 1;<br />
short stream = 1;<br />
//получаем рвзмер Vector4 в байтах<br />
short sizeOfVector4 = sizeof(float) * 4;<br />
for (int i = 0; i < extraElements.Length; i++)<br />
{<br />
extraElements[i] = new VertexElement(stream, offset, VertexElementFormat.Vector4, VertexElementMethod.Default, VertexElementUsage.TextureCoordinate, usageIndex);<br />
offset += sizeOfVector4;<br />
usageIndex++;<br />
}<br />
_vertexDeclaration.Dispose();<br />
int length = originalVertexDeclaration.Length + extraElements.Length;<br />
VertexElement[] elements = new VertexElement[length];<br />
originalVertexDeclaration.CopyTo(elements, 0);<br />
extraElements.CopyTo(elements, originalVertexDeclaration.Length);<br />
_vertexDeclaration = new VertexDeclaration(game.GraphicsDevice, elements);<br />
}<br />
Далее в методе Load() запускается еще один цикл, который заполняет список instanceMatrices матрицами мира так, что бы получился квадрат. Math.Sqrt((double)myModelCount)нужен для того, чтобы указав общее количество моделей, которое хотим увидеть, в данном случае<br />
<br />
private int myModelCount = 10000;<br />
<br />
у нас получился квадрат. Далее следует главный метод Draw(). <br />
<br />
protected override void Draw(GameTime gameTime)<br />
{<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
instancedEffect.CurrentTechnique = instancedEffect.Techniques["HardwareInstancing"];<br />
instancedEffect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(100, 100, -100), new Vector3(100, -50, 200), Vector3.Up));<br />
instancedEffect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(40), 1.6f, 0.1f, 1000f));<br />
instancedEffect.Parameters["Texture"].SetValue(myTexture);<br />
foreach (InstancedModelPart part in ModelParts)<br />
{<br />
GraphicsDevice.VertexDeclaration = part.VertexDeclaration;<br />
GraphicsDevice.Vertices[0].SetSource(part.VertexBuffer, 0, part.VertexStride);<br />
GraphicsDevice.Indices = part.IndexBuffer;<br />
instancedEffect.Begin();<br />
foreach (EffectPass pass in instancedEffect.CurrentTechnique.Passes)<br />
{<br />
pass.Begin();<br />
Matrix[] subList = new Matrix[myModelCount];<br />
instanceMatrices.CopyTo(0, subList, 0, myModelCount);<br />
const int sizeofMatrix = sizeof(float) * 16;<br />
int instanceDataSize = sizeofMatrix * subList.Length;<br />
DynamicVertexBuffer instanceDataStream = new DynamicVertexBuffer(GraphicsDevice, instanceDataSize, BufferUsage.WriteOnly);<br />
instanceDataStream.SetData(subList, 0, subList.Length, SetDataOptions.Discard);<br />
VertexStreamCollection vertices = GraphicsDevice.Vertices;<br />
vertices[0].SetFrequencyOfIndexData(subList.Length);<br />
vertices[1].SetSource(instanceDataStream, 0, sizeofMatrix);<br />
vertices[1].SetFrequencyOfInstanceData(1);<br />
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.VertexCount, 0, part.PrimitiveCount);<br />
instanceDataStream.Dispose();<br />
vertices[0].SetSource(null, 0, 0);<br />
vertices[1].SetSource(null, 0, 0);<br />
pass.End();<br />
}<br />
instancedEffect.End();<br />
}<br />
base.Draw(gameTime);<br />
}<br />
Здесь практически всё вам уже известно. Те же DynamicVertexBuffer и VertexStreamCollection ну и конечно же<br />
GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, part.VertexCount, 0, part.PrimitiveCount);<br />
В качестве модели которую будем "размножать", был выбран куб <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUsdSWYDI8lBTW1hpgAftUUHKBXhxcj2OqPa3R-9r_GYGAgFH47lCkACGsFLBSM74TMxX3FXJeg_Fpmf6lIhLrvnon3HJXUO9eILJ1LuWfs2mlgyqjn3EYo16ypl5lKW-gBglysvR1JtrL/s1600/Instansing2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUsdSWYDI8lBTW1hpgAftUUHKBXhxcj2OqPa3R-9r_GYGAgFH47lCkACGsFLBSM74TMxX3FXJeg_Fpmf6lIhLrvnon3HJXUO9eILJ1LuWfs2mlgyqjn3EYo16ypl5lKW-gBglysvR1JtrL/s1600/Instansing2.png" /></a></div>
<br />
<br />
<br />
В результате всего вышесказанного получится изображение<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7OjsE5-41cljXu6mGOz396oKkK5yM4b2JPToJxtdynei2qNrL_O33TK-5aC77FyuwQ9XAA_ppmHlzIEwJaVgKRq4SX3JkEpt84CNvT6DklzhSejzxua39NqvDdHwxE11sOnjtvPmK_iNb/s1600/Instansing3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="157" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7OjsE5-41cljXu6mGOz396oKkK5yM4b2JPToJxtdynei2qNrL_O33TK-5aC77FyuwQ9XAA_ppmHlzIEwJaVgKRq4SX3JkEpt84CNvT6DklzhSejzxua39NqvDdHwxE11sOnjtvPmK_iNb/s320/Instansing3.png" width="320" /></a></div>
<br />
<br />
состоящее из 10000 обьектов построеных, как и планировалось, квадратом. <br />
А на последок скажу пару слов о шейдере<br />
<br />
// Настройки для камеры<br />
float4x4 View;<br />
float4x4 Projection;<br />
// Это приложение использует модель освещения Ламберта<br />
float3 LightDirection ; <br />
float3 DiffuseLight = 1;<br />
float3 AmbientLight = 1;<br />
texture Texture;<br />
sampler Sampler = sampler_state<br />
{<br />
Texture = (Texture);<br />
<br />
MinFilter = Linear;<br />
MagFilter = Linear;<br />
MipFilter = Linear; <br />
AddressU = Wrap;<br />
AddressV = Wrap;<br />
};<br />
struct VertexShaderInput<br />
{<br />
float4 Position : POSITION0;<br />
float3 Normal : NORMAL0;<br />
float2 TextureCoordinate : TEXCOORD0;<br />
};<br />
struct VertexShaderOutput<br />
{<br />
float4 Position : POSITION0;<br />
float4 Color : COLOR0;<br />
float2 TextureCoordinate : TEXCOORD0;<br />
};<br />
VertexShaderOutput VertexShaderCommon(VertexShaderInput input, float4x4 instanceTransform)<br />
{<br />
VertexShaderOutput output;<br />
<br />
<br />
//Применяя мировуюматрицу и матрицы камеры расчитываем позицию в //пространстве.<br />
float4 worldPosition = mul(input.Position, instanceTransform);<br />
float4 viewPosition = mul(worldPosition, View);<br />
output.Position = mul(viewPosition, Projection);<br />
// Расчитываем свет<br />
float3 worldNormal = mul(input.Normal, instanceTransform); <br />
float diffuseAmount = max(-dot(worldNormal, LightDirection), 0); <br />
float3 lightingResult = saturate(diffuseAmount * DiffuseLight + AmbientLight); <br />
output.Color = float4(lightingResult, 1);<br />
// Копируем текстурные координаты<br />
output.TextureCoordinate = input.TextureCoordinate;<br />
return output;<br />
}<br />
VertexShaderOutput HardwareInstancingVertexShader(VertexShaderInput input, float4x4 instanceTransform : TEXCOORD1)<br />
{<br />
return VertexShaderCommon(input, transpose(instanceTransform));<br />
}<br />
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0<br />
{<br />
return tex2D(Sampler, input.TextureCoordinate) * input.Color;<br />
}<br />
technique HardwareInstancing<br />
{<br />
pass Pass1<br />
{<br />
VertexShader = compile vs_3_0 HardwareInstancingVertexShader();<br />
PixelShader = compile ps_3_0 <br />
PixelShaderFunction();<br />
}<br />
}<br />
Шейдер довольно простой и предназначен только лишь для отображения модели без каких либо преобразований, использующий модель освещения Ламберта (о ней я ранее уже упоминал). Особого внимания требует лишь float4x4 instanceTransform. Именно сюда приходят данные о мировых матрицах для обработки. <br />
На этом мы заканчиваем знакомство с Hardware Instancing. Думаю вы оцените эту технику и найдете ей применение. </div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-5514974705425561744.post-38603109573970976562012-04-09T03:07:00.001-07:002012-04-13T04:44:40.497-07:00Lightning Models<div dir="ltr" style="text-align: left;" trbidi="on">
Для того, что бы игры выглядели более реалистичными, необходимо по максимуму имитировать в них физические процессы происходящие в реальной жизни. Одним из таких процессов является - освещение. Далее мы разберем несколько наиболее известных моделей освещения применяемых в современной игровой индустрии. <br />
<br />
<h2 style="text-align: left;">
<span style="font-size: x-large;">Lambert Lighthing Model</span></h2>
<div style="text-align: left;">
Наиболее простой, на мой взгляд, является модель освещения Ламберта. Она основывается на исключительно диффузном распределении света на поверхности. Это значит что поверхность считается матовой и гладкой, не имеет крупных шероховатостей и глянца. Не плохим примером такой поверхности может служить бумага.</div>
<div style="text-align: left;">
Принцип расчета такого освещения сводится к следующей формуле.</div>
<br />
I = max(0, dot(N, L));<br />
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
где:</div>
<div style="text-align: left;">
I - итоговый коэффициент затенения,</div>
<div style="text-align: left;">
N - нормаль полигона в мировом пространстве,<br />
L - направление света.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Таким образом, идея заключается в следующем:</div>
<div style="text-align: left;">
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
мы получаем угол между нормалью и световым вектором,</div>
</div>
<div style="text-align: left;">
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
и чем этот угол больше, тем меньше освещен полигон.</div>
</div>
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5V5DLO2GXxpSXGFIvoevvkw2dLavLYrsmSF4bM0flz5CTjrNx6AhUVE-xiuNXXiR2N4Fv6iJ2sTRF2qIJYBK6DoUD7FyX8Ou-mYVfi03cr7XjIn2lhOfeUirs-T3lQhxgxs6ggM4YdP7w/s1600/Diagram+4.3.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5V5DLO2GXxpSXGFIvoevvkw2dLavLYrsmSF4bM0flz5CTjrNx6AhUVE-xiuNXXiR2N4Fv6iJ2sTRF2qIJYBK6DoUD7FyX8Ou-mYVfi03cr7XjIn2lhOfeUirs-T3lQhxgxs6ggM4YdP7w/s320/Diagram+4.3.bmp" width="320" /></a></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: left;">
Ниже представлен код пиксельного шейдера, в котором и происходит расчет</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: left;">
<br /></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
{</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
const float3 lightDir = float3(1, 0, 1); </div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
float LdotN = max(0.0, dot(lightDir, input.Normal));</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
return float4(0.5, 0.5, 0.5, 1) * LdotN;</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
}</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<span style="font-family: Courier New; font-size: x-small;"></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieVfOPf06CO30433pcwP7wnYZRiwtwf7buoHh0m7hMgcake5J5KIQI32hFbmxSWMudE6ZnTH9w4HgXQ7wn65F0P9z1PZAdjytYPgPCc_U77pWv8eE_Gv-XSMVCV9sK7BGTZ27SthEhW1lQ/s1600/LambertModel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="191" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieVfOPf06CO30433pcwP7wnYZRiwtwf7buoHh0m7hMgcake5J5KIQI32hFbmxSWMudE6ZnTH9w4HgXQ7wn65F0P9z1PZAdjytYPgPCc_U77pWv8eE_Gv-XSMVCV9sK7BGTZ27SthEhW1lQ/s320/LambertModel.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Исходный код к статье<strong> </strong><a href="http://baybaksoft.ucoz.com/index/0-15" target="_blank"><span style="color: red;"><strong>lambert.zip</strong></span></a></div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
<br /></div>
<br />
<br />
<h2 style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: left;">
<span style="font-size: x-large;">Phong Lightning Model</span></h2>
<div style="text-align: left;">
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none;">
Следующая модель освещения - это бликовая модель освещения Фонга.</div>
</div>
<div style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; text-align: left;">
В отличии от модели Ламберта, она не только учитывает нормали, но так-же учитывает отражение света по отношению к наблюдателю.<br />
<div class="separator" style="border-bottom: medium none; border-left: medium none; border-right: medium none; border-top: medium none; clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUReEeiDf6TjDf8ts7xmMJ-OrCXhfBMi3p-wjw444ObqAJILkX0RmZt3jnY42ol3nmraEGO8lRKd8IQtG4oUM-fy-4li_i8pw96kYsCuHnyXgfmGCrjwCp8hfSqL9uDfg0wBA8K_hFDFib/s1600/Diagram+4.2.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUReEeiDf6TjDf8ts7xmMJ-OrCXhfBMi3p-wjw444ObqAJILkX0RmZt3jnY42ol3nmraEGO8lRKd8IQtG4oUM-fy-4li_i8pw96kYsCuHnyXgfmGCrjwCp8hfSqL9uDfg0wBA8K_hFDFib/s320/Diagram+4.2.bmp" width="320" /></a></div>
Код пиксельного шейдера приведен ниже:<br />
<div style="text-align: left;">
<br />
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0<br />
{<br />
const float3 lightDir = float3(1, 0, 1);<br />
const float3 viewDir = normalize(float3(0, -75, 0));<br />
float LdotN = dot(lightDir, input.Normal);<br />
float3 Reflection = normalize(2.0f * input.Normal * LdotN - lightDir); <br />
float RdotV = max(0.0, dot(Reflection, viewDir));<br />
float SpecExp = 50;<br />
float4 SpecColor = float4(1, 1, 1, 1);<br />
float4 TotalSpecular = SpecColor * pow(RdotV, SpecExp);<br />
return float4(0.5, 0.5, 0.5, 1) * saturate(max(0.0, LdotN)) + TotalSpecular;<br />
}</div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTVxKNvPlxzKJ3CIHTrKU8r7YAAtAEO1hU3-7BfYp_b9TKaX0eHMnI39wfT9iEcvCe4Xq98M2kVjaOUMk5B9t9qLfr0a387HeVi4XzeuM4pIwb5CSDH9P3uAN2FtzpaDJWvIYusbysGlY4/s1600/Phong1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTVxKNvPlxzKJ3CIHTrKU8r7YAAtAEO1hU3-7BfYp_b9TKaX0eHMnI39wfT9iEcvCe4Xq98M2kVjaOUMk5B9t9qLfr0a387HeVi4XzeuM4pIwb5CSDH9P3uAN2FtzpaDJWvIYusbysGlY4/s320/Phong1.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5lULPmuQ5EwB5yYLNaIYywVSgHc28CzWMzxWjZDaX5pJB8srlVuU-URV1bsk4fdMukUtPt3W2Cu8jh3S8vJwoez1NyDp-a-KmSKPtHQEIndT0KkoolXTNIleDUKWbPxqpIV3T5qbgGPn9/s1600/Phong2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5lULPmuQ5EwB5yYLNaIYywVSgHc28CzWMzxWjZDaX5pJB8srlVuU-URV1bsk4fdMukUtPt3W2Cu8jh3S8vJwoez1NyDp-a-KmSKPtHQEIndT0KkoolXTNIleDUKWbPxqpIV3T5qbgGPn9/s320/Phong2.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0FtTq0EKJdphJsCPpgt-puG6CLGuoW4SsTs-ZjFc5ofziL72WY0gdXHpIiDnIjwQt2YfKf5l39zhSlq0AM0ZSW_8S3USNYrZm3jzhqRPSbDtH2uXBO1zSmw6O_yezpw3sYUXHRXzR7KSY/s1600/Phong3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0FtTq0EKJdphJsCPpgt-puG6CLGuoW4SsTs-ZjFc5ofziL72WY0gdXHpIiDnIjwQt2YfKf5l39zhSlq0AM0ZSW_8S3USNYrZm3jzhqRPSbDtH2uXBO1zSmw6O_yezpw3sYUXHRXzR7KSY/s320/Phong3.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy2uqXyUW-3uaICJAs3bwRzItkakYwA8s8UvUg6ZIx-FwrvQTg6vdw8EJp_ZuDE_yPihVt0d1j17kN7JQhf3tLlLd38tq_VFrOEkTbcsf_GRrjt37u0xVcaCamkCpMwxT6xprdNBUw_Eb2/s1600/Phong4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" nda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy2uqXyUW-3uaICJAs3bwRzItkakYwA8s8UvUg6ZIx-FwrvQTg6vdw8EJp_ZuDE_yPihVt0d1j17kN7JQhf3tLlLd38tq_VFrOEkTbcsf_GRrjt37u0xVcaCamkCpMwxT6xprdNBUw_Eb2/s320/Phong4.png" width="320" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Исходный код к статье<strong> </strong><a href="http://baybaksoft.ucoz.com/PhongModel.zip" target="_blank"><span style="color: red;"><strong>PhongModel.zip</strong></span></a></div>
<br /><br />
<h2 style="text-align: left;">
<span style="font-size: x-large;">Normal + Specular Mapping</span></h2>
<div style="text-align: left;">
<br /></div>
<div style="text-align: justify;">
Совмещая бликовую и диффузную модели освещения, можно получать более сложные эффекты. Одним из таких эффектов является Normal Mapping. Мы уже знаем что для расчета диффузного освещения нужно использовать дот продукт векторов нормали и направления света. Нормали обычно хранятся в самой модели. Можно сказать, что для одного полигона</div>
<div style="text-align: justify;">
(треугольника) нормаль расчитывается как среднее арифметическое из трех нормалей вершин полигона. Соответственно, детализация объекта ограничивается сложностью модели. Для того что-бы придать модели более детализированный вид, был придуман следующий способ. Художники сохраняли нормали в текстуру, и после текстурирования такой текстурой модели, нужно считать нормаль из нее и использовать для расчета освещения. В результате, можно добиться высокой визуальной сложности моделей, не затрагивая ее сетки. </div>
<div style="text-align: justify;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhed2zja6lyH8se1oQGktIDUNNM7M8-02hN4zdBznlnSVTyglS026jjuPWDEgPZrHT46aj8z_5TCeo_G_UrM01OjfqpHRxd29VbtOUhyWhoHbi7QOT3o80CFLgSbd20cTIqlb53safWXePk/s1600/specularBump.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="191" qda="true" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhed2zja6lyH8se1oQGktIDUNNM7M8-02hN4zdBznlnSVTyglS026jjuPWDEgPZrHT46aj8z_5TCeo_G_UrM01OjfqpHRxd29VbtOUhyWhoHbi7QOT3o80CFLgSbd20cTIqlb53safWXePk/s320/specularBump.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Реализацию этой техники можно найти здесь <a href="http://baybaksoft.ucoz.com/index/0-15" target="_blank"><span style="color: red;">SpecularBump.zip</span></a></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-37963487260330560282012-03-15T01:31:00.000-07:002012-04-17T05:36:29.827-07:00How To Make A Game<div dir="ltr" style="text-align: left;" trbidi="on">
In this blog I will talk about how to create a computer game step by step using the Microsoft XNA. I start my story from the simple to the complex. And at the end of the story we will create a full 3D game.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwaTrYxto8B3DCSQjb8suak3fM9jpUvmL0uTwHDzfgxOAUh0rxDPfkFsEdVg_U7lZFd7e4U2FBYbJr5Sbd51w7QrHeHFAdN8QseMNCwvoDE3PZI1xfaBUfZsVRudk-bv1AuXzMMm0b75zP/s1600/XNA2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwaTrYxto8B3DCSQjb8suak3fM9jpUvmL0uTwHDzfgxOAUh0rxDPfkFsEdVg_U7lZFd7e4U2FBYbJr5Sbd51w7QrHeHFAdN8QseMNCwvoDE3PZI1xfaBUfZsVRudk-bv1AuXzMMm0b75zP/s320/XNA2.jpg" width="320" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixXmvcduweR_jlbseaNP-fI_QG1dNVi0OLuqr4dP-DwW3iikiiBF4UzDMITtsuzQHySNjMP8oIr4M6boxUh_cDAvTWPEsP_vbkmLQfzWi56mY7d23hsz-0sTzyRYHrLJy-R6i4e_bimHIy/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixXmvcduweR_jlbseaNP-fI_QG1dNVi0OLuqr4dP-DwW3iikiiBF4UzDMITtsuzQHySNjMP8oIr4M6boxUh_cDAvTWPEsP_vbkmLQfzWi56mY7d23hsz-0sTzyRYHrLJy-R6i4e_bimHIy/s320/XNA.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFBpzMTTVRV8Vn91Of5L4QNx8IjW1_IKbecIP8F3nO-6eqxDOxEjzZrOiVFn26LPrD22NlRwtHA6Zu1Pv0xaChu4VaCpCcQK7BjoCfZC44W1RLadeOqcj5b9b0VSrLQfvOF56X5sH7ClCV/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFBpzMTTVRV8Vn91Of5L4QNx8IjW1_IKbecIP8F3nO-6eqxDOxEjzZrOiVFn26LPrD22NlRwtHA6Zu1Pv0xaChu4VaCpCcQK7BjoCfZC44W1RLadeOqcj5b9b0VSrLQfvOF56X5sH7ClCV/s320/XNA.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIewMVGRW_nIxOZFuQup8Q_3Do4oP7yjO3QckSZVihTED9a0UiZbivR7kMteNeAI_4vzvyg0yuF1WPx10MdFkGKW1fmsI6XcV3O9qBQCkN6AHKFzaI9vqJa4PfQ16G4XrFhWekZ2oRklsp/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIewMVGRW_nIxOZFuQup8Q_3Do4oP7yjO3QckSZVihTED9a0UiZbivR7kMteNeAI_4vzvyg0yuF1WPx10MdFkGKW1fmsI6XcV3O9qBQCkN6AHKFzaI9vqJa4PfQ16G4XrFhWekZ2oRklsp/s320/XNA.jpg" width="320" /></a></div>
<br />
As a result, we get this game:<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/Rv52759xgXU?feature=player_embedded' frameborder='0'></iframe></div>
<br />
<br />
So let's get started and good luck to everyone who wants to learn how to create games with their own hands.<br />
<br />
<h2 style="text-align: left;">
<span style="font-size: x-large;">Menu Creation</span></h2>
Our future game would consist of three main modules. This menu, a physical module and render. First, we look at how to create a menu for our, as well as for any other game. To run the menu, we need to create the following classes:<br />
<br />
AnimeFont.cs;<br />
BackGround.cs;<br />
Button.cs;<br />
Cursor.cs;<br />
Delegates.cs;<br />
MainMenu.cs;<br />
<br />
From the title it should be clear that each class will do.<br />
<br />
namespace _MENU_<br />
{<br />
class AnimeFont : DrawableGameComponent<br />
{<br />
SpriteBatch spriteBatch;<br />
private Texture2D Texture1;<br />
int PositionX1;<br />
int PositionY1;<br />
Rectangle Rectangle1;<br />
private Texture2D Texture2;<br />
int PositionX2;<br />
int PositionY2;<br />
Rectangle Rectangle2;<br />
private Texture2D Texture3;<br />
int PositionX3;<br />
int PositionY3;<br />
Rectangle Rectangle3;<br />
private Texture2D Texture4;<br />
int PositionX4;<br />
int PositionY4;<br />
Rectangle Rectangle4;<br />
private Texture2D gameName;<br />
public AnimeFont(Game game) : base(game)<br />
{<br />
spriteBatch = new SpriteBatch(Game.GraphicsDevice);<br />
Texture1 = Game.Content.Load<Texture2D>(@"Menu/RB1");<br />
PositionX1 = game.GraphicsDevice.Viewport.Width - Texture1.Width;<br />
PositionY1 = game.GraphicsDevice.Viewport.Height - Texture1.Height / 2;<br />
Texture2 = Game.Content.Load<Texture2D>(@"Menu/RB2");<br />
PositionX2 = -game.GraphicsDevice.Viewport.Width / 2;<br />
PositionY2 = game.GraphicsDevice.Viewport.Height - Texture2.Height * 2;<br />
Texture3 = Game.Content.Load<Texture2D>(@"Menu/RB3");<br />
PositionX3 = game.GraphicsDevice.Viewport.Width - Texture3.Width / 2;<br />
PositionY3 = 100;<br />
Texture4 = Game.Content.Load<Texture2D>(@"Menu/RB4");<br />
PositionX4 = 50;<br />
PositionY4 = -Game.GraphicsDevice.Viewport.Height;<br />
gameName = Game.Content.Load<Texture2D>(@"Menu/GameName");<br />
}<br />
public override void Initialize()<br />
{<br />
base.Initialize();<br />
}<br />
public override void Update(GameTime gameTime)<br />
{<br />
PositionY1--;<br />
Rectangle1 = new Rectangle(PositionX1, PositionY1, Texture1.Width, Texture1.Height);<br />
if (PositionY1 < -800)<br />
{<br />
PositionY1 = Game.GraphicsDevice.Viewport.Height - Texture1.Height / 6; <br />
}<br />
PositionX2++;<br />
Rectangle2 = new Rectangle(PositionX2, PositionY2, Texture2.Width, Texture2.Height);<br />
if (PositionX2 > Game.GraphicsDevice.Viewport.Width)<br />
{<br />
PositionX2 = -Game.GraphicsDevice.Viewport.Width / 2;<br />
}<br />
PositionX3--;<br />
Rectangle3 = new Rectangle(PositionX3, PositionY3, Texture3.Width, Texture3.Height);<br />
if (PositionX3 < -Game.GraphicsDevice.Viewport.Width / 2)<br />
{<br />
PositionX3 = Game.GraphicsDevice.Viewport.Width; <br />
}<br />
PositionY4++;<br />
Rectangle4 = new Rectangle(PositionX4, PositionY4, Texture4.Width, Texture4.Height);<br />
if (PositionY4 > Game.GraphicsDevice.Viewport.Height)<br />
{<br />
PositionY4 = -Game.GraphicsDevice.Viewport.Height;<br />
}<br />
base.Update(gameTime);<br />
}<br />
public override void Draw(GameTime gameTime)<br />
{<br />
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);<br />
spriteBatch.Draw(Texture1, Rectangle1, new Color(255, 255, 255, 70));<br />
spriteBatch.Draw(Texture2, Rectangle2, new Color(255, 255, 255, 70));<br />
spriteBatch.Draw(Texture3, Rectangle3, new Color(255, 255, 255, 255));<br />
spriteBatch.Draw(Texture4, Rectangle4, new Color(255, 255, 255, 100));<br />
spriteBatch.Draw(gameName, new Rectangle(60, 70, gameName.Width / 2, gameName.Height / 2), new Color(255, 255, 255, 255));<br />
spriteBatch.End();<br />
base.Draw(gameTime);<br />
}<br />
}<br />
}<br />
<br />
namespace _MENU_<br />
{<br />
public class BackGround : Microsoft.Xna.Framework.DrawableGameComponent<br />
{<br />
private SpriteBatch spriteBatch;<br />
private VideoPlayer videoPlayer;<br />
private Texture2D BackGroundTexture = null;<br />
private Video BackGroundVideo = null;<br />
private Rectangle BackGroundRectangle;<br />
private string BackGroundFilePath;<br />
public BackGround(Game game, Rectangle BackGroundRectangle, string BackGroundFilePath) : base(game)<br />
{<br />
this.BackGroundRectangle = BackGroundRectangle;<br />
this.BackGroundFilePath = BackGroundFilePath;<br />
}<br />
public override void Initialize()<br />
{<br />
spriteBatch = new SpriteBatch(Game.GraphicsDevice);<br />
try<br />
{<br />
BackGroundTexture = Game.Content.Load<Texture2D>(BackGroundFilePath);<br />
}<br />
catch<br />
{<br />
BackGroundVideo = Game.Content.Load<Video>(BackGroundFilePath);<br />
videoPlayer = new VideoPlayer();<br />
videoPlayer.IsLooped = true;<br />
videoPlayer.Play(BackGroundVideo);<br />
}<br />
base.Initialize();<br />
}<br />
<br />
public override void Draw(GameTime gameTime)<br />
{<br />
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);<br />
if (BackGroundTexture != null)<br />
{<br />
spriteBatch.Draw(BackGroundTexture, BackGroundRectangle, Color.White);<br />
}<br />
if (BackGroundVideo != null)<br />
{<br />
spriteBatch.Draw(videoPlayer.GetTexture(), BackGroundRectangle, Color.White);<br />
}<br />
spriteBatch.End();<br />
base.Draw(gameTime);<br />
}<br />
}<br />
}<br />
<br />
namespace _MENU_<br />
{<br />
public class MenuButton : Microsoft.Xna.Framework.DrawableGameComponent<br />
{<br />
private SpriteBatch spriteBatch;<br />
private SpriteFont spriteFont;<br />
<br />
private Texture2D ButtonTexture;<br />
private Rectangle ButtonRectangle;<br />
private Color ButtonColor = Color.White;<br />
private string TexturePath;<br />
private string SpriteFontPath = string.Empty;<br />
private string Name = string.Empty;<br />
public event OnMenuButtonClick OnButtonClick;<br />
public MenuButton(Game game, Rectangle ButtonRectangle, string TexturePath)<br />
: base(game)<br />
{ <br />
this.ButtonRectangle = ButtonRectangle;<br />
this.TexturePath = TexturePath;<br />
}<br />
public MenuButton(Game game, Rectangle ButtonRectangle, string TexturePath, string SpriteFontPath, string Name)<br />
: base(game)<br />
{<br />
this.ButtonRectangle = ButtonRectangle;<br />
this.TexturePath = TexturePath;<br />
this.SpriteFontPath = SpriteFontPath;<br />
this.Name = Name;<br />
}<br />
protected override void LoadContent()<br />
{<br />
spriteBatch = new SpriteBatch(GraphicsDevice);<br />
ButtonTexture = Game.Content.Load<Texture2D>(TexturePath);<br />
if (SpriteFontPath != string.Empty)<br />
{<br />
spriteFont = Game.Content.Load<SpriteFont>(SpriteFontPath);<br />
}<br />
base.LoadContent();<br />
}<br />
public override void Update(GameTime gameTime)<br />
{<br />
Rectangle MouseRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 1, 1);<br />
if (MouseRectangle.Intersects(ButtonRectangle))<br />
{<br />
ButtonColor = Color.White;<br />
if (Mouse.GetState().LeftButton == ButtonState.Pressed)<br />
{<br />
OnButtonClick();<br />
}<br />
}<br />
else<br />
{<br />
ButtonColor = Color.Gray;<br />
}<br />
base.Update(gameTime);<br />
}<br />
public override void Draw(GameTime gameTime)<br />
{<br />
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);<br />
spriteBatch.Draw(ButtonTexture, ButtonRectangle, ButtonColor);<br />
if (Name != string.Empty)<br />
{<br />
spriteBatch.DrawString(spriteFont, Name, new Vector2(ButtonRectangle.X, ButtonRectangle.Y + 8), Color.AliceBlue);<br />
}<br />
spriteBatch.End();<br />
base.Draw(gameTime);<br />
}<br />
}<br />
}<br />
<br />
<br />
namespace _MENU_<br />
{<br />
public class Cursor : Microsoft.Xna.Framework.DrawableGameComponent<br />
{<br />
SpriteBatch spriteBatch;<br />
private Texture2D CursorTexture;<br />
private Rectangle CursorRectangle;<br />
private string CursorFilePath;<br />
public Cursor(Game game, Rectangle CursorRectangle, string CursorFilePath) : base(game)<br />
{<br />
this.CursorRectangle = CursorRectangle;<br />
this.CursorFilePath = CursorFilePath;<br />
}<br />
public override void Initialize()<br />
{<br />
spriteBatch = new SpriteBatch(Game.GraphicsDevice);<br />
CursorTexture = Game.Content.Load<Texture2D>(CursorFilePath);<br />
base.Initialize();<br />
}<br />
public override void Update(GameTime gameTime)<br />
{<br />
CursorRectangle = new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 50, 50);<br />
base.Update(gameTime);<br />
}<br />
public override void Draw(GameTime gameTime)<br />
{<br />
spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);<br />
spriteBatch.Draw(CursorTexture, CursorRectangle, Color.White); <br />
spriteBatch.End();<br />
base.Draw(gameTime);<br />
}<br />
}<br />
}<br />
<br />
<br />
namespace _MENU_<br />
{<br />
public delegate void OnMenuButtonClick();<br />
public delegate void SwitchMainMenuToNewGame();<br />
public delegate void SwitchMainMenuToOptionsMenu();<br />
public delegate void SwitchMainMenuToTitleMenu();<br />
public delegate void SwitchTitleMenuToMainMenu();<br />
public delegate void SwitchMainMenuToExit();<br />
}<br />
namespace _MENU_<br />
{<br />
public class MainMenu <br />
{<br />
public event SwitchMainMenuToNewGame SwitchMainMenuToGame;<br />
public event SwitchMainMenuToTitleMenu SwitchMainMenuToTitleMenu;<br />
public event SwitchMainMenuToExit SwitchMainMenuToExit;<br />
BackGround FirstBackGround;<br />
AnimeFont AFont;<br />
MenuButton StartNewGameButton;<br />
MenuButton OptionsGameButton;<br />
MenuButton TitleButton;<br />
MenuButton ExitGameButton;<br />
public MainMenu(Game game) <br />
{<br />
FirstBackGround = new BackGround(game, new Rectangle(0, 0, game.GraphicsDevice.Viewport.Width, game.GraphicsDevice.Viewport.Height), @"Menu/menu");<br />
game.Components.Add(FirstBackGround);<br />
AFont = new AnimeFont(game);<br />
game.Components.Add(AFont);<br />
StartNewGameButton = new MenuButton(game, new Rectangle(80, 150, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", " START");<br />
StartNewGameButton.OnButtonClick += new OnMenuButtonClick(StartNewGameButton_OnButtonClick);<br />
game.Components.Add(StartNewGameButton);<br />
OptionsGameButton = new MenuButton(game, new Rectangle(80, 200, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", " OPTIONS");<br />
OptionsGameButton.OnButtonClick += new OnMenuButtonClick(OptionsGameButton_OnButtonClick);<br />
game.Components.Add(OptionsGameButton);<br />
TitleButton = new MenuButton(game, new Rectangle(80, 250, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", " TITLE");<br />
TitleButton.OnButtonClick += new OnMenuButtonClick(TitleButton_OnButtonClick);<br />
game.Components.Add(TitleButton);<br />
ExitGameButton = new MenuButton(game, new Rectangle(80, 300, 200, 40), @"Menu/Button3", @"Menu/DefaultFont", " EXIT");<br />
ExitGameButton.OnButtonClick += new OnMenuButtonClick(ExitButton_OnButtonClick);<br />
game.Components.Add(ExitGameButton);<br />
game.Components.Add(new Cursor(game, new Rectangle(),@"Menu/cursor"));<br />
}<br />
void TitleButton_OnButtonClick()<br />
{<br />
SwitchMainMenuToTitleMenu();<br />
}<br />
void OptionsGameButton_OnButtonClick()<br />
{<br />
}<br />
void StartNewGameButton_OnButtonClick()<br />
{<br />
SwitchMainMenuToGame();<br />
}<br />
void ExitButton_OnButtonClick()<br />
{<br />
SwitchMainMenuToExit();<br />
}<br />
<br />
}<br />
}<br />
<br />
The result of these classes you can see in the pictures above. Using delegates used in menu, we can switch between various modes of our future game engine. Need to immediately say that this game is based on the physical engine PhysX, and actually to the NET version of these libraries:<br />
<br />
PhysXLoader.dll<br />
NxCooking.dll<br />
NxCharacter.dll<br />
<br />
These libraries are available online for download.<br />
<br />
This class fully configures the physics of the game, and synchronizes with the game render.<br />
namespace _GAME_<br />
{<br />
public delegate void ContactCallback(Actor a, Actor b, ContactPairFlag events);<br />
public class PhysX<br />
{<br />
private Model _box;<br />
private Model _sphere;<br />
public Core _core;<br />
public static Scene _scene;<br />
public static Actor Bitok;<br />
public static Actor ball1, ball2, ball3, ball4,<br />
ball5, ball6, ball7, ball8,<br />
ball9, ball10, ball11, ball12,<br />
ball13, ball14, ball15, ball16;<br />
public static Actor Border1, Border2, Border3, Border4, Border5, Border6; <br />
public PhysX(Game game)<br />
{<br />
_core = new Core();<br />
_core.SetParameter(PhysicsParameter.VisualizationScale, 2.0f);<br />
_core.SetParameter(PhysicsParameter.VisualizationScale, 1.0f);<br />
_core.SetParameter(PhysicsParameter.VisualizeCollisionShapes, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeClothMesh, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeJointLocalAxes, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeJointLimits, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeFluidPosition, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeFluidEmitters, false); // Slows down rendering a bit to much<br />
_core.SetParameter(PhysicsParameter.VisualizeForceFields, true);<br />
_core.SetParameter(PhysicsParameter.VisualizeSoftBodyMesh, true);<br />
_core.SetParameter(PhysicsParameter.DefaultSleepLinearVelocitySquared, 2.0f * 2.0f);<br />
_core.SetParameter(PhysicsParameter.DefaultSleepAngularVelocitySquared, 2.0f * 2.0f);<br />
_core.SetParameter(PhysicsParameter.SkinWidth, 0.01f);<br />
_core.SetParameter(PhysicsParameter.VisualizeActorAxes, true);<br />
SceneDescription sceneDesc = new SceneDescription();<br />
sceneDesc.SimulationType = SimulationType.Software;<br />
sceneDesc.Gravity = new Vector3(0.0f, -50f, 0.0f);<br />
_scene = _core.CreateScene(sceneDesc);<br />
// Create the default material<br />
Material defaultMaterial = _scene.Materials[0];<br />
defaultMaterial.Restitution = 0.5f;<br />
defaultMaterial.StaticFriction = 0.1f;<br />
defaultMaterial.DynamicFriction = 0.1f;<br />
}<br />
<br />
public void Dispose()<br />
{<br />
_core.Dispose();<br />
_scene.Dispose();<br />
}<br />
public void LoadPhysX(ContentManager Content)<br />
{<br />
_box = Content.Load<Model>(@"PhysicsMeshes/cube");<br />
_sphere = Content.Load<Model>(@"PhysicsMeshes/sphere");<br />
SetupPhysicsScene();<br />
}<br />
void SetupPhysicsScene()<br />
{<br />
// --- Borders ---<br />
Border1 = CreateBox(new Vector3(0, -15, 44), new Vector3(20.5f, 10, 2), 0);<br />
Border1.Name = "b";<br />
Border2 = CreateBox(new Vector3(0, -15, -44), new Vector3(20.5f, 10, 2), 0);<br />
Border2.Name = "b";<br />
Border3 = CreateBox(new Vector3(24, -15, 20.75f), new Vector3(2, 10, 19.75f), 0);<br />
Border3.Name = "b";<br />
Border4 = CreateBox(new Vector3(24, -15, -20.75f), new Vector3(2, 10, 19.75f), 0);<br />
Border4.Name = "b";<br />
Border5 = CreateBox(new Vector3(-24, -15, 20.75f), new Vector3(2, 10, 19.75f), 0);<br />
Border5.Name = "b";<br />
Border6 = CreateBox(new Vector3(-24, -15, -20.75f), new Vector3(2, 10, 19.75f), 0);<br />
Border6.Name = "b";<br />
// --- Planes ---<br />
Actor Box = CreateBox(new Vector3(0, -18, 0), new Vector3(22, 10.35f, 42), 0);<br />
Box = CreateBox(new Vector3(0, -50, 0), new Vector3(2500, 10f, 2500), 0);<br />
// --- zaplatki ---<br />
Box = CreateBox(new Vector3(25, -15, 45), new Vector3(6, 10f, 2), 0);<br />
Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));<br />
Box = CreateBox(new Vector3(-25, -15, 45), new Vector3(2, 10f, 6), 0);<br />
Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));<br />
Box = CreateBox(new Vector3(25, -15, -45), new Vector3(2, 10f, 6), 0);<br />
Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));<br />
Box = CreateBox(new Vector3(-25, -15, -45), new Vector3(6, 10f, 2), 0);<br />
Box.GlobalOrientation = Matrix.Transform(Box.GlobalOrientation, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver4)));<br />
Box = CreateBox(new Vector3(27, -15, 0), new Vector3(2, 10f, 10), 0);<br />
Box = CreateBox(new Vector3(-27, -15, 0), new Vector3(2, 10f, 10), 0);<br />
// --- Balls ---<br />
float AngDump = 100f;<br />
float Mass = 300f;<br />
Bitok = CreateSphere(new Vector3(0, 2.6f, 20), 1, 1);<br />
Bitok.AngularDamping = AngDump;<br />
Bitok.Mass = Mass;<br />
Bitok.Name = "Bitok";<br />
Bitok.Sleep();<br />
ball1 = CreateSphere(new Vector3(-2, 2.6f, -20), 1, 1);<br />
ball1.AngularDamping = AngDump;<br />
ball1.Mass = Mass;<br />
ball1.Name = "ball1";<br />
ball1.Sleep();<br />
ball2 = CreateSphere(new Vector3(0, 2.6f, -20), 1, 1);<br />
ball2.AngularDamping = AngDump;<br />
ball2.Mass = Mass;<br />
ball2.Name = "ball2";<br />
ball2.Sleep();<br />
ball3 = CreateSphere(new Vector3(2, 2.6f, -20), 1, 1);<br />
ball3.AngularDamping = AngDump;<br />
ball3.Mass = Mass;<br />
ball3.Name = "ball3";<br />
ball3.Sleep();<br />
ball4 = CreateSphere(new Vector3(-3, 2.6f, -21.8f), 1, 1);<br />
ball4.AngularDamping = AngDump;<br />
ball4.Mass = Mass;<br />
ball4.Name = "ball4";<br />
ball4.Sleep();<br />
ball5 = CreateSphere(new Vector3(-1, 2.6f, -21.8f), 1, 1);<br />
ball5.AngularDamping = AngDump;<br />
ball5.Mass = Mass;<br />
ball5.Name = "ball5";<br />
ball5.Sleep();<br />
ball6 = CreateSphere(new Vector3(1, 2.6f, -21.8f), 1, 1);<br />
ball6.AngularDamping = AngDump;<br />
ball6.Mass = Mass;<br />
ball6.Name = "ball6";<br />
ball6.Sleep();<br />
ball7 = CreateSphere(new Vector3(3, 2.6f, -21.8f), 1, 1);<br />
ball7.AngularDamping = AngDump;<br />
ball7.Mass = Mass;<br />
ball7.Name = "ball7";<br />
ball7.Sleep();<br />
ball8 = CreateSphere(new Vector3(-4, 2.6f, -23.6f), 1, 1);<br />
ball8.AngularDamping = AngDump;<br />
ball8.Mass = Mass;<br />
ball8.Name = "ball8";<br />
ball8.Sleep();<br />
ball9 = CreateSphere(new Vector3(-2, 2.6f, -23.6f), 1, 1);<br />
ball9.AngularDamping = AngDump;<br />
ball9.Mass = Mass;<br />
ball9.Name = "ball9";<br />
ball9.Sleep();<br />
ball10 = CreateSphere(new Vector3(0, 2.6f, -23.6f), 1, 1);<br />
ball10.AngularDamping = AngDump;<br />
ball10.Mass = Mass;<br />
ball10.Name = "ball10";<br />
ball10.Sleep();<br />
ball11 = CreateSphere(new Vector3(2, 2.6f, -23.6f), 1, 1);<br />
ball11.AngularDamping = AngDump;<br />
ball11.Mass = Mass;<br />
ball11.Name = "ball11";<br />
ball11.Sleep();<br />
ball12 = CreateSphere(new Vector3(4, 2.6f, -23.6f), 1, 1);<br />
ball12.AngularDamping = AngDump;<br />
ball12.Mass = Mass;<br />
ball12.Name = "ball12";<br />
ball12.Sleep();<br />
ball13 = CreateSphere(new Vector3(-1, 2.6f, -18.2f), 1, 1);<br />
ball13.AngularDamping = AngDump;<br />
ball13.Mass = Mass;<br />
ball13.Name = "ball13";<br />
ball13.Sleep();<br />
ball14 = CreateSphere(new Vector3(1, 2.6f, -18.2f), 1, 1);<br />
ball14.AngularDamping = AngDump;<br />
ball14.Mass = Mass;<br />
ball14.Name = "ball14";<br />
ball14.Sleep();<br />
ball15 = CreateSphere(new Vector3(0, 2.6f, -16.4f), 1, 1);<br />
ball15.AngularDamping = AngDump;<br />
ball15.Mass = Mass;<br />
ball15.Name = "ball15";<br />
ball15.Sleep();<br />
<br />
_scene.UserContactReport = new ContactReport();<br />
_scene.SetActorGroupPairFlags(0, 0, ContactPairFlag.OnTouch);<br />
}<br />
public void UpdatePhysX(GameTime gameTime)<br />
{<br />
_scene.Simulate((float)gameTime.ElapsedGameTime.TotalMilliseconds / 300f); <br />
_scene.FetchResults(SimulationStatus.RigidBodyFinished, true);<br />
}<br />
public void DrawPhysX()<br />
{<br />
foreach (Actor actor in _scene.Actors)<br />
{<br />
Vector3 actualColor = new Vector3(1, 1, 1);<br />
foreach (Shape shape in actor.Shapes)<br />
{<br />
switch (shape.Type)<br />
{<br />
case ShapeType.Box:<br />
DrawPhysicsBox((BoxShape)shape, actualColor);<br />
break;<br />
case ShapeType.Sphere:<br />
if (actor.IsSleeping)<br />
{<br />
DrawPhysicsSphere((SphereShape)shape, /*new Vector3(0, 0, 1)*/actualColor, true);<br />
}<br />
else<br />
{<br />
DrawPhysicsSphere((SphereShape)shape, actualColor, false);<br />
}<br />
break;<br />
}<br />
}<br />
}<br />
}<br />
Actor CreateKinematicBox(Vector3 pos, Vector3 boxDim, string name, float mass)<br />
{<br />
BodyDescription bodyDescription = new BodyDescription(mass);<br />
bodyDescription.BodyFlags = BodyFlag.Kinematic;<br />
ActorDescription actorDesc = new ActorDescription();<br />
actorDesc.GlobalPose = Matrix.CreateTranslation(pos);<br />
actorDesc.BodyDescription = bodyDescription;<br />
actorDesc.Name = name;<br />
actorDesc.Shapes.Add(new BoxShapeDescription(boxDim));<br />
return _scene.CreateActor(actorDesc);<br />
}<br />
Actor CreateBox(Vector3 pos, Vector3 boxDim, float density)<br />
{<br />
ActorDescription actorDesc = new ActorDescription();<br />
BodyDescription bodyDesc = new BodyDescription();<br />
BoxShapeDescription boxShapeDesc = new BoxShapeDescription();<br />
boxShapeDesc.Dimensions = new Vector3(boxDim.X, boxDim.Y, boxDim.Z);<br />
boxShapeDesc.LocalPose = Matrix.CreateTranslation(0, boxDim.Y, 0);<br />
actorDesc.Shapes.Add(boxShapeDesc);<br />
actorDesc.GlobalPose = Matrix.CreateTranslation(pos);<br />
if (density > 0)<br />
{<br />
actorDesc.BodyDescription = bodyDesc;<br />
actorDesc.Density = density;<br />
}<br />
//if (!actorDesc.IsValid())<br />
// throw new InvalidDataException();<br />
return _scene.CreateActor(actorDesc);<br />
}<br />
void DrawPhysicsBox(BoxShape shape, Vector3 color)<br />
{<br />
{<br />
Matrix[] transforms = new Matrix[_box.Bones.Count];<br />
_box.CopyAbsoluteBoneTransformsTo(transforms);<br />
foreach (ModelMesh mesh in _box.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.DiffuseColor = color;<br />
effect.EnableDefaultLighting();<br />
effect.World = Matrix.CreateScale(shape.Dimensions.X, shape.Dimensions.Y, shape.Dimensions.Z) * transforms[mesh.ParentBone.Index] * shape.GlobalPose;<br />
effect.View = Camera.ViewMatrix;<br />
effect.Projection = Camera.ProjectionMatrix;<br />
}<br />
mesh.Draw();<br />
}<br />
}<br />
}<br />
Actor CreateSphere(Vector3 pos, float radius, float density)<br />
{<br />
// Add a single-shape actor to the scene<br />
ActorDescription actorDesc = new ActorDescription();<br />
BodyDescription bodyDesc = new BodyDescription();<br />
// The actor has one shape, a sphere<br />
SphereShapeDescription sphereDesc = new SphereShapeDescription(radius);<br />
sphereDesc.LocalPose = Matrix.CreateTranslation(new Vector3(0, radius, 0));<br />
actorDesc.Shapes.Add(sphereDesc);<br />
if (density > 0)<br />
{<br />
actorDesc.BodyDescription = bodyDesc;<br />
actorDesc.Density = density;<br />
}<br />
actorDesc.GlobalPose = Matrix.CreateTranslation(pos);<br />
Actor pActor = _scene.CreateActor(actorDesc);<br />
return pActor;<br />
}<br />
void DrawPhysicsSphere(SphereShape sphereShape, Vector3 color, bool IsSleeping)<br />
{<br />
Matrix[] transforms = new Matrix[_sphere.Bones.Count];<br />
_sphere.CopyAbsoluteBoneTransformsTo(transforms);<br />
foreach (ModelMesh mesh in _sphere.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.World = Matrix.CreateScale(sphereShape.Radius, sphereShape.Radius, sphereShape.Radius) * transforms[mesh.ParentBone.Index] * sphereShape.GlobalPose;<br />
effect.View = Camera.ViewMatrix;<br />
effect.Projection = Camera.ProjectionMatrix;<br />
effect.DiffuseColor = Color.White.ToVector3();<br />
//effect.DiffuseColor = color;<br />
effect.EnableDefaultLighting();<br />
}<br />
mesh.Draw();<br />
}<br />
}<br />
}<br />
<br />
public class ContactReport : UserContactReport<br />
{<br />
private List<ContactReportPair> _contactPairs;<br />
public struct ContactReportPair<br />
{<br />
public Actor A, B;<br />
public ContactCallback Callback;<br />
public ContactReportPair(Actor a, Actor b, ContactCallback callback)<br />
{<br />
A = a;<br />
B = b;<br />
Callback = callback;<br />
}<br />
}<br />
public ContactReport()<br />
{<br />
_contactPairs = new List<ContactReportPair>();<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball15, PhysX.Border6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball1, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.Bitok, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball2, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball1, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball3, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball2, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball4, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball3, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball5, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball4, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball6, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball5, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball7, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball6, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball8, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball7, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball9, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball8, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball10, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball9, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball11, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball10, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball12, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball11, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball13, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball12, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.ball14, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball13, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
_contactPairs.Add(new ContactReportPair(PhysX.ball14, PhysX.ball15, new ContactCallback(CapsuleAndGroundPlaneContact)));<br />
}<br />
public override void OnContactNotify(ContactPair contactInformation, ContactPairFlag events)<br />
{<br />
foreach (ContactReportPair pair in _contactPairs)<br />
{<br />
if ((pair.A == contactInformation.ActorA || pair.A == contactInformation.ActorB) && (pair.B == contactInformation.ActorA || pair.B == contactInformation.ActorB))<br />
{<br />
pair.Callback(contactInformation.ActorA, contactInformation.ActorB, events);<br />
}<br />
}<br />
}<br />
private void CapsuleAndGroundPlaneContact(Actor a, Actor b, ContactPairFlag events)<br />
{ <br />
if (b.Name == "b")<br />
{<br />
float Volume = (a.LinearVelocity.X + a.LinearVelocity.Z) / 2;<br />
Sound.PlayBoard(Volume);<br />
}<br />
else<br />
{<br />
float Volume = (a.LinearVelocity.X + a.LinearVelocity.Z) / 2;<br />
Sound.PlayBall1(Volume);<br />
}<br />
}<br />
}<br />
}<br />
<br />
As you can see, in this class also manages audio that sounds when the balls is collide in this game.<br />
<br />
namespace _GAME_<br />
{<br />
public class Sound<br />
{<br />
static SoundEffect soundEffectPopadanie;<br />
static SoundEffect soundEffectBitokLost;<br />
static SoundEffect soundEffectCue;<br />
static SoundEffect soundEffectBorder;<br />
static SoundEffect soundEffectBall1;<br />
static SoundEffect soundEffectBall2;<br />
static SoundEffect soundEffectBall3;<br />
static SoundEffect soundEffectBall4;<br />
static SoundEffect soundEffectMusik;<br />
static SoundEffect soundEffectAplod;<br />
static SoundEffectInstance soundInstancePopadanie;<br />
static SoundEffectInstance soundInstanceBitokLost;<br />
static SoundEffectInstance soundInstanceCue;<br />
static SoundEffectInstance soundInstanceBorder;<br />
static SoundEffectInstance soundInstanceBall1;<br />
static SoundEffectInstance soundInstanceBall2;<br />
static SoundEffectInstance soundInstanceBall3;<br />
static SoundEffectInstance soundInstanceBall4;<br />
static SoundEffectInstance soundInstanceMusik;<br />
static SoundEffectInstance soundInstanceAplod;<br />
Random rnd = new Random();<br />
public static void Init(Game game)<br />
{<br />
soundEffectBitokLost = game.Content.Load<SoundEffect>("Sound/Piu");<br />
soundInstanceBitokLost = soundEffectBitokLost.CreateInstance();<br />
soundEffectPopadanie = game.Content.Load<SoundEffect>("Sound/in");<br />
soundInstancePopadanie = soundEffectPopadanie.CreateInstance(); <br />
soundEffectCue = game.Content.Load<SoundEffect>("Sound/cue");<br />
soundInstanceCue = soundEffectCue.CreateInstance();<br />
soundEffectBorder = game.Content.Load<SoundEffect>("Sound/border");<br />
soundInstanceBorder = soundEffectBorder.CreateInstance();<br />
soundEffectBall1 = game.Content.Load<SoundEffect>("Sound/ball1");<br />
soundInstanceBall1 = soundEffectBall1.CreateInstance();<br />
soundEffectBall2 = game.Content.Load<SoundEffect>("Sound/ball2");<br />
soundInstanceBall2 = soundEffectBall2.CreateInstance();<br />
soundEffectBall3 = game.Content.Load<SoundEffect>("Sound/ball3");<br />
soundInstanceBall3 = soundEffectBall2.CreateInstance();<br />
soundEffectBall4 = game.Content.Load<SoundEffect>("Sound/ball4");<br />
soundInstanceBall4 = soundEffectBall4.CreateInstance();<br />
soundEffectMusik = game.Content.Load<SoundEffect>("Sound/TwinPeaks");<br />
soundInstanceMusik = soundEffectMusik.CreateInstance();<br />
soundInstanceMusik.IsLooped = true;<br />
soundEffectAplod = game.Content.Load<SoundEffect>("Sound/aplod");<br />
soundInstanceAplod = soundEffectAplod.CreateInstance();<br />
}<br />
public static void PlayCue(float volume)<br />
{<br />
soundInstanceCue.Volume = volume;<br />
soundInstanceCue.Play();<br />
}<br />
public static void PlayBall1(float volume)<br />
{<br />
//int n = rnd.Next(1, 5);<br />
float v = Math.Abs(volume / 10);<br />
//if (n == 1)<br />
//{<br />
// soundInstanceBall1.Volume = Clamp(0, 1, v);<br />
// soundInstanceBall1.Play(); <br />
//}<br />
//else if (n == 2)<br />
//{<br />
// soundInstanceBall2.Volume = Clamp(0, 1, v);<br />
// soundInstanceBall2.Play();<br />
//}<br />
//else if (n == 3)<br />
//{<br />
// soundInstanceBall3.Volume = Clamp(0, 1, v);<br />
// soundInstanceBall3.Play();<br />
//}<br />
//else if (n == 4)<br />
//{<br />
// soundInstanceBall4.Volume = Clamp(0, 1, v);<br />
// soundInstanceBall4.Play();<br />
//}<br />
if (soundInstanceBall1.State == SoundState.Stopped)<br />
{<br />
soundInstanceBall1.Volume = Clamp(0, 1, v);<br />
soundInstanceBall1.Play();<br />
}<br />
else if (soundInstanceBall2.State == SoundState.Stopped)<br />
{<br />
soundInstanceBall2.Volume = Clamp(0, 1, v);<br />
soundInstanceBall2.Play();<br />
}<br />
else if (soundInstanceBall3.State == SoundState.Stopped)<br />
{<br />
soundInstanceBall3.Volume = Clamp(0, 1, v);<br />
soundInstanceBall3.Play();<br />
}<br />
else if (soundInstanceBall4.State == SoundState.Stopped)<br />
{<br />
soundInstanceBall4.Volume = Clamp(0, 1, v);<br />
soundInstanceBall4.Play();<br />
}<br />
}<br />
public static void PlayBoard(float volume)<br />
{<br />
float v = Math.Abs(volume / 2);<br />
soundInstanceBorder.Volume = Clamp(0, 1, v);<br />
soundInstanceBorder.Play();<br />
}<br />
public static void PlayPopadanie()<br />
{<br />
soundInstancePopadanie.Play(); <br />
}<br />
public static void BitokLost()<br />
{<br />
soundInstanceBitokLost.Play();<br />
}<br />
public static void PlayMusik()<br />
{<br />
soundEffectMusik.Play();<br />
}<br />
public static void PlayAplod()<br />
{<br />
if (soundInstanceAplod.State == SoundState.Stopped)<br />
{<br />
soundInstanceAplod.Play();<br />
}<br />
}<br />
static float Clamp(float min, float max, float value)<br />
{<br />
if (value < min)<br />
value = 0;<br />
if (value > 1)<br />
value = 1; <br />
return value;<br />
}<br />
}<br />
}<br />
<br />
To be continue...</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-46362837714382567552012-03-12T02:29:00.001-07:002012-04-12T05:31:08.624-07:00Практическое применение XNA. Часть 10.<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-ansi-language: RU; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: RU; mso-no-proof: yes;"><strong><span style="font-size: large;">Генерация примитивов (Primitives)</span></strong></span><br />
<br />
<span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-ansi-language: RU; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: RU; mso-no-proof: yes;">Сегодня я хочу рассказать об альтернативном способе "рисования" на платформе XNA. До этого, что бы отобразить объект на экране, мы использовали модель. Это в большинстве случаев, оправдано если объект имеет достаточно сложную геометрическую форму. Однако для рисования простой геометрии, в XNA есть возможность создания простых примитивов.</span><span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-ansi-language: RU; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: RU; mso-no-proof: yes;"></span><br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">В этой главе мы с вами рассмотрим принципы создания простых обьектов из самых простых объектов, таких как точки и линии. Вообще любые примитивы "ручного" изготовления в XNA отрисовуются с помощью метода.</span><span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Courier New"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">DrawUserPrimitives<>()</span></span><span style="font-family: "Courier New"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;"><span style="font-family: "Times New Roman", "serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Начнем наше изучение с самого простейшего объекта - точки. Для того, что бы нарисовать точки нам надо</span><span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"> <span style="font-size: small;">создать список 3D вершин, которые определяют рисуемые точки. Для этого создадим отдельный метод с названием.</span></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; line-height: 115%;"></span><span style="font-family: Times New Roman;"><span style="font-size: small;"></span></span><br />
<span style="font-family: "Courier New"; line-height: 115%; mso-ansi-language: RU; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">InitializePointList()</span></span></div>
<span style="font-family: Times New Roman;"><span style="font-size: small;"></span><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPDVol-Svy0J_CTsy04kVxM1WBCNQ1mKnsEo0nUR__e-no275yk2ngberfT4FS1QCmlB3WqqWBN1BmQJrVMCo-8rRMYpI7lady_Gy8aiskdqTV4qYzbnF-h3Yj22DujqXHwYq_kka8GHtI/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPDVol-Svy0J_CTsy04kVxM1WBCNQ1mKnsEo0nUR__e-no275yk2ngberfT4FS1QCmlB3WqqWBN1BmQJrVMCo-8rRMYpI7lady_Gy8aiskdqTV4qYzbnF-h3Yj22DujqXHwYq_kka8GHtI/s320/XNA.jpg" width="320" /></a></div>
<br />Результат так же визуально не отличается от предыдущего примера. <br /><div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">Так схематично можно представить индексацию или топологию примитивов.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhXeTJF6yBsa-IJjIOca1cft7uvvUwd4jxl4y-U2eLYgiM8lZqGesjslascyMrNmiBOH8nEZcbIGCxEwrbIYBy_KI_tLPpBYq1D1a_X_GvmvKUfbHjkwSUg0wgVStSNyFY8CazvVSb9615/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhXeTJF6yBsa-IJjIOca1cft7uvvUwd4jxl4y-U2eLYgiM8lZqGesjslascyMrNmiBOH8nEZcbIGCxEwrbIYBy_KI_tLPpBYq1D1a_X_GvmvKUfbHjkwSUg0wgVStSNyFY8CazvVSb9615/s320/XNA.jpg" width="320" /></a></div>
<br />В XNA есть еще один менее распространенный способ отрисовки треугольников. Это TriangleFan. Справедливости ради надо упомянуть и о нем. В переводе с английского слово Fun означает - веер. Уже из названия многим станет понятно, что и треугольники должны распологаться в виде веера.<br /><br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-qfjvY6C_VIS6a5-osWyaIsnmz1xAMlVu7mciVaj7X25eEe3gKugd5D3GIQfUTv3VLHYa8JrHgp25Rn7uEnb8zJbNZyrMdFhKNtW3XkuP8C2gX-VOgiqz37DdjJMn6osOvK_DqyjKCd6N/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-qfjvY6C_VIS6a5-osWyaIsnmz1xAMlVu7mciVaj7X25eEe3gKugd5D3GIQfUTv3VLHYa8JrHgp25Rn7uEnb8zJbNZyrMdFhKNtW3XkuP8C2gX-VOgiqz37DdjJMn6osOvK_DqyjKCd6N/s320/XNA.jpg" width="320" /></a></div>
<br />На основе предыдущих примеров вы должны были понять как создаются вершины и индексы, и для тех кто это понял я предлагаю в качестве тренировки самостоятельно сделать такой пример.<br /><br />В этой главе, мы рассмотрели и вершины, и полигоны. Теперь пришло время на основании этих знаний создать более сложный примитив, например куб.<br />Для этого создадим новый проект под названием myCube и создадим в нем уже известные и необходимые нам вершинный и индексный буферы.<br />private void InitializeVertices()<br />{<br /> vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);<br /> vertices = new VertexPositionColor[8];<br /> vertices[0] = new VertexPositionColor(new Vector3(0, 300, 300), Color.White);<br /> vertices[1] = new VertexPositionColor(new Vector3(300, 300, 300), Color.White);<br /> vertices[2] = new VertexPositionColor(new Vector3(300, 0, 300), Color.White);<br /> vertices[3] = new VertexPositionColor(new Vector3(0, 0, 300), Color.White);<br /> vertices[4] = new VertexPositionColor(new Vector3(0, 300, 0), Color.White);<br /> vertices[5] = new VertexPositionColor(new Vector3(300, 300, 0), Color.White);<br /> vertices[6] = new VertexPositionColor(new Vector3(300, 0, 0), Color.White);<br /> vertices[7] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);<br /> // Вершинный буфер для точек<br /> vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.SizeInBytes * (vertices.Length), BufferUsage.None);<br /> // передаем массив точек в вершинный буфер<br />vertexBuffer.SetData<VertexPositionColor>(vertices);<br />}<br />private void InitializeTriangleCube()<br />{<br /> // массив индексов для треугольников<br /> triangleStripIndices = new short[36] { 0, 1, 2, <br /> 2, 3, 0,<br /> 5, 4, 7, <br /> 7, 6, 5,<br /> 4, 5, 1,<br /> 1, 0, 4,<br /> 3, 2, 6,<br /> 6, 7, 3,<br /> 1, 5, 6,<br /> 6, 2, 1,<br /> 4, 0, 3,<br /> 3, 7, 4};<br /> }<br />Количество индексов взято 36 потому что у куба шесть сторон, на каждой из них лежит по два треугольника, каждый из которых имеет три вершины. А значит получается 6 * 2 * 3 = 36. В данном случае самое важное не запутаться с расстановкой индексов. И после создания вершин и индексов отправляем данные в метод Draw<br /> protected override void Draw(GameTime gameTime)<br /> {<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br /> GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;<br /> GraphicsDevice.RenderState.CullMode = CullMode.None;<br /> GraphicsDevice.VertexDeclaration = vertexDeclaration;<br /> effect.World = Matrix.CreateRotationX(0.01f) * Matrix.CreateRotationY(0.01f) * Matrix.CreateRotationZ(0.01f) * effect.World;<br /> effect.Begin();<br /> foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br /> {<br /> pass.Begin();<br /> graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, 8, triangleStripIndices, 0, 12);<br /> pass.End();<br /> }<br /> effect.End();<br /> base.Draw(gameTime);<br /> }<br />используя PrimitiveType.TriangleList для рисования полигонов. В результате у нас получился самый настоящий куб.<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidRRzFk3soZvzB70QykUdHvxX3UWlGmKpxHL53osqFcX7LyNi04clw4LQaVvJGwJS66sjk72JZynlToiDuwLN3mw39FajnDMVfzAsO5cIERtB2ih7qaY4AJ5FvbnNIat2gLkNxrBm7vEyl/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidRRzFk3soZvzB70QykUdHvxX3UWlGmKpxHL53osqFcX7LyNi04clw4LQaVvJGwJS66sjk72JZynlToiDuwLN3mw39FajnDMVfzAsO5cIERtB2ih7qaY4AJ5FvbnNIat2gLkNxrBm7vEyl/s320/XNA.jpg" width="320" /></a></div>
<br />На данный момент у нас получился куб состоящий из сетки, как и все трехмерные модели. Однако в компьютерных играх, модели кажутся нам объемными, стены гладкие, земля хоть и не гладкая, но и не сетчатая. Как же нам превратить наш куб в подобие вышесказанного. Если мы укажем рендеру режим заливки<br />GraphicsDevice.RenderState.FillMode = FillMode.Solid;<br />то получим белый куб, такой как на рисунке<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9M4r_8-LX1MAvudt1XaAaFtjq-oO6rS_y13p4nAisCRvj1AI3PtyMr6FrflH8BFZAWl3F0Gr1VBpMa9mjy1lAR9sxFLIikda5ZxciSdOWdycOMfpLTvxyeexl_FdhKh4fDJJLxzx6KcHs/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9M4r_8-LX1MAvudt1XaAaFtjq-oO6rS_y13p4nAisCRvj1AI3PtyMr6FrflH8BFZAWl3F0Gr1VBpMa9mjy1lAR9sxFLIikda5ZxciSdOWdycOMfpLTvxyeexl_FdhKh4fDJJLxzx6KcHs/s320/XNA.jpg" width="320" /></a></div>
<br />а если помимо этого мы заставим наш шейдер выводить на экран цвет вершин, при этом между двумя разноцветными вершинами, цвет линейно интерполируется автоматически. <br /><br />effect.VertexColorEnabled = true;<br />И в момент создания вершин мы каждой из них присвоим различные цвета<br /> vertices[0] = new VertexPositionColor(new Vector3(0, 300, 300), Color.White);<br /> vertices[1] = new VertexPositionColor(new Vector3(300, 300, 300), Color.Green);<br /> vertices[2] = new VertexPositionColor(new Vector3(300, 0, 300), Color.Blue);<br /> vertices[3] = new VertexPositionColor(new Vector3(0, 0, 300), Color.Red);<br /> vertices[4] = new VertexPositionColor(new Vector3(0, 300, 0), Color.Yellow);<br /> vertices[5] = new VertexPositionColor(new Vector3(300, 300, 0), Color.Violet);<br /> vertices[6] = new VertexPositionColor(new Vector3(300, 0, 0), Color.Pink);<br /> vertices[7] = new VertexPositionColor(new Vector3(0, 0, 0), Color.Black); <br />то получим раскрашенный куб с разноцветными сторонами<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDYDaha26gYIVFEpKZv-gpTQ8V_xY_edaJCnag_wLrJwOnq4WYJ7UWj79C86ZrBmwm9WuflgPeVAXp2xtQe_O3qSq67fDOl9qMZrQmBi8ZbKfBD8JE01uYAflK9xfDm-bdIq3b2ZCXmWS/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDDYDaha26gYIVFEpKZv-gpTQ8V_xY_edaJCnag_wLrJwOnq4WYJ7UWj79C86ZrBmwm9WuflgPeVAXp2xtQe_O3qSq67fDOl9qMZrQmBi8ZbKfBD8JE01uYAflK9xfDm-bdIq3b2ZCXmWS/s320/XNA.jpg" width="320" /></a></div>
<br />Завершающим этапом знакомства с генерированием примитивов можно назвать текстурирование. Создадим новый проект с названием myCubeTextured. Для корректного отображения нам понадобятся вершинный и индексный буферы.<br />VertexBuffer vertexBuffer;<br />IndexBuffer indexBuffer;<br />И естественно структура для хранения информации о вершинах. В отличии от предыдущего примера мы теперь будем использовать <br />VertexPositionTexture[] cubeVertices;<br />а не <br />VertexPositionColored[] cubeVertices;<br />Потому, что теперь вершина должна хранить не цвет, а координату текстуры и, само собой разумеется, позицию в трехмерном пространстве. Еще нам потребуется массив индексов, так же как и в предыдущем примере. <br />short[] cubeIndices;<br />Его величина по прежнему остаётся равной 36, по той же причине, ведь мы собираемся рисовать куб с помощью TriangleList, а не TriangleStrip. Далее следуют два метода которые заполнят данными оба буфера. <br /> private void InitializeVertices()<br /> {<br /> vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);<br /> cubeVertices = new VertexPositionTexture[36];<br /><br /> //Установка параметров точек, которые будут использованы для рисования фигуры<br /> Vector3 topLeftFront = new Vector3(0, 300.0f, 300.0f);<br /> Vector3 bottomLeftFront = new Vector3(0, 0, 300.0f);<br /> Vector3 topRightFront = new Vector3(300.0f, 300.0f, 300.0f);<br /> Vector3 bottomRightFront = new Vector3(300.0f, 0, 300.0f);<br /> Vector3 topLeftBack = new Vector3(0, 300.0f, 0);<br /> Vector3 topRightBack = new Vector3(300.0f, 300.0f, 0);<br /> Vector3 bottomLeftBack = new Vector3(0, 0, 0);<br /> Vector3 bottomRightBack = new Vector3(300.0f, 0, 0);<br /> // Координаты текстуры<br /> Vector2 textureTopLeft = new Vector2(0.0f, 0.0f);<br /> Vector2 textureTopRight = new Vector2(1.0f, 0.0f);<br /> Vector2 textureBottomLeft = new Vector2(0.0f, 1.0f);<br /> Vector2 textureBottomRight = new Vector2(1.0f, 1.0f);<br /> // Массив для хранения списка вершин<br /> // Он используется для передачи данных в вершинный буфер<br /> cubeVertices = new VertexPositionTexture[36];<br /> // Передняя часть фигуры<br /> cubeVertices[0] =<br /> new VertexPositionTexture(<br /> topLeftFront, textureTopLeft); // 0<br /> cubeVertices[1] =<br /> new VertexPositionTexture(<br /> bottomLeftFront, textureBottomLeft); // 1<br /> cubeVertices[2] =<br /> new VertexPositionTexture(<br /> topRightFront, textureTopRight); // 2<br /> cubeVertices[3] =<br /> new VertexPositionTexture(<br /> bottomRightFront, textureBottomRight); // 3<br /> // Задняя часть фигуры<br /> cubeVertices[4] =<br /> new VertexPositionTexture(<br /> topLeftBack, textureTopRight); // 4<br /> cubeVertices[5] =<br /> new VertexPositionTexture(<br /> topRightBack, textureTopLeft); // 5<br /> cubeVertices[6] =<br /> new VertexPositionTexture(<br /> bottomLeftBack, textureBottomRight); //6<br /> cubeVertices[7] =<br /> new VertexPositionTexture(<br /> bottomRightBack, textureBottomLeft); // 7<br /> // Верхняя часть фигуры<br /> cubeVertices[8] =<br /> new VertexPositionTexture(<br /> topLeftFront, textureBottomLeft); // 8<br /> cubeVertices[9] =<br /> new VertexPositionTexture(<br /> topRightBack, textureTopRight); // 9<br /> cubeVertices[10] =<br /> new VertexPositionTexture(<br /> topLeftBack, textureTopLeft); // 10<br /> cubeVertices[11] =<br /> new VertexPositionTexture(<br /> topRightFront, textureBottomRight); // 11<br /> // Нижняя часть фигуры<br /> cubeVertices[12] =<br /> new VertexPositionTexture(<br /> bottomLeftFront, textureTopLeft); // 12<br /> cubeVertices[13] =<br /> new VertexPositionTexture(<br /> bottomLeftBack, textureBottomLeft); // 13<br /> cubeVertices[14] =<br /> new VertexPositionTexture(<br /> bottomRightBack, textureBottomRight); // 14<br /> cubeVertices[15] =<br /> new VertexPositionTexture(<br /> bottomRightFront, textureTopRight); // 15<br /> // Левая часть фигуры<br /> cubeVertices[16] =<br /> new VertexPositionTexture(<br /> topLeftFront, textureTopRight); // 16<br /> cubeVertices[17] =<br /> new VertexPositionTexture(<br /> bottomLeftFront, textureBottomRight); // 17<br /> cubeVertices[18] =<br /> new VertexPositionTexture(<br /> topRightFront, textureTopLeft); // 18<br /> cubeVertices[19] =<br /> new VertexPositionTexture(<br /> bottomRightFront, textureBottomLeft); // 19<br /><br /> // Вершинный буфер для точек<br /> vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionTexture.SizeInBytes * (cubeVertices.Length), BufferUsage.None);<br /> // передаем массив точек в вершинный буфер<br /> vertexBuffer.SetData<VertexPositionTexture>(cubeVertices);<br /> }<br />Подготовили вершинный буфер<br /> private void InitializeIndices()<br /> {<br /> // С помощью этого массива определяем, к каким частям фигуры<br /> //Относятся те или иные компоненты массива cubeVertices<br /> cubeIndices = new short[] {<br /> 0, 1, 2, // Передняя //плоскость<br /> 1, 3, 2,<br /> 4, 5, 6, // Задняя //плоскость<br /> 6, 5, 7,<br /> 8, 9, 10, // Верхняя //плоскость<br /> 8, 11, 9,<br /> 12, 13, 14, // Нижняя //плоскость<br /> 12, 14, 15,<br /> 16, 13, 17, // Левая //плоскость<br /> 10, 13, 16,<br /> 18, 19, 14, // Правая //плоскость<br /> 9, 18, 14 };<br /> //Индексный буфер<br /> indexBuffer = new IndexBuffer(graphics.GraphicsDevice,<br /> sizeof(short) * cubeIndices.Length,<br /> BufferUsage.None,<br /> IndexElementSize.SixteenBits);<br /> //Добавляем данные в индексный буфер<br /> indexBuffer.SetData<short>(cubeIndices);<br /> }<br />Подготовили индексный буфер. В качестве эфекта для отображения используем старый добрый BasicEffect. Осталось только передать данные в метод Draw для рисования, и не забыть передать шейдеру саму текстуру, которую мы хотим видеть на сторонах нашего куба. Как все это сделать, показано ниже.<br /> protected override void Draw(GameTime gameTime)<br /> {<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br /> GraphicsDevice.RenderState.FillMode = FillMode.Solid;<br /> GraphicsDevice.RenderState.CullMode = CullMode.None;<br /> GraphicsDevice.VertexDeclaration = vertexDeclaration; <br /> graphics.GraphicsDevice.Indices = indexBuffer; <br /> graphics.GraphicsDevice.Vertices[0].SetSource(vertexBuffer, 0, VertexPositionTexture.SizeInBytes);<br /> effect.World = Matrix.CreateRotationX(0.01f) * Matrix.CreateRotationY(0.01f) * Matrix.CreateRotationZ(0.01f) * effect.World;<br /> effect.TextureEnabled = true;<br /> effect.Texture = Content.Load<Texture2D>("picture");<br /> effect.Begin();<br /> foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br /> {<br /> pass.Begin();<br /> graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, cubeVertices, 0, cubeVertices.Length, cubeIndices, 0, 12);<br /> pass.End();<br /> }<br /> effect.End();<br /> base.Draw(gameTime);<br /> }<br /><br />И если вы всё сделали верно, то результатом станет следующее изображение.<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq_cRMwF2kimEwSMIR7GcSFvT5QWsT6fMSLbu_hJ-N2vpsC0PlEpfZXgh_srd_gBDfnkU0uH4-b24ZbSPp7Bi7SPCv-yuvFs_KIOirzjyavw81WU4DIhRjnjoYR0viE3TQg1Zj7IMym5dS/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq_cRMwF2kimEwSMIR7GcSFvT5QWsT6fMSLbu_hJ-N2vpsC0PlEpfZXgh_srd_gBDfnkU0uH4-b24ZbSPp7Bi7SPCv-yuvFs_KIOirzjyavw81WU4DIhRjnjoYR0viE3TQg1Zj7IMym5dS/s320/XNA.jpg" width="320" /></a></div>
<br />На этом мы заканчиваем знакомство с примитивами. В качестве упражнения советую вам создать несколько собственных геометрических фигур на досуге. Принципы создания одинаковы как для кубов, так и для всех остальных объектов. <br /><br /><br /><br /><br /><br /><div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
</span><br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Courier New"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"></span></div>
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;"><span style="mso-spacerun: yes;"> </span>в нем создадим два экземпляра классов</span> </span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; line-height: 115%;">VertexDeclaration vertexDeclaration;<br />VertexPositionColor[] pointList;<br />vertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionColor.VertexElements);<br />pointList = new VertexPositionColor[8];</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; line-height: 115%;">и заполним pointList в цикле</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">for (int x = 0; x < points / 2; x++)<br />{<br /> for (int y = 0; y < 2; y++)<br /> {<br /> pointList[(x * 2) + y] = new VertexPositionColor(new Vector3(x * 100, y * 100, 0), Color.White);<br /> }<br />}</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">указав каждой вершине ее позицию в пространстве и ее цвет. После этого нам в обязательном порядке нужно создать вершинный буфер для точек (вершин)</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">vertexBuffer = new VertexBuffer(GraphicsDevice, VertexPositionColor.SizeInBytes * (pointList.Length), BufferUsage.None);</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">и передать массив точек в вершинный буфер</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">vertexBuffer.SetData<VertexPositionColor>(pointList);</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">Не лишним будет подумать и об шейдере с помощью которого мы будем выводить на экран наши точки. Воспользуемся старым добрым Basic Effect, но его инициализацию осуществим в методе </span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">protected override void Initialize()</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">а не в методе отрисовки точек. Выглядит это вот так:<br /> protected override void Initialize()<br /> {<br /> effect = new BasicEffect(graphics.GraphicsDevice, null);<br /> effect.World = Matrix.CreateTranslation<br /> (GraphicsDevice.Viewport.Width / 2f - 150,<br /> GraphicsDevice.Viewport.Height / 2f - 50, 0);<br /> effect.View = Matrix.CreateLookAt<br /> (new Vector3(0.0f, 0.0f, 1.0f), <br /> Vector3.Zero, Vector3.Up);<br /> effect.Projection = Matrix.CreateOrthographicOffCenter<br /> (0, (float)GraphicsDevice.Viewport.Width, <br /> (float)GraphicsDevice.Viewport.Height, 0, 1.0f, 1000.0f);<br /> <br /> InitializePointList();<br /> base.Initialize();<br /> }<br />как видите все параметры касающиеся матриц создаются и присваиваются здесь. После подготовки нашего эффекта нам остаётся лишь передать наш PointList в метод Draw для отрисовки.<br />protected override void Draw(GameTime gameTime)<br />{<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br /> GraphicsDevice.VertexDeclaration = vertexDeclaration;<br /> GraphicsDevice.RenderState.PointSize = 10;<br /> effect.Begin();</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;"> foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br /> {<br /> pass.Begin();<br /> graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.PointLi st, pointList, 0, 8);<br /> pass.End();<br /> }<br /> effect.End();</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;"> base.Draw(gameTime);<br />}<br />как видите здесь мы установили размер точки равной 10 , что бы лучше было видно, и методом DrawUserPrimitives() нарисовали наши вершины.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUI10pOeO72TpJ_OEWdhN-iklpVUZEHEDmFyfGVG4xRIwQugPs3fr_lP7NTzix5ME80IqCn7CRByGWL7Ss7lvAv3KUm_RdYgAM6rkmCVbMOGQctqbpEWCOVZDF_zmvIZdeKzKTEzt77fLv/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="234" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUI10pOeO72TpJ_OEWdhN-iklpVUZEHEDmFyfGVG4xRIwQugPs3fr_lP7NTzix5ME80IqCn7CRByGWL7Ss7lvAv3KUm_RdYgAM6rkmCVbMOGQctqbpEWCOVZDF_zmvIZdeKzKTEzt77fLv/s320/XNA.jpg" width="320" /></a></div>
при этом передавая первым параметром тип примитива, в нашем случае это точки (тип примитива определяет как данные в переданном массиве вершин будут интерпретированы и показаны), вторым параметром данные о точках (позиция и цвет) хранящиеся в массиве pointList, третьим параметром номер примитива с которого начинать рисовать (у нас это может быть значение от 0 до 8), и накнец, четвертым параметром количество примитивов для отрисовки начиная с указанного. Исходный код примера находится на диске в папке Samples / myPoints.<br />Аналогично точкам можно рисовать линии. Рисуются они от точки до точки по кратчайшему расстоянию. Для наглядной реализации мы добавим к нашему примеру метод создания индексов для линий.<br /> private void InitializeLineList()<br /> {<br /> // массив индексов для линий<br /> lineListIndices = new short[(8 * 2) - 2];<br /> // заполняем индексы<br /> for (int i = 0; i < 8 - 1; i++)<br /> {<br /> lineListIndices[i * 2] = (short)(i);<br /> lineListIndices[(i * 2) + 1] = (short)(i + 1);<br /> }<br /> }<br />Другими словами, данный код равнозначен следующему и определяет линии соеденяющие наши точки - pointList[0] и pointList[1], pointList[1] и pointList[2], и так далее 7 линий между 8-ми точек.<br />lineListIndices = new short[14]{ 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7 };<br />а в методе Draw вместо функции <br />DrawUserPrimitives()<br />применим функцию <br />DrawUserIndexedPrimitives()<br />и наш метод для рисования линий станет таким<br />protected override void Draw(GameTime gameTime)<br />{<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br /> GraphicsDevice.VertexDeclaration = vertexDeclaration;<br /> effect.Begin();<br /> foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br /> {<br /> pass.Begin();<br /> graphics.GraphicsDevice.DrawUserIndexedPrimitives<br /> (PrimitiveType.LineList,<br /> pointList,<br /> 0,<br /> 8,<br /> lineListIndices,<br /> 0,<br /> 7);<br /> pass.End();<br /> }<br /> effect.End();<br /> base.Draw(gameTime);<br />}<br />где первым параметром передается тип примитива LineList, далее все так же как и с точками до пятого параметра,а им передаётся LineListIndices массив индексов, шестым и седьмым параметрами номер и количество линий. В качестве шейдера по прежнему используется BasicEffect. На рисунке ниже изображен результат этих изменений.<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio7h1R1y7DtHOeHbqLjKNJsod92kZZtLc8JYSmdHXPJxLlO0cKZ4Y0SJ_p2Cfy4yYMhmiZXO2Vii1XMIu_HHfwLekwmg1C5zslWY17yYC4aEixIlowCahABaKu5WN3W4CRXy2CasDCzzR-/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio7h1R1y7DtHOeHbqLjKNJsod92kZZtLc8JYSmdHXPJxLlO0cKZ4Y0SJ_p2Cfy4yYMhmiZXO2Vii1XMIu_HHfwLekwmg1C5zslWY17yYC4aEixIlowCahABaKu5WN3W4CRXy2CasDCzzR-/s320/XNA.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman", "serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;"></span></span> Следующий пример показывает как вместо линий рисовать, так называемую, ленту линий или LineStrip.<br />
Вся разница между этим и предыдущим примером заключается в том, что для ленты нам понадобится в двое меньше индексов так как лента подразумевает последовательно соединенные примитивы (точки). Метод для создания индексов теперь будет выглядеть так.<br />
private void InitializeLineStrip()<br />
{<br />
// массив индексов для линий<br />
lineStripIndices = new short[8];</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// заполняем индексы<br />
for (int i = 0; i < 8; i++)<br />
{<br />
lineStripIndices[i] = (short)(i);<br />
}<br />
}<br />
или <br />
lineStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 };<br />
Важно сказать, что рисование лент с точки зрения производительности более выгодное, так как индексы выводимых вершин содержат меньшее количество повторений. Результат изображен на рисунке ниже и внешне ничем не отличается от предыдущего примера.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio7h1R1y7DtHOeHbqLjKNJsod92kZZtLc8JYSmdHXPJxLlO0cKZ4Y0SJ_p2Cfy4yYMhmiZXO2Vii1XMIu_HHfwLekwmg1C5zslWY17yYC4aEixIlowCahABaKu5WN3W4CRXy2CasDCzzR-/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEio7h1R1y7DtHOeHbqLjKNJsod92kZZtLc8JYSmdHXPJxLlO0cKZ4Y0SJ_p2Cfy4yYMhmiZXO2Vii1XMIu_HHfwLekwmg1C5zslWY17yYC4aEixIlowCahABaKu5WN3W4CRXy2CasDCzzR-/s320/XNA.jpg" width="320" /></a></div>
Следующим этапом будет создание треугольника или полигона. Как уже упоминалось ранее из полигонов состоят практически все трехмерные модели. Сразу скажу, что треугольники, подобно линиям, тоже можно рисовать как из списка так и лентой. Следующий пример показывает способ рисования из списка или TriangleList. Создаем новый массив индексов, в котором будет пронумерованный список треугольников, по 3 вершины на один треугольник. Массив вершин не меняем. Метод для этого выглядит так.<br /> private void InitializeTriangleList()<br />{<br /> int width = 4;<br /> int height = 2;<br /> // массив индексов для треугольников<br /> triangleListIndices = new short[(width - 1) * (height - 1) * 6];<br /> for (int x = 0; x < width - 1; x++)<br /> {<br /> for (int y = 0; y < height - 1; y++)<br /> {<br /> triangleListIndices[(x + y * (width - 1)) * 6] = (short)(2 * x);<br /> triangleListIndices[(x + y * (width - 1)) * 6 + 1] = (short)(2 * x + 1);<br /> triangleListIndices[(x + y * (width - 1)) * 6 + 2] = (short)(2 * x + 2);<br /> triangleListIndices[(x + y * (width - 1)) * 6 + 3] = (short)(2 * x + 2);<br /> triangleListIndices[(x + y * (width - 1)) * 6 + 4] = (short)(2 * x + 1);<br /> triangleListIndices[(x + y * (width - 1)) * 6 + 5] = (short)(2 * x + 3);<br /> }<br /> }<br /> }<br />Этот код индексирует вершины так что каждые 3 точки у нас образуют один треугольник, причем в каждом треугольнике есть общие вершины с соседним. Ниже аналог этого кода <br />triangleListIndices = new short[18]{ 0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5, 4, 5, 6, 6, 5, 7 };<br />Выводим список треугольников на экран используя функцию DrawUserIndexedPrimitives() которая в качестве первого аргумента принимает тип выводимого примитива - PrimitiveType.TriangleList. И еще, перед отрисовкой важно указать видеокарте режим рендеринга.<br />GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;<br />GraphicsDevice.RenderState.CullMode = CullMode.None;<br />Здесь мы назначили режим отображения обьектов в виде сетки.<br />В результате метод Draw будет выглядеть так:<br /> protected override void Draw(GameTime gameTime)<br /> {<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br /> GraphicsDevice.RenderState.FillMode = FillMode.WireFrame;<br /> GraphicsDevice.RenderState.CullMode = CullMode.None;<br /> GraphicsDevice.VertexDeclaration = vertexDeclaration;<br /> effect.Begin();<br /> foreach (EffectPass pass in effect.CurrentTechnique.Passes)<br /> {<br /> pass.Begin();<br /> graphics.GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, pointList, 0, 8, triangleListIndices, 0, 6);<br /> pass.End();<br /> }<br /> effect.End();<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPDVol-Svy0J_CTsy04kVxM1WBCNQ1mKnsEo0nUR__e-no275yk2ngberfT4FS1QCmlB3WqqWBN1BmQJrVMCo-8rRMYpI7lady_Gy8aiskdqTV4qYzbnF-h3Yj22DujqXHwYq_kka8GHtI/s1600/XNA.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPDVol-Svy0J_CTsy04kVxM1WBCNQ1mKnsEo0nUR__e-no275yk2ngberfT4FS1QCmlB3WqqWBN1BmQJrVMCo-8rRMYpI7lady_Gy8aiskdqTV4qYzbnF-h3Yj22DujqXHwYq_kka8GHtI/s320/XNA.jpg" width="320" /></a></div>
base.Draw(gameTime);<br /> }<br />а итоговое изображение так:<br /><div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Следующий пример демонстрирует рисование тех же треугольников, но с помощью ленты, а не списка. Как вы уже наверное догадались, изменений потребует лишь метод индексирующий вершины. Этот метод будет выглядеть так<br />
private void InitializeTriangleStrip()<br />
{<br />
// массив индексов для треугольников<br />
triangleStripIndices = new short[8];</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// заполняем индексы<br />
for (int i = 0; i < 8; i++)<br />
{<br />
triangleStripIndices[i] = (short)i;<br />
}<br />
}<br />
он практически идентичен методу InitializeLineStrip()из примера myLineStrip. И результат его деятельности так же можно представить в виде<br />
triangleStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 };<br />
В лентах, треугольники последовательно соеденены между собой, так что индексов нам нужно намного меньше – каждый последующий треугольник использует две вершины предыдущего треугольника. Это наиболее эффективный способ вывода треугольников.</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-16211737090441282092012-03-06T03:13:00.000-08:002012-04-12T05:35:27.513-07:00Практическое применение XNA. Часть 9.<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: x-large;"><strong>Камера (XNA Camera)</strong></span><br />
<br />
<br />
Вы не могли не заметить, что все примеры рассмотренные ранее - неподвижны, то есть в отличии от привычных нам компьютерных игр мы пока не могли перемещаться среди созданых нами обьектов. Теперь пришло время это исправить. В современных играх роль головы игрока или наблюдателя играет так называемая камера - аналог кино или видео камеры. Она должна стать нашими "глазами" в игре. Вообще в компьютерных играх существует несколько видов камер. Зависит это от жанра игры и в зависимости от этого камера наделяется определенными свойствами. Например во всех стрелялках от первого лица должна использоваться камера от первого лица имитируя поведение человека от лица которого вы и играете. Такая камера должна уметь поворачиваться влево вправо вверх вниз, прыгать приседать и ползать. Если же вы решили написать космический симулятор, то ваша камера должна уметь летать так как требует обьект который собственно и будет играть роль летательного аппарата.<br />
<br />
Вы уже должны знать, что за отображение обьектов на экране монитора главным образом отвечают две матрицы. Матрица вида и проекции. А если говорить точнее, то определив однажды матрицу проекции вам останется управлять лишь матрицой вида, если вы не ставите себе цель изменять настройки проекции. Напомню, что матрица проекции может управлять такими эффектами как Zoom (приближение или удаление обьектов), или изменять угол обзора сцены. Итак вырисовуется следующая картина. Для создания собственного приложения с использованием камеры, нам надо отобразить модель любым известным нам способом и написать некоторый код который позволит нам управлять матрицей вида. Для этого создадим новый проект под названием myCamera. И игровой компонент с названием Camera. Он позволит нам отделить код камеры от общего кода и даст нам возможность многократного его использования в других приложениях. <br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using Microsoft.Xna.Framework;<br />
using Microsoft.Xna.Framework.Audio;<br />
using Microsoft.Xna.Framework.Content;<br />
using Microsoft.Xna.Framework.GamerServices;<br />
using Microsoft.Xna.Framework.Graphics;<br />
using Microsoft.Xna.Framework.Input;<br />
using Microsoft.Xna.Framework.Media;<br />
using Microsoft.Xna.Framework.Net;<br />
using Microsoft.Xna.Framework.Storage;<br />
namespace myCamera<br />
{<br />
public class Game1 : Microsoft.Xna.Framework.Game<br />
{<br />
GraphicsDeviceManager graphics;<br />
SpriteBatch spriteBatch;<br />
Model _model;<br />
Camera _camera;<br />
public Game1()<br />
{<br />
graphics = new GraphicsDeviceManager(this);<br />
Content.RootDirectory = "Content";<br />
}<br />
protected override void Initialize()<br />
{<br />
_camera = new Camera(this);<br />
Components.Add(_camera);<br />
base.Initialize();<br />
}<br />
protected override void LoadContent()<br />
{ <br />
spriteBatch = new SpriteBatch(GraphicsDevice);<br />
_model = Content.Load<Model>("torknot");<br />
}<br />
protected override void UnloadContent()<br />
{<br />
<br />
}<br />
protected override void Update(GameTime gameTime)<br />
{<br />
if (Keyboard.GetState().IsKeyDown(Keys.Escape))<br />
{<br />
this.Exit();<br />
}<br />
base.Update(gameTime);<br />
}<br />
protected override void Draw(GameTime gameTime)<br />
{<br />
GraphicsDevice.Clear(Color.CornflowerBlue);<br />
foreach (ModelMesh mesh in _model.Meshes)<br />
{<br />
foreach (BasicEffect effect in mesh.Effects)<br />
{<br />
effect.World = Matrix.CreateTranslation(new Vector3(0, 0, 0));<br />
effect.View = _camera.viewMatrix;<br />
effect.Projection = _camera.projectionMatrix;<br />
effect.EnableDefaultLighting();<br />
}<br />
mesh.Draw();<br />
}<br />
base.Draw(gameTime);<br />
}<br />
}<br />
}<br />
<br />
<br />
Этот код вам должен быть понятен. Здесь мы лишь вывели модель на экран, но в оличии от предыдущих примеров матрицы вида и проекции взяты из класса камеры. Вот о нем дальше реч и пойдет. Для начала мы обьявили переменные обеих матриц.<br />
<br />
public Matrix viewMatrix;<br />
public Matrix projectionMatrix;<br />
<br />
Они, как вы могли догадаться, будут хранить вид и проекцию.<br />
<br />
float _Pitch;<br />
float _Yaw;<br />
<br />
Будут хранить угол поворота камеры вверх вниз и влево вправо<br />
<br />
float distancePerFrame = 10; <br />
<br />
Расстояние которое будет проходить наша камера за один кадр. Меняя это значение мы сможем управлять её скоростью перемещения.<br />
<br />
Vector2 CenterScreen;<br />
<br />
Хранит позицию середины экрана<br />
<br />
private void CenteredCursor()<br />
{<br />
Mouse.SetPosition((int)CenterScreen.X, (int)CenterScreen.Y);<br />
}<br />
<br />
Кроме предусмотренных игровым компонентом методов мы создадим еще один вспомогательный. Он будет возвращать позицию мыши на середину экрана<br />
<br />
public Camera(Game game) : base(game)<br />
{<br />
CenterScreen = <br />
new Vector2(game.GraphicsDevice.Viewport.Width / 2, <br />
game.GraphicsDevice.Viewport.Height / 2);<br />
viewMatrix = Matrix.CreateLookAt(new Vector3(5, 5, 500), <br />
new Vector3(0, 0, 0), <br />
new Vector3(0, 1, 0));<br />
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), <br />
game.GraphicsDevice.Viewport.Width / <br />
game.GraphicsDevice.Viewport.Height, <br />
0.1f, 1000.0f);<br />
CenteredCursor();<br />
}<br />
<br />
<br />
В конструкторе класса мы определии центр игрового экрана, создали матрицы вида и проекции и поставили указатель мыши на середину экрана. Далее в методе Update() <br />
<br />
Vector2 cursorPosition = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);<br />
Vector2 deltaDampened = (cursorPosition - CenterScreen) * 0.0015f;<br />
<br />
мы находим текущее положение указателя мыши, оно могло измениться с момента определения. Вычитаем это значение из значения центра экрана и умножаем результат на 0.0015 для того что бы подобрать подходящую нам скорость поворотов камеры. Движение мышью в этом примере будет управлять вращением камеры соответственно. То есть если перемещать мышь вправо, то камера будет поворачиваться тоже вправо. Аналогично влево, вверх, вниз, по диагоналям да и вообще в любом направлении.<br />
<br />
_Yaw -= deltaDampened.X;<br />
_Pitch -= deltaDampened.Y;<br />
<br />
переменные _Yaw и _Pitch получают значения в зависимости от манипуляций мышью. <br />
<br />
if (_Pitch < -1.55f)<br />
{<br />
_Pitch = -1.55f;<br />
}<br />
if (_Pitch > 1.55f)<br />
{<br />
_Pitch = 1.55f;<br />
}<br />
<br />
Здесь мы установили пределы поворотов камеры по вертикали, чтобы она поднималась и опускалась подобно человеческой голове.<br />
<br />
Matrix cameraRotation = Matrix.CreateFromYawPitchRoll(_Yaw,_Pitch,0.0f);<br />
Vector3 translateDirection = Vector3.Zero;<br />
<br />
Здесь мы создали матрицу вращения и вектор перемещения камеры, заметим что при создании cameraRotation мы воспользовались _Yaw и _Pitch переменными. <br />
<br />
KeyboardState states = Keyboard.GetState();<br />
if (states.IsKeyDown(Keys.W))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Forward, cameraRotation);<br />
if (states.IsKeyDown(Keys.S))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Backward, cameraRotation);<br />
if (states.IsKeyDown(Keys.A))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Left, cameraRotation);<br />
if (states.IsKeyDown(Keys.D))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Right, cameraRotation);<br />
if (states.IsKeyDown(Keys.Q))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Up, cameraRotation);<br />
if (states.IsKeyDown(Keys.E))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Down, cameraRotation);<br />
<br />
Здесь мы определили клавиши на клавиатуре которые будут отвечать за перемещение камеры: W - движение вперед, S - движение назад, A - движение влево, D - движение вправо, Q - движение вверх, E - движение вниз. Повороты сюда не входят, за них отвечает позиция мыши. <br />
<br />
Vector3 newPosition = Matrix.Invert(viewMatrix).Translation;<br />
if (translateDirection != Vector3.Zero)<br />
{<br />
newPosition += Vector3.Normalize(translateDirection) * distancePerFrame;<br />
}<br />
Vector3 newForward = Vector3.TransformNormal(Vector3.Forward, cameraRotation);<br />
viewMatrix = Matrix.CreateLookAt(newPosition, newPosition + newForward, Vector3.Up);<br />
<br />
Здесь мы инвертируем текущую позицию камеры, проверяем изменилось ли направление движения камеры, вычисляем новое направление "взгляда" камеры и на основании этих данных, создаётся новая матрица вида.<br />
<br />
CenteredCursor();<br />
<br />
И напоследок вызываем метод возврата курсора на середину экрана. Этим завершается метод Update(). Если теперь запустить приложение, то вы увидите на экране модель и сможете "полетать" вокруг нее с помощью нашей камеры.<br />
<br />
На этом мы заканчиваем главу посвященную камере. Осталось добавить, что получившаяся камера носит название "летающая камера", потому что она может перемещаться во всех направлениях имитируя тем самым полет. Немного поразмыслив вы догадаетесь какие парамерры камеры можно ограничичть, что бы "летающая камера" превратилась в камеру от первого лица. <br />
<br />
Весь код класса камера:<br />
<br />
using System;<br />
using System.Collections.Generic;<br />
using System.Linq;<br />
using Microsoft.Xna.Framework;<br />
using Microsoft.Xna.Framework.Audio;<br />
using Microsoft.Xna.Framework.Content;<br />
using Microsoft.Xna.Framework.GamerServices;<br />
using Microsoft.Xna.Framework.Graphics;<br />
using Microsoft.Xna.Framework.Input;<br />
using Microsoft.Xna.Framework.Media;<br />
using Microsoft.Xna.Framework.Net;<br />
using Microsoft.Xna.Framework.Storage;<br />
<br />
namespace myCamera<br />
{<br />
public class Camera : Microsoft.Xna.Framework.GameComponent<br />
{<br />
public Matrix viewMatrix;<br />
public Matrix projectionMatrix;<br />
Vector2 CenterScreen;<br />
float _Pitch;<br />
float _Yaw;<br />
float distancePerFrame = 10;<br />
public Camera(Game game) : base(game)<br />
{<br />
CenterScreen = new Vector2(game.GraphicsDevice.Viewport.Width / 2, <br />
game.GraphicsDevice.Viewport.Height / 2);<br />
viewMatrix = Matrix.CreateLookAt(new Vector3(5, 5, 500), <br />
new Vector3(0, 0, 0), <br />
new Vector3(0, 1, 0));<br />
projectionMatrix = <br />
Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), <br />
game.GraphicsDevice.Viewport.Width / <br />
game.GraphicsDevice.Viewport.Height, <br />
0.1f, 1000.0f);<br />
CenteredCursor();<br />
}<br />
public override void Update(GameTime gameTime)<br />
{<br />
Vector2 cursorPosition = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);<br />
Vector2 deltaDampened = (cursorPosition - CenterScreen) * 0.0015f;<br />
_Yaw -= deltaDampened.X;<br />
_Pitch -= deltaDampened.Y;<br />
if (_Pitch < -1.55f)<br />
{<br />
_Pitch = -1.55f;<br />
}<br />
if (_Pitch > 1.55f)<br />
{<br />
_Pitch = 1.55f;<br />
}<br />
<br />
Matrix cameraRotation = Matrix.CreateFromYawPitchRoll(_Yaw, _Pitch, 0.0f);<br />
Vector3 translateDirection = Vector3.Zero;<br />
KeyboardState states = Keyboard.GetState();<br />
<br />
if (states.IsKeyDown(Keys.W))<br />
translateDirection += <br />
Vector3.TransformNormal(Vector3.Forward, cameraRotation);<br />
<br />
if (states.IsKeyDown(Keys.S))<br />
translateDirection += <br />
Vector3.TransformNormal(Vector3.Backward, cameraRotation);<br />
<br />
if (states.IsKeyDown(Keys.A))<br />
translateDirection += <br />
Vector3.TransformNormal(Vector3.Left, cameraRotation);<br />
<br />
if (states.IsKeyDown(Keys.D))<br />
translateDirection += <br />
Vector3.TransformNormal(Vector3.Right, cameraRotation);<br />
<br />
if (states.IsKeyDown(Keys.Q))<br />
translateDirection +=<br />
Vector3.TransformNormal(Vector3.Up, cameraRotation);<br />
<br />
if (states.IsKeyDown(Keys.E))<br />
translateDirection += <br />
Vector3.TransformNormal(Vector3.Down, cameraRotation);<br />
<br />
Vector3 newPosition = Matrix.Invert(viewMatrix).Translation;<br />
if (translateDirection != Vector3.Zero)<br />
{<br />
newPosition += Vector3.Normalize(translateDirection) * distancePerFrame;<br />
}<br />
Vector3 newForward = Vector3.TransformNormal(Vector3.Forward, cameraRotation);<br />
viewMatrix = Matrix.CreateLookAt(newPosition, newPosition + newForward, Vector3.Up);<br />
CenteredCursor();<br />
base.Update(gameTime);<br />
}<br />
<br />
private void CenteredCursor()<br />
{<br />
Mouse.SetPosition((int)CenterScreen.X, (int)CenterScreen.Y);<br />
}<br />
}<br />
}<br />
<br />
Результат полета камеры.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy4wlnMqlRdLIm2KA0f-O3wuWhb6zXi4YKG74rmuJ3Meu-4F2zEItayUrvSb3S6cx4UebfV8d_-09Hw-aJULh0ivFjW0RmZvumQoAlUG94R0KrD4lVigBG3QjQthleZ_bB-lNLX-RlzAIo/s1600/XNA23.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="67" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy4wlnMqlRdLIm2KA0f-O3wuWhb6zXi4YKG74rmuJ3Meu-4F2zEItayUrvSb3S6cx4UebfV8d_-09Hw-aJULh0ivFjW0RmZvumQoAlUG94R0KrD4lVigBG3QjQthleZ_bB-lNLX-RlzAIo/s320/XNA23.jpg" width="320" /></a></div>
<br />
<br />
Удачи!<br />
<br />
<br />
<br />
<br />
<br /></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-14029444370942371292012-03-06T02:34:00.000-08:002012-03-06T02:34:02.286-08:00Практическое применение XNA. Часть 8.<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: large;"><b style="mso-bidi-font-weight: normal;"><span style="font-family: "Times New Roman","serif"; line-height: 115%;">4.Введение
в </span></b><b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">HLSL</span></b></span><br />
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<v:shapetype coordsize="21600,21600" filled="f" id="_x0000_t75" o:preferrelative="t" o:spt="75" path="m@4@5l@4@11@9@11@9@5xe" stroked="f"><span style="font-family: Calibri;">
<v:stroke joinstyle="miter">
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0">
<v:f eqn="sum @0 1 0">
<v:f eqn="sum 0 0 @1">
<v:f eqn="prod @2 1 2">
<v:f eqn="prod @3 21600 pixelWidth">
<v:f eqn="prod @3 21600 pixelHeight">
<v:f eqn="sum @0 0 1">
<v:f eqn="prod @6 1 2">
<v:f eqn="prod @7 21600 pixelWidth">
<v:f eqn="sum @8 21600 0">
<v:f eqn="prod @7 21600 pixelHeight">
<v:f eqn="sum @10 21600 0">
</v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:formulas>
<v:path gradientshapeok="t" o:connecttype="rect" o:extrusionok="f">
<o:lock aspectratio="t" v:ext="edit">
</o:lock></v:path></v:stroke></span></v:shapetype><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">Вот мы и
разобрались с выводом моделей на экран. И теперь пришло время поговорить о
шейдерах. Вы не могли не заметить, что наша первая модель появилась на экране
уже раскрашенная в различные цвета, хотя мы не одной строчки для этого не
написали. Кто же за нас поработал? Ответ заключается в следующем. При
добавлении модели в проект, ей автоматически присваевается процессор контента. </span></span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">Если в обозревателе
решений один раз кликнуть мышью на каком-либо файле, будь то текстура, модель,
файл звука или файл эффекта, то в окне свойств появится процессор контента.
Именно он подготовит данные из файла, в формат понятный </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">GameStudio</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">В нашем случае мы
видим, что по умолчанию стоит процессор контента – </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Model</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;"> </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">XNA</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;"> </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">framework</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">. Все аспекты его
деятельности мы сейчас рассматривать не станем, но заметим одно – он установил
эффектом для нашей модели </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">,
а текстурой – текстуру, которая присвоена модели ранее, в той программе, в
которой она была создана. Если вы внимательно читали предыдущий код, то должны
были заметить вот это:<o:p></o:p></span></span></div>
foreach (ModelMesh mesh in myModel.Meshes)<br />{<br /> foreach (BasicEffect effect in mesh.Effects)<br /> {<br /> effect.World = world;<br /> effect.View = view;<br /> effect.Projection = proj;<br /> }<br /> mesh.Draw();<br />}<br />
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%;">В первом цикле </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">foreach</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">мы
проходим по всем сеткам модели, а во втором цикле по всем эффектам для каждой
сетки. Причем ищем </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">,
который нам установил процессор контента. Вот с него мы и начнем знакомство с
шейдерами или иначе сказать эффектами.</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 0pt; mso-layout-grid-align: none; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">Шейдеры – это всего лишь программа, состоящая из набора ключевых слов, которая
позволяет программисту работать напрямую с вершинами и пикселями. Как и в любом
другом языке программирования, освоение шейдеров начинается с понимания общего
принципа работы шейдеров и изучению<span style="mso-spacerun: yes;">
</span>языка программирования, на котором пишутся шейдеры (</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">HLSL</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">). Особенность шейдеров заключается в том, что весь
код, который в нем находится, исполняется графическим процессором видеокарты.
Сделано это для того, чтобы<span style="mso-spacerun: yes;"> </span>снизить нагрузку
центрального процессора и обращаться к процессору видеокарты напрямую. Это
увеличивает скорость и качество создаваемых эффектов. Из-за того, что
графический процессор работает не так как центральный процессор, для управления
им требуется свой набор команд или свой язык программирования. Раньше
управление графическим процессором осуществлялось с помощью языка сходного с
ассемблером, он содержал небольшое количество инструкций. Но с течением времени
шейдеры становились все больше и тяжелее, вот и появилась необходимость в
высокоуровневом языке, которым и является </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">HLSL</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">Вообще шейдер
хранится в файле с расширением .</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">fx</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">,
и имеет определенную структуру, его можно разделить на четыре основные части. <span style="mso-spacerun: yes;"> </span>Первая часть - это набор переменных, значения
которых могут быть назначены заранее или передаваться им из кода как параметры.
Ими могут быть матрицы различных размерностей, переменные <span style="mso-spacerun: yes;"> </span>и много чего ещё. Например, так мы передали
шейдеру наши матрицы.</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 0pt; mso-layout-grid-align: none; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 0pt; mso-layout-grid-align: none; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">effect.World = world;<br />effect.View = view;<br />effect.Projection = proj;<o:p></o:p></span></span> </div>
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%;">Вторая часть - это
функция, в которой производятся все преобразования с вершинами называемый вершинный
шейдер. Он, как правило, вызывается первым. Прсле окончания вычислений в
вершинном шейдере, данные передаются пиксельный шейдер. Это третья часть - функция,
в которой производятся все преобразования с пикселями. Присваивание им
необходимого цвета. И четвертая часть – это техника, из которой происходит
вызов вершинного и пиксельного шейдеров. Вообще тема шейдеров настолько
обширна, что для нее надо писать целую книгу, которых уже и так немало
написано. Но мы пока ограничимся изучением </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">, и узнаем на что
способен наш, пока что единственный шейдер. Помимо матриц, </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;"> принимает еще
информацию о направленных источниках освещения, настройки для тумана, может
установить новую текстуру и т.д. Если эти параметры не предать, то </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;"> будет использовать
данные, которые ему присвоены по умолчанию.</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;"><span style="mso-spacerun: yes;"> </span>На рисунке ниже показана реализация тумана на
основе </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">.
Добиться этого удалось добавлением в наш, уже существующий код, всего лишь
нескольких строк в методе </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Draw</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">.<o:p></o:p></span></span></div>
protected override void Draw(GameTime gameTime)<br />{<br /> GraphicsDevice.Clear(Color.CornflowerBlue);<br />
world = Matrix.CreateRotationY(rotation) * Matrix.CreateTranslation(0, 0, 5);<br /> view = Matrix.CreateLookAt(new Vector3(0, 0, -1f), Vector3.Zero, Vector3.Up);<br /> proj = Matrix.CreateOrthographic(5, 5, 1, 1000);<br />
foreach (ModelMesh mesh in myModel.Meshes)<br /> {<br /> foreach (BasicEffect effect in mesh.Effects)<br /> {<br /> effect.World = world;<br /> effect.View = view;<br /> effect.Projection = proj;<br />
effect.FogEnabled = true;<br /> effect.FogColor = Color.CornflowerBlue.ToVector3();<br /> effect.FogStart = 0;<br /> effect.FogEnd = 6;<br /> }<br /> mesh.Draw();<br /> }<br />
base.Draw(gameTime);<br />}<br />
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Переписав код так как указано выше, вы можете добиться
таких же результатов, как на рисунке</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizHAwVbVj8bQkgyC9H3h7YhTZsQqFzpz7GWA4v9FpBt0kQ-9SwpnC1K5h4gaohRjfrZQJsQ9Q7o7hOc8y26hyHEjhFEWcuk48rAOvaiUNp9vxv1mJabDtbzk9JImxz8u-8YxtUtd4FUM4o/s1600/XNA17.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizHAwVbVj8bQkgyC9H3h7YhTZsQqFzpz7GWA4v9FpBt0kQ-9SwpnC1K5h4gaohRjfrZQJsQ9Q7o7hOc8y26hyHEjhFEWcuk48rAOvaiUNp9vxv1mJabDtbzk9JImxz8u-8YxtUtd4FUM4o/s320/XNA17.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><o:p><span style="font-size: small;">
</span></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Следующий пример демонстрирует, как спомощью </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span></span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">можно поексперриментировать с цветом модели не изменяя,
при этом ее текстуру.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia-SU2h5mutoFXbsFBMX3eRJKkMgQ6zh3z8ECKVSnrjYw3qb0U6V8GF3WIztfOdO2fPY3EXrPH-YToyR7lDLaUNaFW_Tx3kqAVFr07V7vUp1COlFxwJ2bhGPPxAPhVxRtbUxj5b6d11I-P/s1600/XNA18.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="217" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEia-SU2h5mutoFXbsFBMX3eRJKkMgQ6zh3z8ECKVSnrjYw3qb0U6V8GF3WIztfOdO2fPY3EXrPH-YToyR7lDLaUNaFW_Tx3kqAVFr07V7vUp1COlFxwJ2bhGPPxAPhVxRtbUxj5b6d11I-P/s320/XNA18.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Такой еффект достигается с помощью строчки</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"></span><span style="font-family: "Courier New"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><o:p><span style="font-size: small;">effect.DiffuseColor = new Vector3(0, 0, 0);</span></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Передавая в шейдер какой-либо цвет, он автоматически
применяется к модели. </span></span><span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Следующий пример показывает, как можно простым способом,
добавить освещение к вашей модели или всей сцене. Для этого вам необходимо
просто написать следующую строку в методе </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">Draw</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
effect.EnableDefaultLighting();
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Добавляя тем самым источник света определенный шейдером по
умолчанию. Результат изображен на рисунке.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGoKk55itJEWl77hDuEqVHiXBI8GkbfJnKYsXUucUCCnNGPwBU5xvmdwp9O44qkPLMMlyTQGoWZFxyseY3vzhwT29272uzBQ-IsMEw0s7gYg6-HQ_LYpE7dlGxmO7GLvZaDOzKuryozEc-/s1600/XNA19.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGoKk55itJEWl77hDuEqVHiXBI8GkbfJnKYsXUucUCCnNGPwBU5xvmdwp9O44qkPLMMlyTQGoWZFxyseY3vzhwT29272uzBQ-IsMEw0s7gYg6-HQ_LYpE7dlGxmO7GLvZaDOzKuryozEc-/s320/XNA19.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Следующий пример показывает, как можно заменить текстуру,
которой покрыта наша модель, либо вообще ее убрать. Строка:<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;">
effect.TextureEnabled = false;</span>
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">отключает текстурирование, и как результат, мы получим
следующее изображение:</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3Q265-oGk-fBaVrZrx5jifKp8o4XWoiMOXkTsLH-Lmrtv5L9cEIqw0xgmOLw8BDqfyFdjGOXt9LngJvhrvWXkbQIz82DYWCkZh4QTMrjkv-V7H-7Rz33L30uPcBo0MCni-knzZ0tzwr3P/s1600/XNA20.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3Q265-oGk-fBaVrZrx5jifKp8o4XWoiMOXkTsLH-Lmrtv5L9cEIqw0xgmOLw8BDqfyFdjGOXt9LngJvhrvWXkbQIz82DYWCkZh4QTMrjkv-V7H-7Rz33L30uPcBo0MCni-knzZ0tzwr3P/s320/XNA20.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><o:p><span style="font-size: small;">
</span></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Как видите, текстура не рисуется, остается лиш силует
нашей модели, залитый белым цветом. Для того, чтобы присвоить модели новую
текстуру, нам для начала, надо ее загрузить в проект. Делатся это старым
проверенным способом. И после этого текстура передается в шейдер, заменяя при
этом предыдущую.</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">effect.Texture = stone;</span></span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">В результате получилось такое изображение<o:p></o:p></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaR2w-gOulVJvfNuQYpSxtseZtLbJ69ofe6VA1qJDDhNrdrNeF6kLPpv2m6O9SpvzHWvbpTtLlQz1AbkntOTeV2_TGlqGl-Xi5XZ-8pv3IV2reaEBjYUfS7ZqpW3iGc5GMRpR-NooXlz6d/s1600/XNA21.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaR2w-gOulVJvfNuQYpSxtseZtLbJ69ofe6VA1qJDDhNrdrNeF6kLPpv2m6O9SpvzHWvbpTtLlQz1AbkntOTeV2_TGlqGl-Xi5XZ-8pv3IV2reaEBjYUfS7ZqpW3iGc5GMRpR-NooXlz6d/s320/XNA21.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Само собой розумеется, что все эти приемы можно
использоавть как по отдельности, так и все вместе, во всевозможных комбинациях,
с различными параметрами. Сам по себе </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">служит своеобразной универсальной заглушкой, для того, что
бы начинающие программисты, не знакомые с языками программиования шейдеров,
имели возможность отобразить модель на экране. По функциональности он не несет
в себе ничего сверхестественного, но с базовыми задачами справляется прекрасно.
Его главная заслуга в том, что он уже написан, оптимизирован и удобен в
эксплуатации. К тому же, если учесть, что </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">XNA</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">может использоваться не только для написания игр, а,
например для каких нибудь 3</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">D</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> презентаций, то большей функциональности чем дает </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">, и не требуется. Но если говорить о
серьезных игровых проектах, то в них используются десятки, а то и сотни
шейдеров, которые выполняют различные задачи, это и работа с тенями,
отражениями, преломлениями света и многое многое другое. Файл еффекта, так же
как и стартовый проект </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">GameStudio можно сгенерировать. Для этого, на папке контент кликаем правой кнопкой мыши, и выбираем мункт меню Добавить/Создать Елемент/EffectFile. </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">После этого в проект будет добавлен шейдер, название
которому можете присвоить сами, при создании. Этот еффект в отличии от </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">будет пустым, и сможет только лишь отображать залитую
красным цветом модель. Без освещения, тумана, текстуры. Всё это и многое другое
вам предстоит делать самому. Но об этом потом, а сейчас давайте посмотрим, что
же находится внутри нового шейдера.<o:p></o:p></span></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4x4 World;<br />float4x4 View;<br />float4x4 Projection;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// TODO: add effect parameters here.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
struct VertexShaderInput<br />{<br /> float4 Position : POSITION0;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// TODO: add input channels such as texture<br /> // coordinates and vertex colors here.<br />};</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
struct VertexShaderOutput<br />{<br /> float4 Position : POSITION0;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// TODO: add vertex shader outputs such as colors and texture<br /> // coordinates here. These values will automatically be interpolated<br /> // over the triangle, and provided as input to your pixel shader.<br />};</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)<br />{<br /> VertexShaderOutput output;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4 worldPosition = mul(input.Position, World);<br /> float4 viewPosition = mul(worldPosition, View);<br /> output.Position = mul(viewPosition, Projection);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
// TODO: add your vertex shader code here.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
return output;<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0<br />{<br /> // TODO: add your pixel shader code here.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
return float4(1, 0, 0, 1);<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
technique Technique1<br />{<br /> pass Pass1<br /> {<br /> // TODO: set renderstates here.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
VertexShader = compile vs_1_1 VertexShaderFunction();<br /> PixelShader = compile ps_1_1 PixelShaderFunction();<br /> }<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Теперь давайте подробнее разберемся в этом коде. Как
видите сходство синтаксиса </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">HLSL</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">и Си прослеживается сразу же. Как уже было сказано ранее,
мы видим четыре основные части.</span> </span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<b style="mso-bidi-font-weight: normal;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">1.</span></b><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;"> Переменные хранящие матрицы и две структуры хранящие
входящие и исходящие данные о вершинах. В данном случае это только позиция.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4x4 World;<br />float4x4 View;<br />float4x4 Projection;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
struct VertexShaderInput<br />{<br /> float4 Position : POSITION0;<br />};</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
struct VertexShaderOutput<br />{<br /> float4 Position : POSITION0;<br />};</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">2</span></b><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Вершинный</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">шейдер</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)<br />{<br /> VertexShaderOutput output;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4 worldPosition = mul(input.Position, World);<br /> float4 viewPosition = mul(worldPosition, View);<br /> output.Position = mul(viewPosition, Projection);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
return output;<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">В нем произведены вычисления позиции каждой вершины,
базируясь на трех матрицах, а после результат сохраняется в екземпляре
структуры </span><span lang="EN-US" style="font-family: "Courier New"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">output</span><span style="font-family: "Courier New"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">.</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">3</span></b><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Пиксельный</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">шейдер</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0<br />{</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
return float4(1, 0, 0, 1);<br />}
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Здесь вообще всё просто, не производится ни каких
вычислний, просто возвращается, всегда красный, цвет пикселя.</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<b style="mso-bidi-font-weight: normal;"><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">4</span></b><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Техника</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
technique Technique1<br />{<br /> pass Pass1<br /> {<br /> VertexShader = compile vs_1_1 VertexShaderFunction();<br /> PixelShader = compile ps_1_1 PixelShaderFunction();<br /> }<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 0pt; mso-layout-grid-align: none; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">Дословно здесь сказано, что перед вызовом вершинного, а
затем пиксельного шейдера, сперва определяется шейдерная модель (</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">vs</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">_1_1 и </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">ps</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">_1_1). </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">В момент появления языка HLSL была предложена так называемая Shaders Models
1.0. Это своего рода версия описания шейдерной модели для использования
шейдерных программ в программировании графики. Шейдерная модель предъявляет
набор требований для графического процессора видеокарты, которые он обязан иметь
или исполнять. На сегодняшний день имеются уже три версии Shaders Models.
Каждая последующая шейдерная модель лишь усовершенствует свою предшественницу.
То есть имеется набор определенных инструкций, которые может выполнить
видеоадаптер с поддержкой Shaders Models 1.0, и если вы попробуете на этом
видеоадаптере использовать шейдерную модель третьей версии, то произойдет
ошибка в работе программы. Фактически шейдерная модель описывает тот набор
операций, который графический процессор видеокарты может или даже способен
выполнить. Например, в Shaders Models 1.0 нет возможности использовать циклы, а
во второй версии такая возможность (для видеоадаптеров) была добавлена. Технология
не стоит на месте и развивается, мощности и возможности графических процессоров
растут, поэтому и Shaders Models постепенно усовершенствуется. Первая версия
Shaders Models со временем отошла и уже практически не используется в
программировании шейдеров, но поддерживается всеми видео картами без
исключения, если, конечно, производитель умышленно не исключил </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">такой возможности. Сейчас отправной точкой в версии модели шейдеров
считается версия ShadersModels 2.0, которая также используется в консольной
приставке Xbox 360 (приставка не может использовать Shaders Models 1.0). Более
дорогие компьютерные </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">видеоадаптеры могут работать с </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Shaders</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Models</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> 2.0, </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Shaders</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Models</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> 3.0 и с </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Shaders</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">Models</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU;"><span style="font-size: small;"> 4.0. Но
это мы немного отвлеклись. Теперь нам надо изменить свой проект так, что бы мы
могли отобразить модель с помощью нашего шейдера. Чтобы это сделать, нужно, во
первых, создать новый еффект следующими строками,</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 0pt; mso-layout-grid-align: none; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Effect myEffect; <br />myEffect = Content.Load<Effect>("Shaders/Effect1");</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">а потом надо заменить уже готовый </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">установленый в модель, на новый. Сделаем</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">это</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">с</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">помощью</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">функции</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
public void ChangeEffectUsedByModel(Model model, Effect replacementEffect)<br />{<br /> Dictionary<Effect, Effect> effectMapping = new Dictionary<Effect, Effect>();</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMesh mesh in model.Meshes)<br /> {<br /> foreach (BasicEffect oldEffect in mesh.Effects)<br /> {<br /> if (!effectMapping.ContainsKey(oldEffect))<br /> {<br /> Effect newEffect = replacementEffect.Clone(replacementEffect.GraphicsDevice);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
effectMapping.Add(oldEffect, newEffect);<br /> }<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMeshPart meshPart in mesh.MeshParts)<br /> {<br /> meshPart.Effect = effectMapping[meshPart.Effect];<br /> }<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">И поменять код в методе </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">Draw</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">, потому что теперь нам надо искать
не </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">BasicEffect</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;">, а наш </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU; mso-no-proof: yes;">Effect</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">. Делается это следующим образом, с учетом всей специфики изменений.<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMesh mesh in myModel.Meshes)<br />{<br /> foreach (Effect effect in mesh.Effects)<br /> {<br /> effect.Parameters["World"].SetValue(world);<br /> effect.Parameters["View"].SetValue(view);<br /> effect.Parameters["Projection"].SetValue(proj);<br /> }<br /> mesh.Draw();<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">Общий вид нашего кода теперь стал выглядеть так:<o:p></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using Microsoft.Xna.Framework;<br />using Microsoft.Xna.Framework.Audio;<br />using Microsoft.Xna.Framework.Content;<br />using Microsoft.Xna.Framework.GamerServices;<br />using Microsoft.Xna.Framework.Graphics;<br />using Microsoft.Xna.Framework.Input;<br />using Microsoft.Xna.Framework.Media;<br />using Microsoft.Xna.Framework.Net;<br />using Microsoft.Xna.Framework.Storage;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
namespace SimplyModel<br />{<br /> public class Game1 : Microsoft.Xna.Framework.Game<br /> {<br /> GraphicsDeviceManager graphics;<br /> SpriteBatch spriteBatch;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Model myModel;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Matrix world;<br /> Matrix view;<br /> Matrix proj;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Effect myEffect;<br /> Texture2D stone;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
float rotation;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
public Game1()<br /> {<br /> graphics = new GraphicsDeviceManager(this);<br /> Content.RootDirectory = "Content";<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Initialize()<br /> {<br /> base.Initialize();<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void LoadContent()<br /> {<br /> spriteBatch = new SpriteBatch(GraphicsDevice);<br /> myModel = Content.Load<Model>("models/BTR");<br /> stone = Content.Load<Texture2D>("textures/stone");<br /> myEffect = Content.Load<Effect>("Shaders/Effect1");<br /> ChangeEffectUsedByModel(myModel, myEffect);<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
public void ChangeEffectUsedByModel(Model model, Effect replacementEffect)<br /> {<br /> Dictionary<Effect, Effect> effectMapping = new Dictionary<Effect, Effect>();</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMesh mesh in model.Meshes)<br /> {<br /> foreach (BasicEffect oldEffect in mesh.Effects)<br /> {<br /> if (!effectMapping.ContainsKey(oldEffect))<br /> {<br /> Effect newEffect = replacementEffect.Clone(replacementEffect.GraphicsDevice);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
effectMapping.Add(oldEffect, newEffect);<br /> }<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMeshPart meshPart in mesh.MeshParts)<br /> {<br /> meshPart.Effect = effectMapping[meshPart.Effect];<br /> }<br /> }<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void UnloadContent()<br /> {<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Update(GameTime gameTime)<br /> {<br /> rotation += MathHelper.ToRadians(0.5f);<br /> base.Update(gameTime);<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Draw(GameTime gameTime)<br /> {<br /> GraphicsDevice.Clear(Color.CornflowerBlue);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
world = Matrix.CreateRotationY(rotation) * Matrix.CreateTranslation(0, 0, 5);<br /> view = Matrix.CreateLookAt(new Vector3(0, 0, -1f), Vector3.Zero, Vector3.Up);<br /> proj = Matrix.CreateOrthographic(7, 7, 1, 1000);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMesh mesh in myModel.Meshes)<br /> {<br /> foreach (Effect effect in mesh.Effects)<br /> {<br /> effect.Parameters["World"].SetValue(world);<br /> effect.Parameters["View"].SetValue(view);<br /> effect.Parameters["Projection"].SetValue(proj);<br /> }<br /> mesh.Draw();<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
base.Draw(gameTime);<br /> }<br /> }<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%; mso-fareast-language: RU; mso-no-proof: yes;"><span style="font-size: small;">А результат выглядит вот так:<o:p></o:p></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTfW7nJRnWcei4bE0AahYVEMM-g3z7riIwaAPvVtcPehjHubh8exKLjuJ14r-cDvNAD1T1nYAdgMMNZi2AlsorQtN8_TPqtf1rVdmG3Ij6l5IH_dVR3nsovJYAd4mqtZ6_8trY8Fm-NO21/s1600/XNA22.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiTfW7nJRnWcei4bE0AahYVEMM-g3z7riIwaAPvVtcPehjHubh8exKLjuJ14r-cDvNAD1T1nYAdgMMNZi2AlsorQtN8_TPqtf1rVdmG3Ij6l5IH_dVR3nsovJYAd4mqtZ6_8trY8Fm-NO21/s320/XNA22.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Для начала не плохо, как мне кажется.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-5514974705425561744.post-32181860774944047152012-03-06T01:57:00.000-08:002012-03-06T01:57:10.527-08:00Практическое применение XNA. Часть 7.<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-size: large;"><strong>Рисуем первую модель.</strong></span><br />
<br />
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">Перед тем, как мы нарисуем нашу первую модель,
давайте разберемся, что же такое трёхмерное пространство, и каким образом
организовано отображение моделей в нем.</span> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">Первое с чего мы
начнем, это система трехмерных координат. Важно отметить, что в </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">XNA</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;"> используется правосторонняя система
координат, в </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">DirectX</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">
левосторонняя, и это следует помнить. Вся разница между ними в том, что если
ось </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">X</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">
(ширина, начинается в нижнем левом углу экрана монитора и направлена вправо) и
ось </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Y</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">
(высота, начинается в нижнем левом углу экрана монитора и направлена вверх), и
в левосторонней и правосторонней системах одинаковы, то с осью </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;"> дела обстоят диаметрально
противоположно. В правосторонней системе ось </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">своим
положительным лучом направлена к вам</span><v:shapetype coordsize="21600,21600" filled="f" id="_x0000_t75" o:preferrelative="t" o:spt="75" path="m@4@5l@4@11@9@11@9@5xe" stroked="f"><span style="font-family: Calibri;">
<v:stroke joinstyle="miter">
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0">
<v:f eqn="sum @0 1 0">
<v:f eqn="sum 0 0 @1">
<v:f eqn="prod @2 1 2">
<v:f eqn="prod @3 21600 pixelWidth">
<v:f eqn="prod @3 21600 pixelHeight">
<v:f eqn="sum @0 0 1">
<v:f eqn="prod @6 1 2">
<v:f eqn="prod @7 21600 pixelWidth">
<v:f eqn="sum @8 21600 0">
<v:f eqn="prod @7 21600 pixelHeight">
<v:f eqn="sum @10 21600 0">
</v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:formulas>
<v:path gradientshapeok="t" o:connecttype="rect" o:extrusionok="f">
<o:lock aspectratio="t" v:ext="edit">
</o:lock></v:path></v:stroke></span></v:shapetype><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><span style="font-size: small;">, как бы выходя
из экрана монитора. </span></span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%;">В левосторонней системе
ось </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;">
</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">положительным
лучом наоборот уходит вдаль от вас вглубь монитора. Если представить, что
камера находится в начале системы координат, то - есть </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">X</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0, </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Y</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0, </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0, а модель которую предстоит
отобразить в координатах </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">X</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0,
</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Y</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0,
</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=5
, то в правосторонней системе координат мы ее не увидим, так как фактически она
должна будет находиться в воздухе между монитором и вами. Если же координаты
модели будут </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">X</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0,
</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Y</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=0,
</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">=-5,
то ее местоположение переместится вглубь монитора, и наша видеокарта сможет ее
отобразить. Однако важно понимать, что если камеру переместить в отрицательную
часть системы координат по оси </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">,
и развернуть на 180 градусов, например для того чтобы посмотреть на объект с
обратной стороны, то теперь чтобы отдалить объект нам надо его двигать в
положительном направлении по оси </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">.</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%;"><span style="font-size: small;">С пространством, в
котором нам предстоит отображать нашу игру, вроде бы всё понятно. Теперь
движемся дальше. Как уже было сказано, игра и ее игровой мир состоит из
примитивов и моделей. Давайте рассмотрим примитив как простейшую форму модели.
и пойдем мы от сложного к простому. Любой примитив состоит из полигонов, а
любой полигон из вершин и линий соединяющих эти вершины. Итак, у нас появились новые
понятия, суть которых такова:</span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%;"><span style="mso-spacerun: yes;"> </span>Вершина – это точка в трехмерном пространстве
имеющая координаты </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">X</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">,</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Y</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">,</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Z</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">. </span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%;"><span style="font-size: small;">Полигон – это
совокупность трех вершин соединенных между собой линиями и образующих
треугольник, вершины которого теперь имеют не только координаты, но еще и
индекс, так называемый порядковый номер вершины. </span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<v:shapetype coordsize="21600,21600" filled="f" id="_x0000_t75" o:preferrelative="t" o:spt="75" path="m@4@5l@4@11@9@11@9@5xe" stroked="f"><span style="font-family: Calibri;"><span style="font-size: small;">
<v:stroke joinstyle="miter">
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0">
<v:f eqn="sum @0 1 0">
<v:f eqn="sum 0 0 @1">
<v:f eqn="prod @2 1 2">
<v:f eqn="prod @3 21600 pixelWidth">
<v:f eqn="prod @3 21600 pixelHeight">
<v:f eqn="sum @0 0 1">
<v:f eqn="prod @6 1 2">
<v:f eqn="prod @7 21600 pixelWidth">
<v:f eqn="sum @8 21600 0">
<v:f eqn="prod @7 21600 pixelHeight">
<v:f eqn="sum @10 21600 0">
</v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:formulas>
<v:path gradientshapeok="t" o:connecttype="rect" o:extrusionok="f">
<o:lock aspectratio="t" v:ext="edit">
</o:lock></v:path></v:stroke></span></span></v:shapetype><v:shape id="_x0000_s1027" style="height: 292.1pt; left: 0px; margin-left: 270.95pt; margin-top: 448.9pt; mso-position-horizontal-relative: margin; mso-position-vertical-relative: margin; position: absolute; text-align: left; width: 239.8pt; z-index: 251627008;" type="#_x0000_t75"><span style="font-family: Calibri;"><span style="font-size: small;">
<v:imagedata cropbottom="4569f" cropleft="17561f" cropright="17525f" croptop="1646f" o:title="" src="file:///C:\Users\URRI\AppData\Local\Temp\msohtmlclip1\01\clip_image001.png">
<w:wrap anchorx="margin" anchory="margin" type="square">
</w:wrap></v:imagedata></span></span></v:shape><v:shape id="_x0000_s1026" style="height: 292.1pt; left: 0px; margin-left: 7.6pt; margin-top: 448.9pt; mso-position-horizontal-relative: margin; mso-position-vertical-relative: margin; position: absolute; text-align: left; width: 248.2pt; z-index: 251625984;" type="#_x0000_t75"><span style="font-family: Calibri;"><span style="font-size: small;">
<v:imagedata cropbottom="3107f" cropleft="15256f" cropright="18608f" croptop="2686f" o:title="" src="file:///C:\Users\URRI\AppData\Local\Temp\msohtmlclip1\01\clip_image003.png">
<w:wrap anchorx="margin" anchory="margin" type="square">
</w:wrap></v:imagedata></span></span></v:shape><span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%;">Все модели и
примитивы состоят из полигонов (треугольников). Модели сложной формы могут
включать в себя сотни тысяч полигонов и называются они, на языке создателей
игр, высокополигональными моделями (</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">highpoly</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">) , и наоборот, модели, состоящие из
нескольких десятков полигонов, которые называются низкополигональными (</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">lowpoly</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;">). <span style="font-size: small;">Чем больше полигонов
в модели, тем правильней и точней будет ее форма, округлые части станут менее
угловатые и более округлые. Однако высокополигональная модель требует больше
компьютерных ресурсов для обработки, поэтому здесь также необходим компромисс
между качеством и производительностью.<o:p></o:p></span></span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO8cnGIRpM7rZ7I99GWeUhVTveqb3RgnFmAwjTDqJHGEjitCEt6oiL8julekC_LWqtszAWoHlxN-b9vNO3qSTaqe-WNnMViqfZV06CaVZHuPgmkrNb71ixdrHwjnAmo4deZOj7Uuzz7NWv/s1600/XNA15.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO8cnGIRpM7rZ7I99GWeUhVTveqb3RgnFmAwjTDqJHGEjitCEt6oiL8julekC_LWqtszAWoHlxN-b9vNO3qSTaqe-WNnMViqfZV06CaVZHuPgmkrNb71ixdrHwjnAmo4deZOj7Uuzz7NWv/s320/XNA15.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-size: small;">
<span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><o:p><span style="font-size: small;"><span style="font-family: "Times New Roman","serif"; line-height: 115%;">Для того чтобы вывести
на экран нашу модель, создадим новый проект </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">SimplyModel</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">. В папку </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">Content</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%;"> </span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">мы
добавляем папку </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">models</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">,
а в неё модель под названием </span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">BTR</span><span style="font-family: "Times New Roman","serif"; line-height: 115%;">.</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US;">x</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;">. <span style="font-size: small;">Следующий код показывает
реализацию.</span></span></span></o:p></span></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<br /></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using Microsoft.Xna.Framework;<br />using Microsoft.Xna.Framework.Audio;<br />using Microsoft.Xna.Framework.Content;<br />using Microsoft.Xna.Framework.GamerServices;<br />using Microsoft.Xna.Framework.Graphics;<br />using Microsoft.Xna.Framework.Input;<br />using Microsoft.Xna.Framework.Media;<br />using Microsoft.Xna.Framework.Net;<br />using Microsoft.Xna.Framework.Storage;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
namespace SimplyModel<br />{<br /> public class Game1 : Microsoft.Xna.Framework.Game<br /> {<br /> GraphicsDeviceManager graphics;<br /> SpriteBatch spriteBatch;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Model myModel;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Matrix world;<br /> Matrix view;<br /> Matrix proj;<br /> float rotation;</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
public Game1()<br /> {<br /> graphics = new GraphicsDeviceManager(this);<br /> Content.RootDirectory = "Content";<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Initialize()<br /> {<br /> base.Initialize();<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void LoadContent()<br /> {<br /> spriteBatch = new SpriteBatch(GraphicsDevice);<br /> myModel = Content.Load<Model>("models/BTR");<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void UnloadContent()<br /> {<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Update(GameTime gameTime)<br /> {<br /> rotation += MathHelper.ToRadians(1);<br /> base.Update(gameTime);<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
protected override void Draw(GameTime gameTime)<br /> {<br /> GraphicsDevice.Clear(Color.CornflowerBlue);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
world = Matrix.CreateRotationY(rotation) * Matrix.CreateTranslation(0, 0, 5);<br /> view = Matrix.CreateLookAt(new Vector3(0, 0, -1f), Vector3.Zero, Vector3.Up);<br /> proj = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(90),<br /> (float)graphics.GraphicsDevice.Viewport.Width / <br /> (float)graphics.GraphicsDevice.Viewport.Height, 1, 1000);</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
foreach (ModelMesh mesh in myModel.Meshes)<br /> {<br /> foreach (BasicEffect effect in mesh.Effects)<br /> {<br /> effect.World = world;<br /> effect.View = view;<br /> effect.Projection = proj;<br /> }<br /> mesh.Draw();<br /> }</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
base.Draw(gameTime);<br /> }<br /> }<br />}</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
В этом примере, тоже все достаточно просто. Аналогично предыдущим урокам, мы загружаем</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
модель из предварительно созданного файла. Далее проходим по всем сеткам модели, их может быть несколько, и вызываем у каждой из них метод Draw(), который и изобразит ее на экране. </div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<o:p>Для рисования модели нам так-же потребуются три матрицы. </o:p><o:p></o:p></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<span style="font-family: "Times New Roman","serif"; line-height: 115%;"><span style="mso-spacerun: yes;"> </span><b style="mso-bidi-font-weight: normal;"><span style="mso-spacerun: yes;"> </span>Мировая матрица </b>– позволяет производить
такие преобразования объекта как масштабирование, перемещение, вращение,
различные трансформации сетки на вершинном уровне. Этой матрицей наделяется
каждый объект, это локальная система координат каждой модели. </span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">Новая локальная система координат значительно упрощает
аффинные преобразования объекта в пространстве. Например, чтобы перенести
объект с левого верхнего угла дисплея в нижний правый угол дисплея, необходимо
просто перенести его локальную точку отсчета, системы координат на новое место.
А представьте, если бы не было мировой матрицы и этот объект пришлось
переносить по одной вершине из угла в угол монитора... Поэтому любой объект, а
точнее все вершины этого объекта проходят через мировую матрицу преобразования.</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<b style="mso-bidi-font-weight: normal;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">Матрица вида</span></b><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> – определяет местоположение камеры в игровом пространстве. Это вторая по
счету матрица, на которую умножаются все вершины моделей. С ее помощью задается
направление просмотра 3</span><span lang="EN-US" style="font-family: "Times New Roman","serif"; line-height: 115%; mso-ansi-language: EN-US; mso-fareast-language: RU;">D</span><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;"> объекта
или сцены.</span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
<v:shapetype coordsize="21600,21600" filled="f" id="_x0000_t75" o:preferrelative="t" o:spt="75" path="m@4@5l@4@11@9@11@9@5xe" stroked="f"><span style="font-family: Calibri;">
<v:stroke joinstyle="miter">
<v:formulas>
<v:f eqn="if lineDrawn pixelLineWidth 0">
<v:f eqn="sum @0 1 0">
<v:f eqn="sum 0 0 @1">
<v:f eqn="prod @2 1 2">
<v:f eqn="prod @3 21600 pixelWidth">
<v:f eqn="prod @3 21600 pixelHeight">
<v:f eqn="sum @0 0 1">
<v:f eqn="prod @6 1 2">
<v:f eqn="prod @7 21600 pixelWidth">
<v:f eqn="sum @8 21600 0">
<v:f eqn="prod @7 21600 pixelHeight">
<v:f eqn="sum @10 21600 0">
</v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:f></v:formulas>
<v:path gradientshapeok="t" o:connecttype="rect" o:extrusionok="f">
<o:lock aspectratio="t" v:ext="edit">
</o:lock></v:path></v:stroke></span></v:shapetype><b style="mso-bidi-font-weight: normal;"><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">Матрица проекции – </span></b><span style="font-family: "Times New Roman","serif"; line-height: 115%; mso-fareast-language: RU;">это наиболее сложная из трех матриц, которая создает проекцию нашего
трехмерного игрового мира, на плоскую двухмерную поверхность нашего монитора. С
помощью этой матрицы задаются передняя и задняя плоскости отсечения
пространства, чтобы снизить нагрузку на видеопроцессор. Чтобы лучше понять выше
сказанное, представьте себе, будто вы стоите перед аквариумом прямоугольной
формы и смотрите на плавающих внутри него рыб. Стеклянная стенка аквариума,
которая ближе к вам – это передняя плоскость отсечения, стенка, которая дальше
от вас – это дальняя плоскость. Все что находится к нам ближе, чем ближняя
плоскость нас не интересует, равно как и все, что находится за дальней
плоскостью отсечения. Наше внимание, как и внимание видеопроцессора, привлекает
только содержимое аквариума, а именно все что находится между ближней и дальней
плоскостями отсечения. Соответственно увеличивая или уменьшая этот диапазон, мы
регулируем нагрузку видеокарты.</span><span style="font-family: "Times New Roman","serif"; font-size: 14pt; line-height: 115%;"><o:p></o:p></span></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
А вот и наша моделька.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU7tQTbrzF8E-AVvhmHEohQKkJCRhK0RNuH-vjzWz4f56OcDtgtfY1n4N2frQeClYQXneCXLbvUjIzp5CEk5FxmTCI8CBpWrpc-Dja7IaCFzs3WJbVuRdJt_nMBO022Faag5b3EcAH9oI_/s1600/XNA16.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgU7tQTbrzF8E-AVvhmHEohQKkJCRhK0RNuH-vjzWz4f56OcDtgtfY1n4N2frQeClYQXneCXLbvUjIzp5CEk5FxmTCI8CBpWrpc-Dja7IaCFzs3WJbVuRdJt_nMBO022Faag5b3EcAH9oI_/s320/XNA16.jpg" width="320" /></a></div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
Как вы снова могли заметить, вся основная работа выполнена внутри классов XNA, мы лишь указали какую модель загрузить, и как ее нарисовать.</div>
<div class="MsoNormal" style="margin: 0cm 0cm 10pt; text-align: justify;">
</div>
</div>Unknownnoreply@blogger.com0