Skip to content

miccall/ShaderToy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 

Repository files navigation

ShaderToy for vscode Test

最终图像,内容来自iq大神的sahdertoy

finalImage

1. Camera的设置

camera vector

mat3 setCamera( in vec3 ro, in vec3 ta, float cr )
{
    vec3 cw = normalize(ta-ro);
    vec3 cp = vec3(sin(cr), cos(cr),0.0);
    vec3 cu = normalize( cross(cw,cp) );
    vec3 cv = normalize( cross(cu,cw) );
    return mat3( cu, cv, cw );
}

这个方法设置了一个转化矩阵,根据摄像机的空间位置,我们需要把摄像机转化到世界坐标系

在main()中调用 :

void main() {
    // camera, ro:摄像机圆心,ta:目标点 
    vec3 ro = vec3( 2 , 2 ,  4 );
    vec3 ta = vec3( 0, 0, 0 );
    // camera-to-world transformation
    mat3 ca = setCamera( ro, ta, 0.0 );
    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}

为了更加可控,可以增加鼠标控制:

vec2 mo = iMouse.xy/iResolution.xy;

获得鼠标的空间坐标点 那么我们可以直接使用这个值进行对camera的位置更改

// 鼠标控制的摄像机位置 
vec3 ro = vec3( cos(mo.x), mo.y, sin(mo.x) );
// 测试
gl_FragColor = vec4(ro,1.0);

2. Pixel Coordinates 设置

我们把屏幕的坐标从 0-1 ,改到 -0.5 到 0.5

    vec2 p = (-iResolution.xy + 2.0*gl_FragCoord.xy)/iResolution.y;
    //
    gl_FragColor = vec4(p,1.0,1.0);

3. Ray Direction

    vec3 rd = ca * normalize( vec3(p.xy,2.0) );     
    // 测试
    gl_FragColor = vec4(rd,1.0);

ca * viewSpace ,把坐标空间从 viewspace 转化到世界空间

4. Sphere 的 SDF 函数

// p是球心位置,s是球的半径
float sdSphere( vec3 p, float s )
{
    return length(p)-s;
}

5. Ray Marching

我们已经有了两个基本值,一个是相机原点 ro ,一个是光线方向 rd

我们定义一个场景来存放所有marching的内容

float map( in vec3 pos )
{
    return sdSphere( pos , 1.0 );
}
  • 光线的位置更新: ro + rd * t ,从初始点ro开始,朝着方向rd ,走t倍的距离
  • 我们循环 Max_Step 次,代表我们最多marching 步数 ,
  • 还要满足t被距离之后,不能超过我们预定的Max_Dist。
  • 就算SDF函数,我们每一次都得到一个 最小 距离 h
  • 然后我们每一步都用 h 更新 t
  • 最后,当我们得到的最小距离h很接近表面时,我们就知道他已经时表面的点,我们更新一个res返回

vec3 castRay( in vec3 ro, in vec3 rd )
{
    vec3  res = vec3(0.0,0.0,0.0);
    float t = 0.0 ;
    for( int i = 0 ; i < Max_Step && t < Max_Dist ; i++ )
    {
        float h = map( ro+rd*t );
        if( abs(h)<( Surf_Dist * t ))
        { 
            res = vec3(t,h,1.0); 
            break;
         }
         t += h;
    }
    return res;
}

当然我么还要define 这些值

#define Max_Step  250 
#define Max_Dist 100.0
#define Surf_Dist 0.000001 

6. Render

最后,我们希望对这个图进行渲染,我们为了综合,开一个单独的函数,为了以后反射,高光,阴影等计算

vec3 render( in vec3 ro, in vec3 rd )
{ 
    vec3 res = castRay(ro,rd) ;
    return vec3(res.x);
}

测试:

    vec3 ro = vec3( cos(mo.x)-5.0, mo.y, sin(mo.x) );

    vec3 col = render(ro,rd);
    gl_FragColor = vec4(col,1.0);

render sphere

7. combine

