Skip to content

Instantly share code, notes, and snippets.

@xkisu
Created March 3, 2023 09:19
Show Gist options
  • Save xkisu/01188a68d5465b1a15c06cf119d012f7 to your computer and use it in GitHub Desktop.
Save xkisu/01188a68d5465b1a15c06cf119d012f7 to your computer and use it in GitHub Desktop.
PixiJS smoke effect in React
import {BlurFilter, Filter, RENDERER_TYPE, TextStyle} from 'pixi.js';
import {Container, Sprite, Stage, Text, useApp, useTick} from '@pixi/react';
import {PropsWithChildren, useMemo} from 'react';
// Adapted from https://gist.github.com/OmarShehata/9650b8ee419db3696ce555f10712d499
// ref: https://codepen.io/omarshe7ta/pen/xVeWWy
// debugging ref: https://www.html5gamedevs.com/topic/44542-need-help-of-migrating-shader-from-v4-to-v5/
// https://www.pixiplayground.com/#/edit/6ThqOOz-SVJe3AyvkbnaU
// https://github.com/pixijs/pixijs/wiki/v5-Creating-filters
const fragShader = `#ifdef GL_ES
precision mediump float;
#endif
// Cant be named 'resolution' since it's a default uniform
// provided by PIXI.Filter(), see: https://pixijs.download/dev/docs/PIXI.Filter.html
// And the PIXI's resolution uniform is the ratio of screen (CSS) pixels to real pixels.
uniform vec2 dimensions;
uniform float time;
uniform float alpha;
uniform vec2 speed;
uniform float shift;
float rand(vec2 n) {
//This is just a compounded expression to simulate a random number based on a seed given as n
return fract(cos(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
float noise(vec2 n) {
//Uses the rand function to generate noise
const vec2 d = vec2(0.0, 1.0);
vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
}
float fbm(vec2 n) {
// fbm stands for 'Fractal Brownian Motion',
// see more in: https://thebookofshaders.com/13/
// https://en.wikipedia.org/wiki/Fractional_Brownian_motion
float total = 0.0, amplitude = 1.0;
for (int i = 0; i < 4; i++) {
total += noise(n) * amplitude;
n += n;
amplitude *= 0.5;
}
return total;
}
void main() {
//This is where our shader comes together
const vec3 c1 = vec3(126.0/255.0, 0.0/255.0, 97.0/255.0);
const vec3 c2 = vec3(173.0/255.0, 0.0/255.0, 161.4/255.0);
const vec3 c3 = vec3(0.2, 0.0, 0.0);
const vec3 c4 = vec3(164.0/255.0, 1.0/255.0, 214.4/255.0);
const vec3 c5 = vec3(0.1);
const vec3 c6 = vec3(0.9);
//This is how "packed" the smoke is in our area. Try changing 8.0 to 1.0, or something else
vec2 p = gl_FragCoord.xy * 8.0 / dimensions.xx;
//The fbm function takes p as its seed (so each pixel looks different) and time (so it shifts over time)
float q = fbm(p - time * 0.1);
vec2 r = vec2(fbm(p + q + time * speed.x - p.x - p.y), fbm(p + q - time * speed.y));
vec3 c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y);
float grad = gl_FragCoord.y / dimensions.y;
gl_FragColor = vec4(c * cos(shift * gl_FragCoord.y / dimensions.y), 1.0);
// gl_FragColor.xyz *= 1.0-grad;
}
`
/*
#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
void main() {
gl_FragColor = vec4(abs(sin(time)),0.0,0.0,1.0);
}*/
function SmokeContainer ({ children }: PropsWithChildren<{}>) {
const app = useApp();
if (app.renderer.type == RENDERER_TYPE.WEBGL) {
console.log("using webgl")
} else {
console.log("using canvas")
}
// let scale = [1,1]
app.renderer.resize(window.innerWidth, window.innerHeight);
// window.onresize = function() {
// console.log('resized')
// // app.renderer.resize(window.innerWidth, window.innerHeight);
// // app.resize()
// }
const smokeShader = useMemo(() => new Filter(undefined, fragShader, {
dimensions: [app.renderer.width, app.renderer.height],
alpha: 1.0,
shift: 1.6,
time: 1,
speed: { x: 0.7, y: 0.4 }
}), []);
var count = 0
useTick(delta => {
count += 0.01 // *delta
smokeShader.uniforms.time = count;
})
return (
<Sprite
image="/pixi_v3_github-pad.png"
anchor={[0,0]}
width={app.renderer.width}
height={app.renderer.height}
filters={[smokeShader]}
/>
)
}
export default function SmokeBackground () {
const blurFilter = useMemo(() => new BlurFilter(4), []);
return (
<Stage options={{
backgroundColor: 0xffffff,
backgroundAlpha: 0,
antialias: true,
resizeTo: window
}}>
<SmokeContainer />
<Container>
<Sprite
image="https://pixijs.io/pixi-react/img/bunny.png"
x={400}
y={270}
anchor={{ x: 0.5, y: 0.5 }}
filters={[blurFilter]}
/>
<Text
text="Hello World"
anchor={{ x: 0.5, y: 0.5 }}
style={new TextStyle({
fill: 'white'
})}
/>
</Container>
</Stage>
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment