OpenGL Shader-常见特效毛刺,故障风,灵魂出窍的实现

880 阅读3分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

毛刺

毛刺效果实现原理是通过修改纹理坐标产生随机偏移量。但是产生偏移左右偏移的量是微小的并且是个别y轴上的像素需要发生偏移。因此需要设置一个随机数生成器,设定一个闸值,这个闸值应该是比较小的并且小于闸值才能进行偏移。

const float PI = 3.1415926;

float rand(float n) {
    return fract(sin(n) * 43758.5453123);
}

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    //最大抖动上限
    float maxJitter = 0.06;
    //一次毛刺效果的时长
    float duration = 0.3;
    //红色颜色偏移
    float colorROffset = 0.01;
    //绿色颜色偏移
    float colorBOffset = -0.025;

    float time = mod(iTime, duration * 2.0);
    //振幅,随着时间变化,范围是[0, 1]    
    float amplitude = max(sin(time * (PI / duration)), 0.0);
    //像素随机偏移范围 [-1,1]
    float jitter = rand(uv.y) * 2.0 - 1.0; // -1~1
    //判断是否需要偏移,如果jitter范围 < 最大范围*振幅
    bool needOffset = abs(jitter) < maxJitter * amplitude;

    float textureX = uv.x + (needOffset ? jitter : (jitter * amplitude * 0.006));
    vec2 textureCoords = vec2(textureX, uv.y);

    vec4 mask = texture(iChannel1, textureCoords);
    vec4 maskR = texture(iChannel1, textureCoords + vec2(colorROffset * amplitude, 0.0));
    vec4 maskB = texture(iChannel1, textureCoords + vec2(colorBOffset * amplitude, 0.0));

    gl_FragColor = mask;
}

毛刺效果同时增加上色差效果,最终呈现效果为最佳。

    vec4 mask = texture(iChannel1, textureCoords);
    vec4 maskR = texture(iChannel1, textureCoords + vec2(colorROffset * amplitude, 0.0));
    vec4 maskB = texture(iChannel1, textureCoords + vec2(colorBOffset * amplitude, 0.0));
    gl_FragColor = vec4(maskR.r, mask.g, maskB.b, mask.a);
11b25138179f4efa1e5354f12ed7a4fb.gifVideo_20220204_050744_955.gif

灵魂出窍

灵魂出窍效果实现原理是通过两个纹理叠加,根据时间上层纹理做缩放并且不断变化其不透明度来逐渐显现。之前在缓动函数介绍中已经知道如何实现缩放效果,灵魂出窍效果就是在其基础之上再多个纹理对象叠加就能够实现了。

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    // 持续时间
    float duration = 0.7;
    // 最大透明度
    float maxAlpha = 0.4;
    // 最大缩放系数
    float maxScale = 1.8;
    // 时间
    float progress = mod(iTime, duration) / duration; // 0~1
    // 当前透明度
    float alpha = maxAlpha * (1.0 - progress);
    // 当前缩放值
    float scale = 1.0 + (maxScale - 1.0) * progress;
    // 中心位置
    float weakX = 0.5 + (uv.x - 0.5) / scale;
    float weakY = 0.5 + (uv.y - 0.5) / scale;
    vec2 weakTextureCoords = vec2(weakX, weakY);
    // 放大后的纹理
    vec4 weakMask = texture(iChannel1, weakTextureCoords);
    vec4 mask = texture(iChannel1, uv);
    // 纹理叠加
    gl_FragColor = mask * (1.0 - alpha) + weakMask * alpha;
}

ed73df8cefcfa59cf4306c9e7f46ac2f.gif

故障

故障效果其实有点像是毛刺效果的衍生。毛刺效果偏移数较多且每个偏移高度都比较小,故障效果的偏移高度会更大一些。除此之外毛刺采用一维随机函数而故障效果是利用二维随机函数。同样是通过随机函数生成需要被偏移的Y轴并且通过闸值函数判断是否进行偏移操作。

//2D随机数生成器
float random2d(vec2 n) { 
    return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}

float randomRange (in vec2 seed, in float min, in float max) {
		return min + random2d(seed) * (max - min);
}

// 判断闸值函数 是否在故障内
float insideRange(float v, float bottom, float top) {
   return step(bottom, v) - step(top, v);
}

float AMT = 0.1; //故障数 0-1. 最大10
float SPEED = 0.2; //0 - 1 speed

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    vec3 outCol = texture(iChannel1, uv).rgb;

    float time = floor(iTime * SPEED * 60.0);    
    float maxOffset = AMT/2.0;
    vec2 uvOff = uv;
    float hOffset = 0.;
    for (float i = 0.0; i < 10.0 * AMT; i += 1.0) {
        float sliceY = random2d(vec2(time , 2345.0 + float(i)));
        float sliceH = random2d(vec2(time , 9035.0 + float(i))) * 0.25;
        hOffset = randomRange(vec2(time , 9625.0 + float(i)), -maxOffset, maxOffset);
        uvOff.x += hOffset;
        // 判断是否在故障内 在故障中就需要对x轴做偏移操作
        if (insideRange(uv.y, sliceY, fract(sliceY+sliceH)) == 1.0){
        	outCol = texture(iChannel1, uvOff).rgb;
        }
    }
    gl_FragColor = vec4(outCol,1.0);
}

只有故障偏移好像看起来效果并不凸显,同时再结合色差偏移达到特效最佳效果。

outCol.r = texture(iChannel1, uvOff + vec2(colorROffset + hOffset, 0.0)).r;
outCol.b = texture(iChannel1, uvOff + vec2(colorBOffset + hOffset, 0.0)).b;
Video_20220204_050750_79.gif5e4833a88b8f34c052d9b1bc725fe63c.gif