63

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

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

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

这是如何运作的?

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

谁能解释Shadertoy是如何工作的?

4

5 回答 5

80

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. 此外,UVs有些人更喜欢使用片段着色器的内置变量gl_FragCoord,而不是显式设置,然后将其除以例如 a 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(又名uScreenResolutioniGlobalTime、、、iMouse...,您可以在像素着色器中使用这些制服。

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

希望这可以帮助。

于 2013-10-18T14:59:11.817 回答
9

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

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

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

于 2014-02-16T15:25:01.673 回答
7

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

于 2014-02-18T18:31:53.550 回答
5

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 提供了一些其他输入。最常见的是渲染为 as 的分辨率iResolution。如果你除以fragCoordiResolution那么你得到的值在画布上从 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秒为单位的,随着时间的推移,您可以在函数中使用动画参数。

所以,给定这些输入,如果你应用足够的数学,你可以制作出令人难以置信的图像,例如这个 shadertoy 着色器生成这个图像

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

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

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

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

在此处输入图像描述

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

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

于 2020-07-01T11:23:49.117 回答
3

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

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

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

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

于 2015-05-14T13:41:28.977 回答