ShaderJoy —— 安利一个 Visual Studio Code 的 Shader 插件【免费】|8月更文挑战

2,230 阅读5分钟

Visual Studio Code - Shader Toy

这个扩展可以在 VSCode 中查看 GLSL 着色器的 WebGL 实时预览,通过提供 “Show GLSL Preview” 命令可以达到类似于访问 shadertoy.com 的效果。

要运行该命令,可以打开 “Command Palette” 并输入“ Shader Toy: Show GLSL Preview ”,或者在文本编辑器中右键单击并从上下文菜单中选择 “Shader Toy: Show GLSL Preview” 。

运行该命令将视图分割并显示一个应用了您的着色器的全屏四方的视图。您的片段着色器的入口点是 void main() ,或者如果前者不可用,那么入口点则是 void mainImage(vec4 out,vec2 in) ,其中第一个参数是输出颜色,第二个参数是片段屏幕位置。

另一个可用的命令是 “Shader Toy: Show Static GLSL Preview” ,它将打开一个预览,但不会对变化的编辑器作出反应。可以一次打开任意数量的编辑视图,这使得依赖于多个通道的着色器可以被一个唯一的工作流进行编辑。

特点

Uniforms 变量

目前,iResolutioniGlobalTime(也称 iTime)、iTimeDeltaiFrameiMouseiMouseButtoniDateiSampleRateiChannelN (其中 N 在 [0,9] 区间内)和 iChannelResolution[] 都是可用的 uniform 变量。

纹理输入

纹理通道 iChannelN 可以通过在着色器顶部插入如下形式的代码来定义

#iChannel0 "file://duck.png"
#iChannel1 "https://66.media.tumblr.com/tumblr_mcmeonhR1e1ridypxo1_500.jpg"
#iChannel2 "file://other/shader.glsl"
#iChannel2 "self"
#iChannel4 "file://music/epic.mp3"

以上代码演示了

  1. 使用本地和远程图像作为纹理(注意,纹理大小是 2 的幂次是你通常需要遵守的规范);
  2. 使用另一个着色器结果作为纹理;
  3. 通过指定 self 使用这个着色器的最后一帧;
  4. 使用音频输入。

注意,要使用本地输入的相对路径,您必须在可视代码中打开一个文件夹。

纹理示例为了影响纹理的采样行为,使用以下语法:

#iChannel0::MinFilter "NearestMipMapNearest"
#iChannel0::MagFilter "Nearest"
#iChannel0::WrapMode "Repeat"

注意,因为 WebGL 标准,许多选项只能在宽度和高度都是 2 的幂次情况下工作。

立方贴图输入

Cubemaps 可以指定为任何其他纹理,事实上,它们是 Cubemaps 的路径包含通配符和它们的类型被显式声明的组合。

#iChannel0 "file://cubemaps/yokohama_{}.jpg" // 注意 '{}'
#iChannel0::Type "CubeMap"

通配符将通过替换下列集合中的任何值来解析

  • [ 'e', 'w', 'u', 'd', 'n', 's' ],
  • [ 'east', 'west', 'up', 'down', 'north', 'south' ],
  • [ 'px', 'nx', 'py', 'ny', 'pz', 'nz' ] or
  • [ 'posx', 'negx', 'posy', 'negy', 'posz', 'negz' ].

如果找不到这六个文件中的任何一个,就从下一个集合的第一个开始尝试。

音频输入(实验性的)

注意: 默认情况下音频输入是禁用的,更改设置 “Enable Audio Input” 来启用它。

不支持音频输入从内部_Visual Studio代码,因为ffmpeg不是附带Visual Studio代码,所以您必须生成一个独立的版本和主机本地服务器能够加载本地文件(或摆弄你的浏览器安全设置)如果您想要在着色器使用音频的话。如果您的通道定义了音频输入,它将从文件扩展名中推断出来。通道将是一个 2 像素高和 512 像素宽的纹理,宽度可以通过“Audio Domain Size” 设置来调整。第一行包含音频频谱,第二行包含其波形。

键盘输入

如果你想使用键盘输入,你可以在着色器前加上 #iKeyboard。这将暴露给你的着色器以下功能:

bool isKeyPressed(int);
bool isKeyReleased(int);
bool isKeyDown(int);
bool isKeyToggled(int);

此外,它还将暴露从 Key_AKey_Z、从 Key_0Key_9Key_UpArrowKey_LeftArrowKey_Shift 等变量。使用这些常量和上面提到的函数来查询键盘的状态。

引用其他(include)着色器

你也可以通过一个标准的 c 类语法来包括其他文件到你的着色器中:

#include "some/shared/code.glsl"
#include "other/local/shader_code.glsl"
#include "d:/some/global/code.glsl"

注意:这些着色器不能定义void main() 函数,因此只能用于工具函数、常量定义等。

自定义 Uniforms 变量 (实验性的,易变的)

使用自定义 uniform 变量定义那些直接在你的着色器中使用的 uniform 变量,给一个初始值以及一个可选的范围。

