shadertoy for beginners
shadertoy入门
暑假想自学渲染,图形学基础并不扎实,打算开始边实践边学习!
在shadertoy看到了很多厉害的渲染画面,但自己都不知道从哪看起,在youtube上看了入门教程,做笔记如下:
本文参考如下:
参考视频:https://www.youtube.com/watch?v=u5HAYVHsasc
参考文章:
https://blog.csdn.net/candycat1992/article/details/44039077
https://blog.csdn.net/weixin_28710515/article/details/89784772
https://zhuanlan.zhihu.com/p/157758600
介绍
点击右下角的❓可以查看相关信息,包括一些可使用的函数、以及等等;
这个网上的所有shader都是GLSL的pixel shaders。那么什么是pixel shader呢?如果我们需要渲染一个刚好铺盖整个屏幕的全屏的方形平板,那么这个方形的fragment shader就是一个pixel shader。这是因为此时,每一个fragment对应了屏幕上的一个pixel。也因此,pixel shader的很多输入都是相同的。(摘自 https://blog.csdn.net/candycat1992/article/details/44039077)
输入输出:
fragCoord:屏幕输入位置坐标(x, y);
fragColor:对应坐标的像素颜色(r, g, b, a);
一些公共变量:
uniform vec3 iResolution; // 屏幕分辨率
uniform float iGlobalTime; // 当前渲染运行的时间
uniform float iChannelTime[4]; // channel playback time (in seconds)
uniform vec3 iChannelResolution[4]; // channel resolution (in pixels)
uniform vec4 iMouse; // 鼠标位置
uniform samplerXX iChannel0..3; // input channel. XX = 2D/Cube
uniform vec4 iDate; // (year, month, day, time in seconds)
uniform float iSampleRate; // sound sample rate (i.e., 44100)
主函数样例说明:TODO
画一个圆
首先看一下uv坐标关于屏幕位置的变化规律
-
令 fragColor = vec4(uv.x, 0, 0, 1.0); 可以得到如此的渐变图片,这是因为最左边uv.x = 0,而最右边uv.x = 1;所以屏幕颜色沿着x轴由(0, 0, 0)渐变到(1, 0, 0)。
-
将fragColor 改成 vec4(0, uv.y, 0, 1.0) 和 vec4(uv.x, uv.y, 0, 1.0)可以分别看到竖下来渐变的绿到黑,以及红绿渐变。
画一个椭圆
由此,我们得知uv坐标的变化规律(当然本来大概也知道),可以根据此画一个圆:距离圆心某个距离内的点为白色,外部的为黑色;而距离即为uv坐标到圆心坐标的向量长度
float drawCircle(vec2 uv, vec2 center, float radius)
{
float c = length(uv - center);
if(c > radius)
return 0.;
else
return 1.;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord/iResolution.xy;
float col = drawCircle(uv, vec2(0.5, 0.5), 0.4);
// Output to screen
fragColor = vec4(vec3(col), 1.0);
}
画一个圆
x、y的比例变化得到正圆:
由于屏幕长宽不同,而归一化后的长宽范围又都在[0,1]之间,因此需要进行一个转化使得x,y的单位长度相同,以获得正圆。在主函数调用drawCircle前加入转化:
uv.x *= iResolution.x / iResolution.y;
此时画出的圆不在正中间,可以在本语句前加入uv-=.5;让原点在正中间。
边缘模糊:
加上内置smoothstep函数可以使得边缘模糊
float drawCircle(vec2 uv, vec2 center, float radius, float blur)
{
float c = length(uv - center);
float res = smoothstep(radius, radius-blur, c);
return res;
}
其中,smoothstep函数内部实现如下:
float smoothstep(float t1, float t2, float x) {
x = clamp((x - t1) / (t2 - t1), 0.0, 1.0);
return x * x * (3 - 2 * x);
}
smoothstep可以用来生成0到1的平滑过渡值,它也叫平滑阶梯函数。其函数图像如下:
画多个圆
简单的图像叠加就能画出多个圆:因为(0,0,0)是黑色,而(1,1,1)是白色,因此颜色相加时能让黑色区域加白圆,相减时在白色区域加黑圆。
float drawCircle(vec2 uv, vec2 center, float radius, float blur)
{
float c = length(uv - center);
float res = smoothstep(radius, radius-blur, c);
return res;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
uv -= .5;
uv.x *= iResolution.x / iResolution.y;
float col = drawCircle(uv, vec2(0, 0), 0.4, 0.01);
col += drawCircle(uv, vec2(0.5, 0), 0.1, 0.02);
col -= drawCircle(uv, vec2(0, -0.2), 0.2, 0.03);
// Output to screen
fragColor = vec4(vec3(col), 1.0);
}
*乘的操作也可以得到别的结果,也可以试试。
画一个笑脸:)
通过圆环的叠加、上色等等操作就能画上一个笑脸啦。
float drawCircle(vec2 uv, vec2 center, float radius, float blur)
{
float c = length(uv - center);
float res = smoothstep(radius, radius-blur, c);
return res;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
uv -= .5;
uv.x *= iResolution.x / iResolution.y;
float mask = drawCircle(uv, vec2(0.), .4, .05);
//draw eyes
mask -= drawCircle(uv, vec2(-.12, .155), .07, .02);
mask -= drawCircle(uv, vec2(.12, .155), .07, .02);
//draw mouth
float mouth = drawCircle(uv, vec2(0., 0.), .3, .02);
mouth -= drawCircle(uv, vec2(0., .1), .3, .02);
mask -= mouth;
//color the face
vec3 col = vec3(1., 1., 0) * mask;
fragColor = vec4(col, 1.0);
}