Translated

This commit is contained in:
Starryi 2021-12-18 20:23:57 +08:00
parent de2cb75d07
commit ae2594544f
2 changed files with 232 additions and 243 deletions

View File

@ -1,243 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (Starryi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Getting started with shaders: signed distance functions!)
[#]: via: (https://jvns.ca/blog/2020/03/15/writing-shaders-with-signed-distance-functions/)
[#]: author: (Julia Evans https://jvns.ca/)
Getting started with shaders: signed distance functions!
======
Hello! A while back I learned how to make fun shiny spinny things like this using shaders:
![][1]
My shader skills are still extremely basic, but this fun spinning thing turned out to be a lot easier to make than I thought it would be to make (with a lot of copying of code snippets from other people!).
The big idea I learned when doing this was something called “signed distance functions”, which I learned about from a very fun tutorial called [Signed Distance Function tutorial: box & balloon][2].
In this post Ill go through the steps I used to learn to write a simple shader and try to convince you that shaders are not that hard to get started with!
### examples of more advanced shaders
If you havent seen people do really fancy things with shaders, here are a couple:
1. this very complicated shader that is like a realistic video of a river: <https://www.shadertoy.com/view/Xl2XRW>
2. a more abstract (and shorter!) fun shader with a lot of glowing circles: <https://www.shadertoy.com/view/lstSzj>
### step 1: my first shader
I knew that you could make shaders on shadertoy, and so I went to <https://www.shadertoy.com/new>. They give you a default shader to start with that looks like this:
![][3]
Heres the code:
```
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Time varying pixel color
vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
// Output to screen
fragColor = vec4(col,1.0);
}
```
This doesnt do anythign that exciting, but it already taught me the basic structure of a shader program!
### the idea: map a pair of coordinates (and time) to a colour
The idea here is that you get a pair of coordinates as an input (`fragCoord`) and you need to output a RGBA vector with the colour of that. The function can also use the current time (`iTime`), which is how the picture changes over time.
The neat thing about this programming model (where you map a pair of coordinates and the time to) is that its extremely trivially parallelizable. I dont understand a lot about GPUs but my understanding is that this kind of task (where you have 10000 trivially parallelizable calculations to do at once) is exactly the kind of thing GPUs are good at.
### step 2: iterate faster with `shadertoy-render`
After a while of playing with shadertoy, I got tired of having to click “recompile” on the Shadertoy website every time I saved my shader.
I found a command line tool that will watch a file and update the animation in real time every time I save called [shadertoy-render][4]. So now I can just run:
```
shadertoy-render.py circle.glsl
```
and iterate way faster!
### step 3: draw a circle
Next I thought Im good at math! I can use some basic trigonometry to draw a bouncing rainbow circle!
I know the equation for a circle (`x**2 + y**2 = whatever`!), so I wrote some code to do that:
![][5]
Heres the code: (which you can also [see on shadertoy][6])
```
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
// Draw a circle whose center depends on what time it is
vec2 shifted = uv - vec2((sin(iGlobalTime) + 1)/2, (1 + cos(iGlobalTime)) / 2);
if (dot(shifted, shifted) < 0.03) {
// Varying pixel colour
vec3 col = 0.5 + 0.5*cos(iGlobalTime+uv.xyx+vec3(0,2,4));
fragColor = vec4(col,1.0);
} else {
// make everything outside the circle black
fragColor = vec4(0,0,0,1.0);
}
}
```
This takes the dot product of the coordinate vector `fragCoord` with itself, which is the same as calculating `x^2 + y^2`. I played with the center of the circle a little bit in this one too I made the center `vec2((sin(iGlobalTime) + 1)/2, (1 + cos(faster)) / 2)`, which means that the center of the circle also goes in a circle depending on what time it is.
### shaders are a fun way to play with math!
One thing I think is fun about this already (even though we havent done anything super advanced!) is that these shaders give us a fun visual way to play with math I used `sin` and `cos` to make something go in a circle, and if you want to get some better intuition about how trigonometric work, maybe writing shaders would be a fun way to do that!
I love that you get instant visual feedback about your math code if you multiply something by 2, things get bigger! or smaller! or faster! or slower! or more red!
### but how do we do something really fancy?
This bouncing circle is nice but its really far from the super fancy things Ive seen other people do with shaders. So whats the next step?
### idea: instead of using if statements, use signed distance functions!
In my circle code above, I basically wrote:
```
if (dot(uv, uv) < 0.03) {
// code for inside the circle
} else {
// code for outside the circle
}
```
But the problem with this (and the reason I was feeling stuck) is that its not clear how it generalizes to more complicated shapes! Writing a bajillion if statements doesnt seem like it would work well. And how do people render those 3d shapes anyway?
So! **Signed distance functions** are a different way to define a shape. Instead of using a hardcoded if statement, instead you define a **function** that tells you, for any point in the world, how far away that point is from your shape. For example, heres a signed distance function for a sphere.
```
float sdSphere( vec3 p, float center )
{
return length(p)-center;
}
```
Signed distance functions are awesome because theyre:
* simple to define!
* easy to compose! You can take a union / intersection / difference with some simple math if you want a sphere with a chunk taken out of it.
* easy to rotate / stretch / bend!
### the steps to making a spinning top
When I started out I didnt understand what code I needed to write to make a shiny spinning thing. It turns out that these are the basic steps:
1. Make a signed distance function for the shape I want (in my case an octahedron)
2. Raytrace the signed distance function so you can display it in a 2D picture (or raymarch? The tutorial I used called it raytracing and I dont understand the difference between raytracing and raymarching yet)
3. Write some code to texture the surface of your shape and make it shiny
Im not going to explain signed distance functions or raytracing in detail in this post because I found this [AMAZING tutorial on signed distance functions][2] that is very friendly and honestly it does a way better job than I could do. It explains how to do the 3 steps above and the code has a ton of comments and its great.
* The tutorial is called “SDF Tutorial: box &amp; balloon” and its here: <https://www.shadertoy.com/view/Xl2XWt>
* Here are tons of signed distance functions that you can copy and paste into your code <http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm> (and ways to compose them to make other shapes)
### step 4: copy the tutorial code and start changing things
Here I used the time honoured programming practice here of “copy the code and change things in a chaotic way until I get the result I want”.
My final shader of a bunch of shiny spinny things is here: <https://www.shadertoy.com/view/wdlcR4>
The animation comes out looking like this:
![][7]
Basically to make this I just copied the tutorial on signed distance functions that renders the shape based on the signed distance function and:
* changed `sdfBalloon` to `sdfOctahedron` and made the octahedron spin instead of staying still in my signed distance function
* changed the `doBalloonColor` colouring function to make it shiny
* made there be lots of octahedrons instead of just one
### making the octahedron spin!
Heres some the I used to make the octahedron spin! This turned out to be really simple: first copied an octahedron signed distance function from [this page][8] and then added a `rotate` to make it rotate based on time and then suddenly its spinning!
```
vec2 sdfOctahedron( vec3 currentRayPosition, vec3 offset ){
vec3 p = rotate((currentRayPosition), offset.xy, iTime * 3.0) - offset;
float s = 0.1; // what is s?
p = abs(p);
float distance = (p.x+p.y+p.z-s)*0.57735027;
float id = 1.0;
return vec2( distance, id );
}
```
### making it shiny with some noise
The other thing I wanted to do was to make my shape look sparkly/shiny. I used a noise funciton that I found in [this github gist][9] to make the surface look textured.
Heres how I used the noise function. Basically I just changed parameters to the noise function mostly at random (multiply by 2? 3? 1800? who knows!) until I got an effect I liked.
```
float x = noise(rotate(positionOfHit, vec2(0, 0), iGlobalTime * 3.0).xy * 1800.0);
float x2 = noise(lightDirection.xy * 400.0);
float y = min(max(x, 0.0), 1.0);
float y2 = min(max(x2, 0.0), 1.0) ;
vec3 balloonColor = vec3(y , y + y2, y + y2);
```
### writing shaders is fun!
Thats all! I had a lot of fun making this thing spin and be shiny. If you also want to make fun animations with shaders, I hope this helps you make your cool thing!
As usual with subjects I dont know tha well, Ive probably said at least one wrong thing about shaders in this post, let me know what it is!
Again, here are the 2 resources I used:
1. “SDF Tutorial: box &amp; balloon”: <https://www.shadertoy.com/view/Xl2XWt> (which is really fun to modify and play around with)
2. Tons of signed distance functions that you can copy and paste into your code <http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm>
--------------------------------------------------------------------------------
via: https://jvns.ca/blog/2020/03/15/writing-shaders-with-signed-distance-functions/
作者:[Julia Evans][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://jvns.ca/
[b]: https://github.com/lujun9972
[1]: https://jvns.ca/images/spinny.gif
[2]: https://www.shadertoy.com/view/Xl2XWt
[3]: https://jvns.ca/images/colour.gif
[4]: https://github.com/alexjc/shadertoy-render
[5]: https://jvns.ca/images/circle.gif
[6]: https://www.shadertoy.com/view/tsscR4
[7]: https://jvns.ca/images/octahedron2.gif
[8]: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
[9]: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83

View File

@ -0,0 +1,232 @@
[#]: collector: (lujun9972)
[#]: translator: (Starryi)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Getting started with shaders: signed distance functions!)
[#]: via: (https://jvns.ca/blog/2020/03/15/writing-shaders-with-signed-distance-functions/)
[#]: author: (Julia Evans https://jvns.ca/)
着色器入门:符号距离函数!
======
大家好!不久前我学会了如何使用着色器制作有趣的闪亮旋转八面体:
![][1]
我的着色器能力仍然非常基础,但事实证明制作这个有趣的旋转八面体比我想象中要容易得多(从其他人那里复制了很多代码片段!)。
我在做这件事时, 从一个非常有趣的叫做[符号距离函数教程:盒子和气球][2]的教程中学到了“符号距离函数”的重要想法。
在本文中,我将介绍我用来学习编写简单着色器的步骤,并努力让你们相信着色器并不难入门!
### 更高级着色器的示例
如果你还没有看过用着色器做的真正有趣的事情,这里有几个例子:
1. 这个非常复杂的着色器就像一条河流的真实视频:<https://www.shadertoy.com/view/Xl2XRW>
2. 一个更抽象(更短!)有趣的着色器,它有很多发光的圆圈:<https://www.shadertoy.com/view/lstSzj>
### 步骤一:我的第一个着色器
我知道你可以在 shadertoy 上制作着色器,所以我去了<https://www.shadertoy.com/new>。它们提供了一个默认着色器,如下图所示:
![][3]
代码如下:
```cpp
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// 规范像素坐标 (从 0 到 1)
vec2 uv = fragCoord / iResolution.xy;
// 随时间改变像素颜色
vec3 col = 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][4] 命令行工具,它会在每次保存时实时查看文件并更新动画。现在我可以运行:
```bash
shadertoy-render.py circle.glsl
```
并更快地开发迭代!
### 步骤三:画一个圆圈
接下来我想——我擅长数学!我可以用一些基本的三角学来画一个会弹跳的彩虹圈!
我知道圆的方程为(`x^2 + y^2 = 任意正数`!),所以我写了一些代码来实现它:
![][5]
代码如下:(你也可以[在 shadertoy 上查看][6]
```cpp
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// 规范像素坐标 (从 0 到 1)
vec2 uv = fragCoord / iResolution.xy;
// 绘制一个中心位置依赖于时间的圆
vec2 shifted = uv - vec2((sin(iGlobalTime) + 1) / 2, (1 + cos(iGlobalTime)) / 2);
if (dot(shifted, shifted) < 0.03) {
// 改变像素颜色
vec3 col = 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` 相同。我还在这个圆圈的中心玩了一点花活 圆心为 `vec2siniGlobalTime + 1/ 21 + cosfaster / 2`,这意味着圆心也随着时间沿另一个圆移动。
### 着色器是一种学习数学的有趣方式!
我觉得有意思的(即使我们没有做任何超级高级的事情!)是这些着色器为我们提供了一种有趣的可视化方式学习数学 - 我用 `sin``cos` 来使某些东西沿着圆移动,如果你想更直观地了解三角函数的工作方式, 也许编写着色器会是一种有趣的方法!
我喜欢的是,可以获得有关数学代码的即时视觉反馈 - 如果你把一些东西乘以2图像里的东西会变得更大或更小或更快或更慢或更红
### 但是我们如何做一些真正有趣的事情呢?
这个会弹跳的圆圈很好,但它与我见过的其他人使用着色器所做的非常奇特的事情相去甚远。那么下一步要做什么呢?
### 想法:不要使用 if 语句,而是使用符号距离函数!
在我上面的圆圈代码中,我基本上是这样写的:
```cpp
if (dot(uv, uv) < 0.03) {
// 圆里的代码
} else {
// 圆外的代码
}
```
但问题(也是我感到卡住的原因)是不清楚如何将它推广到更复杂的形状!编写大量的 if 语句似乎不太好用。那人们要如何渲染这些 3d 形状呢?
所以!**符号距离函数** 是定义形状的另一种方式。不是使用硬编码的 if 语句,而是定义一个 **函数**,该函数告诉你,对于世界上的任何一个点,该点与你的形状有多远。比如,下面是球体的符号距离函数。
```cpp
float sdSphere( vec3 p, float center )
{
return length(p) - center;
}
```
符号距离函数非常棒,因为它们:
* 易于定义!
* 易于组合!如果你想要一个被切去一块的球体, 你可以用一些简单的数学来计算并集/交集/差集。
* 易于旋转/拉伸/弯曲!
### 制作旋转陀螺的步骤
当我开始时,我不明白需要编写什么代码来制作一个闪亮的旋转东西。结果表明如下是基本步骤:
1. 为想要的形状创建一个符号距离函数(在我的例子里是八面体)
2. 光线追踪符号距离函数,以便可以在 2D 图片中显示它(或光线行进?我使用的教程称之为光线追踪,我还不明白光线追踪和光线行进之间的区别)
3. 编写代码处理形状的表面纹理并使其发光
我不打算在本文中详细解释符号距离函数或光线追踪,因为我发现这个[关于符号距离函数的惊人教程][2]非常友好老实说它比我做的更好它解释了如何执行上述3个步骤并且代码有大量的注释非常棒。
* 该教程名为“符号距离函数教程:盒子和气球”,它在这里:<https://www.shadertoy.com/view/Xl2XWt>
* 这里有大量符号距离函数,你可以将其复制粘贴到代码中<http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm>(以及组合它们以制作其他形状的方法)
### 步骤四:复制教程代码并开始更改内容
我在这里使用了久负盛名的编程实践,即“复制代码并以混乱的方式更改内容,直到得到我想要的结果”。
最后一堆闪亮的旋转八面体着色器在这里:<https://www.shadertoy.com/view/wdlcR4>
动画出来的样子是这样的:
![][7]
为了做到这一点,我基本上只是复制了关于符号距离函数的教程,该函数根据符号距离函数呈现形状,并且:
* 将 `sdfBalloon` 更改为 `sdfOctahedron`,并使八面体旋转而不是在我的符号距离函数中静止不动
* 修改 `doBalloonColor` 着色功能,使其有光泽
* 有很多八面体而不是一个
### 使八面体旋转!
下面是我用来使八面体旋转的代码!事实证明这真的很简单:首先从 [这个页面][8] 复制一个八面体符号距离函数,然后添加一个 `rotate` 使其根据时间旋转,然后它就可以旋转了!
```cpp
vec2 sdfOctahedron( vec3 currentRayPosition, vec3 offset ){
vec3 p = rotate((currentRayPosition), offset.xy, iTime * 3.0) - offset;
float s = 0.1; // s 是啥?
p = abs(p);
float distance = (p.x + p.y + p.z - s) * 0.57735027;
float id = 1.0;
return vec2( distance, id );
}
```
### 用一些噪音让它发光
我想做的另一件事是让我的形状看起来闪闪发光/有光泽。我使用了在[这个github gist][9]中找到的噪声函数使表面看起来有纹理。
以下是我如何使用噪声函数的代码。基本上我只是随机地将参数更改为噪声函数乘以231800随你直到得到喜欢的效果。
```cpp
float x = noise(rotate(positionOfHit, vec2(0, 0), iGlobalTime * 3.0).xy * 1800.0);
float x2 = noise(lightDirection.xy * 400.0);
float y = min(max(x, 0.0), 1.0);
float y2 = min(max(x2, 0.0), 1.0);
vec3 balloonColor = vec3(y, y + y2, y + y2);
```
## 编写着色器很有趣!
上面就是全部的步骤了!让这个八面体旋转并闪闪发光使我很开心。如果你也想用着色器制作有趣的动画,希望本文能帮助你制作出很酷的东西!
通常对于不太了解的主题,我可能在文章中说了至少一件关于着色器的错误事情,请让我知道错误是什么!
再说一遍,如下是我用到的两个资源:
1. “符号距离函数教程:盒子和气球”:<https://www.shadertoy.com/view/Xl2XWt>(修改和玩起来真的很有趣)
2. 可以将大量符号距离函数复制并粘贴到你的代码中<http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm>
--------------------------------------------------------------------------------
via: https://jvns.ca/blog/2020/03/15/writing-shaders-with-signed-distance-functions/
作者:[Julia Evans][a]
选题:[lujun9972][b]
译者:[Starryi](https://github.com/Starryi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://jvns.ca/
[b]: https://github.com/lujun9972
[1]: https://jvns.ca/images/spinny.gif
[2]: https://www.shadertoy.com/view/Xl2XWt
[3]: https://jvns.ca/images/colour.gif
[4]: https://github.com/alexjc/shadertoy-render
[5]: https://jvns.ca/images/circle.gif
[6]: https://www.shadertoy.com/view/tsscR4
[7]: https://jvns.ca/images/octahedron2.gif
[8]: http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
[9]: https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83