2014-12-24 2 views
12

У меня есть <circle> элемента в SVG документе, к которому я применяю <radialGradient> дать иллюзию того, что это сфера:рендеринг изображения в качестве узла Three.js с SVGRenderer (или иначе рендеринг сфер)

<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> 

это выглядит примерно так:

red sphere

JSFiddle

Я могу вынести это в three.js WebGLRenderer контейнере с помощью 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); 

До сих пор, так хорошо. Я даже могу программно изменить градиент и визуализации различных категорий частиц:

spheres

То, что я хотел бы сделать теперь перейти от WebGLRenderer с помощью SVGRenderer, так что я могу позволить конечному пользователю установить желаемую ориентацию, а затем экспортировать векторное изображение (SVG или преобразованное в PDF на заднем конце), которое можно использовать для работы с качеством публикации.

Использование SVG sandbox example от three.js в качестве основы для экспериментов, я пробовал пару разных методов и не имел большой удачи. Я надеюсь, что у кого-то, у кого есть опыт работы с three.js, могут быть предложения.

Моя первая попытка была использовать canvg для визуализации SVG в PNG изображения, а затем применить его к <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); 
} 

Нет узлы не отображаются в моем ViewBox.

Следующая вещь, которую я попытался было THREE.Sprite объект, используя canvg и THREE.Texture процедуры:

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); 
} 

Это было немного лучше, в том, что я получаю белые, непрозрачные коробки, где сферы иначе появляются в оказанной ViewBox ,

Третья была сделана попытка создать <svg> в гнездо в пределах родительского узла SVG, который содержит ссылку-состояние <radialGradient> с идентификатором #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); 
} 

Нет узлов не предоставляются. Корректировки элементов <circle>r, cx или cy не изменяют конечный результат.

