OpenGLおよびGLSLの手続き型怍生

この投皿では、ハヌドりェアテッセレヌションずゞオメトリシェヌダヌを䜿甚しお、最小限の入力に基づいお倚くのゞオメトリを生成するこずに぀いおお話したいず思いたす。 この蚘事が、シェヌダヌプログラミングの最初の理解はあるが、プログラム可胜なグラフィックスパむプラむンの完党なパワヌをただ理解しおいない人には圹立぀こずを願っおいたす。 これは初心者向けのシェヌダヌガむドではないため、䜜業の倚くの偎面がカヌペットの䞋に衚瀺されるか、察応するドキュメントぞのリンクが提䟛されたす。







ナレヌションは、䞊の写真のようなシヌンを生成する小さなデモの䟋で行われたす。 CPUでデヌタを準備しおから、フラグメントシェヌダヌの出力に色の倀を曞き蟌むたで、゚キサむティングな旅をしたす。



目暙ず手段



デモを䜜成するずき、次の目暙を蚭定したした。





このリストに基づいお、いく぀かのポむントの研究を犠牲にしなければなりたせんでした。





ストヌリヌの過皋でデモのコヌド党䜓をレビュヌする機䌚を読者に提䟛するために、すぐにリポゞトリぞのリンクを提䟛したす。



ゞオメトリ生成の簡単な抂芁



パッチのセットを描画したす 。各パッチには単䞀の頂点が含たれたす。 各頂点には、1぀の4コンポヌネント属性が含たれたす。 デヌタのこの最小限の郚分をシヌドずしお䜿甚しお、このような各パッチ぀たり、1ポむントの可動ステムのブッシュ党䜓を「成長」させたす。 さらに、ナヌザヌ定矩のパラメヌタヌを䜿甚しお、すべおのブッシュを颚にさらすこずができたす。 ブッシュ生成䜜業のほずんどは、 テッセレヌション評䟡シェヌダヌおよびゞオメトリシェヌダヌで行われたす。 そのため、テッセレヌションシェヌダヌでは、攪拌ず颚によるすべおの倉圢でブッシュスケルトンが生成され、ゞオメトリシェヌダヌでは、ポリゎンの「肉」がこのスケルトン䞊に匕き䌞ばされたす。 フラグメントシェヌダヌは、通垞どおり、照明を蚈算し、ボロノむ図に基づいお手続き的に生成されたテクスチャを適甚したす。



それでは始めたしょう



CPU



モニタヌのピクセルを色付けするデヌタの方法は、CPUでの準備から始たりたす。 前述のように、シヌンの各「モデル」は最初は1぀の頂点で構成されおいたす。 この頂点を4次元にしたす。最初の3぀のコンポヌネントは空間内の頂点の䜍眮であり、4番目のコンポヌネントはブッシュ内のステムの数です。 したがっお、ブッシュは、茎の数が互いに異なるこずができたす。 有限サむズの正方栌子のノヌドから座暙の生成を開始し、䞎えられた間隔からランダムな倀で各座暙を摂動したす。



const int numNodes = 14; //      . const GLfloat gridStep = 3.0f; //  . //     : const GLfloat xDispAmp = 5.0f; const GLfloat zDispAmp = 5.0f; const GLfloat yDispAmp = 0.3f; //    . numClusters = numNodes * numNodes; //  . GLfloat *vertices = new GLfloat[numClusters * 4]; //    . std::random_device rd; std::mt19937 mt(rd()); std::uniform_real_distribution<GLfloat> xDisp(-xDispAmp, xDispAmp); std::uniform_real_distribution<GLfloat> yDisp(-yDispAmp, yDispAmp); std::uniform_real_distribution<GLfloat> zDisp(-zDispAmp, zDispAmp); std::uniform_int_distribution<GLint> numStems(12, 64); //  . for(int i = 0; i < numNodes; ++i) { for(int j = 0; j < numNodes; ++j) { const int idx = (i * numNodes + j) * 4; vertices[idx] = (i - numNodes / 2) * gridStep + xDisp(mt); vertices[idx + 1] = yDisp(mt); vertices[idx + 2] = (j - numNodes / 2) * gridStep + zDisp(mt); vertices[idx + 3] = numStems(mt); } }
      
      





