three.js使用Shadertoy的着色器

three.js使用Shadertoy的着色器

shadertor上有许多非常有趣的着色器,我们可以借鉴并用在自己的three的项目上。

1 在shadertoy上新建一个着色器,默认有一个主函数,如下图所示。
在这里插入图片描述

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);
}

我们先修改这个基本的demo,然后再去借鉴别人的着色器。

观察一下shadertoy的着色器代码,发现和three中常用的有些区别:

  1. 没有顶点着色器
  2. 没有main函数,而是mainImage。
  3. 没有gl_FragColor,而是fragColor 。
  4. iTime 和 iResolution 、fragCoord的问题

处理方式:
1. 没有顶点着色器,这问题不大,先不关心。
2. 把mainImage改造成main函数,out和in的两个参数就不要了。
3. 把fragColor 换成 gl_FragColor。
4. iTime是一个变量(shadertoy自带的变量),在three中可以通过uniform的方式传入。
5. iResolution是canvas的分辨率,因为shadertoy是‘满屏’显示的,相当于three中用了正交相机然后用一个平面铺满整个canvas的。在three中,可以设置成新建的平面的分辨率。同时fragCoord我们对应的也改成uv的坐标。这样我们就需要引入顶点着色器了,然后传入uv给片元着色器。

关于着色器内置变量:

顶点着色器可用的内置变量如下表:

名称类型描述
gl_Colorvec4输入属性-表示顶点的主颜色
gl_SecondaryColorvec4输入属性-表示顶点的辅助颜色
gl_Normalvec3输入属性-表示顶点的法线值
gl_Vertexvec4输入属性-表示物体空间的顶点位置
gl_MultiTexCoordnvec4输入属性-表示顶点的第n个纹理的坐标
gl_FogCoordfloat输入属性-表示顶点的雾坐标
gl_Positionvec4输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
gl_ClipVertexvec4输出坐标,用于用户裁剪平面的裁剪
gl_PointSizefloat点的大小
gl_FrontColorvec4正面的主颜色的varying输出
gl_BackColorvec4背面主颜色的varying输出
gl_FrontSecondaryColorvec4正面的辅助颜色的varying输出
gl_BackSecondaryColorvec4背面的辅助颜色的varying输出
gl_TexCoord[]vec4纹理坐标的数组varying输出
gl_FogFragCoordfloat雾坐标的varying输出

片段着色器的内置变量如下表:

名称类型描述
gl_Colorvec4包含主颜色的插值只读输入
gl_SecondaryColorvec4包含辅助颜色的插值只读输入
gl_TexCoord[]vec4包含纹理坐标数组的插值只读输入
gl_FogFragCoordfloat包含雾坐标的插值只读输入
gl_FragCoordvec4只读输入,窗口的x,y,z和1/w
gl_FrontFacingbool只读输入,如果是窗口正面图元的一部分,则这个值为true
gl_PointCoordvec2点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。
gl_FragData[]vec4使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
gl_FragColorvec4输出的颜色用于随后的像素操作
gl_FragDepthfloat输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替
  var vertexShader = `
       varying vec2 vUv;
       void main(){
           vUv = uv;
           gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
       }
   `;
   var fragmentShader = `
        varying vec2 vUv;
        uniform float iTime;
        uniform vec3 iResolution;
        void main(){
               vec3 col = 0.5 + 0.5*cos(iTime+vUv.xyx+vec3(0,2,4));
               gl_FragColor = vec4(col,1.0);
           }
   `;
   var uniforms = {
       iTime:{value:0},
       iResolution:  { value: new THREE.Vector3( 40,40,1) },
   };
   var shadertoy = new THREE.ShaderMaterial({
       vertexShader,
       fragmentShader,
       uniforms,
       side:2,
   });
   var plane = new THREE.Mesh(planeGeo, shadertoy);
   setInterval(()=>{
                uniforms.iTime.value += 0.1;
            },20);

运行结果如下:
在这里插入图片描述
修改原理我们已经理解了,下面找个复杂点的例子试一下。

先来个这个吧:https://www.shadertoy.com/view/wtfXW2 ,看起来像细胞分裂。
在这里插入图片描述
shadertoy源码

const int n = 800;
const float rate = 7.;
const float lineThickness = 2.2;
const float colours = 0.05; // proportion of cells to colour in
const bool zoom = true;

const float phi = 1.6180339887498948;
const float tau = 6.2831853071795865;

