【问题标题】:WebGL/GLSL - How does a ShaderToy work?WebGL/GLSL - ShaderToy 是如何工作的?
【发布时间】:2023-03-12 07:22:01
【问题描述】:

我最近一直在研究 Shadertoy - https://www.shadertoy.com/ - 以了解更多关于 OpenGL 和 GLSL 的信息。

据我目前了解,OpenGL 用户首先必须准备所有要使用的几何图形并配置 OpenGL 服务器(允许的灯光数量、纹理存储等)。完成后,用户必须在 OpenGL 程序编译之前提供至少一个顶点着色器程序和一个片段着色器程序。

但是,当我查看 Shadertoy 上的代码示例时,我只看到一个着色器程序,而且使用的大部分几何图形似乎都是直接写入 GLSL 代码的。

它是如何工作的?

我的猜测是顶点着色器已经预先准备好了,而可编辑/样本着色器只是一个片段着色器。但这并不能解释一些更复杂的例子中的几何形状......

谁能解释 Shadertoy 的工作原理?

【问题讨论】:

标签: javascript glsl webgl shader pixel-shader


【解决方案1】:

ShaderToy 是一个编写像素着色器的工具。

什么是像素着色器?

如果您渲染一个全屏四边形,这意味着四个点中的每一个都位于视口的四个角之一,那么该四边形的片段着色器称为像素着色器,因为您可以说现在每个片段对应精确到屏幕的一个像素。所以像素着色器是全屏四边形的片段着色器。

所以属性总是相同的,顶点着色器也是如此:

positions = [ [-1,1], [1,1], [-1,-1], [1,-1] ]
uv = [ [0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0] ]

这个四边形被渲染为TRIANGLE_STRIP。 此外,有些人更喜欢使用片段着色器的内置变量gl_FragCoord,而不是显式设置UVs,然后将其除以例如uniform vec2 uScreenResolution

顶点着色器:

attribute vec2 aPos;
attribute vec2 aUV;
varying vec2 vUV;

void main() {
    gl_Position = vec4(aPos, 0.0, 1.0);
    vUV = aUV;
}

片段着色器看起来像这样:

uniform vec2 uScreenResolution;
varying vec2 vUV;

void main() {
    // vUV is equal to gl_FragCoord/uScreenResolution
    // do some pixel shader related work
    gl_FragColor = vec3(someColor);
}

ShaderToy 可以默认为您提供一些制服,iResolution(又名uScreenResolution)、iGlobalTimeiMouse,...您可以在像素着色器中使用这些制服。

为了将几何图形直接编码到片段着色器(又名像素着色器)中,开发人员使用了一种叫做光线追踪的东西。这是一个相当复杂的编程领域,但简而言之: 您通过一些数学公式呈现您的几何图形,稍后在像素着色器中,当您希望检查某个像素是否是几何图形的一部分时,您可以使用该公式来检索该信息。谷歌一下应该会给你大量的资源来阅读光线追踪器的确切构建方式和构建方式,这可能会有所帮助: How to do ray tracing in modern OpenGL?

希望这会有所帮助。

【讨论】:

  • 你知道为什么它是这样工作的吗?这是一种艺术选择,还是光线追踪通常比使用顶点着色器更有效?
  • 一般来说,光线追踪是在 CPU 上完成的,对于图像中的每个像素,光线都通过一个像素进行追踪。由于像素着色器是为光栅化图元中的每个像素运行的程序,因此使用片段着色器进行光线跟踪比顶点着色器更自然和直观。这是合乎逻辑的事情,对吧?但是,也有类似vertexshaderart.com 的东西。当您考虑某个着色器阶段的实际输出是什么时,会在 VS/FS 之间选择在 gpu 上进行光线跟踪。
  • 很棒的网站,也适合刚接触 OpenGL 并且只学习现代 OpenGL 的人,“attribute = in” 用于顶点着色器,“variing = out”用于顶点着色器,“varying = in”片段着色器。
  • 如果你想自己实现,这对我有用:shadertoy.com/view/3tX3zl WebGL 1 和 2 有版本。
  • @AbstractAlgorithm,嗨,我知道这是一个关于 shadertoy 的老帖子,但我@你希望你能帮助我解决一个似乎没人能回答的简单问题,我在stackoverflow上的这个链接中发布了我的问题:stackoverflow.com/questions/64499166/…
【解决方案2】:

ShaderToy 显示简单的 GLSL,该 GLSL 被编程来处理所有的光照、几何体等,它不是顶点几何体,而是光线投射大部分,3D 东西,或者你可以做 2D 着色器等。

任何颜色和空间数学都可以用 GLSL 语言编程。高级算法的组合制作等值面、形状,然后将纹理投影到等值面上,并进行光线投射,将假想线从观察者发送到远处,拦截任何挡道的东西,有许多用于 3D 的光线投射技术。

