@vvfx/shader2ge
v0.0.1-beta.0
Published
Shader to Galacean Effects conversion engine
Keywords
Readme
@vvfx/shader2ge
将 GLSL Fragment Shader 转换为 Galacean Effects (GE) 格式的转换引擎。支持原生 GE 格式与 Shadertoy 格式(自动桥接 mainImage / iTime / iResolution / iChannel0~3)。
支持矩阵
输入与运行环境
| 输入类型 | API 调用 | 运行环境 | 说明 |
| --- | --- | --- | --- |
| GE 原生 fragment shader | convert(source) | 浏览器 / Node.js | void main() + varying vec2 vUV + varying float vTime |
| Shadertoy fragment shader | convert(source) | 浏览器 / Node.js | 检测到 mainImage 自动桥接,无需改写源码 |
| 带纹理输入 | convert(source, { uniformOverrides }) | 浏览器 / Node.js | 通过 TextureInput 为 sampler2D 赋图 URL |
| Shadertoy 通道纹理 | convert(source, { shadertoyChannels }) | 浏览器 / Node.js | 映射 iChannel0~3 到纹理 URL |
| 仅解析不生成 | parseShader(source) | 浏览器 / Node.js | 返回桥接后的源码、uniform 列表、Shadertoy 标志、结构警告 |
Uniform 类型覆盖
| GLSL 类型 | 状态 | 说明 |
| --- | --- | --- |
| float / int | ✅ 已支持 | 通过 materials.floats / materials.ints |
| vec2 / vec3 / vec4 | ✅ 已支持 | 一律写入 materials.vector4s(vec2/vec3 自动 padding) |
| sampler2D | ✅ 已支持 | 通过 TextureInput 提供 URL,未提供时使用 1×1 白纹理 |
| mat4 等矩阵类型 | ❌ 不支持 | 与 GE Material 字段约定不兼容 |
Shadertoy 自动桥接内容
| Shadertoy 变量/函数 | 桥接处理 |
| --- | --- |
| void mainImage(out vec4 fragColor, in vec2 fragCoord) | 包装为 void main() + 写入 gl_FragColor |
| iTime | #define iTime vTime(GE 的 _Time.y) |
| iResolution | uniform vec4 iResolution(由 previewSize 填充) |
| fragCoord | vUV * iResolution.xy |
| iChannel0~3 | 自动注入 uniform sampler2D iChannelN;,通过 shadertoyChannels 提供纹理 |
已知限制
- 基于 WebGL 1.0:vertex shader 是固定模板(全屏 Quad),fragment shader 必须遵守 GLSL ES 1.0 语法(不能用
#version 300 es/in/out修饰符 /texture()/ 整数mod()等)。 - 向量 uniform 必须声明为
vec4:因为 GE Material 的向量字段统一用glUniform4f设值,uniform vec2/vec3会触发运行时报错Uniform size does not match。需要 vec2/vec3 时声明为vec4后通过 swizzle 取用。 - Shadertoy 多 Pass 不支持:仅支持单 Image pass。Buffer A/B/C/D、
iMouse、iFrame、iDate、iChannelResolution暂未实现。 uTime自动桥接:源码中的uniform float uTime;会被替换为varying float vTime;+#define uTime vTime。建议直接使用vTime。- 构造函数参数限制:WebGL 1.0 下
vec2()/vec3()/vec4()的参数必须是简单变量或常量,不能含算术运算。搬运 Shadertoy 时如遇'constructor' : too many arguments,需要将复杂表达式拆为临时变量。
安装
npm install @vvfx/shader2ge
# 或
pnpm add @vvfx/shader2ge快速开始
转换 GE 原生 shader
import { convert } from '@vvfx/shader2ge';
const result = convert(`
precision highp float;
varying vec2 vUV;
varying float vTime;
void main() {
vec3 col = 0.5 + 0.5 * cos(vTime + vUV.xyx + vec3(0, 2, 4));
gl_FragColor = vec4(col, 1.0);
}
`, {
sceneName: 'CosineGradient',
duration: 5,
previewSize: [1280, 720],
});
console.log(result.scene); // GE JSONScene,可直接交给 @galacean/effects 播放
console.log(result.uniforms); // 解析出的 uniform 列表
console.log(result.warnings); // 转换警告(如缺 main()、未写 gl_FragColor 等)搬运 Shadertoy
无需改写源码——检测到 mainImage 后自动桥接:
const result = convert(`
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));
fragColor = vec4(col, 1.0);
}
`);带纹理输入
const result = convert(`
precision highp float;
varying vec2 vUV;
uniform sampler2D uMainTex;
void main() {
gl_FragColor = texture2D(uMainTex, vUV);
}
`, {
uniformOverrides: {
uMainTex: { url: 'https://example.com/image.png' },
},
});Shadertoy 带纹理通道
const result = convert(shadertoyCode, {
shadertoyChannels: {
iChannel0: { url: 'https://example.com/noise.png', wrapS: 10497, wrapT: 10497 },
iChannel1: { url: 'https://example.com/gradient.png' },
},
});未提供的通道使用内置 1×1 白纹理(DEFAULT_WHITE_TEXTURE_URL)。
仅解析不生成(轻量预检)
某些场景下你只想了解 shader 结构(uniform 列表、是否 Shadertoy、有没有结构错误),并不需要完整生成 JSONScene。例如 AI 流水线中先 parse 检视 warnings 决定是否重试,节省一次完整转换的开销:
import { parseShader } from '@vvfx/shader2ge';
const parsed = parseShader(source);
console.log(parsed.fragment); // 桥接 / 兜底处理后的最终 GLSL 源码(GE-ready)
console.log(parsed.uniforms); // [{ name: 'uColor', type: 'vec4' }, ...]
console.log(parsed.isShadertoy); // 是否为 Shadertoy 格式
console.log(parsed.warnings); // ['Fragment shader does not contain a main() function...']
if (parsed.warnings.length === 0) {
// 结构 OK,可以走完整 convert
}parseShader 与 ConvertOptions 完全无关——convert 内部就是先调它再做选项应用与 scene 生成。
配合 GE Player 播放
import { Player } from '@galacean/effects';
import { convert } from '@vvfx/shader2ge';
const { scene } = convert(fragmentShader, { previewSize: [1280, 720] });
const player = new Player({ container: document.getElementById('canvas')! });
await player.loadScene(scene);API 参考
convert(fragmentShader, options?)
主入口。将 GLSL fragment shader 源码转换为可播放的 GE JSONScene。
function convert(
fragmentShader: string,
options?: ConvertOptions,
): ConvertResult;
interface ConvertOptions {
/** 场景名称,默认 "ShaderScene" */
sceneName?: string;
/** 合成时长(秒),默认 5 */
duration?: number;
/** 合成结束行为,默认 EndBehavior.Restart(循环) */
endBehavior?: EndBehavior;
/** 预览尺寸 [width, height],默认 [512, 512];自动注入到 uResolution */
previewSize?: [number, number];
/** uniform 默认值覆盖(float / vec / color / TextureInput) */
uniformOverrides?: Record<string, UniformValue>;
/** Shadertoy 纹理通道映射,key 为 iChannel0~3 */
shadertoyChannels?: Record<string, TextureInput>;
}
interface ConvertResult {
/** 生成的可播放 GE JSONScene */
scene: JSONScene;
/** 解析出的 uniform 列表 */
uniforms: ParsedUniform[];
/** 转换过程中的警告(结构问题 + 纹理缺省提示等) */
warnings: string[];
}parseShader(source)
输入解析阶段。返回 shader 的中间表示(IR),不依赖任何 ConvertOptions。
function parseShader(source: string): ParsedShader;
interface ParsedShader {
/** 桥接 / 兜底处理后的最终 GLSL fragment shader 源码 */
fragment: string;
/** 解析出的 uniform 列表(uTime 桥接后已从中移除) */
uniforms: ParsedUniform[];
/** 输入是否为 Shadertoy 格式(含 mainImage 函数) */
isShadertoy: boolean;
/** 解析阶段产生的警告 */
warnings: string[];
}parseUniforms(source)
更底层的工具函数,仅做 uniform 声明的正则提取,不做 Shadertoy 桥接、不做结构校验。需要细粒度控制时使用,否则推荐用 parseShader。
function parseUniforms(source: string): ParsedUniform[];
interface ParsedUniform {
name: string;
type: 'float' | 'vec2' | 'vec3' | 'vec4' | 'int' | 'sampler2D';
comment?: string;
}generateScene(fragment, uniforms, options, isShadertoy)
低阶 API,convert 内部使用。一般业务不需要直接调;适合已经在外面做完所有 parse 工作、只想拼装 JSONScene 的场景。
类型定义
type UniformValue =
| number
| { x: number; y: number } // vec2
| { x: number; y: number; z: number } // vec3
| { x: number; y: number; z: number; w: number } // vec4
| { r: number; g: number; b: number; a: number } // color
| TextureInput; // sampler2D
interface TextureInput {
/** 图片 URL(http / https / data URI / 相对路径) */
url: string;
/** GL 纹理环绕模式 S,默认 REPEAT (10497) */
wrapS?: number;
/** GL 纹理环绕模式 T,默认 REPEAT (10497) */
wrapT?: number;
/** GL 放大过滤,默认 LINEAR (9729) */
magFilter?: number;
/** GL 缩小过滤,默认 LINEAR (9729) */
minFilter?: number;
/** 是否翻转 Y 轴,默认 true */
flipY?: boolean;
}
enum EndBehavior {
Destroy = 0,
Forward = 2,
Freeze = 4,
Restart = 5,
}模板常量
也作为 named export 暴露,便于业务侧自行拼装场景或对齐默认值:
import {
DEFAULT_VERTEX_SHADER, // 全屏 Quad vertex shader(固定模板)
FALLBACK_FRAGMENT_SHADER, // 兜底 fragment shader(输入为空时使用)
DEFAULT_WHITE_TEXTURE_URL, // 1×1 白纹理 base64 data URI
GL_TEXTURE_WRAP_REPEAT, // 10497
GL_TEXTURE_WRAP_CLAMP_TO_EDGE,// 33071
GL_TEXTURE_FILTER_LINEAR, // 9729
GL_TEXTURE_FILTER_NEAREST, // 9728
GL_TEXTURE_2D, // 3553
} from '@vvfx/shader2ge';自动注入的内置 uniform
转换引擎会按需注入这些 uniform,即使源码里没有声明也可以使用:
| Uniform | 类型 | 来源 |
| --- | --- | --- |
| vTime | varying float | GE _Time.y(vertex shader 桥接) |
| vUV | varying vec2 | 全屏 Quad 顶点 UV |
| uResolution | vec4 | shader 声明了 uniform vec4 uResolution 时,由 options.previewSize 自动填充为 (width, height, 1, 0),避免预览尺寸变化后宽高比矫正错位 |
| iTime | #define iTime vTime | Shadertoy 模式 |
| iResolution | uniform vec4 | Shadertoy 模式,由 previewSize 填充 |
| iChannel0~3 | uniform sampler2D | Shadertoy 模式 + 引用了对应通道时自动注入 |
架构
shader2ge 内部分两阶段:
源码 ──parseShader──▶ ParsedShader IR ──convert──▶ ConvertResult
(fragment, uniforms, (scene, uniforms,
isShadertoy, warnings) warnings)parseShader:纯输入理解,与 options 无关convert:应用 ConvertOptions(uniformOverrides / shadertoyChannels / previewSize / sceneName / duration),生成 JSONScene
如果你只关心 shader 元信息或想在转换前预检,调 parseShader 即可,避免支付 scene 生成的开销。
License
MIT
