7

目标

我正在尝试编写一个 SVG 过滤器来在给定形状上渲染波浪照明。所以在那个形状里面我想要波浪应用,但我不想要那个形状之外的可见效果。据我了解,我不能将相同的图像源用于波浪的高度图和atop合成的目标域。所以我认为<feImage>可以使用一个元素从一个单独的渐变填充对象中获取高度图。但到目前为止,我还没有成功地让该对象在滤镜效果区域内可见。

第一次尝试

我到目前为止的代码:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" width="512" height="512"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <radialGradient id="waveFill" r="5%" spreadMethod="reflect">
      <stop offset="0%" stop-opacity="1.00"/>
      <stop offset="20%" stop-opacity="0.90"/>
      <stop offset="40%" stop-opacity="0.65"/>
      <stop offset="60%" stop-opacity="0.35"/>
      <stop offset="80%" stop-opacity="0.10"/>
      <stop offset="100%" stop-opacity="0.00"/>
    </radialGradient>
    <rect id="waveImg" x="-210" y="-105" width="420" height="210"
          stroke="none" fill="url(#waveFill)"/>
    <filter id="waveFilter">
      <feImage xlink:href="#waveImg" result="waves"/>
      <!-- feSpecularLighting and so on will be added later on -->
      <feComposite operator="atop" in="waves" in2="SourceImage"/>
    </filter>
  </defs>
  <g transform="translate(256 256)">
    <!-- use xlink:href="#waveImg"/ works -->
    <ellipse rx="200" ry="100" fill="#0000ff" stroke="none"
             style="filter:url(#waveFilter)"/>
  </g>
</svg>

相关文件

状态的文档<feImage>

如果'xlink:href'</a> 引用了独立的图像资源,例如 JPEG、PNG 或 SVG 文件,则根据'image'</a> 元素的行为呈现图像资源;否则,引用的资源将根据'use'</a> 元素的行为呈现。在任何一种情况下,当前用户坐标系都取决于'filter'</a> 元素上的属性'primitiveUnits'</a>的值。

第二次尝试

use而不是在image我看来没问题,但我不确定在这种情况下如何定义矩形区域。的文档use似乎表明在这种情况下(引用的元素既不是 asymbol也不是svg元素),widthandheight属性是无关紧要的,但是它如何描述一个矩形区域呢?我还认为也许改变原始单位会有所帮助。或将图像装箱在symbol. 所以我的第二次尝试是以下一次:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" width="512" height="512"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <radialGradient id="waveFill" r="5%" spreadMethod="reflect">
      <stop offset="0%" stop-opacity="1.00"/>
      <stop offset="20%" stop-opacity="0.90"/>
      <stop offset="40%" stop-opacity="0.65"/>
      <stop offset="60%" stop-opacity="0.35"/>
      <stop offset="80%" stop-opacity="0.10"/>
      <stop offset="100%" stop-opacity="0.00"/>
    </radialGradient>
    <symbol viewBox="0 0 100 100" id="waveImg" preserveAspectRatio="none">
      <rect width="100" height="100" stroke="none" fill="url(#waveFill)"/>
    </symbol>
    <filter id="waveFilter" primitiveUnits="objectBoundingBox">
      <feImage xlink:href="#waveImg" x="0" y="0" width="100%" height="100%"
               preserveAspectRatio="none" result="waves"/>
      <!-- feSpecularLighting and so on will be added later on -->
      <feComposite operator="atop" in="waves" in2="SourceImage"/>
    </filter>
  </defs>
  <g transform="translate(256 256)">
    <!-- use xlink:href="#waveImg"/ works -->
    <ellipse rx="200" ry="100" fill="#0000ff" stroke="none"
             style="filter:url(#waveFilter)"/>
    <ellipse rx="200" ry="100" fill="none" stroke="#ff0000"/>
  </g>
</svg>

仍然没有图像可见。我究竟做错了什么?

核心问题

如何将 SVG 文件的片段用作照明的凹凸贴图,而不将其用作SourceGraphic过滤器?

光栅化器

此图像不适用于 Web 部署,但将在本地进行光栅化。所以浏览器兼容性不是什么大问题。如果东西在 Inkscape 或 rsvg-convert 下正确渲染,那对我来说就足够了。

4

1 回答 1

4

这可能不起作用的原因有很多。

  1. 您可能需要为您的 svg 宽度和高度指定单位(px、pt、em、% 等等)。IE 不喜欢未指定的 SVG 元素尺寸。
  2. 您对 feComposite 输入使用了错误的术语:“SourceImage”应该是“SourceGraphic”。
  3. 过滤器最好通过 filter 属性直接应用,而不是通过 style 属性。
  4. 我认为您不能直接在 feImage 中引用符号,您必须先 <use 它,然后在 use 元素上引用 id。(无论如何,为什么不直接使用 rect 呢?)