访问 www.iquilezles.org 了解在 shadertoy/glsl 图形中使用的不同工具

【讨论】:

    【解决方案3】:

    它基本上只是将 GLSL 像素着色器源代码直接推送到显卡上。真正的魔力发生在人们用来创建令人惊叹的效果的非常聪明的算法中,例如光线行进、光线投射、光线追踪。最好看看其他一些实时 GLSL 沙箱,例如:http://glsl.heroku.com/http://webglplayground.net/。 它基本上是创建一个窗口,通常是两个代表屏幕的三角形,然后着色器像光线追踪器一样在每个像素上工作。
    我已经研究了一段时间了,人们使用的算法令人兴奋,你需要一些严肃的数学知识,并查找“演示编码”源代码才能理解它们。许多着色器玩具,让你大吃一惊! 所以总结一下,你只需要学习 GLSL 着色器编码和算法。没有简单的解决方案。

    【讨论】:

      【解决方案4】:

      Shadertoy 被称为 “玩具” 是有原因的。这基本上是一个谜题。仅给定一个函数,将当前像素位置作为输入,编写一个生成图像的函数。

      该网站设置 WebGL 以绘制单个四边形(矩形),然后让您提供一个函数,该函数将当前渲染为 fragCoord 的像素传递。然后你用它来计算一些颜色。

      例如,如果你这样做了

      void mainImage( out vec4 fragColor, in vec2 fragCoord )
      {
          vec4 red = vec4(1, 0, 0, 1);
          vec4 green = vec4(0, 1, 0, 1);
          fragColor = mix(red, green, sin(fragCoord.x / 10.0) * 0.5 + 0.5);
      }
      

      你会得到像这样的红色和绿色条纹

      https://www.shadertoy.com/view/3l2cRz

      Shadertoy 提供了一些其他输入。最常见的是被渲染为iResolution 的分辨率。如果您将fragCoord 除以iResolution,那么您会得到在画布上从 0 到 1 和从 0 到 1 的值,这样您就可以轻松地使您的函数分辨率独立。

      这样做我们可以像这样在中心画一个椭圆

      void mainImage(out vec4 fragColor, in vec2 fragCoord)
      {
          // uv goes from 0 to 1 across and up the canvas
          vec2 uv = fragCoord / iResolution.xy;
          
          vec4 red = vec4(1, 0, 0, 1);
          vec4 green = vec4(0, 1, 0, 1);
      
          float distanceFromCenter = distance(uv, vec2(0.5));
          fragColor = mix(red, green, step(0.25, distanceFromCenter));
      }
      

      产生

      第二个最常见的输入是iTime(以秒为单位),随着时间的推移,您可以在函数中使用动画参数。

      因此,鉴于这些输入,如果您应用足够的数学运算,您可以制作出令人难以置信的图像,例如 this shadertoy shader 生成此图像

      令人惊奇的是,仅在上述输入的情况下,有人就计算出了生成该图像所需的数学运算。

      许多最令人惊叹的 shadertoy 着色器使用一种称为光线行进的技术和一些称为“有符号距离场”的数学,您可以阅读关于 here

      但是,我认为重要的是要指出,虽然有很多很酷的东西可以从 shadertoy 着色器中学习,但其中许多只适用于“我如何才能制作只有一个函数的漂亮图像”的“难题”输入是像素位置,其输出是单色”。他们没有回答“我应该如何为高性能应用编写着色器”。

      将上面的海豚与这款快艇游戏进行比较

      https://www.youtube.com/watch?v=7v9gZK9HqqI

      海豚着色玩具着色器在我的 NVidia GeForce GT 750 上全屏时以每秒约 2 帧的速度运行,而快艇游戏以 60 fps 运行。游戏运行速度快的原因是它使用了更传统的技术来绘制带有投影三角形的形状。即使是 NVidia 1060 GTX 在全屏时也只能以每秒 10 帧左右的速度运行海豚着色器。

      【讨论】:

        【解决方案5】:

        传统上,在计算机图形学中,几何图形是使用顶点创建的,并使用某种形式的材质(例如带照明的纹理)进行渲染。在 GLSL 中,顶点着色器处理顶点,片段(像素)着色器处理材质。

        但这不是定义形状的唯一方法。正如纹理可以通过程序定义(而不是查找其纹理元素)一样,形状也可以通过程序定义(而不是查找其几何形状)。

        因此,与光线追踪类似,这些片段着色器能够创建形状,而无需通过顶点定义其几何形状。

        还有更多定义形状的方法。例如。体数据(体素)、曲面曲线等。计算机图形文本应涵盖其中的一些。

        【讨论】:

          猜你喜欢
          • 2018-07-01
          • 1970-01-01
          • 2011-11-11
          • 2023-01-04
          • 1970-01-01
          • 1970-01-01
          • 2011-09-03
          • 2013-05-30
          • 2013-04-22
          相关资源
          最近更新 更多