Posts mit dem Label Sinus werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Sinus werden angezeigt. Alle Posts anzeigen

3/11/2016

Up to 1.572.864 Particles with custom Displacement Shader

Bei diesem Experiment habe ich seit längerer Pause mal wieder mit ThreeJS gearbeitet.
Nach den ganzen Canvas Experimenten zuletzt war es mal wieder an der Zeit auf die volle Leistung von WebGL Zugriff zu haben.
Ich habe dazu mein „Distorted Sphere“ Canvas Experiment für ThreeJS portiert.
Wo bei den Canvas Experimenten bei 50.000 bis maximal 100.000 Partikeln Schluss war, geht es nun auch mit bis zu 1.572.864 Partikeln, und dass bei 60FPS (auf einer flotten Maschine).
Ich habe dazu meine ersten (Displacement Particle) Shader geschrieben, damit die ganzen Berechnungen nicht auf der CPU, sondern der GPU berechnet werden. Die beiden Shader (vertex und fragment) sind bestimmt noch nicht perfekt, aber ich stell die euch hier weiter unten zur freien Verfügung. 


Die Partikel habe ich mit Hilfe der „Points“-Klasse, dem ShaderMaterial und der BufferGeometry von ThreeJS realisiert. Auch hier gibt es die entscheidenen Code-Schnipsel weiter unten mit dazu. Die Berechnung, bzw. Anordnung, der Partikel wird hier nicht näher erklärt, aber dafür seht ihr wie ihr Partikel adden könnt und was nötig ist um diese mit den beiden Shadern zu animieren.
 

Wie immer könnt ihr über das Menü diverse Einstellungen vornehmen und so das Experiment aktiv beeinflussen. Die Steuerung erfolgt per Maus (Klick + Drag und Srollrad) oder Touch-Gesten.

Hier geht es direkt zum Experiment. Besonders zu empfehlen für Mobile User/Touch Devices!
Der Fullscreen-Modus funktioniert im Moment leider nur im Chrome Browser fehlerfrei.


Hier die beiden Shader (vertex und fragment):
uniform float size;

varying float distance;
uniform float distanceMax;

uniform float amplitude;
uniform float d;
uniform float pi;

varying vec3 v;

void main() {

    float x = position.x;
    float y = position.y;
    float z = position.z;

    distance = cos( x * d * pi ) * cos( y * d * pi ) * cos( z * d * pi ) * amplitude;

    v = vec3( x, y, z );
    v = normalize( v );
    v *= distance;

    vec3 pos = position + v;

    gl_PointSize = size;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( pos, 1.0 );

}
varying float distance;
uniform float distanceMax;

uniform int colorScheme;

void main() {

    float percent = ( distance + distanceMax ) / ( distanceMax * 2.0 );

    if ( colorScheme == 0 ) {

        gl_FragColor = vec4( 1.0, percent, 0.0, 1.0 );

    } else if ( colorScheme == 1 ) { 

        gl_FragColor = vec4( 1.0 - percent, 1.0 - percent, 1.0 - percent, 1.0 );

    } else if ( colorScheme == 2 ) { 

        gl_FragColor = vec4( 0.0, percent, 1.0 - percent, 1.0 );

    } else if ( colorScheme == 3 ) { 

        gl_FragColor = vec4( percent, 0.0, 0.0, 1.0 );

    } else if ( colorScheme == 4 ) { 

        gl_FragColor = vec4( 0.0, percent, 0.0, 1.0 );

    } else if ( colorScheme == 5 ) { 

        gl_FragColor = vec4( 0.0, 0.0, percent, 1.0 );

    } else if ( colorScheme == 6 ) { 

        gl_FragColor = vec4( percent, 0.0, 1.0 - percent, 1.0 );

    } else if ( colorScheme == 7 ) { 

        gl_FragColor = vec4( 1.0 - percent, percent, percent, 1.0 );

    }

}
Und hier die entscheidene Code Passage zum erstellen der Partikel:
var geometry = new THREE.BufferGeometry();

var positions = new Float32Array( particles * 3 );

var index = 0;

for ( var i = 0, l = positions.length; i < l; i += 3 ) {

    // positions

    var phi = Math.acos( -1 + ( 2 * index ) / particles );
    var theta = Math.sqrt( particles * Math.PI ) * phi;

    var x = radius * Math.cos( theta ) * Math.sin( phi );
    var y = radius * Math.sin( theta ) * Math.sin( phi );
    var z = radius * Math.cos( phi );

    positions[ i     ] = x;
    positions[ i + 1 ] = y;
    positions[ i + 2 ] = z;

    index++;

}

geometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );

uniforms = {

    amplitude: { type: 'f', value: 0 },
    d: { type: 'f', value: d },
    pi: { type: 'f', value: Math.MATHPI180 },
    distanceMax: { type: 'f', value: sinusMaxAmplitude },
    size: { type: 'f', value: particleSize },
    colorScheme: { type: 'i', value: colorScheme }

}

var shaderMaterial = new THREE.ShaderMaterial( {

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

} );

points = new THREE.Points( geometry, shaderMaterial );

scene.add( points );
An dieser Stelle wird der Wert für die Amplitude in der render Function an die Shader übergeben:
uniforms.amplitude.value = sinusAmplitude;

1/25/2016

Sinus Landscape

Und wieder ein kleines Experiment.
Mit Hilfe der Sinus-, oder der Cosinusfunktion wird hier eine Art Landschaft erstellt. Über diverse Parameter lässt sich das Erscheinungsbild der Landschaft frei konfigurieren.
Per Mausklick könnt ihr zwischen verschiedenen Farbeschemen switchen. Die Rotation der Landschaft ist, wie bei den älteren Experimenten hier, auch wieder abhängig von der Mausposition. Aber nicht nur die Rotation, sondern auch die Position der Landschaft selbst ist abhängig von der Mausposition.
Für dieses Experiment wieder mit im Programm, das z-Sorting. Je nach Ausgabegerät solltet ihr das z-Sorting aber nicht bei mehr als 5000 Partikeln aktivieren.  Das Sortieren kostet einfach noch immer sehr viel Performance. Vielleicht finde ich hier noch eine bessere Lösung, aber bis dahin muss es so gehen. Falls jemand Ideen oder Vorschläge zu dem Thema hat gerne hier in den Kommentaren schreiben. Weiter unten gibt´s die Function mit der ich das z-Sorting  erledige.
Schaut euch das Experiment direkt hier an, oder, speziell wenn ihr mit einem mobilen Gerät unterwegs seid, folgt bitte diesem Link hier. Viel Spaß.



Und hier noch die Function die für das z-Sorting verantwortlich ist. Das Array particleHolder beinhaltet alle Partikel die es darzustellen gilt, bzw. enthält die Partikel die sortiert werden müssen:
model.particleHolder = model.particleHolder.sort( function( a, b ) {

    return ( b.vectorPosition.z - a.vectorPosition.z );

} );