void mainImage( out vec4 fragColour, in vec2 fragCoord )
{
    vec2 uv = (fragCoord-iResolution.xy*.5)/iResolution.y;
    float penOut = lineThickness/iResolution.y;
    float penIn = (lineThickness-2.8)/iResolution.y;

    float t = iTime*rate;
    
    fragColour = vec4(0,0,0,1);

    float scale = sqrt(float(n));
    if ( zoom ) scale = min( scale, pow((iTime+7.)*rate*.5,.6) ); // keep the edgemost points in shot as we zoom
    
    float closest = 1e38;
    float closest2 = 1e38;
    for ( int i=0; i < n; i++ )
    {
        float f = float(i);
        f += fract(t);
        float r = sqrt(f/128.);
        r *= 13./scale;
        float a = fract((f-t)*phi)*tau;
        vec2 pos = r*vec2(sin(a),cos(a));
        
        vec3 col = sin(vec3(3,1,6)*(float(i)-floor(t)))*.5+.5;
        if ( fract(col.y*64.) > colours ) col = vec3(1);

        float l = length(pos-uv);

        // add a ring to help me track size (so it doesn't look like we're zooming out)
        //col *= smoothstep(penIn,penOut,abs(l/scale-.001)*scale);
		
        if ( i == 0 ) l += smoothstep(1.,0.,fract(t))*1.2/scale; // grow the new point
		if ( l < closest )
        {
            if ( closest < closest2 ) closest2 = closest;
            closest = l;
			fragColour.rgb = col; // *(1.-l*sqrt(float(n)));
        }
        else if ( l < closest2 )
        {
            closest2 = l;
        }
        fragColour.rgb = mix(fragColour.rgb,vec3(0),smoothstep(penOut,penIn,length(pos-uv)));
    }
    
    // cell borders
    fragColour.rgb *= smoothstep(penIn,penOut,(closest2-closest));//*scale);
}

修改后的代码:

var vertexShader = `
varying vec2 vUv;
void main(){
   vUv = uv;
   gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
var fragmentShader = `
const int n = 800;
const float rate = 7.;
const float lineThickness = 2.2;
const float colours = 0.05; // proportion of cells to colour in
const bool zoom = true;

const float phi = 1.6180339887498948;
const float tau = 6.2831853071795865;
varying vec2 vUv;
uniform vec3 iResolution;
uniform float iTime;
void main()
{
   vec2 uv = vUv-0.5;
   float penOut = lineThickness/iResolution.y;
   float penIn = (lineThickness-2.8)/iResolution.y;

   float t = iTime*rate;

   gl_FragColor = vec4(0,0,0,1);

   float scale = sqrt(float(n));
   if ( zoom ) scale = min( scale, pow((iTime+7.)*rate*.5,.6) ); // keep the edgemost points in shot as we zoom

   float closest = 1e38;
   float closest2 = 1e38;
   for ( int i=0; i < n; i++ )
   {
       float f = float(i);
       f += fract(t);
       float r = sqrt(f/128.);
       r *= 13./scale;
       float a = fract((f-t)*phi)*tau;
       vec2 pos = r*vec2(sin(a),cos(a));

       vec3 col = sin(vec3(3,1,6)*(float(i)-floor(t)))*.5+.5;
       if ( fract(col.y*64.) > colours ) col = vec3(1);

       float l = length(pos-uv);

       // add a ring to help me track size (so it doesn't look like we're zooming out)
       //col *= smoothstep(penIn,penOut,abs(l/scale-.001)*scale);

       if ( i == 0 ) l += smoothstep(1.,0.,fract(t))*1.2/scale; // grow the new point
       if ( l < closest )
       {
           if ( closest < closest2 ) closest2 = closest;
           closest = l;
           gl_FragColor.rgb = col; // *(1.-l*sqrt(float(n)));
       }
       else if ( l < closest2 )
       {
           closest2 = l;
       }
       gl_FragColor.rgb = mix(gl_FragColor.rgb,vec3(0),smoothstep(penOut,penIn,length(pos-uv)));
   }

   // cell borders
   gl_FragColor.rgb *= smoothstep(penIn,penOut,(closest2-closest));//*scale);
}
`;
var uniforms = {
	iTime:{value:0},
	iResolution:  { value: new THREE.Vector3( 100,100,1) },
};
var shadertoy = new THREE.ShaderMaterial({
	vertexShader,
	fragmentShader,
	uniforms,
	side:2,
});          

var box = new THREE.Mesh(
	new THREE.BoxGeometry(100,100,100),
	shadertoy
);
scene.add(box);
setInterval(()=>{
	uniforms.iTime.value += 0.1;
},100);

运行的效果:
在这里插入图片描述
看起来不错吧,这次我把效果应用在立方体上了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X01动力装甲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值