ThreeJS:图像波动及破碎消失效果shader中的应用

图像波动及破碎消失效果

1.随机

shader中,通过fract()和sin()的结合,可以得到一个有一定规律但被打乱的曲线,如下图:

图片

上面例子中,提取了sin函数波形的小数部分,x在[-1,1]之间时返回0到1之间的正值。

通过在sin(x)的值上乘以大些的数(如:100000),这时再也区分不出sin的波形了,小数部分的粒度将sin的循环变成了伪随机的混沌。如下图:

图片

y = fract(sin(x)*100000.0)
  • 二维随机

对于二维坐标,我们需要将其转换成一维浮点数,这里使用dot()函数,其作用是根据两个向量返回一个0到1之间的值。最终二维随机函数如下:

float rand(vec2 n) {
    return fract(sin(dot(n, vec2(12.9898, 78.233))) * 43758.5453);
}

2.Value Noise

  • 简介

Value Noise是最简单的噪声算法,其主要思路是定义若干顶点且每个顶点含有一个随机值,这些顶点会根据自己的随机值对周围坐标产生影响,越靠近顶点则越容易受该顶点影响。当需求某个坐标的输出值是,需要将该坐标附近的各个顶点所造成的影响值进行叠加,从而得到一个总值并输出。

  • 原理

  1. 首先定义一个晶格结构,每个晶格的顶点有一个伪随机值(Value)。对于二维的Value噪声来说,晶格结构就是一个平面网格(通常是正方形)。
  2. 输入一个点(二维坐标),找到和他相邻的那些晶格顶点(4个顶点),得到这些顶点的伪随机值。
  3. 使用缓和曲线来计算这些伪随机值的权重和。原始的噪声使用的缓和曲线是s(t)=3t²-2t³。
  • 理解

  1. 设想有如下纹理,uv范围为0~1,通常我们需要对uv乘上一个大一点的值,使网格数量多一些,例如乘以12,图像如下:

图片


2. 现在将晶格中的每一个顶点都计算出一个随机值,用于后续的噪声计算:

图片


3. 如果我们要取出uv坐标为(2.3, 1.6)处点P的值:

图片


4. 根据P的坐标,即对当前晶格下侧的uv(2,1)到uv(3,1)求一个插值A,再对晶格上侧的uv(2,2)到uv(3,2)求一个插值B,最后在AB间求插值P即可。如下图:

图片


5. 这里我们使用线性插值:

A = 0.11 * 0.7 + 0.57 * 0.3 = 0.248;
B = 0.38 * 0.7 + 0.83 * 0.3 = 0.515;
P = 0.248 * 0.4 + 0.515 * 0.6 = 0.4082;
  1. 使用线性插值,点之间的过渡虽然平滑了,但是晶格间的过渡比较生硬。如上图所示,假设上图0.11为点M,0.57为点N。如果后面还有个点为Q,且其随机值为0.02,则这些值形成的连续图像如下:

图片


7. 从上图中可以明显看到晶格的交界处过渡生硬,所以柏林噪声之父提出了缓和曲线计算插值, 函数及其图像如下:

图片

y = 6x^5 - 15x^4 + 10x^3;

// 2002年以前使用下面这种缓和曲线,后更新为上面的曲线
// 一般情况下使用下面这种即可,可提升计算效率,上面的曲线过渡更加平滑,计算量也更大
// y = 3x^2 - 2x^3;
  1. 使用缓和曲线计算插值之后,形成的连续图像如下:

图片


9. 最终使用到的二维噪声算法如下:

float noise(vec2 p){
    //坐标的整数部分可看作晶格的顶点坐标
    //坐标的小数部分可看作晶格内的偏移坐标
    //取出坐标的整数部分
    vec2 ip = floor(p);
    //取出坐标的小数部分
    vec2 t = fract(p);
    //原始噪声使用的缓和曲线是s(t)=3t²-2t³
    // t = t * t * (3.0 - 2.0 * t);
    //使用缓和曲线对小数部分坐标进行内插
    t = t * t * t * (t * (6.0 * t - 15.0) + 10.0);

    //对A和B在v方向上内插得到最终值
    float res = mix(
        //对晶格下侧u方向进行内插得到A值
        mix(rand(ip), rand(ip + vec2(1.0,0.0)), t.x),
        //对晶格上侧u方向进行内插得到B值
        mix(rand(ip + vec2(0.0,1.0)), rand(ip + vec2(1.0,1.0)), t.x)
        ,t.y
    );
    return res;
}

3.图像破碎效果

  • 创建three对象,并传入shader
const uniforms = {
    u_time: { value: 0.0 },
    //u_texture是传入shader中的纹理
    u_texture: { value: texture },
    //u_opacity设置图像透明度
    u_opacity: { value: 1.0 },
    u_amp: { value: 0.006 },
    //u_range用来放大uv坐标
    u_range: { value: 300 },
}
const geo = new THREE.PlaneBufferGeometry(1, 1, 1, 1);
const mat = new THREE.ShaderMaterial({
    vertexShader: vs,
    fragmentShader: fs,
    uniforms: uniforms,
    transparent: true,
    side: THREE.DoubleSide,
});
const shape = new THREE.Mesh(geo, mat);
scene.add(shape);

  • shader部分主要代码
void main() {
    vec2 uv = v_uv;
    float speed = 0.1;
    //通过噪声算法获取到随机数加到uv的偏移上
    //通过u_range放大uv坐标的范围
    //noise()的值的范围是0~1,这里减去0.5保证随机值的范围在-0.5~0.5
    //通过增大u_amp实现偏移值整体放大,像素坐标错位增大,达到图片破碎的效果
    uv.x += (noise(uv * u_range + u_time * speed) - 0.5) * u_amp;
    uv.y += (noise(uv * u_range + u_time * speed) - 0.5) * u_amp;
    vec4 color = texture2D(u_texture, uv);
    //通过u_opacity控制图像的透明度
    color.a = color.a * u_opacity;

    gl_FragColor = color;
}

效果如下:

图片

  • 图像破碎消失效果:
function hide() {
    //修改u_amp使得uv偏移值增大,达到破碎效果
    TweenMax.to(uniforms.u_amp, 2, { value: 0.3 });
    //修改u_range放大uv坐标,破碎时变得更细腻
    TweenMax.to(uniforms.u_range, 0.1, { value: 400 });
    //修改u_opacity改变透明度,隐藏图像
    TweenMax.to(uniforms.u_opacity, 2, { delay: 1, value: 0.0 });
}

效果如下:

图片

  • 32
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js is a JavaScript library used for creating 3D graphics in the browser. It provides a set of built-in shaders that can be used to create various visual effects like reflections, refractions, shadows, and more. Three.js shaders are written in GLSL (OpenGL Shading Language), a C-like language used for programming graphics processing units (GPUs). To create a custom shader in Three.js, you need to define a new shader material and pass in the GLSL code as a string. Here is an example of a simple custom shader that applies a color gradient based on the object's Y position: ``` var customShader = new THREE.ShaderMaterial({ uniforms: { color1: { value: new THREE.Color(0xff0000) }, color2: { value: new THREE.Color(0x0000ff) } }, vertexShader: ` varying vec3 vPosition; void main() { vPosition = position; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform vec3 color1; uniform vec3 color2; varying vec3 vPosition; void main() { float t = (vPosition.y + 1.0) / 2.0; gl_FragColor = vec4(mix(color1, color2, t), 1.0); } ` }); var cube = new THREE.Mesh( new THREE.BoxGeometry(1, 1, 1), customShader ); scene.add(cube); ``` This shader defines two uniforms (color1 and color2) that represent the two colors of the gradient. In the vertex shader, we pass the vertex position to the fragment shader via a varying variable. In the fragment shader, we calculate the gradient value (t) based on the Y position of the vertex and use the mix() function to interpolate between the two colors. Finally, we set the output color using gl_FragColor.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值