ShaderToy的使用方法

这个网站,之前是知道的,这个网站上有很多好看的例子,也是知道的,比如:

Path Tracer Denoise (shadertoy.com)

 Ray tracing spheres (shadertoy.com)

但是,我是小白,知道了也理解不了;学了也不一定能学会,学会了也会被同行碾压。

但是,本着自我感动式学习的想法,还是象征性的学一学吧。

参考:

渲染技术 - 知乎 (zhihu.com)

网站界面介绍

大概就是这样

代码是这些:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // 这个uv,不是归一化的形式,需要手动归一化纹理坐标到[0-1]范围
    vec2 uv = fragCoord/iResolution.xy;
    // 根据时间变量iTime与纹理坐标组合作为入参计算像素颜色
    vec3 col = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));
    // 输出颜色到fragColor,只需要给该变量赋值就可以展示在最终渲染效果上
    fragColor = vec4(col,1.0);
}

屏幕坐标系

它的屏幕坐标系,是什么样的?

试试

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = vec3(0,0,0);
    if(uv.x<0.5 && uv.y<0.5)//左下设置为红色
        col = vec3(1.0,0.0,0.0);
    else if(uv.x>0.5 && uv.y<0.5)//右下设置为绿色
        col = vec3(0.0,1.0,0.0);
    else if(uv.x<0.5 &&uv.y>0.5)//左上设置为蓝色
        col = vec3(0.0,0.0,1.0);
    else if(uv.x>0.5 && uv.y>0.5)//右上设置为黄色
        col = vec3(1.0,1.0,0.0);
    fragColor = vec4(col,1.0);
}

结果就有了,左下是【0,0】,右上是【1,1】 

纹理的使用

点几下就可以了,小白友好形

 再配合一点点的代码

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = texture(iChannel0,uv).rgb;//从通道0中获取【u,v】坐标处对应的纹理值
    fragColor = vec4(col,1.0);
}

可以了

甚至还可以,混合一下

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col_0 = texture(iChannel0,uv).rgb;//从通道0中获取【u,v】坐标处对应的纹理值
    
    
    // Time varying pixel color
    vec3 col_1 = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4));

    // Output to screen
    fragColor = vec4(col_0*0.5+col_1*0.5,1.0);//混合
}
    

 简陋吗?简陋

简陋就对了,因为我是认识世界能力很低的小白

一个圆的绘制

不合理的绘制

代码不长的

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    
    vec3 col=vec3(0,0,0);
    
    
    vec2 center=vec2(0.5,0.5);//圆心坐标
    float radius=0.3;//圆的半径
    float length=distance(uv,center);
    
    if(length<0.3){
        col=vec3(1,1,1);
    
   

    // Output to screen
    fragColor = vec4(col,1.0);//混合
}

很明显这不是个正常的圆

可能是因为,这个uv的范围都是【0,1】,是一个正方形;而这个视口却不是一个长方形

就像,一个正方形的图,强行横向拉长变扁的那种效果。 

所以,得重新映射一下uv的范围,让range(u)/range(v)==range(x)/range(y),就不会变扁了。

比如: 

如果屏幕是【1000*500】,那uv就不能都取【0,1】了,得按比例把u映射到【0,2】,把v映射到【0,1】,这样就不会变扁了。

实际存在的,其实是屏幕的800*450,没有什么uv,这个uv,其实是自己定义出来的,为了归一化的,方便计算的。

比如,这个例子里又用不着纹理,所以不归一化一点问题都没有,直接按像素来就是啦!

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    //vec2 uv = fragCoord/iResolution.xy;
    
    vec3 col=vec3(0,0,0);
    
    
    vec2 center=vec2(iResolution.xy/vec2(2,2));//圆心坐标
    float radius=150.0;//圆的半径
    float length=distance(fragCoord,center);
    
    if(length<radius){
        col=vec3(1,1,1);
    }
   

    // Output to screen
    fragColor = vec4(col,1.0);
}

归一化也是可分的,比如,正方形的屏幕,可以归一化到都在【0,1】之间的uv;而非正方形的屏幕,也许只能把uv中的一项归一化到【0,1】间。

归一化是个人为的手段,为了简化而已。不一定uv都得在【0,1】之间的,下下面那个,u的范围就超过了【0,1】,也很正常……很合理……

