{"id":2073,"date":"2019-06-23T01:08:42","date_gmt":"2019-06-22T22:08:42","guid":{"rendered":"http:\/\/demensdeum.com\/blog\/?p=2073"},"modified":"2024-12-16T22:32:36","modified_gmt":"2024-12-16T19:32:36","slug":"skeletal-animation-1","status":"publish","type":"post","link":"https:\/\/demensdeum.com\/blog\/pt\/2019\/06\/23\/skeletal-animation-1\/","title":{"rendered":"Anima\u00e7\u00e3o esquel\u00e9tica (Parte 1 &#8211; shader)"},"content":{"rendered":"<p>Neste artigo descreverei minha compreens\u00e3o da anima\u00e7\u00e3o esquel\u00e9tica, que \u00e9 usada em todos os motores 3D modernos para animar personagens, ambientes de jogos, etc.<br \/>Come\u00e7arei a descri\u00e7\u00e3o com a parte mais tang\u00edvel &#8211; sombreador de v\u00e9rtice, pois todo o caminho de c\u00e1lculo, por mais complexo que seja, termina com a transfer\u00eancia dos dados preparados para exibi\u00e7\u00e3o para o sombreador de v\u00e9rtice.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-2080\" src=\"https:\/\/demensdeum.com\/blog\/wp-content\/uploads\/2019\/06\/flower-love-friendship-human-body-illustration-skeleton-913687-pxhere.com_.jpg\" alt=\"\" width=\"360\" height=\"228\" \/><\/p>\n<p>A anima\u00e7\u00e3o do esqueleto, ap\u00f3s ser processada na CPU, vai para o vertex shader.<br \/>Deixe-me lembr\u00e1-lo da f\u00f3rmula para v\u00e9rtice sem anima\u00e7\u00e3o esquel\u00e9tica:<br \/>gl_Position = proje\u00e7\u00e3oMatrix * viewMatrix * modelMatrix * v\u00e9rtice;<br \/>Para quem n\u00e3o entende como surgiu essa f\u00f3rmula, pode ler meu artigo que descreve o princ\u00edpio de trabalhar com matrizes para exibi\u00e7\u00e3o de conte\u00fado 3D no contexto do OpenGL.<br \/>Quanto ao resto &#8211; f\u00f3rmula para implementar anima\u00e7\u00e3o esquel\u00e9tica:<br \/>&#8221; vec4 animadoVertex = bone0matrix * v\u00e9rtice * bone0weight +&#8221;<br \/>&#8220;bone1matrix * v\u00e9rtice * bone1weight +&#8221;<br \/>&#8220;bone2matrix * v\u00e9rtice * bone2weight +&#8221;<br \/>&#8220;bone3matrix * v\u00e9rtice * bone3weight;\\n&#8221;<br \/>&#8221; gl_Position = proje\u00e7\u00e3oMatrix * viewMatrix * modelMatrix * animadoVertex;\\n&#8221;<\/p>\n<p>Ou seja, multiplicamos a matriz de transforma\u00e7\u00e3o \u00f3ssea final pelo v\u00e9rtice e pelo peso desta matriz em rela\u00e7\u00e3o ao v\u00e9rtice. Cada v\u00e9rtice pode ser animado por 4 ossos, a for\u00e7a do impacto \u00e9 regulada pelo par\u00e2metro peso do osso, a soma dos impactos deve ser igual a um.<br \/>O que fazer se menos de 4 ossos afetarem o v\u00e9rtice? Precisamos dividir o peso entre eles e fazer com que o impacto do restante seja igual a zero.<br \/>Matematicamente, multiplicar um peso por uma matriz \u00e9 chamado de \u201cmultiplica\u00e7\u00e3o escalar de matriz\u201d. Multiplicar por um escalar permite resumir o efeito das matrizes no v\u00e9rtice resultante.<\/p>\n<p><strong>As pr\u00f3prias matrizes de transforma\u00e7\u00e3o \u00f3ssea s\u00e3o transmitidas como uma matriz. <\/strong>Al\u00e9m disso, a matriz cont\u00e9m matrizes para todo o modelo como um todo, e n\u00e3o para cada malha separadamente.<\/p>\n<p>Mas para cada v\u00e9rtice as seguintes informa\u00e7\u00f5es s\u00e3o transmitidas separadamente:<br \/>&#8211; \u00cdndice da matriz que afeta o v\u00e9rtice<br \/>&#8211; Peso da matriz que afeta o v\u00e9rtice<br \/>Mais de um osso \u00e9 transmitido, geralmente \u00e9 utilizado o efeito de 4 ossos no v\u00e9rtice.<br \/>Al\u00e9m disso, a soma dos pesos dos 4 dados deve ser sempre igual a um.<br \/>A seguir, vamos ver como fica no shader.<br \/>Matriz matricial:<br \/>&#8220;bonesMatrices mat4 uniformes[kMaxBones];&#8221;<\/p>\n<p>Informa\u00e7\u00f5es sobre o efeito de 4 ossos em cada v\u00e9rtice:<br \/>&#8220;atributo vec2 bone0info;&#8221;<br \/>&#8220;atributo vec2 bone1info;&#8221;<br \/>&#8220;atributo vec2 bone2info;&#8221;<br \/>&#8220;atributo vec2 bone3info;&#8221;<\/p>\n<p>vec2 &#8211; na coordenada X armazenamos o \u00edndice do osso (e convertemos para int no shader), na coordenada Y armazenamos o peso do impacto do osso no v\u00e9rtice. Por que voc\u00ea tem que transmitir esses dados em um vetor bidimensional? Porque o GLSL n\u00e3o suporta a passagem de estruturas leg\u00edveis em C com campos v\u00e1lidos para o shader.<\/p>\n<p>A seguir darei um exemplo de obten\u00e7\u00e3o das informa\u00e7\u00f5es necess\u00e1rias de um vetor para posterior substitui\u00e7\u00e3o na f\u00f3rmula animadoVertex:<\/p>\n<p>&#8220;int bone0Index = int(bone0info.x);&#8221;<br \/>&#8220;float bone0weight = bone0info.y;&#8221;<br \/>&#8220;mat4 bone0matrix = boneMatrices[bone0Index];&#8221;<\/p>\n<p>&#8220;int bone1Index = int(bone1info.x);&#8221;<br \/>&#8220;float bone1weight = bone1info.y;&#8221;<br \/>&#8220;mat4 bone1matrix = boneMatrices[bone1Index];&#8221;<\/p>\n<p>&#8220;int bone2Index = int(bone2info.x);&#8221;<br \/>&#8220;float bone2weight = bone2info.y;&#8221;<br \/>&#8220;mat4 bone2matrix = boneMatrices[bone2Index];&#8221;<\/p>\n<p>&#8220;int bone3Index = int(bone3info.x);&#8221;<br \/>&#8220;float bone3weight = bone3info.y;&#8221;<br \/>&#8220;mat4 bone3matrix = boneMatrices[bone3Index];&#8221;<\/p>\n<p>Agora a estrutura de v\u00e9rtices preenchida na CPU deve ficar assim:<br \/>x, y, z, u, v, bone0index, bone0weight, bone1index, bone1weight, bone2index, bone2weight, bone3index, bone3weight<\/p>\n<p>A estrutura do buffer de v\u00e9rtice \u00e9 preenchida uma vez durante o carregamento do modelo, mas as matrizes de transforma\u00e7\u00e3o s\u00e3o transferidas da CPU para o shader em cada quadro de renderiza\u00e7\u00e3o.<\/p>\n<p>Nas partes restantes, descreverei o princ\u00edpio de c\u00e1lculo da anima\u00e7\u00e3o na CPU, antes de transferi-la para o vertex shader, descreverei a \u00e1rvore de n\u00f3s \u00f3sseos, percorrendo a hierarquia de anima\u00e7\u00e3o-modelo-n\u00f3s-malha, matriz interpola\u00e7\u00e3o.<\/p>\n<h3>Fontes<\/h3>\n<p><a href=\"http:\/\/ogldev.atspace.co.uk\/www\/tutorial38\/tutorial38.html\" target=\"_blank\" rel=\"noopener noreferrer\">http:\/\/ogldev.atspace.co. pt\/www\/tutorial38\/tutorial38.html<\/a><\/p>\n<h3>C\u00f3digo fonte<\/h3>\n<p><a href=\"https:\/\/gitlab.com\/demensdeum\/skeletal-animation\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/gitlab.com\/demensdeum\/skeletal-animation<\/a <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Neste artigo descreverei minha compreens\u00e3o da anima\u00e7\u00e3o esquel\u00e9tica, que \u00e9 usada em todos os motores 3D modernos para animar personagens, ambientes de jogos, etc.Come\u00e7arei a descri\u00e7\u00e3o com a parte mais tang\u00edvel &#8211; sombreador de v\u00e9rtice, pois todo o caminho de c\u00e1lculo, por mais complexo que seja, termina com a transfer\u00eancia dos dados preparados para exibi\u00e7\u00e3o<a class=\"more-link\" href=\"https:\/\/demensdeum.com\/blog\/pt\/2019\/06\/23\/skeletal-animation-1\/\">Continue reading <span class=\"screen-reader-text\">&#8220;Anima\u00e7\u00e3o esquel\u00e9tica (Parte 1 &#8211; shader)&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[61,52],"tags":[108],"class_list":["post-2073","post","type-post","status-publish","format-standard","hentry","category-techie","category-tutorials","tag-skeletal-animation","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"pt","enabled_languages":["en","ru","zh","de","fr","ja","pt","hi"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"ru":{"title":true,"content":true,"excerpt":false},"zh":{"title":true,"content":true,"excerpt":false},"de":{"title":true,"content":true,"excerpt":false},"fr":{"title":true,"content":true,"excerpt":false},"ja":{"title":true,"content":true,"excerpt":false},"pt":{"title":true,"content":true,"excerpt":false},"hi":{"title":false,"content":false,"excerpt":false}}},"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2073","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=2073"}],"version-history":[{"count":13,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2073\/revisions"}],"predecessor-version":[{"id":3953,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/2073\/revisions\/3953"}],"wp:attachment":[{"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=2073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=2073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/demensdeum.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=2073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}