至此,我么已经渲染出一个球,我们再来回顾一下整个过程:顺便我还加入一些其他步骤

  1. 更新设置鼠标和摄像机
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 mo = iMouse.xy/iResolution.xy;
    float time = 15.0 + iTime;

    //cam
    vec3 ro = vec3( 4.6*cos(0.1*time + 6.0*mo.x), 1.0 + 2.0*mo.y, 0.5 + 4.6*sin(0.1*time + 6.0*mo.x) );
    vec3 ta = vec3( -0.5, -0.4, 0.5 );
    mat3 ca = setCamera( ro, ta, 0.0 );
}
  1. 初始化Ray,并且调用render进行marching,加入了gamma 和 tot 总量
    
    vec3 tot = vec3(0.0);
    vec2 p = (-iResolution.xy + 2.0*gl_FragCoord.xy)/iResolution.y;
    vec3 rd = ca * normalize( vec3(p.xy,2.0) );
    vec3 col = render(ro,rd);
    // gamma
    col = pow( col, vec3(0.4545) );
    tot += col;
    gl_FragColor = vec4(tot,1.0);
  1. 在render函数中,我们只是简单的做了一个castRay,并没有做光照渲染。
  2. castRay中,我们根据ro 和 rd ,利用sdf的sphere公式,绘制出了一个球

8. normal

normal vector preview

  1. 方法一 : 来自 iq 的网站 : normalsSDF
vec3 calcNormal( in vec3 pos )
{
    vec2 e = vec2(1.0,-1.0)*0.5773*0.0005;
    return normalize( e.xyy*map( pos + e.xyy ) + 
					  e.yyx*map( pos + e.yyx ) + 
					  e.yxy*map( pos + e.yxy ) + 
					  e.xxx*map( pos + e.xxx ) );
}

2.方法二 : 来自 klems

vec3 calcNormal( in vec3 pos )
{
    vec3 n = vec3(0.0);
    for( int i=ZERO; i<4; i++ )
    {
        vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0);
        n += e*map(pos+0.0005*e).x;
    }
    return normalize(n);
}

我们要在render方法中计算他的法线:

    vec3 res = castRay(ro,rd) ;
    vec3 pos = ro + res.x * rd;
    vec3 normal = calcNormal( pos );

9. diffuse

既然我们法线也有了,我们现在就可以模拟一个灯光,然后做一个漫反射材质了 :

vec3 render( in vec3 ro, in vec3 rd )
{ 
    vec3 col = vec3(0, 0, 0);
    vec3 res = castRay(ro,rd) ;
    vec3 normal = calcNormal(res.x * rd + ro);
    col =  sin( vec3(0.9,0.1,0.1));
    vec3  lig = normalize( vec3(-0.4, 0.7, -0.6) );
    float dif = clamp( dot( normal, lig ), 0.0, 1.0 );
    return dif * col ;
}

diffuse preview

10. Other shading

当然除了diffue ,还有其他光照计算 这里我们一共计算了

  • vertex positon
  • vertex normal
  • vertex refect
  • light direction
  • half vector
  • ambient color
  • back Color
  • cubemap Color
  • Rim Color
  • Diffuse