把长方形的屏幕,从中心出发,裁出一个正方形来用,其实也行……但没必要……

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    vec2 uv = fragCoord/iResolution.xy;
    //uv -= 0.5;   //纹理坐标映射为[-0.5,0.5]
    uv.x *= iResolution.x/iResolution.y;  //矫正纹理坐标,使得x与y轴单位长度一致
    
    
    vec3 col=vec3(0,0,0);
    
    
    vec2 center=vec2(0.5,0.5);//圆心坐标
    float radius=0.3;//圆的半径
    float length=distance(uv,center);
    if(length<0.3){
        col=vec3(1,1,1);
    }
   

    // Output to screen
    fragColor = vec4(col,1.0);//混合
}

【0.5,0.5】不在中心……很合理,因为u的范围被拉长了,超过了【0,1】了。

用纹理的话,看的更清楚,因为纹理的坐标超过1的话,是会自动重复的。

800*450的窗口,大概u的方向重复了2遍纹理,很合理的。u大概是映射到了【0,2】,v大概是映射到了【0,1】左右的样子。

 较合理的绘制

代码【这个代码,有OpenGL先对物体局部坐标系做几何变换再画图内味儿了……】

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    vec2 uv = fragCoord/iResolution.xy;
    uv -= 0.5;   //纹理坐标映射为[-0.5,0.5]
    uv.x *= iResolution.x/iResolution.y;  //矫正纹理坐标,使得x与y轴单位长度一致
    
    
    vec3 col=vec3(0,0,0);
    
    
    vec2 center=vec2(0,0);//圆心坐标
    float radius=0.3;//圆的半径
    float length=distance(uv,center);
    if(length<0.3){
        col=vec3(1,1,1);
    }
   

    // Output to screen
    fragColor = vec4(col,1.0);//混合
}

结果 

 圆的动画

动画,大概得用时间了吧

 代码:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    vec2 uv = fragCoord/iResolution.xy;
    uv -= 0.5;   //纹理坐标映射为[-0.5,0.5]
    uv.x *= iResolution.x/iResolution.y;  //矫正纹理坐标,使得x与y轴单位长度一致
    
    
    vec3 col=vec3(0,0,0);
    
    
    vec2 center=vec2(0.0+0.5*cos(iTime),0);//圆心坐标
    float radius=0.3;//圆的半径
    float length=distance(uv,center);
    if(length<0.3){
        col=vec3(1,1,1);
    }
   

    // Output to screen
    fragColor = vec4(col,1.0);//混合
}

帧缓存

就是,有一种缓冲区,是存图像数据的,叫做FBO,比如1920*1080这么大的一个FBO。

但是,有的缓冲区,是要画到屏幕上;而有的缓冲区,是不画到屏幕上的。

帧缓存,点一点就可以创建了,小白友好形

 写点程序,就可以把图形画到创建的帧缓存而不是屏幕里

代码

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy;
    vec3 col_0 = texture(iChannel0,uv).rgb;//从通道0里获取数据
    vec3 col_1=texture(iChannel1,uv).rgb;//从通道1里获取数据
    fragColor = vec4(col_0*0.7+col_1*0.7,1.0);//混合,存入FBO A中
}

 代码

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{

    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = texture(iChannel0,uv).rgb;//从通道0【帧缓存A】中获取【u,v】坐标处对应的纹理值
    fragColor = vec4(col,1.0);//存入帧缓存【画到屏幕上】
}

帧缓存的实际应用

最上面那个链接,里面是这个图,很高级,很难,小白不友好形

这个数据,好像是从帧缓存D里取的

虽然看不懂,但是知道它用了帧缓存 ,那末,把ABC三个帧缓存添加到空的那3个通道里,看看这几个帧缓存里都是上面吧!

代码大概是这样:

// Path tracer denoise.
//
// https://www.shadertoy.com/view/ldKBzG
//

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	//fragColor = texelFetch(iChannel0, ivec2(fragCoord), 0);

    vec2 uv = fragCoord/iResolution.xy;
    vec3 col = texture(iChannel1,uv).rgb;//从通道0中获取【u,v】坐标处对应的纹理值
    fragColor = vec4(col,1.0);

}

帧缓存A: 

 帧缓存B:

 帧缓存C:

 大概……帧缓存就是这个意思吧……

后记

会Whitted-style-ray-tracing的,到这里可能就已经能用shader toy实现Whitted-style-ray-tracing了

会Path-Tracing的,到这里可能就已经能用shader toy实现Path-Tracing了

……

然而,我是小白,即使知道了shader toy这个网站怎么用,但是脑子里没东西:不会图形编程,不会数学,不会物理……

没有核,就算有了个壳,依旧没什么能力去认识世界,只有能力去自我感动……

怎么办呢?无解。

混啊混……混啊混……混……

 有一天算一天吧……

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值