Интересно, что если изменить атрибут fill от url(#sphere_gradient) к red, я получаю большой круг в основном вынесенный за пределами моего ViewBox, которая не прикрепленный к сцене (она не вращается вместе с другими элементами в моей родительской сцене, как стороны куба).

Существует ли (рабочий и исполнительный) способ рисования сфер или закругленных сферических частиц в пространстве с использованием SVGRenderer в трёхмерном пространстве?

ответ

2

Большинство атрибутов в SVG в нулевом пространстве имен так

red_node.setAttributeNS('http://www.w3.org/2000/svg', 'height', '10'); 
red_node.setAttributeNS('http://www.w3.org/2000/svg', 'width', '10'); 

правильно записать в виде

red_node.setAttribute('height', '10'); 
red_node.setAttribute('width', '10'); 

FWIW

red_node.setAttributeNS('http://www.w3.org/1999/xlink', 'href', red_png_data); 

в идеале должна быть записана в виде

red_node.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', red_png_data); 

хотя в этом случае ваша оригинальная форма будет работать в большинстве UA.

В последнем примере запорные элементы должны быть в SVG имен т.е.

var stop0 = document.createElementNS(null, "stop"); 

должен быть

var stop0 = document.createElementNS(xmlns, "stop"); 

Градиент без остановок не тянет совсем так вот почему вы этого не сделаете видеть все, пока вы не измените заливку на красный.

У SVG есть модель для художников. Вещи рисуются в порядке их появления в файле. Если вы хотите, чтобы что-то появилось поверх чего-то другого, вам нужно поместить его позже в файл.

+0

Спасибо за предложение. Модификация атрибута 'href' для' xlink: href' не изменила конечный результат (* i.e. *, ни один из узлов не отображается, все еще). –

+0

Хорошо, спасибо - изменение пространства имен помогает в том, что теперь применяется градиент! Однако, похоже, что преобразование, применяемое к этому вложенному элементу '', не вычисляется в сцене, поэтому элемент' ', содержащийся в пределах фиксированных по его местоположению элементов, даже если преобразование применяется к его родительскому объекту '. –

+0

У меня был некоторый успех, переместив группу '' в код' SVGRenderer', так что он является частью самого «root-most» ', но я получаю проблемы с рендерингом, где' 'элементы не рисуются в порядке", которая разрушает иллюзию глубины. В частности, элемент '', который находится ближе к камере, сначала нарисован перед другим элементом, который находится дальше от камеры. В результате элемент, который находится ближе к камере (и который должен находиться на переднем плане), вместо этого отображается под элементом дальше. Вращение сцены разрушает иллюзию глубины. –

0

С вашей первой попытки, если вы добавите библиотеку snap svg, вы можете получить элементы svg из canvg и обернуть ее в структуру привязки (как бумагу для привязки), а затем использовать метод paper.toString() для создания svg-кода для вашего svg-файла.

var paper = Snap("#... ");  //this wraps the svg element from the canvg onto snap structure 
console.log(paper.toString()); // this gives you the svg code! 

Edit: Или вы могли бы использовать fabric.js конвертировать холст SVG:

var svg = canvas.toSVG(); 

Или вы могли бы использовать canvas2svg библиотеку.

+0

У меня уже есть SVG. Я буду переходить с SVG на PNG на SVG снова? –

+0

У вас уже есть начальный svg, а не обновленный. Вот шаги, которые, как я думаю, могут вам помочь: у вас есть svg, затем вы добавляете это в холст, чтобы добавить к нему дополнительные, а затем с холста вы должны получить обновленный svg , Ваша последняя часть не работает, чтобы получить обновленную версию svg. Разве это не то, что вы хотите сделать, чтобы упростить свой процесс ?! – Bayan

+0

Я не понимаю, что вы подразумеваете под «обновлением». SVG является самодостаточным; Я уже вношу его в строку: 'var red_svg_html = new String ($ ('# sphere_asset'). Html());' –

0

Вот что вам нужно добавить к вашему SVGRenerer.js, чтобы исправить вашу проблему с рендерингом частицами:

function renderParticle(v1, element, material, scene) { 


     _svgNode = getCircleNode(_circleCount++); 
     _svgNode.setAttribute('cx', v1.x); 
     _svgNode.setAttribute('cy', v1.y); 
     _svgNode.setAttribute('r', element.scale.x * _svgWidthHalf); 

     if (material instanceof THREE.ParticleCircleMaterial) { 

      if (_enableLighting) { 

       _color.r = _ambientLight.r + _directionalLights.r + _pointLights.r; 
       _color.g = _ambientLight.g + _directionalLights.g + _pointLights.g; 
       _color.b = _ambientLight.b + _directionalLights.b + _pointLights.b; 

       _color.r = material.color.r * _color.r; 
       _color.g = material.color.g * _color.g; 
       _color.b = material.color.b * _color.b; 

       _color.updateStyleString(); 

      } else { 

       _color = material.color; 

      } 

      _svgNode.setAttribute('style', 'fill: ' + _color.__styleString); 

     } 

     _svg.appendChild(_svgNode); 


    } 
+0

Спасибо, но, как упоминалось в предыдущем комментарии, есть две проблемы с настройкой SVGRenderer: 1. Я не могу легко назначить программно-генерируемый радиальный градиент, и поэтому я застрял с любым градиентом, который я создаю. 2. Конечный результат не отображает правильное трехмерное представление, поскольку элементы, расположенные ближе к камере, не отображаются поверх элементов, которые находятся дальше от камеры, и не масштабируются правильно. Вращение частиц показывает, как это «ломается». Непонятно, как упорядочить элементы '', чтобы отобразить правильную трехмерную сцену. Обе эти проблемы - деловые выключатели. –

+0

Можете ли вы предоставить ссылку на файл SVGRender.js, который вы используете прямо сейчас? – Bayan

+0

https://dl.dropboxusercontent.com/u/31495717/three.svg-renderer.js –

Смежные вопросы