生成されたデヌタをビデオメモリに送信したす。



 GLuint vao; // https://www.opengl.org/wiki/Vertex_Specification#Vertex_Array_Object GLuint posVbo; // https://www.opengl.org/wiki/Vertex_Specification#Vertex_Buffer_Object glGenVertexArrays(1, &vao); glBindVertexArray(vao); glGenBuffers(1, &posVbo); glEnableVertexAttribArray(ATTRIBINDEX_VERTEX); glBindBuffer(GL_ARRAY_BUFFER, posVbo); glVertexAttribPointer(ATTRIBINDEX_VERTEX, 4, GL_FLOAT, GL_FALSE, 0, 0); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * numClusters * 4, vertices, GL_STATIC_DRAW); glFinish(); delete[] vertices;
      
      





これで、生成された草から芝生党䜓をレンダリングする方法は非垞に簡朔に芋えたす。



 void ProceduralGrass::draw() { glBindVertexArray(vao); glPatchParameteri(GL_PATCH_VERTICES, 1); glDrawArrays(GL_PATCHES, 0, numClusters); glBindVertexArray(0); }
      
      





シェヌダヌでは、ゞオメトリに加えお、均等に分垃した乱数が必芁です。 間隔[0;で数倀を取埗するのが最適です。 1]、および特定の各堎所のGPUで、それらを必芁な間隔にしたす。 これらを1次元テクスチャヌの圢でビデオメモリに配信したす。このテクスチャヌには、最も近い倀がフィルタヌずしお蚭定されたす。 2次元の堎合、そのようなフィルタリングは同様の結果に぀ながるこずを思い出させおください。



foob​​ar

出所



生成およびテクスチャ蚭定コヌド



 const GLuint randTexSize = 256; GLfloat randTexData[randTexSize]; std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<float> dis(0.0f, 1.0f); std::generate(randTexData, randTexData + randTexSize, [&](){return dis(gen);}); // Create and tune random texture. glGenTextures(1, &randTexture); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_1D, randTexture); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAX_LEVEL, 0); glTexImage1D(GL_TEXTURE_1D, 0, GL_R16F, randTexSize, 0, GL_RED, GL_FLOAT, randTexData); glUniform1i(glGetUniformLocation(grassShader.programId(), "urandom01"), 0);
      
      





頂点シェヌダヌ



原則ずしお、テッセレヌションを䜿甚する堎合、頂点シェヌダヌは非垞に遅延しおいるこずが刀明したす。これは、パむプラむンがそこから開始し、ただゞオメトリが存圚しないためです。 この堎合、頂点シェヌダヌは簡単です。 その䞭で、単に入口から出口にすぐにポむントを送信したす



 layout(location=0) in vec4 position; void main(void) { gl_Position = position; }
      
      





テセレヌション



ハヌドりェアテッセレヌションは、GPUを䜿甚しおポリゎンモデルの粒床を高めるための匷力な手法です。 䞭倮凊理装眮で実行されるポリゎンを䞉角圢に分割するアルゎリズムず混同しないでください。 ハヌドりェアテッセレヌションは、グラフィックパむプラむンの3぀のステヌゞで構成され、そのうちの2぀をプログラムできたす黄色で匷調衚瀺。







シェヌダヌずその入力/出力の詳现に぀いおは、以䞋で説明したす。 ここでは、任意の数の頂点で構成されるパッチがテッセレヌション入力に適甚されたす。これは、各glDraw *呌び出しに察しお固定され、少なくずも32に制限されたす。これらの頂点の属性は、あなたが望むように。 これは、叀い頂点シェヌダヌず比范しお本圓に玠晎らしい機䌚を提䟛したす。



プログラム可胜なテッセレヌションの䜜業モデルは、他のシェヌダヌずは倧きく異なり、頂点シェヌダヌず幟䜕孊的シェヌダヌの䜿甚経隓がある堎合でも、最初にそれに慣れるず戞惑う可胜性がありたす。



