メインコンテンツまでスキップ

PenFXシェーダーパッケージ

LooksカテゴリのImport shaderから、PenFX用のshader package(ZIP)を読み込めます。これはMy Blocks Shaderとは独立した拡張経路です。

最小構成

manifestを使わない場合、ZIP内のすべての.glslが引数なしのcommand blockになります。ファイル名がブロック名になります。

my-shaders.zip
├── soft-glow.glsl
└── distort.glsl

この方式ではGLSLからuniformの型や初期値を推測しません。入力欄を追加する場合はmanifestを使います。

推奨構成

tint-wave.zip
└── tint-wave/
├── shading-shader.json
└── tint-wave.glsl

shading-shader.json.glslはZIP直下に置いても構いません。manifestのfileは、manifestがあるディレクトリを基準に解決されます。1つのZIPに含められるmanifestは1個です。

manifestの例

{
"format": "shading.app/penfx-shader",
"version": 1,
"id": "tint-wave",
"name": "Tint Wave",
"blocks": [
{
"id": "tint-wave",
"name": "tint wave",
"text": "tint wave amount: [AMOUNT] tint: [TINT] mode: [MODE] mix: [MIX] %",
"file": "tint-wave.glsl",
"inputs": [
{
"id": "AMOUNT",
"label": "amount",
"type": "number",
"defaultValue": 8,
"uniform": "u_amount"
},
{
"id": "TINT",
"label": "tint",
"type": "color",
"defaultValue": "#6b56d9",
"uniform": "u_tint"
},
{
"id": "MODE",
"label": "mode",
"type": "menu",
"items": ["soft", "hard"],
"defaultValue": "soft",
"uniform": "u_mode"
},
{
"id": "MIX",
"label": "mix",
"type": "number",
"defaultValue": 100,
"scale": 0.01,
"uniform": "u_mix"
}
]
}
]
}

packageフィールド

フィールド必須内容
formatはいshading.app/penfx-shader固定
versionはいsingle-passは1または2。複数program/adapterは2
idはい小文字英数字と-、最大48文字。同じIDは再読込時に置き換え
nameはいツールボックスに表示するpackage名
blocksはい1〜64個のブロック定義
programsversion 2のみPenFX adapterから差し替えるfragment program。最大64個

blockフィールド

フィールド必須内容
idはいpackage内で一意の小文字英数字と-
name推奨textを省略したときの先頭ラベル
text任意Scratchブロックの表示文。入力は[INPUT_ID]で配置
filesingle-passでは必須.glslへの相対パス。implementation blockでは省略可能
inputs任意最大24個。省略時は引数なし
blockType任意command(既定)、またはimplementation blockのreporter
implementationversion 2のみ既存PenFX pipelineへ接続するadapter定義
separatorBefore任意trueならこのブロックの前にpalette separatorを表示

textを指定する場合は、すべてのinput IDを1回以上含め、未定義のplaceholderを置きません。省略時はname label: [ID] ...の順で自動生成されます。

inputの型

typeブロックUIGLSL uniform
number数値floatvalue * scale + offset
integer数値int変換後に四捨五入
angle角度float度数
colorカラーピッカーvec3RGBを0〜1に正規化
boolean真偽値intfalse=0、true=1
menuメニューintitemsの0始まりindex
string文字列adapterへそのまま渡すimplementation blockのみ
costumeコスチュームadapterへそのまま渡すimplementation blockのみ

入力IDはA-Z0-9_を使います。uniformu_で始めます。省略時は、たとえばAMOUNTからu_amountのように生成されます。

numberintegerangleではscale(既定値1)とoffset(既定値0)を使えます。UIの0〜100%をGLSLの0〜1へ渡す場合はscale: 0.01を指定します。

version 2のprogramとadapter

version 2では、複数のfragment programと、既存のPenFX orchestrationへ接続するimplementationを宣言できます。複数pass、depth texture、displacement costume、前フレーム、CPU pixel sort、buffer stackなど、single-passだけでは表しにくい処理のための形式です。

{
"format": "shading.app/penfx-shader",
"version": 2,
"id": "my-gaussian-variant",
"name": "My Gaussian Variant",
"programs": [
{
"id": "gaussian",
"file": "gaussian.glsl",
"bind": "gaussian"
}
],
"blocks": [
{
"id": "gaussian-blur",
"name": "gaussian blur",
"text": "gaussian blur type: [TYPE] value: [VALUE] mix: [MIX] %",
"implementation": {"type": "penfx", "opcode": "gaussianBlur"},
"inputs": [
{"id": "TYPE", "label": "type", "type": "menu", "items": ["normal", "horizontal", "vertical"]},
{"id": "VALUE", "label": "value", "type": "number", "defaultValue": 5},
{"id": "MIX", "label": "mix", "type": "number", "defaultValue": 100}
]
}
]
}

