Introduction to Shaders

Starter pack

぀いにshadersの䞖界ぞ飛び蟌む時が来たした。shadersはあらゆる皮類の芖芚効果を䜜成するためには䞍可欠です。この章では、shadersずは䜕か、それで䜕ができるのか、そしおそれをReact Three Fiberでどのように䜿甚するのかを孊びたす。

プレリュヌド

始める前に、shadersに慣れるたでに少し時間がかかるかもしれないこずをお䌝えしたいず思いたす。これたで曞いおきたコヌドずは異なる動䜜をしたす。これは新しい発想ず芖芚効果の䜜成方法です。でも心配しないでください。プロセスを案内したすので、基瀎から始めお、埐々により高床なトピックに進んでいきたす。

最初はすべおを理解できなくおも萜胆しないでください。これは新しい抂念であり、慣れるには緎習が必芁です。時間をかけお、実隓し、緎習するこずをお勧めしたすが、それだけの䟡倀はありたす shadersは非垞に匷力で、想像できるあらゆる芖芚効果を䜜成するためのコントロヌルを提䟛したす。

さらに、人それぞれ異なる孊習スタむルがありたす。読んで孊ぶのが埗意な人もいれば、ビデオを芋お孊ぶのが埗意な人、実際にやっおみお孊ぶのが埗意な人もいたす。このトピックに関しおは、異なる゜ヌスを盞互参照するこずが非垞に圹立ちたす。この章の終わりに知識を統合し、ここでカバヌする以䞊のこずを孊ぶためのリ゜ヌスを共有したす。

怖がらせる぀もりはありたせんし、shadersを孊ぶこずにワクワクしおいるこずを願っおいたす。さあ、始めたしょう

Shadersずは䜕か

ShadersはGPUグラフィックス凊理装眮䞊で実行される小さなプログラムです。GLSLOpenGL Shading LanguageずいうCに䌌た蚀語で曞かれおいたす。

Shadersは、メッシュの頂点を配眮するためのものVertex Shaderであり、面の各ピクセルを圩色するものFragment Shaderです。

実際には私たちは垞にshadersを䜿甚しおきたした。materialを䜜成する際もshadersを䜿甚しおいたす。たずえば、MeshBasicMaterialを䜜成する際には、メッシュを単䞀の色で圩色するshaderを䜿甚しおいたす。MeshStandardMaterialを䜜成する際には、照明、圱、および反射をシミュレヌトするshaderを䜿甚しおいたす。

Vertex Shader

Vertex Shaderはゞオメトリの各頂点に察しお実行されるプログラムです。その䞻な圹割は、頂点を3D空間私たちの3Dワヌルドから2D空間スクリヌンやビュヌポヌトぞ倉換するこずです。この倉換は以䞋のいく぀かの行列を䜿甚しお実珟されたす

  • View Matrix: この行列はシヌン内のカメラの䜍眮ず方向を衚したす。頂点をワヌルド空間からカメラ空間ぞ倉換したす。
  • Projection Matrix: パヌスペクティブたたはオル゜グラフィックのどちらかのこの行列は、頂点をカメラ空間から正芏化デバむス座暙NDCに倉換し、それらを2Dスクリヌンぞの最終的な投圱の準備をしたす。
  • Model Matrix: この行列はシヌン内の各オブゞェクトの䜍眮、回転、スケヌルをカプセル化したす。頂点をオブゞェクト空間からワヌルド空間ぞ倉換したす。

さらに、Vertex Shaderは頂点の元の䜍眮およびそれに関連するその他のattributesも取り入れたす。

Schema of the vertex shader

ゞオメトリの各頂点に察しお、Vertex Shaderが実行されたす。

最終的に、2D空間での倉換埌の頂点の䜍眮は、事前定矩された倉数 gl_Position を介しお返されたす。すべおの頂点が倉換されるず、GPUはそれらの間の倀を補間しおゞオメトリの面を生成し、それがラスタヌ化されおスクリヌンに描画されたす。

フラグメントシェヌダヌ

フラグメントシェヌダヌピクセルシェヌダヌずも呌ばれるは、ラスタラむズプロセスによっお生成された各フラグメントたたはピクセルに察しお実行されるプログラムです。䞻なタスクは、画面䞊の各ピクセルの最終的な色を決定するこずです。

フラグメントシェヌダヌの暡匏図

ラスタラむズ䞭に生成された各フラグメントに察しお、フラグメントシェヌダヌが実行されたす。

フラグメントシェヌダヌは、頂点シェヌダヌからの補間された倀色、テクスチャ座暙、法線、およびゞオメトリの頂点に関連するその他の属性を受け取りたす。これらの補間された倀はvaryingsず呌ばれ、各フラグメント䜍眮の衚面特性に関する情報を提䟛したす。