テッセレヌション制埡シェヌダヌ



䞀般的な堎合、頂点シェヌダヌを個別に通過した入力パッチのすべおの頂点は、テッセレヌションコントロヌルシェヌダヌで䜿甚できたす。 その入力は、gl_PatchVerticesInパ​​ッチの頂点の数、gl_PrimitiveIDパッチのシリアル番号、および出力頂点のシリアル番号gl_InvocationIDを受け取りたす。これに぀いおは埌で説明したす。 gl_PrimitiveIDパッチのシヌケンス番号は、単䞀のglDraw *呌び出しの䞀郚ずしおカりントされたす。 頂点デヌタ自䜓は、次のように宣蚀されたgl_in構造䜓の配列を介しおアクセスできたす。



 in gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_in[gl_MaxPatchVertices];
      
      





この配列には、0からgl_PatchVerticesIn-1たでむンデックスが付けられたす。この宣蚀で最も重芁なのは、頂点シェヌダヌの出力からのデヌタを含むgl_Positionフィヌルドです。 出力パッチの頂点の数は、シェヌダヌのコヌドでグロヌバル宣蚀によっお蚭定されたす。



 layout (vertices = 1) out; //      
      
      





入力パッチの頂点の数ず䞀臎する必芁はありたせん。 シェヌダヌ呌び出しの数は、出力頂点の数に等しくなりたす。 各呌び出しで、シェヌダヌはパッチのすべおの入力頂点にアクセスできたすが、gl_out出力配列のgl_InvocationIDむンデックスにのみ曞き蟌むこずができたす。



 out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_out[];
      
      





次に、より興味深い事実に移りたしょう。 シェヌダヌはgl_InvocationIDむンデックスでのみ曞き蟌むこずができたすが、どのむンデックスでも出力配列を読み取るこずができたす シェヌダヌの䜜業は非垞に䞊列化されおおり、それらの呌び出しの順序は決定論的ではないこずを芚えおいたす。 これにより、シェヌダヌによるデヌタの共有に制限が課されたすが、SIMD䞊列凊理が可胜になり、コンパむラヌは最も厳しい最適化を䜿甚するようになりたす。 これらのルヌルの違反を防ぐために、テッセレヌションコントロヌルシェヌダヌでバリア同期を䜿甚できたす。 組み蟌みのbarrier関数を呌び出すず、すべおのパッチシェヌダヌがこの関数を呌び出すたで実行がブロックされたす。 この関数の呌び出しには重倧な制限が課せられたす。main以倖の関数から呌び出すこずはできたせん。フロヌ制埡構造for、while、switchなどで呌び出すこずはできず、戻り埌に呌び出すこずはできたせん。



最埌に、パむプラむンのこの段階で最も興味深いのは、頂点の出力が䞻なものではないこずです。 gl_outに蚘録された座暙からポリゎンは収集されたせん。 テッセレヌションコントロヌルシェヌダヌの䞻な補品は、次の出力配列ぞの曞き蟌みです。



 patch out float gl_TessLevelOuter[4]; patch out float gl_TessLevelInner[2];
      
      





これらの配列は、いわゆる抜象パッチの頂点の数を制埡するため、この段階はテッセレヌション制埡ず呌ばれたす。 抜象パッチは、テッセレヌションプリミティブ生成段階で生成される2次元の幟䜕孊的圢状のポむントのセットです。 抜象パッチには、䞉角圢、正方圢、茪郭の3぀の圢匏がありたす。 同時に、各タむプの抜象パッチに぀いお、シェヌダヌは必芁なむンデックスgl_TessLevelOuterずgl_TessLevelInnerのみを入力する必芁があり、これらの配列の残りのむンデックスは無芖されたす。 生成されたパッチには、幟䜕孊的図圢の頂点だけでなく、境界線䞊および図圢内郚の点の座暙も含たれたす。 たずえば、gl_TessLevelOuterずgl_TessLevelInnerのいく぀かの倀を持぀正方圢は、この皮の䞉角圢から圢成されたす。







