Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert THREE.js Shader to PIXI.js

I'm new to both PIXI.js and custom Shaders so a little out of my depth here..

There is a GLSL Shader ( courtesy of DonKarlssonSan ) which I want to convert to PIXI.js to compare performance, any help would be hugely appreciated!

var container;
var camera, scene, renderer;
var uniforms;
var startTime;

init();
animate();

function init() {
  container = document.getElementById('container');

  startTime = Date.now();
  camera = new THREE.Camera();
  camera.position.z = 1;

  scene = new THREE.Scene();

  var geometry = new THREE.PlaneBufferGeometry(16, 9);

  uniforms = {
    iGlobalTime: { type: "f", value: 1.0 },
    iResolution: { type: "v2", value: new THREE.Vector2() }
  };

  var material = new THREE.ShaderMaterial( {

    uniforms: uniforms,
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent

  } );

  var mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  renderer = new THREE.WebGLRenderer();
  container.appendChild(renderer.domElement);

  onWindowResize();

  window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize(event) {
  uniforms.iResolution.value.x = window.innerWidth;
  uniforms.iResolution.value.y = window.innerHeight;

  renderer.setSize(window.innerWidth, window.innerHeight);
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  var currentTime = Date.now();
  uniforms.iGlobalTime.value = (currentTime - startTime) * 0.0005;
  renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>

<div id="container"></div>

<script id="vertexShader" type="x-shader/x-vertex">
void main() { gl_Position = vec4(position, 1.0); }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 iResolution;
uniform float iGlobalTime;
vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }

float snoise(vec2 v){
    const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                        -0.577350269189626, 0.024390243902439);
    vec2 i  = floor(v + dot(v, C.yy) );
    vec2 x0 = v -   i + dot(i, C.xx);
    vec2 i1;
    i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    vec4 x12 = x0.xyxy + C.xxzz;
    x12.xy -= i1;
    i = mod(i, 289.0);
    vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
                      + i.x + vec3(0.0, i1.x, 1.0 ));
    vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
                            dot(x12.zw,x12.zw)), 0.0);
    m = m*m ;
    m = m*m ;
    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;
    m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
    vec3 g;
    g.x  = a0.x  * x0.x  + h.x  * x0.y;
    g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    return 130.0 * dot(m, g);
}

void main(void)
{
  vec2 uv = gl_FragCoord.xy / iResolution.xy;
  float xnoise = snoise(vec2(uv.x, iGlobalTime / 5.0 + 10000.0));
  float ynoise = snoise(vec2(uv.y, iGlobalTime / 5.0 + 500.0));
  vec2 t = vec2(xnoise, ynoise);
  float s1 = snoise(uv + t / 2.0 + snoise(uv + snoise(uv + t/3.0) / 5.0));
  float s2 = snoise(uv + snoise(uv + s1) / 7.0);
  vec3 hsv = vec3(s1, 1.0, 1.0-s2);
  vec3 rgb = hsv2rgb(hsv);
  gl_FragColor = vec4(rgb, 1.0);
}
</script>

JSFiddle: https://jsfiddle.net/9t0ayrmh/1/

*I can post my efforts thus far if needs be, but essentially comprises a PIXI.js template which is all functional, however connecting the shaders to the filters is where I fall short.

Thanks!

like image 988
Cult Digital Avatar asked Oct 09 '19 23:10

Cult Digital


1 Answers

There is not need to convert the GLSL code. You can use exactly the same shader. Just crate a PIXI.Filter and set the values of the uniforms iResolution and iGlobalTime. The filter has to be applied to the PIXI.Container. e.g.:

let app = new PIXI.Application({width : window.innerWidth, height : window.innerHeight});
app.resizeTo = window;

let fragmentShader = document.getElementById('fragmentShader').textContent;

let filter = new PIXI.Filter(null, fragmentShader);
filter.uniforms.iResolution = [app.screen.width, app.screen.height];
filter.uniforms.iGlobalTime = 0.0;

let container = new PIXI.Container();
container.filterArea = app.screen;
container.filters = [filter];

app.stage.addChild(container);
document.body.appendChild(app.view);

Note, the vertex shader can be skipped, because the default vertex shader of the filter does the job well.

The animation and the update of the time uniform (iGlobalTime), is very similar to the update corresponding update in THREE.js. e.g.:

startTime = Date.now();
app.ticker.add(function(delta) {
    var currentTime = Date.now();
    filter.uniforms.iGlobalTime = (currentTime - startTime) * 0.0005;
});

See the example (Pixi v5.1.5):

var app = new PIXI.Application({width : window.innerWidth, height : window.innerHeight});
app.resizeTo = window;

let fragmentShader = document.getElementById('fragmentShader').textContent;

let filter = new PIXI.Filter(null, fragmentShader);
filter.uniforms.iResolution = [app.screen.width, app.screen.height];
filter.uniforms.iGlobalTime = 0.0;

let container = new PIXI.Container();
container.filterArea = app.screen;
container.filters = [filter];

app.stage.addChild(container);
document.getElementById('container').appendChild(app.view);

function onresize(event) {
    if (app.resize)
        app.resize();
    container.filterArea = app.screen;
    filter.uniforms.iResolution = [app.screen.width, app.screen.height];
}
window.addEventListener('resize', onresize, false);

startTime = Date.now();
app.ticker.add(function(delta) {
    var currentTime = Date.now();
    filter.uniforms.iGlobalTime = (currentTime - startTime) * 0.0005;
});
html, body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.8.2/pixi.min.js"></script>
<div id="container"></div>

<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 iResolution;
uniform float iGlobalTime;
vec3 hsv2rgb(vec3 c)
{
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); }

float snoise(vec2 v){
    const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                        -0.577350269189626, 0.024390243902439);
    vec2 i  = floor(v + dot(v, C.yy) );
    vec2 x0 = v -   i + dot(i, C.xx);
    vec2 i1;
    i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    vec4 x12 = x0.xyxy + C.xxzz;
    x12.xy -= i1;
    i = mod(i, 289.0);
    vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
                      + i.x + vec3(0.0, i1.x, 1.0 ));
    vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy),
                            dot(x12.zw,x12.zw)), 0.0);
    m = m*m ;
    m = m*m ;
    vec3 x = 2.0 * fract(p * C.www) - 1.0;
    vec3 h = abs(x) - 0.5;
    vec3 ox = floor(x + 0.5);
    vec3 a0 = x - ox;
    m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
    vec3 g;
    g.x  = a0.x  * x0.x  + h.x  * x0.y;
    g.yz = a0.yz * x12.xz + h.yz * x12.yw;
    return 130.0 * dot(m, g);
}

void main(void)
{
  vec2 uv = gl_FragCoord.xy / iResolution.xy;
  float xnoise = snoise(vec2(uv.x, iGlobalTime / 5.0 + 10000.0));
  float ynoise = snoise(vec2(uv.y, iGlobalTime / 5.0 + 500.0));
  vec2 t = vec2(xnoise, ynoise);
  float s1 = snoise(uv + t / 2.0 + snoise(uv + snoise(uv + t/3.0) / 5.0));
  float s2 = snoise(uv + snoise(uv + s1) / 7.0);
  vec3 hsv = vec3(s1, 1.0, 1.0-s2);
  vec3 rgb = hsv2rgb(hsv);
  gl_FragColor = vec4(rgb, 1.0);
}
</script>
like image 171
Rabbid76 Avatar answered Sep 20 '22 07:09

Rabbid76