#iUniform float my_scalar = 1.0 in { 0.0, 5.0 }                   // 这将暴露一个滑动条来编辑该值
#iUniform float my_discreet_scalar = 1.0 in { 0.0, 5.0 } step 0.2 // 步长为 0.2

#iUniform float other_scalar = 5.0                                // 这将公开一个文本字段以给出任意值
#iUniform vec2 position_in_2d = vec2(1.0) // 暴露两个文本字段
#iUniform color3 my_color = color3(1.0)                           // 这将是一个可编辑的颜色选择器
#iUniform vec4 other_color = vec4(1.0) in { 0.0, 1.0 }            // 这将暴露四个滑块

与 Shadertoy.com 的兼容性

下面是一个从 shadertoy.com 移植的 shader 的例子:

// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// Created by S.Guillitte
void main() {
  float time = iGlobalTime * 1.0;
  vec2 uv = (gl_FragCoord.xy / iResolution.xx - 0.5) * 8.0;
  vec2 uv0 = uv;
  float i0 = 1.0;
  float i1 = 1.0;
  float i2 = 1.0;
  float i4 = 0.0;
  for (int s = 0; s < 7; s++) {
    vec2 r;
    r = vec2(cos(uv.y * i0 - i4 + time / i1), sin(uv.x * i0 - i4 + time / i1)) / i2;
    r += vec2(-r.y, r.x) * 0.3;
    uv.xy += r;

    i0 *= 1.93;
    i1 *= 1.15;
    i2 *= 1.7;
    i4 += 0.05 + 0.1 * time * i1;
  }
  float r = sin(uv.x - time) * 0.5 + 0.5;
  float b = sin(uv.y + time) * 0.5 + 0.5;
  float g = sin((uv.x + uv.y + sin(time * 0.5)) * 0.5) * 0.5 + 0.5;
  gl_FragColor = vec4(r, g, b, 1.0);
}

注意,与 shadertoy.com 相比,gl_FragCoord 代替了 fragCoord, gl_FragColor 代替了原始演示中的 fragColor。但是,对于插入一个简单的 void main() 有一个基本的支持,它将委托给一个 void mainImage(vec4 out,vec2 in) 函数。void main() 的定义是通过匹配 regex /void\s+main\s (\s)\s*{/g** 找到的,因此,如果除了扩展生成的定义之外还需要定义 void main() ,则可以将其定义为 void main(void) 。例如,如果您的 main 定义将被预处理器处理掉,并因此扩展不接受该定义,则可能有必要这么做。

英文原文:Note that compared to shadertoy.com gl_FragCoord replaces fragCoord and gl_FragColor replaces fragColor in the original demo. There is however a rudimentary support for inserting a trivial void main() which will delegate to a void mainImage(out vec4, in vec2) function. The definition of void main() is found by matching the regex /void\s+main\s*(\s*)\s*{/g, thus if you require to define void main() in addition to the extension generating a definition you may define it as void main(void). This might be necessary, for example, if your main definition would be processed away by the preprocessor and should thus not be picked up by the extension.

glslify 的集成

您可以在设置中启用对 glslify 的支持,但是因为 glslify 不支持转换前后的行映射,所以只要启用了设置,就会遗憾地禁用错误上的行号。使用 glslify 允许为您的着色器使用 node.js 风格的模块系统:

#pragma glslify: snoise = require('glsl-noise/simplex/2d')

float noise(in vec2 pt) {
    return snoise(pt) * 0.5 + 0.5;
}

void main () {
    float r = noise(gl_FragCoord.xy * 0.01);
    float g = noise(gl_FragCoord.xy * 0.01 + 100.0);
    float b = noise(gl_FragCoord.xy * 0.01 + 300.0);
    gl_FragColor = vec4(r, g, b, 1);
}

GLSL 预览交互

该扩展在GLSL预览中提供了一个暂停按钮来停止时间的前进。与此相结合,您可以使用 GLSL 预览中提供的屏幕快照按钮来捕获和保存帧。默认情况下,保存的屏幕截图的分辨率与 GLSL 预览的分辨率相同的,不过有一个设置允许用户使用任意值覆盖该分辨率。最后,扩展提供了一个关于着色器性能和内存消耗的浅显视图。

错误高亮

该扩展还支持在文本编辑器中突出显示编译错误,对于单个着色器也是如此,对于多个通道的也是如此。它通过在文本编辑器中直接显示作为诊断的错误,以及在 GLSL 预览窗口中以可理解的格式显示错误,并允许用户与错误消息交互跳转到相关行,并在必要时打开相关文件来实现:

要求

  • 一个支持 WebGL 的显卡.

已知的问题

  • 目前某些着色器的性能不是很好,原因正在调查中
  • 带有来自远程源的音频的着色器目前不能正常工作,这是VSCode方面的一个问题,将在 Electron 6 发布时修复。
  • 似乎有一个非常罕见的错误,导致音频输入声音损坏。

Todo

  • 得到更多的反馈;

  • 在不可移植的版本中禁用音频;

Cubemap 采样。

贡献

任何形式的贡献都是受欢迎和鼓励的。

GitHub 项目页面

Visual Studio 商店地址

Release Notes

(略)

英文原文