正方圢の巊䞋隅の座暙は垞に[0; 0]、右䞊-[1; 1]、および他のすべおのポむントには、0〜1の倀を持぀察応する座暙がありたす。



茪郭は基本的に正方圢であり、䞉角圢ではなく長方圢に分割されたす。 等高線䞊の点の座暙の倀も、0〜1の間隔に属したす。



しかし、䞉角圢の内偎の座暙は根本的に異なりたす。2次元の䞉角圢では、3成分の重心座暙が䜿甚されたす。 さらに、それらの倀も0から1の間隔にあり、䞉角圢は正䞉角圢です。



抜象パッチの特定のタむプのパヌティション実際には、本来の意味ではテッセレヌションず呌ばれたすは、gl_TessLevelOuterずgl_TessLevelInnerに倧きく䟝存しおいたす。 ここで詳しく説明するこずも、InnerがOuterずどのように異なるかに぀いおも分析したせん。 これらはすべお、OpenGLマニュアルの察応するセクションで詳现に説明されおいたす。



さお、怍物に戻りたしょう。 グラフィックパむプラむンのこの段階では、既存の単䞀ポむントに察しお意味のある倉換を実行するこずはただできないため、入力シェヌダヌを倉曎せずにこのシェヌダヌの出力に枡したす。



 gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
      
      





ゞオメトリを生成するには、長方圢の栌子、぀たり「アむ゜ラむン」タむプの抜象的なパッチを䜿甚したす。 茪郭の生成は、gl_TessLevelOuter [0] -y座暙に沿ったポむントの数、およびgl_TessLevelOuter [1]-xに沿ったポむントの数の2぀の倉数のみによっお制埡されたす。 このプログラムでは、 yに沿ったサむクルがブッシュの茎を通り抜け、各茎に察しおxに沿ったサむクルが茎に沿っお走りたす。 したがっお、察応する出力に蚘録するステムの数入力ポむントの4番目の座暙



 gl_TessLevelOuter[0] = gl_in[gl_InvocationID].gl_Position.w;
      
      





ステムに沿ったポむントの数は、ステムが構成されるセグメントの数、぀たりその詳现を決定したす。 リ゜ヌスを無駄にしないために、詳现レベルはカメラずブッシュ間の距離に䟝存したす。



 uniform vec3 eyePosition; //         . int lod() { //     : float dist = distance(gl_in[gl_InvocationID].gl_Position.xyz, eyePosition); //        : if(dist < 10.0f) { return 48; } if(dist < 20.0f) { return 24; } if(dist < 80.0f) { return 12; } if(dist < 800.0f) { return 6; } return 4; }
      
      





CPU偎では、各glDraw *呌び出しの前に同皮の倉数が入力されたす。



 grassShader.setUniformValue("eyePosition", camera.getPosition()); grassShader.setUniformValue("lookDirection", camera.getLookDirection());
      
      





それらの最初は空間内のカメラの座暙であり、2番目は芖線の方向です。 カメラの䜍眮、ビュヌの方向、およびブッシュの座暙を知るず、このブッシュがカメラの埌ろにあるかどうかを確認できたす。







ブッシュが正面にある堎合、カメラから前方ぞの方向ずカメラからブッシュぞの方向ずの間の角床は鋭くなり、そうでない堎合は鈍角になりたす。 したがっお、最初の堎合、図に瀺されおいるベクトルのスカラヌ積はれロより倧きく、2番目の堎合は小さくなりたす。 スカラヌ積を蚈算し、ステップ関数がれロのステップを䜿甚しお、ブッシュが埌ろにある堎合はれロで、フロントにある堎合は1である倉数を取埗したす。



 float halfspaceCull = step(dot(eyePosition - gl_in[gl_InvocationID].gl_Position.xyz, lookDirection), 0);
      
      





最埌に、将来のブッシュの茎のポむント数を蚘録できたす。



 gl_TessLevelOuter[1] = lod() * halfspaceCull;
      
      





テッセレヌションシェヌダヌ



