序
这个网站,之前是知道的,这个网站上有很多好看的例子,也是知道的,比如:
Path Tracer Denoise (shadertoy.com)
Ray tracing spheres (shadertoy.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这个网站怎么用,但是脑子里没东西:不会图形编程,不会数学,不会物理……
没有核,就算有了个壳,依旧没什么能力去认识世界,只有能力去自我感动……
怎么办呢?无解。
混啊混……混啊混……混……
有一天算一天吧……