0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

p5.jsの通常の3D描画で使われるシェーダーを見てみる

0
Last updated at Posted at 2026-08-18

はじめに

 現在のp5.js2.3.1において通常の3D描画に使われているシェーダーを見てみよう。

使うのはWebGLのgetShaderSourceという関数。これは現状動いているプログラムにアタッチされているシェーダーから、ソースをまるごと取得するための関数。

もっともまずはアタッチされているシェーダーを取らないといけないけど。

この二段構え。


function setup() {
	createCanvas(400, 400, WEBGL);
	console.clear();
	
	const gl = drawingContext;
	noStroke();
	fill(255);
	lights();
	background(0);
	sphere(40);
	const pg = gl.getParameter(gl.CURRENT_PROGRAM);
	
	const shaders = gl.getAttachedShaders(pg);
	const vs = gl.getShaderSource(shaders[0]);
	const fs = gl.getShaderSource(shaders[1]);
	console.log(vs);
	console.log(fs);
}

プログラムはgetParameterで取得できる。それにはプログラムが動いている必要がある。単純にスフィアをライティングで描画するとそれができる。そして描画の後も特にプログラムが切れることは無いので、そのまま取得できる。ちょっとvsだけ見てみようか。

#version 300 es
#define WEBGL2
#define VERTEX_SHADER
precision highp float;
#ifdef WEBGL2

#define IN in
#define OUT out

#ifdef FRAGMENT_SHADER
out vec4 outColor;
#define OUT_COLOR outColor
#endif
#define TEXTURE texture

#else

#ifdef FRAGMENT_SHADER
#define IN varying
#else
#define IN attribute
#endif
#define OUT varying
#define TEXTURE texture2D

#ifdef FRAGMENT_SHADER
#define OUT_COLOR gl_FragColor
#endif

#endif

vec4 getTexture(in sampler2D content, vec2 coord) {
  vec4 color = TEXTURE(content, coord);
  if (color.a > 0.) color.rgb /= color.a;
  return color;
}
precision highp int;





IN vec3 aPosition;
IN vec3 aNormal;
IN vec2 aTexCoord;
IN vec4 aVertexColor;

#ifdef AUGMENTED_HOOK_getWorldInputs
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat3 uModelNormalMatrix;
uniform mat3 uCameraNormalMatrix;
#else
uniform mat4 uModelViewMatrix;
uniform mat3 uNormalMatrix;
#endif
uniform mat4 uProjectionMatrix;

uniform bool uUseVertexColor;
uniform vec4 uMaterialColor;

OUT vec3 vNormal;
OUT vec2 vTexCoord;
OUT vec3 vViewPosition;
OUT vec3 vAmbientColor;
OUT vec4 vColor;

struct Vertex {
  vec3 position;
  vec3 normal;
  vec2 texCoord;
  vec4 color;
};


void HOOK_beforeVertex() {}
Vertex HOOK_getObjectInputs(Vertex inputs) { return inputs; }
Vertex HOOK_getWorldInputs(Vertex inputs) { return inputs; }
Vertex HOOK_getCameraInputs(Vertex inputs) { return inputs; }
void HOOK_afterVertex() {}
void main(void) {
  HOOK_beforeVertex();

  Vertex inputs;
  inputs.position = aPosition;
  inputs.normal = aNormal;
  inputs.texCoord = aTexCoord;
  inputs.color = (uUseVertexColor && aVertexColor.x >= 0.0) ? aVertexColor : uMaterialColor;
#ifdef AUGMENTED_HOOK_getObjectInputs
  inputs = HOOK_getObjectInputs(inputs);
#endif

#ifdef AUGMENTED_HOOK_getWorldInputs
  inputs.position = (uModelMatrix * vec4(inputs.position, 1.)).xyz;
  inputs.normal = uModelNormalMatrix * inputs.normal;
  inputs = HOOK_getWorldInputs(inputs);
#endif

#ifdef AUGMENTED_HOOK_getWorldInputs
  // Already multiplied by the model matrix, just apply view
  inputs.position = (uViewMatrix * vec4(inputs.position, 1.)).xyz;
  inputs.normal = uCameraNormalMatrix * inputs.normal;
#else
  // Apply both at once
  inputs.position = (uModelViewMatrix * vec4(inputs.position, 1.)).xyz;
  inputs.normal = uNormalMatrix * inputs.normal;
#endif
#ifdef AUGMENTED_HOOK_getCameraInputs
  inputs = HOOK_getCameraInputs(inputs);
#endif

  // Pass varyings to fragment shader
  vViewPosition = inputs.position;
  vTexCoord = inputs.texCoord;
  vNormal = inputs.normal;
  vColor = inputs.color;

  gl_Position = uProjectionMatrix * vec4(inputs.position, 1.);
  HOOK_afterVertex();
}

fsも同様に見られるが、長すぎるので自分でチェックしてください...なお文字列なのでp5のテキストセーブ関数で普通に取得することもできます。興味のある人は取得してみましょう。