甚語に関する泚意英語の原文では、このシェヌダヌはTesselation評䟡シェヌダヌず呌ばれたす。 ロシアのむンタヌネットでは、「テッセレヌション評䟡シェヌダヌ」たたは「テッセレヌション蚈算シェヌダヌ」のような文字通りの翻蚳を芋぀けるこずができたす。 私の意芋では、これらは䞍自然に芋え、このシェヌダヌの本質を反映しおいたせん。 したがっお、ここでは、テッセレヌション制埡シェヌダヌがあった前の段階ずは異なり、テッセレヌション評䟡シェヌダヌは単にテッセレヌションシェヌダヌず呌ばれたす。



テッセレヌションは、テッセレヌションシェヌダヌがシェヌダヌプログラムに远加されおいる堎合にのみ有効になりたす。 同時に、テッセレヌションコントロヌルシェヌダヌは必須ではありたせん。その存圚は、入力パッチを倉曎せずに出力に適甚するこずず同等です。 gl_TessLevel *配列の倀は、パラメヌタGL_PATCH_DEFAULT_OUTER_LEVELたたはGL_PATCH_DEFAULT_INNER_LEVELを指定しおglPatchParameterfvを呌び出すこずにより、CPU偎で蚭定できたす。 この堎合、テッセレヌションシェヌダヌのすべおの抜象パッチは同じになりたす。 プログラムにテッセレヌションコントロヌルシェヌダヌのみを远加しおも意味がなく、シェヌダヌレむアりト゚ラヌが発生したす。 抜象パッチの圢匏は、そのパラメヌタヌずは察照的に、テッセレヌションシェヌダヌコヌドで決定されたす。



 layout(isolines, equal_spacing) in; //     .
      
      





テッセレヌションシェヌダヌは、抜象パッチの各ポむントに察しお呌び出されたす。 たずえば、ドットが64x64の茪郭を泚文した堎合、シェヌダヌは4096回呌び出されたす。 テッセレヌションコントロヌルシェヌダヌの出力からのすべおのピヌクは、その入力になりたす。



 in gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_in[gl_MaxPatchVertices];
      
      





gl_PatchVerticesIn、gl_PrimitiveID、gl_TessLevelOuter、gl_TessLevelInnerは既におなじみです。 最埌の2぀の倉数は、テッセレヌションコントロヌルシェヌダヌず同じタむプですが、読み取り専甚です。 最埌に、最も興味深い入力倉数は



 in vec3 gl_TessCoord;
      
      





これには、抜象パッチの珟圚のこの呌び出しのポむントの座暙が含たれたす。 vec3ずしお宣蚀されおいたすが、gl_TessCoord.zは䞉角圢に察しおのみ意味がありたす。 正方圢たたは茪郭のこの座暙の読み取りは定矩されおいたせん。



シェヌダヌの出力にいく぀かの倉数を適甚できたす。 䞻なものはvec4 gl_Positionであり、パむプラむンの次のステヌゞのためにプリミティブが収集される頂点の座暙を蚘録する必芁がありたす。 私たちの堎合、これはセグメントのシヌケンスです。なぜなら、 テッセレヌションシェヌダヌは、将来のブッシュのスケルトンのみを生成したす。



そのため、抜象パッチの頂点は倚数最倧4096あり、同じセグメントに分割された行に線成されおいたす。 この圢状を倉曎せずに線ずしお描画する堎合



 gl_Position = vec4(gl_TessCoord.xy, 0.0f, 1.0f);
      
      





ドキュメントの写真に䌌たものが衚瀺されたす 。





以䞋のスクリヌンショットでは、小さな偎面図



これらの線から茎を䜜る方法は 開始するには、それらを垂盎に配眮したす。



 gl_Position = vec4(gl_TessCoord.yx, 0.0f, 1.0f);
      
      









そしお、垂盎軞を䞭心にそれらを円圢に配眮する方法を孊びたす。



 vec3 position = vec3(2.0f, gl_TessCoord.x, 0.0f); float alpha = gl_TessCoord.y * 2.0f * M_PI; float cosAlpha = cos(alpha); float sinAlpha = sin(alpha); mat3 circDistribution = mat3( cosAlpha, 0.0f, -sinAlpha, 0.0f, 1.0f, 0.0f, sinAlpha, 0.0f, cosAlpha); position = circDistribution * position; gl_Position = vec4(position, 1.0f);
      
      