这是使用您的方法在 Chrome(可能还有 Safari)中运行的版本。

<svg version="1.1" width="512px" height="512px" viewbox="0 0 512 512"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">   <defs>
    <radialGradient id="waveFill" r="5%" spreadMethod="reflect">
      <stop offset="0%" stop-opacity="1.00"/>
      <stop offset="20%" stop-opacity="0.90"/>
      <stop offset="40%" stop-opacity="0.65"/>
      <stop offset="60%" stop-opacity="0.35"/>
      <stop offset="80%" stop-opacity="0.10"/>
      <stop offset="100%" stop-opacity="0.00"/>
    </radialGradient>

      <rect id="waveImg" width="100%" height="100%" stroke="none" fill="url(#waveFill)"/>

    <filter id="waveFilter" primitiveUnits="objectBoundingBox">
      <feImage xlink:href="#waveImg" x="0%" y="0%" width="100%" height="100%" result="waves"/>
      <!-- feSpecularLighting and so on will be added later on -->
      <feComposite operator="atop" in="waves" in2="SourceGraphic"/>
    </filter>   
     </defs>  
     <g transform="translate(256 256)">


    <ellipse rx="200" ry="100" fill="#0000ff" stroke="none"
             filter="url(#waveFilter)"/>
    <ellipse rx="200" ry="100" fill="none" stroke="#ff0000"/>   </g> </svg>

但是,这在 Firefox 中不起作用,因为不支持对 feImage 的对象输入,并且根据我的测试,feImage 单元在 IE 中存在相当多的错误。

您没有理由不能将您的源图形重新用于照明效果 - 这是您尝试做的更短的版本,它可以跨浏览器工作并避免使用 feImage。

<svg version="1.1" x="0px" y="0px" width="512px" height="512px" viewbox="0 0 512 512"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">   <defs>
    <radialGradient id="waveFill" r="5%" spreadMethod="reflect">
      <stop offset="0%" stop-opacity="1.00"/>
      <stop offset="20%" stop-opacity="0.90"/>
      <stop offset="40%" stop-opacity="0.65"/>
      <stop offset="60%" stop-opacity="0.35"/>
      <stop offset="80%" stop-opacity="0.10"/>
      <stop offset="100%" stop-opacity="0.00"/>
    </radialGradient>

    <filter id="waveFilter" x="0%" y="0%" width="100%" height="100%">
      <feFlood flood-color="#0000ff" result="blue"/>
      <feComposite operator="in" in2="SourceGraphic" in="blue"/>
    </filter>  
    </defs> 
    <g transform="translate(256 256)">
    <!-- use xlink:href="#waveImg"/ works -->
    <ellipse rx="200" ry="100" fill="url(#waveFill)" filter="url(#waveFilter)"/>
    <ellipse rx="200" ry="100" fill="none" stroke="#ff0000"/>   </g> </svg>

而且(因为为什么不)这是一个添加了镜面照明的示例:

<svg version="1.1" x="0px" y="0px" width="512px" height="512px" viewbox="0 0 512 512"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs>
    <radialGradient id="waveFill" r="5%" spreadMethod="reflect">
      <stop offset="0%" stop-opacity="1.00"/>
      <stop offset="20%" stop-opacity="0.90"/>
      <stop offset="40%" stop-opacity="0.65"/>
      <stop offset="60%" stop-opacity="0.35"/>
      <stop offset="80%" stop-opacity="0.10"/>
      <stop offset="100%" stop-opacity="0.00"/>
    </radialGradient>

    <filter id="waveFilter" x="0%" y="0%" width="100%" height="100%">
      <feFlood flood-color="#0000ff" result="blue"/>
      <feComposite operator="in" in2="SourceGraphic" in="blue"                 
          result="sourcewithblue"/>

      <feSpecularLighting in="SourceAlpha" result="specOut" 
         specularConstant="2" specularExponent="20" lighting-color="white" 
         surfaceScale="2">
        <fePointLight  x="-200" y="-200" z="200"/> 
      </feSpecularLighting>

      <feComposite operator="arithmetic" in="specOut" in2="sourcewithblue" k1="0" k2="1" k3="1" result="unclippedoutput"/>
      <feComposite operator="in" in="unclippedoutput" in2="SourceGraphic"/>

    </filter>
  </defs>
 <g transform="translate(256 256)">
<ellipse rx="200" ry="100" fill="url(#waveFill)" filter="url(#waveFilter)"/>
<ellipse rx="200" ry="100" fill="none" stroke="#ff0000"/>
</g>
</svg>
于 2013-11-07T00:33:54.953 回答