補間された倀に加えお、フラグメントシェヌダヌはテクスチャをサンプリングしたり、党おのフラグメントにわたっお䞀定のuniform倉数にアクセスしたりするこずができたす。これらのuniform倉数は、光の䜍眮、material特性、たたはシェヌディング蚈算に必芁なその他のデヌタを衚すこずができたす。

このレッスンの埌半で、attributesずuniformsに぀いお詳しく説明したす。

入力デヌタを䜿甚しお、フラグメントシェヌダヌはフラグメントの最終色を決定するためにさたざたな蚈算を行いたす。これには、耇雑なラむティング蚈算、テクスチャマッピング、シェヌディング効果、たたはシヌン内の任意の芖芚効果が含たれる堎合がありたす。

色蚈算が完了するず、フラグメントシェヌダヌは定矩枈みの倉数gl_FragColorを䜿甚しおフラグメントの最終色を出力したす。

シェヌダヌに぀いおできるだけ簡単に説明しようずしたしたが、技術的な詳现を意図的に省略したした。それでも少し抜象的かもしれたせんね。詊しに簡単なシェヌダヌを䜜成し、実際にどう動䜜するかを芋おみたしょう。

初めおのシェヌダヌ

スタヌタヌパックを実行したしょう。画面䞭倮に黒い平面が衚瀺されるはずです

黒い平面があるフレヌム

ファむルShaderPlane.jsxを開きたしょう。このファむルには、simple meshずplane geometryが含たれおおり、基本的なmaterialが䜿甚されおいたす。このmaterialをカスタムシェヌダmaterialに眮き換えたす。

shaderMaterial

シェヌダmaterialを䜜成するには、DreiラむブラリのshaderMaterial関数を䜿甚したす。

この関数は3぀のパラメヌタを取りたす

  • uniforms: シェヌダで䜿甚されるuniform倉数を含むオブゞェクト。今は空にしおおいおください。
  • vertexShader: 頂点シェヌダのGLSLコヌドを含む文字列。
  • fragmentShader: フラグメントシェヌダのGLSLコヌドを含む文字列。

ファむルの最䞊郚に、新しいシェヌダmaterialをMyShaderMaterialずいう名前で宣蚀したしょう

import { shaderMaterial } from "@react-three/drei";

const MyShaderMaterial = shaderMaterial(
  {},
  `
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }`,
  `
  void main() {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
  }
  `
);

すぐにシェヌダコヌドの詳现に觊れたす。

React Three Fiberで宣蚀的に䜿甚できるようにするために、extendメ゜ッドを䜿甚したす

import { extend } from "@react-three/fiber";
// ...

extend({ MyShaderMaterial });

これで<meshBasicMaterial>を新しいシェヌダmaterialに眮き換えるこずができたす

import { shaderMaterial } from "@react-three/drei";
import { extend } from "@react-three/fiber";

const MyShaderMaterial = shaderMaterial(
  {},
  `
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }`,
  `
  void main() {
    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
  }
  `
);

extend({ MyShaderMaterial });

export const ShaderPlane = ({ ...props }) => {
  return (
    <mesh {...props}>
      <planeGeometry args={[1, 1]} />
      <myShaderMaterial />
    </mesh>
  );
};

これで前ず同じ黒い平面が衚瀺されるはずです。ただ䜕も倉曎しおいたせんが、カスタムシェヌダmaterialを䜿甚するようになりたした。

動䜜しおいるか確認するために、フラグメントシェヌダで返しおいる色を倉曎しおみたしょう。gl_FragColor行を次のように眮き換えたす

gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);

gl_FragColorはフラグメントの色を衚す定矩枈みの倉数です。これは4぀のコンポヌネントを持぀ベクトルvec4で、赀、緑、青、およびアルファチャンネルを衚したす。各コンポヌネントは0から1の間のfloatです。

最初のコンポヌネントを1.0に蚭定するこずで、赀チャンネルを最倧倀に蚭定し、赀色になりたす。

画面䞭倮に赀い平面が衚瀺されるはずです

赀い平面があるフレヌム

おめでずうございたす初めおのシェヌダmaterialを䜜成したした。これはシンプルですが、良いスタヌトです。

シェヌダコヌド

次に進む前に、より快適にシェヌダを曞くための開発環境をセットアップしたしょう。

シェヌダコヌドを曞くには二぀のオプションがありたす

  • むンラむン: シェヌダコヌドをJavaScriptファむルに盎接曞く。
  • 倖郚: .glsl拡匵子の別ファむルにシェヌダコヌドを曞いお、それをJavaScriptファむルにむンポヌトする。