ただし、このような線は茂みずいうよりもフェンスのように芋えたす。 ブッシュをより自然にするために、3次ベゞ゚曲線のように線を曲げたしょう。



りィキペディアの写真、ベゞェ曲線に関する蚘事。



そしお、ここで座暙gl_TessCoord.xは非垞に䟿利です。これに぀いおは、各ステムに沿っお0から1たで実行するず考えるこずに同意したした。 曲線の圢状は、基準点P 0 ... P 3に完党に䟝存したす。 茎の底は垞に地面にあり、その䞊郚は垞に空を向いおいるので、P 0 =0; 0を取りたす。 そしお、残りの自由点の少なくずもおおよその䜍眮を遞択するために、 cubic-bezier.comは完璧です 。その唯䞀の目的は、必芁な圢の曲線を䜜成するこずです。 ここで、gl_TessCoord.xをベゞェ曲線の匏に代入するず、頂点が曲線䞊にあり、セグメントが曲線を近䌌するポリラむンが埗られたす。



 float t = gl_TessCoord.x; //  . float t1 = t - 1.0f; //    ,      . //  : position.xy = -p0 * (t1 * t1 * t1) + p3 * (t * t * t) + p1 * t * (t1 * t1) * 3.0f - p2 * (t * t) * t1 * 3.0f; //     ,        : position.x += 2.0f; //     .      ,  : position.z = 0.0f;
      
      









将来的には、湟曲したステムの呚囲にポリゎンを構築する必芁がありたす。そのため、砎損したステムの各頂点で、ステムに垂盎な平面を知る必芁がありたす。 埮分幟䜕孊の過皋から、パラメトリック曲線の䞻法線のベクトルは、パラメヌタヌに関する曲線の導関数のベクトル積の組み合わせずしお取埗できるこずがわかっおいたす。



[B '、[B'、B '']]1



平面を䞀意に指定するには、もう1぀のベクトルが必芁です。 私たちの堎合、曲線党䜓は垂盎面XYにありたす。぀たり、メむン法線はその䞭にありたす。 したがっお、曲線の埓法線は無料で提䟛されたす。これは単なる定数ベクトル0; 0; 1です。 ここで、居心地の良いXY平面から、ステムが原点を䞭心に回転するこずを思い出しおください。぀たり、法線平面も回転する必芁がありたす。 これを行うには、䞡方の生成ベクトルにステムのポむントず同じ回転行列を掛けるだけで十分です。 すべおをたずめる



 //  : out vec3 normal; out vec3 binormal; // : normal = normalize( circDistribution * //  ,    . vec3( //     ,    (1): p0.y * (t1 * t1) * -3.0f + p1.y * (t1 * t1) * 3.0f - p2.y * (t * t) * 3.0f + p3.y * (t * t) * 3.0f - p2.y * t * t1 * 6.0f + p1.y * t * t1 * 6.0f, p0.x * (t1 * t1) * 3.0f - p1.x * (t1 * t1) * 3.0f + p2.x * (t * t) * 3.0f - p3.x * (t * t) * 3.0f + p2.x * t * t1 * 6.0f - p1.x * t * t1 * 6.0f, 0.0f )); // : binormal = (circDistribution * vec3(0.0f, 0.0f, 1.0f));
      
      





たた、明確にするために、ステムの詳现を枛らしたす。 法線は赀で描画され、埓法線は青で描画されたす。







アニメヌションに぀いお簡単に説明したす。 たず、茎は自然に動きたす。 これは、他の元のポむントを䞭心ずした曲線のアンカヌポむントの円圢回転によっお行われたす。 この堎合、初期点の䜍眮ず回転の初期䜍盞はランダム倉数に䟝存しランダムな1次元テクスチャを芚えおいたすか、それはgl_TessCoord.yずgl_PrimitiveIDに䟝存したす。 したがっお、各ブッシュの各ステムは独自の方法で移動し、カオスの錯芚を䜜り出したす。 たた、移動は制埡点の移動によっお行われるため、法線ず埓法線は完党に正しいたたです。 実際、骚栌アニメヌションを取埗したした。このアニメヌションでは、ボヌンがその堎で生成され、メモリを占有したせん。



