大家好!前段时间我学会了如何用着色器制作一个有趣的闪亮旋转八面体:我的着色器能力仍然很基础,但事实证明制作这个有趣的旋转八面体比我想象的要容易得多(从其他人那里复制了很多代码片段!).在这样做的同时,我从一个非常有趣的教程SignedDistanceFunctionsTutorial:BoxesandBalloons中学到了“符号距离函数”的重要思想。在本文中,我将逐步介绍我学习编写简单着色器所采取的步骤,并试图让您相信着色器并不难上手!更高级着色器的示例如果您还没有看到使用着色器完成的真正有趣的事情,这里有几个示例:这个非常复杂的着色器像河流一样的真实视频:https://www.shadertoy。com/view/Xl2XRW一个更抽象(更短!)的有趣着色器,有很多发光的圆圈:https://www.shadertoy.com/view/lstSzj第1步:我知道我的第一个着色器你可以在shadertoy上制作着色器,所以我去了https://www.shadertoy.com/new。它们使用以下代码提供默认着色器:voidmainImage(outvec4fragColor,invec2fragCoord){//标准化像素坐标(从0到1)vec2uv=fragCoord/iResolution.xy;//随时间改变像素颜色vec3col=0.5+0.5*cos(iTime+uv.xyx+vec3(0,2,4));//输出到屏幕fragColor=vec4(col,1.0);虽然还没有做任何令人兴奋的事情,但它教会了我着色器程序的基本结构!思路:将一对坐标(和时间)映射到一个颜色这里的思路是得到一对坐标作为输入(fragCoord),需要输出一个RGBA向量作为这个坐标的颜色。这个函数也可以使用当前时间(iTime),这样图像就可以随时间变化。这种编程模型(您将一对坐标和时间映射到该模型)的巧妙之处在于它非常容易并行化。我对GPU了解不多,但我的理解是这种任务(一次10000次平凡可并行计算)正是GPU擅长的。第二步:使用shadertoy-render加速开发迭代玩了一段时间shadertoy后,我厌倦了每次保存我的着色器时都必须在shadertoy网站上单击“重新编译”。我找到了一个名为shadertoy-render的命令行工具,它可以查看文件并在每次保存时实时更新动画。现在我可以运行:shadertoy-render.pycircle.glsl并开发更快的迭代!第三步:画一个圆接下来我想到的是——我擅长数学!我可以用一些基本的三角函数画出一个弹跳的彩虹圈!我知道圆的等式是(x^2+y^2=任何正数!),所以我写了一些代码来做到这一点:这是代码:(您也可以在shadertoy上查看)voidmainImage(outvec4fragColor,invec2fragCoord){//归一化像素坐标(从0到1)vec2uv=fragCoord/iResolution.xy;//画一个圆,其中心位置取决于时间vec2shifted=uv-vec2((sin(iGlobalTime)+1)/2,(1+cos(iGlobalTime))/2);if(dot(shifted,shifted)<0.03){//改变像素颜色vec3col=0.5+0.5*cos(iGlobalTime+uv.xyx+vec3(0,2,4));fragColor=vec4(col,1.0);}else{//将圆外的其他像素设为黑色fragColor=vec4(0,0,0,1.0);}}代码将向量fragCoord与其自身的坐标点积进行了转换,这与计算x^2+y^2是一样的。我也玩了一下这个圆的中心-圆的中心是vec2((sin(iGlobalTime)+1)/2,(1+cos(faster))/2,这意味着圆的中心也沿着另一个圆移动。着色器是学习数学的有趣方式!我觉得有趣的是(即使我们没有做任何超级高级的事情!)是这些着色器为我们提供了一种有趣的视觉方式来学习数学——我使用sin和cos使某些东西沿着一个圆圈移动,如果你稍微更多关于三角学如何工作的直觉,也许编写着色器将是一种有趣的方式!我喜欢的是你可以获得关于数学代码的即时视觉反馈——如果你将某个东西乘以2,图像中的东西会变大!或更小!或更快!或更慢!或更红!但是我们如何做一些真正有趣的事情呢?弹跳圆很好,但它与我看到其他人使用着色器所做的非常奇特的事情相去甚远。那么下一步是什么?想法:不使用if语句,而是使用带符号的距离函数!在我上面的圈子代码中,我基本上是这样写的:if(dot(uv,uv)<0.03){//codeinsidethecircle}else{//codeoutsidethecircle}但是问题(也是我觉得它得到的原因stuck)是不清楚如何将其概括为更复杂的形状!写很多if语句似乎效果不是很好。那么人们如何渲染这些3d形状呢?所以!符号距离函数是另一种定义形状的方法。不要使用硬编码的if语句,而是定义一个函数来告诉您,对于世界上的任何一点,该点离您的形状有多远。例如,下面是球体的带符号距离函数。floatsdSphere(vec3p,floatcenter){returnlength(p)-center;}有符号距离函数很棒,因为它们:易于定义!易于组装!如果你想要一个切掉一块的球体,你可以用一些简单的数学来计算并集/交集/差集。易于旋转/拉伸/弯曲!制作陀螺的步骤刚开始时,我不明白需要编写什么代码才能制作出闪亮的陀螺。事实证明,以下是基本步骤:为所需形状(在我的例子中是八面体)创建一个带符号的距离函数本教程称之为光线追踪,我还不明白光线追踪和光线漫游之间的区别)编写代码来处理形状的表面纹理并使其发光这个关于符号距离函数的精彩教程非常友好,老实说做得更好比我,它解释了如何执行上述3个步骤,并且代码有大量注释,这很棒。该教程称为“有符号距离函数教程:盒子和气球”,它位于:https://www.shadertoy.com/view/Xl2XWt您可以将大量有符号距离函数复制粘贴到您的代码(和方法将它们组合成其他形状):http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm第四步:复制教程代码并开始更改我在这里使用的是成熟的编程实践,即“复制代码并以混乱的方式更改内容,直到得到我想要的结果”。最后一堆闪亮的旋转八面体着色器在这里:https://www.shadertoy.com/view/wdlcR4为了做到这一点,我基本上只是复制了符号距离函数的教程,它根据符号距离工作函数呈现形状,并且:将sdfBalloon更改为sdfOctahedron,并使八面体旋转而不是在我的符号距离函数中静止不动下面是我用来使八面体旋转的代码!原来真的很简单:先从这个页面复制一个八面体记法距离函数,然后加一个rotate让它按时间旋转,就可以旋转了!vec2sdfOctahedron(vec3currentRayPosition,vec3offset){vec3p=rotate((currentRayPosition),offset.xy,iTime*3.0)-offset;浮动s=0.1;//什么是s?p=绝对值(p);浮动距离=(p.x+p.y+p.z-s)*0.57735027;浮点数=1.0;returnvec2(distance,id);}让它发出一些噪音我想做的另一件事是让我的形状看起来闪亮/闪亮。我使用了这个GitHub要点中的噪声函数来使表面看起来有纹理。下面是我如何使用噪音功能的代码。基本上,我只是随机更改噪声函数的参数(乘以2?3?1800?随便!),直到得到我喜欢的结果。floatx=noise(rotate(positionOfHit,vec2(0,0),iGlobalTime*3.0).xy*1800.0);floatx2=noise(lightDirection.xy*400.0);浮动y=min(max(x,0.0),1.0);浮点数y2=min(max(x2,0.0),1.0);vec3气球颜色=vec3(y,y+y2,y+y2);编写着色器很有趣!以上就是全部步骤!让这个八面体旋转和闪耀让我很高兴。如果你也想用着色器制作有趣的动画,我希望这篇文章能帮助你做出很酷的东西!通常对于我不太了解的主题,我可能在一篇文章中至少说了一个关于着色器的错误,请告诉我错误是什么!同样,这是我使用的两个资源:“有符号距离函数教程:盒子和气球”:https://www.shadertoy.com/view/Xl2XWt(修改和玩真的很有趣)将有符号距离函数复制并粘贴到你的代码:http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
