SVG灯光和阴影制造3D效果

3
我想通过在顶部和左侧边框添加小灯和在底部和右侧边框添加阴影,使一个SVG看起来像是在3D中。
类似于这样的效果:

#div1 {
  background: #ddd;
}
#div1, #div2, #div3 {
  width: 100px;
  height: 100px;
  position: relative;
}
#div2 {
  box-shadow: inset -2px -2px 10px 1px #000;
  position: absolute;
}
#div3 {
  box-shadow: inset 2px 2px 14px 1px #fff;
  position: absolute;
}
<div id="div1">
  <div id="div2"></div>
  <div id="div3"></div>
</div>

但是我不知道如何使用svg过滤器实现这一点。

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1000">


<defs>
  <filter id="filter1" x="0" y="0">
    <feSpecularLighting result="specOut"
        specularExponent="20" lighting-color="#bbbbbb">
      <fePointLight x="-100" y="-100" z="600"/>
    </feSpecularLighting>
    <feComposite in="SourceGraphic" in2="specOut"
        operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
  </filter>
</defs>

<path filter="url(#filter1)" fill="#fff" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>



</svg>

Help please and thanks

1个回答

7
首先,你试图用暗淡的白光照亮一个纯白色的矩形。这样是看不到任何东西的。
如果让矩形变暗,你就会开始看到效果了。

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">

  <defs>
    <filter id="filter1" x="0" y="0">
      <feSpecularLighting result="specOut"
          specularExponent="20" lighting-color="#bbbbbb">
        <fePointLight x="-100" y="-100" z="600"/>
      </feSpecularLighting>
      <feComposite in="SourceGraphic" in2="specOut"
          operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
    </filter>
  </defs>

  <path filter="url(#filter1)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>

但在上面的例子中,我们只获得了矩形上光线的渐变。那么怎样才能在矩形上制作出类似斜角的边缘呢?
要知道,实际上并不是元素的RGB通道确定了光照滤镜组件的行为。光照组件将颜色的alpha分量视为凸起映射。不同值的alpha成为影响像素受光程度的拓扑地图。
创建不同值的alpha的一种方法是使用高斯模糊滤镜。下面是它的效果。请注意,我们正在模糊的是形状的alpha通道(SourceAlpha)。

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
  <defs>
    <filter id="filter2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
      <feBlend in="SourceGraphic" in2="blur1" mode="multiply"/>
    </filter>
  </defs>

  <path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>

现在,如果使用模糊的alpha通道,我们会得到接近你想要的效果。你可以调整模糊、光照滤镜和feComposite值来调整效果。
请注意,我也切换到使用feDistantLight。我认为这更适合此目的。

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
  <defs>
    <filter id="filter2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
      <feSpecularLighting result="specOut" in="blur1" specularConstant="1.2" specularExponent="12" lighting-color="#fff">
        <feDistantLight azimuth="225" elevation="45"/>
      </feSpecularLighting>
      <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
    </filter>
  </defs>

  <path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>

更新

为了处理形状重叠的情况(见评论),您需要剪切掉模糊效果超出形状外部的任何部分。您可以使用另一个feComposite操作来实现。

<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
  <defs>
    <filter id="filter2">
      <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
      <feSpecularLighting result="specOut" in="blur1" specularConstant="1.2" specularExponent="12" lighting-color="#fff">
        <feDistantLight azimuth="225" elevation="45"/>
      </feSpecularLighting>
      <feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="result"/>
      <feComposite operator="atop" in2="SourceGraphic"/>
    </filter>
  </defs>

  <path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
  <path filter="url(#filter2)" fill="#666" stroke="#000" d="M40,40 L200,40 L200,110 L40,110 L40,40 "></path>
  <path filter="url(#filter2)" fill="#666" stroke="#000" d="M40,120 L200,120 L200,200 L40,200 L40,120 "></path>
</svg>


谢谢你的回答,但是有一个问题,当路径重叠时会出现问题:https://jsfiddle.net/qeb590f2/ - evgeni fotia
@PaulLeBeau 如果你将文件保存为*.svg或*.html格式,那么在Firefox中它就无法正常工作。这似乎是因为百分比的原因:width="100%" height="300"。如果你将宽度设置为像素,则可以正常工作。 - Alexandr_TT
“不起作用”是什么意思?你是指它不是全宽,只有300像素宽吗?那是因为它是一个独立的文件,没有父容器。"100%"表示父容器宽度的100%。将其放在一个HTML文件中的<body><div>内,它将会是全宽的。如果没有这样做,宽度将默认为300像素。这是浏览器的特性,而不仅限于Firefox。 - Paul LeBeau
@PaulLeBeau 首先感谢你详细的回答,在我的应用程序中,颜色非常重要,所以是否有其他方法可以在白色上实现3D效果? - evgeni fotia
我觉得你需要发布一个模拟图片的链接,这样我才能明白你想要什么。 - Paul LeBeau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接