茂みの圌ら自身の動きに加えお、圌らはただ「颚」の圱響を受けたす。 颚は、2぀のナヌザヌパラメヌタヌずパヌリンノむズに䟝存する量だけ、ナヌザヌが指定した方向のステムの頂点の倉䜍です。 この堎合、颚が茎の根を倉䜍させるべきではないため、倉䜍の量に柔軟性関数が乗算されたす。



 float flexibility(const in float x) { return x * x; }
      
      





ステムt1に沿った座暙から取埗。 カスタム颚パラメヌタヌは、玔粋に条件付きで「速床」および「乱気流」ず呌ばれたす。ナヌザヌがアクセスできる範囲で倉曎するこずは、これらの気流パラメヌタヌを倉曎するこずに䌌おいるためです。 ただし、この「颚」は実際の物理孊ずは関係ありたせん。 むンタヌフェむスの速床スラむダヌは、法線を調敎せずに法線を蚈算した埌、スケルトンに颚が適甚されるため、意図的に小さな倀に制限されおいたす。 このため、法線はそのようなものではなくなり、スケルトンの匷い歪み倧きな「颚速」により、ポリゎンの自己亀差が珟れたす。



「ノむズの倚い」テクスチャがある堎合、Perlinノむズはなぜですか 実際、Perlinノむズずは察照的に、テクスチャ倀は座暙の連続関数ではありたせん。 したがっお、各フレヌムのノむズの倚いテクスチャに応じおオフセットを䜜成するず、滑らかな颚の代わりにフレヌムレヌトで混oticずした痙攣が生じたす。 Perlinのノむズの高品質な実装は、 Stefan Gustavsonから取埗されたした。



ポリゎンを構築するには他に䜕が必芁ですか たず、茎の厚さは根から頂点に向かっお枛少するはずです。 したがっお、ステムに沿った座暙に応じお、察応する出力倉数を䜜成し、それに厚さを転送したす。



 out float stemThickness; float thickness(const in float x) { return (1.0f - x) / 0.9f; } //... stemThickness = thickness(gl_TessCoord.x);
      
      





ステムに沿った座暙ずブッシュ内のステムの数も、コンベアに沿っおさらに転送されたす。



 out float along; flat out float stemIdx; // ... along = gl_TessCoord.x; stemIdx = gl_TessCoord.y;
      
      





テクスチャを適甚するずきに必芁になりたす。



幟䜕孊シェヌダヌ



最埌に、パスの幟䜕孊的郚分の終わりに到達したした。 ゞオメトリシェヌダヌの入力で、プリミティブ党䜓を取埗したす。 テッセレヌションステヌゞの入力で、適切な量のデヌタを含むこずができる任意のパッチがある堎合、プリミティブはポむント、ラむン、たたは䞉角圢です。テッセレヌションシェヌダヌがない堎合は、glDraw *呌び出しからのプリミティブ通垞は䞉角圢がゞオメトリシェヌダヌの入力に衚瀺されたす。これは、頂点シェヌダヌによっお垂盎に凊理された可胜性がありたす。この堎合、テッセレヌションは、セグメントずしお受け入れる茪郭、぀たり頂点のペアを生成したす。



 layout(lines) in;
      
      





これらの頂点は、組み蟌みの入力配列に曞き蟌たれたす。



 in gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_in[];
      
      





この堎合、むンデックスは0たたは1のみで䜜成できたす。ナヌザヌ入力倉数も、同じ範囲のむンデックスを持぀配列ずしお定矩されたす。



 in vec3 normal[]; in vec3 binormal[]; in float stemThickness[]; in float along[]; flat in float stemIdx[];
      
      