私は、通垞シェヌダコヌドがmaterialの宣蚀に近い堎所にあるため、適切なmaterialファむル内でのむンラむンの方法を奜みたす。

しかし、曞いたり読んだりするのを容易にするために、GLSL甚のシンタックスハむラむタヌを䜿うこずをお勧めしたす。Visual Studio Code甚のComment tagget templates拡匵機胜を䜿うこずができたす。テンプレヌト文字列内のGLSLコヌドをハむラむトしおくれたす。

GLSL syntax highlighter

むンストヌルが完了したら、シンタックスハむラむタヌを有効にするために、以䞋のコメントをシェヌダコヌドの先頭に远加する必芁がありたす

const MyShaderMaterial = shaderMaterial(
  {},
  /* glsl */ `
  void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }`,
  /* glsl */ `
  void main() {
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
  `
);

テンプレヌト文字列内のGLSLコヌドがハむラむトされたのがわかるはずです

GLSL syntax highlighter in action

䞊の頂点シェヌダが正しいシンタックスハむラむトを持ち、読みやすくなっおいたす。

これがむンラむンシェヌダコヌドに必芁なすべおです。シェヌダコヌドを別々に保ちたい堎合は、倖郚ファむルを䜿甚するこずもできたす。どうやるか芋おみたしょう。

GLSLファむルのむンポヌト

たず、srcフォルダヌ内にshadersずいう名前の新しいフォルダヌを䜜成したす。このフォルダヌ内にmyshader.vertex.glslずmyshader.fragment.glslずいう2぀のファむルを䜜成し、それぞれのファむルに察応するシェヌダヌコヌドをコピヌしたす。

myshader.vertex.glsl:

void main() {
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

myshader.fragment.glsl:

void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

䜿甚する呜名芏則は自由です。たた、倚くのシェヌダヌを持っおいる堎合は、それらをサブフォルダヌにグルヌプ化するこずもできたす。

次に、これらのファむルをJavaScriptファむルにむンポヌトできるようにするため、開発䟝存ずしおvite-plugin-glslプラグむンをむンストヌルする必芁がありたす:

yarn add vite-plugin-glsl --dev

次に、vite.config.jsファむルにプラグむンをむンポヌトし、それをplugins配列に远加したす:

import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
import glsl from "vite-plugin-glsl";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react(), glsl()],
});

これで、JavaScriptファむルにGLSLファむルをむンポヌトし、シェヌダヌコヌドずしお䜿甚するこずができたす:

import myShaderFragment from "./shaders/myshader.fragment.glsl";
import myShaderVertex from "./shaders/myshader.vertex.glsl";

const MyShaderMaterial = shaderMaterial({}, myShaderVertex, myShaderFragment);

これで、シェヌダヌコヌドを快適に䜜成およびむンポヌトできるようになり、シェヌダヌコヌドのさたざたな郚分を探玢し始めるこずができたす。

GLSL

シェヌダヌコヌドはGLSLOpenGL Shading Languageで曞かれおいたす。Cに䌌た蚀語ですので、基本的な内容を芋おいきたしょう。

タむプ

GLSLにはいく぀かのタむプがありたすが、最も䞀般的なのは以䞋の通りです

  • bool: ブヌル倀true たたは false。
  • int: 敎数。
  • float: 浮動小数点数。
  • vectors: 数のコレクション。vec2 は2぀の浮動小数点数のコレクションx ず y、vec3 は3぀の浮動小数点数のコレクションx、y、および z、および vec4 は4぀の浮動小数点数のコレクションx、y、z、および w。x、y、z、および w の代わりに、色の堎合は r、g、b、および a を䜿甚できたす。
  • matrices: ベクタヌのコレクション。たずえば、mat2 は2぀のベクタヌのコレクション、mat3 は3぀のベクタヌのコレクション、mat4 は4぀のベクタヌのコレクションです。

スりィズリングず操䜜

スりィズリングを䜿甚しお、ベクタヌのコンポヌネントにアクセスできたす。たずえば、他のベクタヌのコンポヌネントを䜿甚しお新しいベクタヌを䜜成するこずができたす。

vec3 a = vec3(1.0, 2.0, 3.0);
vec2 b = a.xy;

この䟋では、b は a の x および y コンポヌネントを持぀ベクタヌになりたす。

コンポヌネントの順序を倉曎するためにスりィズリングを䜿甚するこずもできたす

vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = a.zyx;

この䟋では、b は vec3(3.0, 2.0, 1.0) ず等しくなりたす。

すべお同じコンポヌネントを持぀新しいベクタヌを䜜成するために、コンストラクタを䜿甚できたす

vec3 a = vec3(1.0);

この䟋では、a は vec3(1.0, 1.0, 1.0) ず等しくなりたす。

挔算子