vec3 render( in vec3 ro, in vec3 rd )
{ 
    vec3 col = vec3(0, 0, 0);
    vec3 res = castRay(ro,rd) ;
    vec3 pos = res.x * rd + ro ;
    vec3 normal = calcNormal(pos);
    vec3 ref = reflect( rd, normal );
    col =  sin( vec3(0.9,0.1,0.1));
    
    
    vec3  lig = normalize( vec3(-0.4, 0.7, -0.6) );
    // Half vector 
    vec3  hal = normalize( lig-rd );
    // ambient 
    float amb = clamp( 0.5+0.5*normal.y, 0.0, 1.0 );
    // back 
    float bac = clamp( dot( normal, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
    // cube map rel
    float dom = smoothstep( -0.2, 0.2, ref.y );
    // rim 
    float fre = pow( clamp(1.0+dot(normal,rd),0.0,1.0), 2.0 );
    // diffuse 
    float dif = clamp( dot( normal, lig ), 0.0, 1.0 );
    
    return vec3(fre);

}

可以逐个的输出测试:

ambient preview

backColor preview

cubemap preview

rim preview

11. plane 地面

我们在castray的时候指定一个范围来当作地面

    float tmax = Max_Dist ;
    float tp1 = (0.0-ro.y)/rd.y;
    if( tp1>0.0 )
    {
        tmax = min( tmax, tp1 );
        res = vec3( tp1, 1.0 ,1.0);
    }

normal vector preview

12. 材质分离

我们需要一种方法来区分不同的问题,地板还是sphere? 所以我们把map函数改一下,我们增加一个通道来区分他们

vec2 map( in vec3 pos )
{
    return vec2(sdSphere(  pos, 1.0 ),40) ;
}

第二个通道随便指定一个值就行

此外我们就很多地方需要修改一下 calcNormal函数中返回指定x通道 castRay函数中,判断指定x通道,res指定y通道

    for( int i = 0 ; i < Max_Step && t < Max_Dist ; i++ )
    {
        vec2 h = map( ro+rd*t );
        if( abs(h.x)<( Surf_Dist * t ))
        { 
            res = vec3(t,h.y,1.0); 
            break;
         }
         t += h.x;
    }
    return res;

同样报错的地方,比如法线计算,我们也要指定到x通道

这样我们就可以根据res的y通道来区分材质了

vec3 render( in vec3 ro, in vec3 rd )
{ 
    vec3 col = vec3(0, 0, 0);
    vec3 res = castRay(ro,rd) ;
    float m = res.y;
    vec3 pos = res.x * rd + ro ;
    vec3 normal = calcNormal(pos);
    vec3 ref = reflect( rd, normal );
    // light dir 
    vec3  lig = normalize( vec3(-0.4, 0.7, -0.6) );
    // Half vector 
    vec3  hal = normalize( lig-rd );
    // ambient 
    float amb = clamp( 0.5+0.5*normal.y, 0.0, 1.0 );
    // back 
    float bac = clamp( dot( normal, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
    // cube map rel
    float dom = smoothstep( -0.2, 0.2, ref.y );
    // rim 
    float fre = pow( clamp(1.0+dot(normal,rd),0.0,1.0), 2.0 );
    // diffuse 
    float dif = clamp( dot( normal, lig ), 0.0, 1.0 );

    if( m>-0.5 ){
    
        if(m<1.5)
        	return vec3(1.0,1.0,1.0);
    	col =  sin( vec3(0.9,0.1,0.1));
    
    }
    return col ;
}

normal vector preview

13. plane Checkboard

同样来自iq大神的文章 checkerfiltering

// 
float checkersGradBox( in vec2 p )
{
    // filter kernel
    vec2 w = fwidth(p) + 0.001;
    // analytical integral (box filter)
    vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w;
    // xor pattern
    return 0.5 - 0.5*i.x*i.y;                  
}

在地板选择材质中调用他 :

        if(m<1.5)
        {  
            float f = checkersGradBox( 5.0*pos.xz );
            col = 0.3 + f*vec3(0.1);
            return col ;
        }

我们在更新一下球:

vec2 map( in vec3 pos )
{
    return vec2(sdSphere(  pos - vec3(0.0,0.25,0.0), 0.25 ),40) ;
}

normal vector preview

14. 计算AO

float calcAO( in vec3 pos, in vec3 nor )
{
	float occ = 0.0;
    float sca = 1.0;
    for( int i=0; i<5; i++ )
    {
        float hr = 0.01 + 0.12*float(i)/4.0;
        vec3 aopos =  nor * hr + pos;
        float dd = map( aopos ).x;
        occ += -(dd-hr)*sca;
        sca *= 0.95;
    }
    return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y);
}
    // lighting
    float occ = calcAO( pos, normal );

normal vector preview

15.重置render函数

我们回过头去重新看代码,我重新理了一下render函数,使他看起来更加流畅:

vec3 render( in vec3 ro, in vec3 rd )
{ 
    vec3 col = vec3(0.7, 0.9, 1.0) + rd.y * 0.8 ;
    vec3 res = castRay(ro,rd) ;
    float t = res.x;
	float m = res.y;    
    if( m>-0.5 ){

      vec3 pos = t * rd + ro ;
      vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos );
      vec3 ref = reflect( rd, nor );

      col = 0.45 + 0.35*sin( vec3(0.05,0.08,0.10)*(m-1.0) );
      if( m<1.5 )
      {
          
          float f = checkersGradBox( 5.0*pos.xz );
          col = 0.3 + f*vec3(0.1);
      }
        // lighting
      float occ = calcAO( pos, nor );
	    vec3  lig = normalize( vec3(-0.4, 0.7, -0.6) );
      vec3  hal = normalize( lig-rd );
	    float amb = clamp( 0.5+0.5*nor.y, 0.0, 1.0 );
      float dif = clamp( dot( nor, lig ), 0.0, 1.0 );
      float bac = clamp( dot( nor, normalize(vec3(-lig.x,0.0,-lig.z))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0);
      float dom = smoothstep( -0.2, 0.2, ref.y );
      float fre = pow( clamp(1.0+dot(nor,rd),0.0,1.0), 2.0 );
      
    }
   return col ;
}

16. 组合颜色

      	vec3 lin = vec3(0.0);
        lin += 1.40*dif*vec3(1.00,0.80,0.55);
        lin += 0.20*amb*vec3(0.40,0.60,1.00)*occ;
        lin += 0.40*dom*vec3(0.40,0.60,1.00)*occ;
        lin += 0.50*bac*vec3(0.25,0.25,0.25)*occ;
        lin += 0.25*fre*vec3(1.00,1.00,1.00)*occ;
        col = col*lin;
        col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) );
        

最后返回颜色时,最好clamp一下

normal vector preview

17.计算高光

		float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0)*
                    dif *
                    (0.04 + 0.96*pow( clamp(1.0+dot(hal,rd),0.0,1.0), 5.0 ));
        
        col = col*lin;
		// 合并高光 
        col += 9.00*spe*vec3(1.00,0.90,0.70);
        col = mix( col, vec3(0.8,0.9,1.0), 1.0-exp( -0.0002*t*t*t ) );