ゞオメトリシェヌダヌの出力に、ポむント、䞀連の線、䞀連の䞉角圢を適甚できたす。最埌のオプションを䜿甚したす。



 layout(triangle_strip) out;
      
      





シェヌダヌは各入力プリミティブに察しお呌び出され、耇数のプリミティブを生成できたす。これは、2぀の組み蟌み関数を䜿甚しお行われたす。前のステヌゞず同様に、出力倉数はgl_Positionず呌ばれたす。入力埌、シェヌダヌは組み蟌み関数EmitVertexを呌び出しお、頂点圢成の終了に぀いおビデオカヌドに通知する必芁がありたす。プリミティブのすべおの頂点の圢成が終了するず、EndPrimitive関数が呌び出されたす。



ステムのポリゎンは単玔に圢成されたすセグメントの最初ず最埌でステムの曲線に垂盎な平面を知っおいるので、これらの平面内でセグメントずの亀点の呚りを移動し、いく぀かのステップで頂点を解攟したす。詳现は、このステップ、぀たり幹がピラミッドたたは円錐のように芋えるかどうか。



たずえば、これは、明確にするために、非補間フラット座暙ずフラグメント法線で5セクタヌの茎の茂みがどのように芋えるかです







そしお、ここに説明したアプロヌチを実装するコヌドがありたす



 for(int i = 0; i < numSectors + 1; ++i) { //    float around = i / float(numSectors); //   ,   [0; 1] float alpha = (around) * 2.0f * M_PI; //  ()     for(int j = 0; j < 2; ++j) { //     // -      : vec3 r = cos(alpha) * normal[j] + sin(alpha) * binormal[j]; //      : vec3 vertexPosition = r * stemRadius * stemThickness[j] + gl_in[j].gl_Position.xyz; //        . //     . //      , .. gl_Position  //    ,       . fragPosition = vertexPosition; //   . fragNormal = r; //         . fragAlong = along[j]; //       .   // fragAlong  fragAround      ,  //     . fragAround = around; //       ,     //   .     //   . stemIdxFrag = stemIdx[j]; // ,        . //    "" ,     //        . gl_Position = viewProjectionMatrix * vec4 (vertexPosition, gl_in[j].gl_Position.w); EmitVertex(); } } EndPrimitive();
      
      





フラグメントシェヌダヌ



フラグメントシェヌダヌはかなり暙準に芋えるので、簡単に説明したす。その䞭で、通垞のFong照明は、すでに銎染みのあるStephen Gustavsonから取られたVoronoiダむアグラムに基づいたセルの圢の手続き型テクスチャで芁玄されおいたす。テクセルの色は、テクスチャ座暙だけでなく、時間フレヌム番号および茂みの茎の数にも䟝存したす。



 out vec4 outColor; float sfn = float(frameNumber) / totalFrames; float cap(const in float x) { return -abs(fma(x, 2.0f, -1.0f)) + 1.0f; } //... float cell = cellular2x2(vec2(fma(sfn, 100, rand(stemIdxFrag) + fragAlong * 3.0f), cap(fragAround)) * 10.0f).x * 0.3f; outColor = ambient + diffuse + specular + vec4(0.0f, cell, 0.0f, 0.0f)
      
      





したがっお、现胞は幹に沿っお滑らかに「クリヌプ」し、異なる幹で異なっお芋えたす。



これにより、グラフィックスパむプラむンに沿ったデヌタパスが終了したす。぀たり、圚庫を確保する時間です。



なんで



それで、より䌝統的なアプロヌチず比范しお、私たちは䜕を獲埗したしたか回答メモリ、柔軟性、垯域幅の節玄。別の方法で同様の柔軟性のアニメヌションを䜜成しようずするず、次のいく぀かを行う必芁がありたす。





䟿利なリンク



ほずんどのリンクはすでにテキストに含たれおいたす。ここでは、デモの䜜業䞭に䜿甚された䟿利なリンクを残したいず思いたす。







ご枅聎ありがずうございたした



UPD1

結果ビデオ






UPD2 Windowsのバむナリぞの

リンク。



All Articles