programs[].bindは、既定PenFX pipelineが公開するprogram slotに限られます。実行時だけpackageのprogramがslotへ割り当てられ、他のpackageや既定ブロックのshaderをグローバルに上書きしません。

implementation.opcodeも既定ZIPが公開する59個のPenFX block opcodeに限られます。任意のJavaScript関数は呼べません。外部packageはpackage固有のopcodeを使い、penfx-builtinsというpackage IDは使用できません。

GLSLの契約

PenFXはWebGL 1のGLSL ES 1.00 fragment shaderを使います。完全なfragment shaderを.glslに書きます。#version 300 esinouttexture()などWebGL 2専用構文は使えません。

PenFXが用意するuniformとvaryingは次のとおりです。

precision highp float;

varying vec2 v_uv; // 左下 (0, 0) から右上 (1, 1)
uniform sampler2D u_image; // 現在のPenレイヤー
uniform vec2 u_resolution; // Penレイヤーのpixelサイズ
uniform float u_time; // Movie timelineの秒数
uniform int u_frame; // timeline time × frame rate

u_imageu_resolutionu_timeu_frameは予約名で、manifestのinputには指定できません。u_timeu_frameは実時間ではなくMovie timeline由来です。

Penレイヤーはpremultiplied alphaです。RGBを処理するときは、必要ならstraight colorへ戻し、出力時にalphaを掛け直します。

precision highp float;

varying vec2 v_uv;
uniform sampler2D u_image;
uniform vec2 u_resolution;
uniform float u_time;
uniform int u_frame;
uniform float u_amount;
uniform vec3 u_tint;
uniform int u_mode;
uniform float u_mix;

vec3 straightColor(vec4 pixel) {
return pixel.a > 0.00001 ? pixel.rgb / pixel.a : vec3(0.0);
}

void main() {
vec4 pixel = texture2D(u_image, v_uv);
vec3 original = straightColor(pixel);
float wave = sin((v_uv.y * 24.0) + u_time * 2.0) * u_amount * 0.01;
float strength = u_mode == 0 ? 0.5 : 1.0;
vec3 changed = original + (u_tint * wave * strength);
vec3 result = mix(original, changed, clamp(u_mix, 0.0, 1.0));
gl_FragColor = vec4(clamp(result, 0.0, 1.0) * pixel.a, pixel.a);
}

ZIPの作成と読み込み

次のように、manifestとGLSLをZIPにします。

zip -r tint-wave.zip shading-shader.json tint-wave.glsl

ShadingでLooksCustom ShadersImport shaderを選びます。読み込み時にGLSLはWebGLでcompile/link検証され、成功したブロックがツールボックスへ追加されます。ブロックの実行は既定PenFXと同様に同一VM tick内で完了し、Promiseや待ち時間をScratch VMへ返しません。

manifestとGLSL本文は.shadepenFXShadersへ保存できます。そのため、元のZIPがなくてもプロジェクトを開き直せます。常に存在する既定packageはプロジェクトへ重複保存しません。custom shaderを含むプロジェクトはMovie専用機能として扱われます。

制限と安全性

  • ZIPは10 MB以下です。
  • 1 packageは最大64 blocks/64 programs、1 blockは最大24 inputsです。
  • 1 shaderは512 KB以下です。
  • 仕様の検証レイヤーには、展開後shader合計2 MB以下、およびpackage内shader合計4 MB以下の上限があります。余裕を持ったサイズで作成してください。
  • ZIP内のJavaScript、HTML、画像などは実行も読み込みもしません。
  • GLSLはGPU上で実行されるため、複雑すぎるloopや極端に重いsamplingは描画停止やGPU resetの原因になります。
  • version 1はsingle-pass effectです。version 2のadapterは許可されたPenFX pipelineだけを利用します。

よくあるエラー

エラー確認すること
Shader file not found in zipmanifestの位置を基準にしたfileの相対パス
text placeholders must matchinputs[].idtext内の[ID]の一致
compile/link errorWebGL 1構文、uniform型、varying vec2 v_uvvoid main()
同名packageが置き換わる同じidは更新として扱われる。別packageには別のidを使う