18.计算shadow

const float maxHei = 0.8;
#define ZERO (min(iFrame,0))
float calcSoftshadow( in vec3 ro, in vec3 rd, in float mint, in float tmax )
{
    // bounding volume
    float tp = (maxHei-ro.y)/rd.y; if( tp>0.0 ) tmax = min( tmax, tp );

    float res = 1.0;
    float t = mint;
    for( int i=0; i<16; i++ )
    {
		float h = map( ro + rd*t ).x;
        res = min( res, 8.0*h/t );
        t += clamp( h, 0.02, 0.10 );
        if( res<0.005 || t>tmax ) break;
    }
    return clamp( res, 0.0, 1.0 );
}

在render中合并 :

      dif *= calcSoftshadow( pos, lig, 0.02, 2.5 );
      dom *= calcSoftshadow( pos, ref, 0.02, 2.5 );

19. 修正天空

在castray 初始化时:

vec3 res = vec3(0.0,-1.0,0.0);

20.抗锯齿

定义抗锯齿等级

#define AA 3   // make this 2 or 3 for antialiasing

在主循环中:

#if AA>1
    for( int m=ZERO; m<AA; m++ )
    for( int n=ZERO; n<AA; n++ )
    {
        // pixel coordinates
        vec2 o = vec2(float(m),float(n)) / float(AA) - 0.5;
        vec2 p = (-iResolution.xy + 2.0*(gl_FragCoord.xy + o))/iResolution.y;
#else    
        vec2 p = (-iResolution.xy + 2.0*gl_FragCoord.xy )/iResolution.y;
#endif

        // ray direction
        vec3 rd = ca * normalize( vec3(p.xy,2.0) );

        // render	
        vec3 col = render( ro, rd );

		// gamma
        col = pow( col, vec3(0.4545) );

        tot += col;
#if AA>1
    }
    tot /= float(AA*AA);
#endif
	gl_FragColor = vec4( tot, 1.0 );
    

Releases

No releases published

Packages

 
 
 

Languages