#defineでINとOUTをエイリアスで使ってます。まあなんか美学があるんでしょう(よくわかんないです)。フラグメントどうのこうののところもよくわかんないです。でもなんか色々やってるんでしょう。

行列計算で行列を左から掛けてるのはあんま好きくないですね...やっぱ数式が右に流れていくので、左から掛け算されているのを見るとちょっと違和感がね...まあ仕方ないっすね。
これできちんと3D描画になっています。自分の素人仕事よりずっと複雑な処理をしています。プロの仕事です。やはり難しいことはプロに任せるのが一番です。

余談:前回のmodifyでちょっと遊ぶ

 紙数が余ったので余談。baseMaterialShaderのmodifyにはレファレンス:

には書いてないことがいくつかあるんで、それを紹介します。前回vertexDeclarationsとfragmentDeclarationsは無いじゃないか、と愚痴を言いましたが、きちんと読んだら下の方に書いてありました。

You can also add a declarations key, where the value is a GLSL string declaring custom uniform variables, globals, and functions shared between hooks. To add declarations just in a vertex or fragment shader, add vertexDeclarations and fragmentDeclarations keys.

しかしサンプルに出てこないので...分かりませんね...なおこれを記述するとどこに追記されるかというと、メイン関数の近く、before何とかのちょっと上の方です。ちなみにdeclarationsに書いた場合、両方に同じ記述がされます。ですからvaryingの場合declarationsは使えず、vertexとfragmentに分けて書く必要があります。uniformも、それぞれの箇所に記述する必要がありますが、実は宣言する方法も一応、あります。

記述例:

  myShader = baseMaterialShader().modify({
    // Manually specifying a uniform and attribute
	  declarations:'// ほげほげ~',
	  uniforms:{'float uFish':4.0, 'mat4 uMat4':[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]},
	  // 何これ。ていうかバリューの仕事が分からん。バリューなにすんの?これだと両方に出るんだけど。分かった。これその値がそのまま使われる。だから4.0ってやるとvsとfs双方で4.0が使われる。
	  // デフォルトユニフォームっていうらしいです。もちろんuniformでもあるので後からいじることもできるのでしょう。
	  varyingVariables:['float vTime', 'vec4 vKing', 'vec3 vQueen'], // 冗談に思えるが、これでvaryingできるんだと。うへ~うへ~~~~~うへ~~~~ありえねぇ
    vertexDeclarations: 'uniform float time; in vec3 aSubPosition; in float aNoiseValue; out float vNoiseValue;',
	  fragmentDeclarations: 'in float vNoiseValue;',
    'Vertex getWorldInputs': `(Vertex inputs) {
      inputs.position = (0.5-0.5*sin(time))*aSubPosition + inputs.position*(0.5+0.5*sin(time));
      return inputs;
    }`,
	  'void afterVertex': `() { vNoiseValue = aNoiseValue; }`,
	  'vec4 getFinalColor': `(vec4 color, vec2 texCoord) { color.rgb *= vNoiseValue * 4.0; return color; }`
  });

反映の様子(一部)


#define AUGMENTED_HOOK_getFinalColor
uniform float uFish;
uniform mat4 uMat4;

// ほげほげ~

in float vNoiseValue;
IN float vTime;
IN vec4 vKing;
IN vec3 vQueen;

varyingVariables

 配列で指定します。これのように、宣言部分をセミコロン無しで用意すると、そのままvsとfsにOUTとINで分散して記述されます。

uniforms

 uniformも扱えますが制約があります。なんと両方で同じ内容が記述されます。つまり重複uniformです。エラーは出ませんが、コードによっては無駄でしょう。なお、宣言はここにあるようにさっきと同じく型と名前をキーとして記述、valueですが、これはデフォルトの値です。uniformに詳しい人は知っていますがuniform変数というのはプログラムがその内容を記憶する変数(UBOの場合は外部バッファが記憶するものを流用する)ですから、デフォルトの値を予め設定しておいて使おうというわけですね。constとの違いはuniformなので書き換えできる点です。もちろん、両方で同じ値がセットされます。それゆえvaryingとは違ってオブジェクト形式となっています。
 通常通り個別にuniformを扱いたい場合は、vertexDeclarations/fragmentDeclarationsを使いましょう。(当然ですが、その場合デフォルト値は自身で設定することになります。)

おわりに

 p5の1系はすでに新機能の追加や更新は凍結されています。

Because p5.js v1 has been feature-frozen for over a year, each patch contains mostly documentation updates. In this upcoming patch, there are 3 non-documentation changes. Please check them out, try to find any side-effects, and report them as bugs!

そしてp5.js web editorのデフォルトはすでに2系に移行し、主に教師向けのマイグレーションガイドが付記されています。

Sets p5.js v2 to default in version dropdown, and add "Default" and "Not Maintained" labels. Also updates copy in Preferences modal.

ここまでお読みいただいてありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?