原文是官网的教程,下面是添油加醋的翻译。
本文不会讲glsl的基本语法,需要的可以看这篇WebGL 着色语言,以及glsl es 3.2
uniform vec3 iResolution; // viewport resolution (in pixels) 画布尺寸像素
uniform float iTime; // shader playback time (in seconds) 时刻 单位 秒
uniform float iTimeDelta; // render time (in seconds)时间段
uniform int iFrame; // shader playback frame
uniform float iChannelTime[4]; // channel playback time (in seconds)
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec4 iMouse; // mouse pixel coords. xy: current (if MLB down), zw: click
uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube
uniform vec4 iDate; // (year, month, day, time in seconds)
uniform float iSampleRate; // sound sample rate (i.e., 44100)
他的mainImage(out vec4 arg1, vec2 arg2){}
类似glsl的main函数,当然并不是。
把第一个参数看做gl_FragColor即可,第二个参数看做gl_FragCoord.xy即可。
鼠标交互
vec4 iMouse
-
iMouse.xy:最后一次鼠标点击或当前鼠标拖动位置。
注意:这些是整数,而像素在整数之间(鼠标事件的坐标确实是整数,但是shader声明的是浮点数)。 -
iMouse.zw:
-
0:持续拖动;起始拖动位置。
- 否则 abs() 无论如何都包含最后一个拖动开始位置。
- 简言之保险起见,直接取绝对值可以获得
-
-
鼠标按钮事件(2020 年 11 月添加):
下面是鄙人所写的一个毫无美感的demo,默认(鼠标松开)的情况下会看到蓝色的方圆。
鼠标按下不放,会看到,圆闪了一下,而方保持变亮后的色彩,松开鼠标恢复原状。
用码上掘金嵌套之后,鼠标事件有点不同,大家可以直接访问shaderToy,没开vpn也能正常访问。
float circle(vec2 xy, vec2 center){
vec2 r = center -xy;
return step( dot(r,r),.005 );
}
float square(vec2 xy, vec2 center){
vec2 d = center -xy;
vec2 dd= step( abs(d),vec2(.1) );
return dd.x* dd.y ;
}
void mainImage( out vec4 O, vec2 U )
{
vec2 uv = U/iResolution.xy;
float mz = step(0. ,iMouse.z);// edge, val z>0 mz 就等于1
float mw = step(0. ,iMouse.w);
vec3 color = vec3(.8,.5,.6) ;
vec3 color2 = vec3(.1,.1,.6) ;
vec3 color3 = vec3(uv,.6) ;
color= mix(color, color2 + mw*vec3(.5),circle(uv, vec2(.3))) ;
color= mix(color, color2 + mz*vec3(.7),square(uv, vec2(.6))) ;
O = vec4(color, 1.);
}
键盘交互
- 纹理键盘:尺寸 256 x 3 ;.x 字段中的值
- (ascii+.5)/256, 0.5/3。: 如果当前按下键 #ascii (keydown) 则为 1,否则为 0 按下的那一刻
- (ascii+.5)/256, 1.5/3。: 在按键被按下(keypressed)时为 1,否则为 0; 按住不放
- (ascii+.5)/256, 2.5/3。: 键 #ascii 的 1/0 切换 按一下是1,两下是0 ,如此交替,开关
请注意,最初只实现了 2 个状态;keypressed 是在未确定的时间添加的。但是,如果仅测试非零或 > .5 而不是值 1,则兼容性应该是安全的。
提醒人们,虽然人们没有 Qwerty 键盘:对于游戏和探索,请使用箭头(可能除了您通常的控件之外)。
仅供参考:左:37 上:38 右:39 下:40 上
页:33 下页:34 结束:35 主页:36
修饰符:SHIFT:16 CTRL:17 ALT:18
上面说的我也没看懂,看了示例之后,终于明白,它是存有一张贴图,当触发键盘事件时,会把ASCII码存到这个贴图里. 贴图尺寸为256 X 3。
最后使用的时候,就读取这个贴图对应位置的颜色的x分量即可。就把这个纹理看做是3行 256列的表格。
第一行的值表示 这个键的按下或者放空的状态
第二行的值表示 这个键的按下的那一刻
第三行的值表示 这个键的按下的奇偶数 开关
// ichannel0 我已经设置为keyboard了 uv.y = 0就是第一行 x= 32就是 第32列
float arrowdown = texelFetch(iChannel0, ivec2(32,0),0).x * .4;
float arrowUp = texelFetch(iChannel0, ivec2(32,1),0).x + .4;
float arrowL = texelFetch(iChannel0, ivec2(32,2),0).x ; // 默认是0
float arrowR = 1. - arrowL; // 1- 取反
下面仍然是一个毫无美感的demo, 按空格键可以看到
- 左右会切换
- 按住不放 上会常亮
- 在按住的那一刻 下会亮起
时间
[many values] (www.shadertoy.com/view/llySRh)
参考着色器:iTime,iTimeDelta )
- float iTime(或iGlobalTime ):自着色器(重新)启动以来的秒数(+fracs) 单位是秒,有小数位。
- vec4 iDate : year-1, month-1, day, seconds(+fracs) 自午夜以来。
- int iFrame:自着色器(重新)启动以来的帧数。
- float iTimeDelta:自上一帧以来的持续时间。 就是一帧的时间。
- float iFrameRate: 平均 FPS。
- float iChannelTime [4] :视频或声音中的当前时间。 没用这些资源就不用管了
分辨率
在此处测试当前值。
- vec3 iResolution:用于窗口和缓冲区。 简单一点可以理解就是画布尺寸。
- vec3 iChannelResolution [4]:用于任何类型的纹理输入。 使用的纹理贴图的尺寸
-
.x,y 是宽度,高度。
- 不要期望所有计算机上的比率都相同(甚至宽度>高度,例如在智能手机和桌子上)。事实上,它甚至可以在图标、工作视图和全屏之间切换。
- 不要期望视频(和纹理)与您的窗口具有相同的比率:纹理(fragCoord/iResolution.xy)包裹整个图像,这意味着可能的失真。
- 提醒一下,无失真缩放意味着按标量而不是矢量进行缩放。例如 fragCoord/iResolution.y ,而不是 .xy。我们经常喜欢居中坐标,y 在 [-1,1] 范围内:
vec2 uv = (2.*fragCoord-iResolution.xy ) / iResolution.y ;
-
.z 是像素形状的比率。现在它似乎总是1。 一个像素点的宽高比。
对于依赖持久性的缓冲区,如果在运行时分辨率发生变化,它们将不会保持有效:考虑监测变化,或者重新初始化的魔术键。同样,如果您想允许全屏,让用户可以在开始时切换 3" ( if (iFrame<200)... )
缓冲区 A..D
结果不是被显示(如缓冲区“图像”),而是存储在同名的特殊纹理中。 就是输出到纹理而不是屏幕,webgl基础知识,帧缓冲区。
- 在每一帧,它们按 A..D 顺序进行求值,然后保存为纹理图像
- 对于持久或增量效果,让您的缓冲区读取它产生的相同纹理(即前一个状态与下一个状态)。它似乎被初始化为0。
- 要使用持久性,您可能只在第 0 帧计算一些东西:
if (iFrame==0)...
尽管如此,如果它依赖于图像纹理,它们需要一些时间来异步加载,因此请考虑类似if (iFrame<30) { init } else { fragColor = texture(sameBuffer); 但这
是天真的,并不总是足够的。在这里查看更好的解决方案。 - 请注意,这些纹理是浮点数,而不是字节,因此不受 [0,1] 的限制。在大多数系统上,它似乎是完整的普通 32 位浮点数。
- 如果要将纹理用作数组,请注意 fragCoords 是中间整数 → 考虑 fragCoord-.5 来获取整数索引,或
(vec(i,j)+.5)/iResolution.xy来获取纹理坐标。
或使用texelFetch(ch, ivec2(fragCoord) , 0 )。 - 注意:缓冲区的 MIPmap 默认是关闭的。将纹理绑定中的标志视为通道N。
立方体地图A
与 BufferA 类似,具有以下特点:
- 结果是一个立方体贴图,因此您确实渲染了立方体的 6 个面。
所以不是main(out vec4 fragColor, in vec2 fragCoord),你有一个更复杂的mainCubemap( out vec4 fragColor, in vec2 fragCoord, in vec3 fragRayOri, in vec3 fragRayDir )(注意它类似于备用mainVR())。
fragCoord和iResolution是相对于每个面的,
fragRayOri是vec3(0)(= 立方体中心)
fragRayDir是撞击立方体贴图纹理的光线方向,与获取立方体贴图值时使用它的方式相同。 - 该缓冲区始终为 1024 × 1024 × 6,无论窗口分辨率如何(避免由于分辨率过高,而造成渲染耗费巨大)。
- 此缓冲区存储为半浮点数
- 提醒
texelFetch()在 CubeMaps 上不起作用。(但textureLod()和 MIPmap 可以)。 - 至于 Buffers,MIPmap 默认是关闭的。将纹理绑定中的标志视为ChannelN。
请注意,您可以在每一帧重新计算环境,或者只使用一次if (iFrame==0)。
令人讨厌的细节:这是唯一的二次幂缓冲区,这意味着这里的动态 MIPmap 没有被窃听。享受
因此有许多潜在的使用方式:Egn 作为球形环境(甚至可能通过每帧的光线追踪构建),作为 6 个额外的缓冲区(具有上述可爱的属性),甚至作为对任何内容进行编码的大容量存储,包括体积(例如 128³ RGBA 插值体积、原始 ~900³二进制体素或体积光或 流体模拟)或视频图像(另一个)。
注意:如果你只是想把它用作“一个 1024×1024 的缓冲区”,一定要确定你在第 0 面,否则其他 5 面的计算也将毫无用处。例如:if ( dir.x > max(abs(dir.y),abs(dir.z)) )
每个面坐标的方向都很奇怪,所以最好看下面的例子。
常见的
这是一个特殊的便利选项卡,其内容包含在所有其他选项卡中。
仅对所有全局常量、宏和实用程序函数设置一次很有用。
注意:为避免缓冲区间不连贯,大多数uniform(iChannel*、iResolution、iTime 等)在此处不直接可用(或将它们作为函数参数传递)。
但它仍然可以在宏中工作,因为代码在 Buffers 中只内联一次。例如,当vec2 iResolution.xy 不是时,接受#define R iResolution.xy (如果没有常规函数在 Common 中使用它) 。现在,仅仅因为您需要使用制服而依赖宏函数而不是常规函数可能会很麻烦。一个技巧是使用宏作为翻译器,如下所示(在 Common 的末尾): type myfunc(type1 param1,...N, type1' uniform1,...M) { ... } ``#define myfunc( param1,...N) myfunc( ``param1,...N, uniform1,...M)``` [](https://www.shadertoy.com/view/stdyRr)
``
提醒一下 iChannelN 的类型是sampler2D` 。
提供的纹理
请注意,它们中的大多数(包括体积、缓冲区和视频)可以在最近/线性/MIPmap 插值和钳制/重复扭曲标志之间切换。
注意:黑白纹理现在编码为红色纹理。使用vec4(texture())或texture().rrrr获取对应的 RGBA。请注意,您可以使用textureSize()来检测 1 通道纹理。
注意:纹理通常需要几帧来加载,如果在iFrame==0完成,这可能会破坏自定义初始化。->如果纹理尚未准备好,iChannelResolution[]保持为 0(参见测试)。对于textureSize()中更高的 LOD 也是如此。
-> 安全的纹理加载相关初始化。
特殊纹理:
- 键盘(见上文)、网络摄像头(视频)、麦克风(声音)、soundcloud(声音)
- 缓冲器 A、B、C、D(见上文)
- 噪声随机纹理(见下文),拜耳纹理(见下文)
- 字体纹理(见下文)
常规纹理:
-
图像纹理:提供各种分辨率的各种颜色或灰度图像。
- 有些连续包裹,有些则没有。
- .a 始终为 1。 不透明
- 颜色空间是 sRGB:显示不需要转换,但如果您想对它们进行计算(物理渲染、图像处理),您应该在 (pow(img, 2.2) 之前将它们转换为平面,然后在 (pow (图像,1./2.2))。请注意,这可以通过 img^2 和 sqrt(img) 来近似。
-
视频:随时间变化的纹理(声音会立即播放,着色器无法访问它)。
- 它们是矩形的:检查 iChannelResolution。
- 无(水平竖直)重复:如果需要,使用fract,实现纹理重复。
- 没有 MIP 映射。
-
Nyan cat:256 x 32。存储 8 帧卡通动画。.a 已定义。
-
Cubemaps:将环境编码为 6 个地图。与 textureCube() 一起使用
-
音频纹理:(见下文)
噪声纹理
提供了 6 个随机均匀值的纹理:两个 2D 低分辨率、两个 2D 高分辨率、一个 2D 蓝噪声、两个 3D 纹理;3 灰色和 3 RGB。
- 2D 颜色纹理:如果没有 vflip,G 和 A 通道是由 (37.,17.) 平移的 R 和 B。这允许伪造插值的 3D 噪声纹理。参考这里的例子。
- 2D 蓝噪声纹理,即具有“分布良好”而不是纯随机的值(即白噪声)。使用示例在 这里。
- 3D 纹理。参考这里的例子。
- 提醒一下,统一随机值位于像素中心,即整数坐标之间。其他 uv 值可用于很好的插值,但不再统一且标准差较小)。
- 这些纹理在平面色彩空间中:准备好用于计算,但需要进行伽马/sRGB 转换才能忠实显示。
程序噪声(例如,基础噪声=纹理): –此处
的参考着色器。– 查看所有变体的标题注释:经典噪声与单纯噪声、值噪声与梯度噪声等。
拜耳纹理
是一个有序的拜耳矩阵 :
- 它允许简单的抖动和半色调(只需使用灰度级对其进行阈值处理),例子。
- 它还在 [0,63] 中提供了一个排列表,例子。
- 此纹理位于平面色彩空间中:准备好用于计算,但需要进行伽马校正/sRGB 转换才能真实显示。
字体纹理
这种特殊的纹理在图块中编码 256 个字符。 就是把字母和符号放在一张图里,还不理解纹理的,按精灵图理解
-
它包含 16×16 个 64×64 像素的图块。
- int c=#ascii 位于 vec2( c % 16, 15-c / 16 )/16。
- 提醒 #ascii = 64+#letter 和小写 = 64+32+#letter
-
.x 提供字符的抗锯齿掩码。
-
.w 给出到字符边界的有符号距离。
-
.yz 给出距离梯度。
更多细节在这里。Example & utils here and here和 here and there。
声音输入(音频纹理)
SoundCloud 提供的音乐或用户选择的音乐可以用作纹理。
-
纹理大小为 bufferSize x 2。(例如,512 x 2)。
-
index, .75 : 音乐样本
- 它是一个随时间刷新的缓冲区,但精确的工作似乎非常依赖于系统,而且不能保证与图像帧的同步。因此,绘制完整的声波或使用简单的图像声音编码将不可靠(或者可能只适合您)。
-
index, .25:缓冲区的 FFT
- x / bufferSize = f / (iSampleRate /4.) ,这里的例子。
iSampleRate给出了采样率,但在许多系统上似乎初始化不正确:如果精度很重要,请手动尝试 44100 或 48000。
- x / bufferSize = f / (iSampleRate /4.) ,这里的例子。
声音输出(声音缓冲区)
结果不是显示(如缓冲区“图像”),而是存储在音频缓冲区中。
- in:您可以选择使用时间或样本 id 索引您的声音公式。时间是最简单的,但在几十秒后会丢失一些精度(有时在某些声音中很明显)。使用样本 id 的方法:这里。
- out: x,y 通道对应左右音频立体声。FragCoord 对应于时间样本。(iSampleRate给出了采样率,但在许多系统上似乎初始化不正确)。
- 此缓冲区在图像着色器运行之前评估一次,然后静态播放音乐。所以目前没有办法在图像和声音着色器之间进行交互。甚至无法读取纹理或访问与图像相关的 Uniforms 参数。
更多在这里。
虚拟现实
如果您的系统可以显示立体,则可以计算立体着色器。
只需实现附加的改编 mainImage 变体:
mainVR( fragColor, fragCoord, fragRayOrigin, fragRayDir )
更多在这里。
断言
此调试助手已于 2020 年 10 月添加。如果 cond 失败(默认 Id = 0)
,st_assert( cond [, alertId ] )将显示与 alertId 0,1,2,3 对应的颜色。
注意:警报 ID 1 胜过其他警报 0,等等。
两种典型用途:
– 在表达式(uv)达到无效范围时使像素变为红色
– 当与 uv 无关的值达到无效范围时,使某些红色区域变为红色。
注意:仅允许在 Image Buffer 中使用。
参考例子在这里。Utils 在这里测试几个表达式。
由于即使在访问深度函数时它也允许显示颜色,它也可以被扭曲以将变量显示为 viewmeters或numbers。