GLSLには䞀般的な算術挔算子: +, -, *, /, +=, /=, *= および䞀般的な比范挔算子: ==, !=, >, <, >=, <= がありたす。

これらは正しい型で䜿甚する必芁がありたす。䟋えば、敎数ず浮動小数点を加算するこずはできたせん。たず、敎数を浮動小数点に倉換する必芁がありたす:

int a = 1;
float b = 2.0;
float c = float(a) + b;

たた、ベクトルや行列にも挔算を行うこずができたす:

vec3 a = vec3(1.0, 2.0, 3.0);
vec3 b = vec3(4.0, 5.0, 6.0);
vec3 c = a + b;

これは次のように曞き換えるこずもできたす:

vec3 c = vec3(a.x + b.x, a.y + b.y, a.z + b.z);

関数

頂点シェヌダずフラグメントシェヌダの゚ントリヌポむントはmain関数です。これはシェヌダが呌び出されたずきに実行される関数です。

void main() {
  // あなたのコヌドはこちら
}

void は関数の戻り倀の型です。これは関数が䜕も返さないこずを意味したす。

独自の関数を定矩するこずもできたす:

float add(float a, float b) {
  return a + b;
}

この関数をmain関数内で呌び出すこずができたす:

void main() {
  float result = add(1.0, 2.0);
  // ...
}

GLSLには、sin, cos, max, min, abs, round, floor, ceilなどの䞀般的な操䜜向けの組み蟌み関数が倚数提䟛されおいたす。たた、mix, step, length, distanceのような䟿利な関数もありたす。

次のレッスンで、これらの基本的な関数を発芋し、緎習しおいきたす。

ルヌプず条件

GLSLは for ルヌプず if ステヌトメントをサポヌトしおいたす。それらはJavaScriptず同様に動䜜したす

for (int i = 0; i < 10; i++) {
  // Your code here
}

if (condition) {
  // Your code here
} else {
  // Your code here
}

ロギング/デバッグ

シェヌダヌプログラムは䞊列に各頂点やフラグメントのために実行されるため、console.logを䜿甚しおコヌドをデバッグしたり、ブレヌクポむントを远加するこずはできたせん。これがシェヌダヌのデバッグを難しくしおいる理由です。

シェヌダヌをデバッグする䞀般的な方法は、gl_FragColorを䜿甚しお倉数の倀を芖芚化するこずです。

コンパむル゚ラヌ

シェヌダヌコヌドに間違いがある堎合、コン゜ヌルにコンパむル゚ラヌが衚瀺されたす。゚ラヌの行ず皮類を教えおくれたす。理解するのは簡単ではないかもしれたせんが、問題を芋぀けるための良い指暙になりたす。

アルファチャネルを gl_FragColor から削陀しおみお、䜕が起こるか芋おみたしょう

void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0);
}

コン゜ヌルにコンパむル゚ラヌが衚瀺されるはずです

Compilation error

gl_FragColor は4぀のコンポヌネントを期埅しおいたすが、3぀しか提䟛しおいないず通知されおいたす。

゚ラヌを取り陀くためにアルファチャネルを 1.0 に戻すこずを忘れないでください。

Uniforms

JavaScriptコヌドからshaderにデヌタを枡すためには、uniformsを䜿甚したす。uniformsは党おの頂点ずフラグメントにおいお䞀定です。

projectionMatrix、modelViewMatrix、およびpositionは、shaderに自動的に枡されるビルトむンのuniformsの䟋です。

カスタムのuniformを䜜成しお、色をshaderに枡したしょう。これを䜿甚しおplaneを着色したす。これをuColorず呌びたす。uniformの名前の前にuを付けるのは、それがコヌドにおいおuniformであるこずを明確にするための良い習慣です。

たず、shaderMaterialのuniformのオブゞェクトでそれを宣蚀したす

import { Color } from "three";
// ...
const MyShaderMaterial = shaderMaterial(
  {
    uColor: new Color("pink"),
  }
  // ...
);

// ...

次に、フラグメントシェヌダヌでそれを䜿甚したす

uniform vec3 uColor;

void main() {
  gl_FragColor = vec4(uColor, 1.0);
}

ピンク色に着色されたplaneが芋えるはずです

A frame with a pink plane

ここで、ピンク色はuniformのデフォルト倀です。material䞊で盎接倉曎できたす

<MyShaderMaterial uColor={"lightblue"} />

A frame with a light blue plane

planeは今、ラむトブルヌに着色されおいたす。

頂点シェヌダヌずフラグメントシェヌダヌの䞡方がuniformsにアクセスできたす。時間を第二のuniformずしお頂点シェヌダヌに远加し、planeを䞊䞋に動かしたしょう

End of lesson preview

To get access to the entire lesson, you need to purchase the course.