Ho un elemento <circle>
in un documento SVG, a cui si applicano una <radialGradient>
per dare l'illusione di esso che è una sfera:rendering un'immagine come nodo Three.js con SVGRenderer (o altrimenti sfere di rendering)
<svg version="1.1" id="sphere_svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="640px" height="640px" viewBox="0 0 640 640" enable-background="new 0 0 640 640" xml:space="preserve">
<defs>
<radialGradient id="sphere_gradient" cx="292.3262" cy="287.4077" r="249.2454" fx="147.7949" fy="274.5532" gradientTransform="matrix(1.0729 0 0 1.0729 -23.3359 -23.3359)" gradientUnits="userSpaceOnUse">
<stop id="sphere_gradient_0" offset="0" style="stop-color:#F37D7F"/>
<stop id="sphere_gradient_1" offset="0.4847" style="stop-color:#ED1F24"/>
<stop id="sphere_gradient_2" offset="1" style="stop-color:#7E1416"/>
</radialGradient>
</defs>
<circle fill="url(#sphere_gradient)" cx="320" cy="320" r="320"/>
</svg>
sembra qualcosa di simile a questo:
I ca n rendono questo in un contenitore di WebGLRenderer
Three.js utilizzando Gabe Lerner's canvg
library:
/* sphere_asset is a div containing the svg element */
var red_svg_html = new String($('#sphere_asset').html());
var red_svg_canvas = document.createElement("canvas");
canvg(red_svg_canvas, red_svg_html);
var red_svg_texture = new THREE.Texture(red_svg_canvas);
var red_particles = new THREE.Geometry();
var red_particle_material = new THREE.PointCloudMaterial({
map: red_svg_texture,
transparent: true,
size: 0.15,
alphaTest: 0.10
});
var red_particle_count = 25;
for (var p = 0; p < red_particle_count; p++) {
var pX = 0.9 * (Math.random() - 0.5),
pY = 0.9 * (Math.random() - 0.5),
pZ = 0.9 * (Math.random() - 0.5),
red_particle = new THREE.Vector3(pX, pY, pZ);
red_particles.vertices.push(red_particle);
}
var red_particle_system = new THREE.PointCloud(red_particles, red_particle_material);
scene.add(red_particle_system);
Fin qui, tutto bene. Posso anche programmazione modificare il gradiente e rendering diverse categorie di particelle:
Cosa vorrei fare è ora passare da WebGLRenderer
all'utilizzo di un SVGRenderer
, in modo che possa consentire all'utente finale di impostare l'orientamento desiderato e quindi esportare un'immagine vettoriale (SVG o convertita in PDF sul back-end) che può essere utilizzata per lavori di qualità di pubblicazione.
Utilizzando il SVG sandbox example da three.js come base per la sperimentazione, ho provato un paio di tecniche diverse e non ho avuto molta fortuna. Spero che qualcuno con esperienza con three.js possa avere qualche suggerimento.
Il mio primo tentativo è stato quello di utilizzare canvg
per rendere l'SVG un'immagine PNG in, e quindi applicare che ad un nodo <image>
:
var red_svg_html = new String($('#sphere_asset').html());
var red_svg_canvas = document.createElement("canvas");
canvg(red_svg_canvas, red_svg_html);
var red_png_data = red_svg_canvas.toDataURL('image/png');
var red_node = document.createElementNS('http://www.w3.org/2000/svg', 'image');
red_node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', red_png_data);
red_node.setAttributeNS('http://www.w3.org/2000/svg', 'height', '10');
red_node.setAttributeNS('http://www.w3.org/2000/svg', 'width', '10');
var red_particle_count = 25;
for (var i = 0; i < red_particle_count; i++) {
var object = new THREE.SVGObject(red_node.cloneNode());
object.position.x = 0.9 * (Math.random() - 0.5);
object.position.y = 0.9 * (Math.random() - 0.5);
object.position.z = 0.9 * (Math.random() - 0.5);
scene.add(object);
}
Nessun nodo mostrano nel mio viewBox.
La prossima cosa che ho provato è stato un oggetto THREE.Sprite
, utilizzando canvg
e THREE.Texture
routine:
var red_svg_html = new String($('#sphere_asset').html());
var red_svg_canvas = document.createElement("canvas");
canvg(red_svg_canvas, red_svg_html);
var red_svg_texture = new THREE.Texture(red_svg_canvas);
red_svg_texture.needsUpdate = true;
var red_sprite = THREE.ImageUtils.loadTexture(red_png_data);
var red_particle_count = 25;
for (var p = 0; p < red_particle_count; p++) {
var material = new THREE.SpriteMaterial({
map: red_svg_texture,
transparent: true,
size: 0.15,
alphaTest: 0.10
});
var sprite = new THREE.Sprite(material);
sprite.position.x = 0.9 * (Math.random() - 0.5),
sprite.position.y = 0.9 * (Math.random() - 0.5),
sprite.position.z = 0.9 * (Math.random() - 0.5),
sprite.scale.set(0.1, 0.1, 0.1);
scene.add(sprite);
}
Questo era un po 'meglio, nel senso che ho bianche, scatole opache in cui le sfere sarebbero altrimenti apparire nel viewBox reso .
Un terzo tentativo è stato fatto per creare un <svg>
a nido all'interno del nodo padre SVG, che contiene un riferimento-grado <radialGradient>
con l'id #sphere_gradient
:
var xmlns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(xmlns, 'svg');
svg.setAttributeNS(null, 'version', '1.1');
svg.setAttributeNS(null, 'x', '0px');
svg.setAttributeNS(null, 'y', '0px');
svg.setAttributeNS(null, 'width', '640px');
svg.setAttributeNS(null, 'height', '640px');
svg.setAttributeNS(null, 'viewBox', '0 0 640 640');
svg.setAttributeNS(null, 'enable-background', 'new 0 0 640 640');
var defs = document.createElementNS(xmlns, "defs");
var radialGradient = document.createElementNS(xmlns, "radialGradient");
radialGradient.setAttributeNS(null, "id", "sphere_gradient");
radialGradient.setAttributeNS(null, "cx", "292.3262");
radialGradient.setAttributeNS(null, "cy", "287.4077");
radialGradient.setAttributeNS(null, "r", "249.2454");
radialGradient.setAttributeNS(null, "fx", "147.7949");
radialGradient.setAttributeNS(null, "fy", "274.5532");
radialGradient.setAttributeNS(null, "gradientTransform", "matrix(1.0729 0 0 1.0729 -23.3359 -23.3359)");
radialGradient.setAttributeNS(null, "gradientUnits", "userSpaceOnUse");
var stop0 = document.createElementNS(null, "stop");
stop0.setAttributeNS(null, "offset", "0");
stop0.setAttributeNS(null, "stop-color", "#f37d7f");
radialGradient.appendChild(stop0);
var stop1 = document.createElementNS(null, "stop");
stop1.setAttributeNS(null, "offset", "0.4847");
stop1.setAttributeNS(null, "stop-color", "#ed1f24");
radialGradient.appendChild(stop1);
var stop2 = document.createElementNS(null, "stop");
stop2.setAttributeNS(null, "offset", "1");
stop2.setAttributeNS(null, "stop-color", "#7e1416");
radialGradient.appendChild(stop2);
defs.appendChild(radialGradient);
svg.appendChild(defs);
var red_circle = document.createElementNS(xmlns, "circle")
red_circle.setAttribute('fill', 'url(#sphere_gradient)');
red_circle.setAttribute('r', '320');
red_circle.setAttribute('cx', '320');
red_circle.setAttribute('cy', '320');
svg.appendChild(red_circle);
var red_particle_count = 25;
for (var i = 0; i < red_particle_count; i++) {
var object = new THREE.SVGObject(svg.cloneNode(true));
object.position.x = 0.85 * (Math.random() - 0.5);
object.position.y = 0.85 * (Math.random() - 0.5);
object.position.z = 0.85 * (Math.random() - 0.5);
scene.add(object);
}
Nessun nodo sono resi. Le regolazioni degli elementi r
, cx
o cy
dell'elemento <circle>
non modificano il risultato finale.
È interessante notare che, se cambio l'attributo fill
url(#sphere_gradient)
-red
, ho un grande cerchio in gran parte reso fuori dalla mia viewBox, che non è attaccato alla scena (non ruota con altri elementi nella mia scena genitore, come il lati di un cubo).
Esiste un modo (funzionante e performante) per disegnare sfere o particelle di forma arrotondata nello spazio utilizzando uno SVGRenderer
in three.js?
Grazie per il suggerimento. La modifica dell'attributo 'href' in' xlink: href' non ha modificato il risultato finale (* i.e. *, nessuno dei nodi è reso, ancora). –
Ok, grazie - la modifica dello spazio dei nomi aiuta a far applicare una sfumatura! Tuttavia, sembra che la trasformazione applicata a questo elemento '
Ho avuto qualche successo nello spostamento del gruppo '' nel codice' SVGRenderer' in modo che faccia parte della '' ' principale - ma ottengo problemi di rendering in cui gli elementi '' non sono disegnati "in ordine", che rompe l'illusione della profondità. In particolare, un elemento '' che è più vicino alla telecamera viene disegnato per primo, prima di un altro elemento che è più lontano dalla telecamera. Il risultato è che l'elemento più vicino alla videocamera (e che dovrebbe essere in primo piano) viene invece visualizzato sotto l'elemento più lontano. Ruotando la scena si spezza l'illusione della profondità. –