WebGL / GLSL - ShaderToy如何工作?

时间:2013-10-18 12:24:02

标签: javascript glsl webgl shader pixel-shader

我一直在敲Shadertoy - https://www.shadertoy.com/ - 最近,为了更多地了解OpenGL和GLSL。

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

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

这是如何运作的?

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

任何人都可以解释Shadertoy是如何运作的吗?

5 个答案:

答案 0 :(得分:59)

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,然后将其与例如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?

希望这有帮助。

答案 1 :(得分:7)

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

任何颜色和空间数学都可以用GLSL语言编程。高级算法的组合使等值面,形状,然后将纹理投影到等值面上,并进行光线投射,将观察者的虚线发送到距离,拦截任何方式,有许多3D的光线投射技术。

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

答案 2 :(得分:6)

它基本上是将GLSL像素着色器源代码直接推送到显卡上。真正的神奇之处在于人们用来创造惊人效果的难以置信的聪明算法,如光线行进,光线投射,光线追踪。最好看一下其他一些现场GLSL沙箱,例如:http://glsl.heroku.com/http://webglplayground.net/。 它基本上创建了一个窗口,通常是两个代表屏幕的三角形,然后着色器就像光线跟踪器一样在每个像素上工作 我一直在看这些,人们使用的算法是令人兴奋的,你需要一些严肃的数学印章,并查找“演示编码”源代码,以便能够围绕它们。许多关于着色器的玩具,只是打击你的心灵! 总而言之,您只需要学习GLSL着色器编码和算法。没有简单的解决方案。

答案 3 :(得分:2)

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

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

因此,与光线跟踪类似,这些片段着色器能够创建形状而不用顶点定义它们的几何体。

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

答案 4 :(得分:1)

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);
}

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

enter image description here

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));
}

产生

enter image description here

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

因此,考虑到这些输入,如果您应用足够的数学,就可以制作出令人难以置信的图像,例如this shadertoy shader生成该图像

仅在上述输入的情况下,有人想到生成该图像所需的数学运算就很神奇了。

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

但是,我认为重要的是要指出,虽然可以从shadertoy着色器中学习很多很酷的东西,但其中很多仅适用于“我如何仅用一个输入功能就能制作出精美图像”这一“难题”。是一个像素位置,其输出是单一颜色”,但对于“我应该如何为我的应用程序编写着色器”可能没有那么多。

与上面的海豚相比,这款快艇游戏

enter image description here

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

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