SGV 渐变在停止处变暗

SGV Gradients Darker at Stops

我正在尝试制作一个行星的球形图像,它的夜面在阴影中,以便在浏览器中呈现以用于模拟网页,但我一直在与一个我似乎无法弄清楚的奇怪的视觉问题作斗争.

这是 SVG 的图像片段,它在 Chrome 中显示(这也发生在 IE 中)然后放大 3 倍:

您会注意到,就在 day/night 终止符上方,有一条较暗的水平带,宽度约为终止符上方距离的 1/3。那应该在那里,我不知道为什么它在那里或如何摆脱它。

这里是 HTML 片段中的 SVG 代码,便于查看:

<div style="position:absolute; z-index:1; margin:15px; 
            width:640px; height:640px; 
            background-color:black">

  <svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                <!-- NOTE: All internal units are in KM (or %)  -->
    
                <defs id="defsEa">
                    <clipPath id="svgEaClip">
                        <rect width="100%" height="100%" />
                    </clipPath>
                    <linearGradient id="lgdEaSphere">
                        <stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00" id="stopEarthCenter" />
                        <stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30" id="stopEarthInner" />
                        <stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31" id="stopEarthMid" />
                        <stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264" id="stopEarthOuter" />
                        <stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264" id="stopAirInner" />
                        <stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00" id="stopAirOuter"  />
                    </linearGradient>
                    <radialGradient id="rgdEaSphere"  xlink:href="#lgdEaSphere"
                        gradientUnits="userSpaceOnUse"
                        cx="0" cy="0"
                        fx="0" fy="0"
                        r="3339.05" 
                        />
    
                    <linearGradient id="lgdEaNightSide" 
                        x1="0%" y1="0%" x2="0%" y2="100%"
                        spreadMethod="pad" >
                        <stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight" />
                        <!-- this stop seems to cause the artifact -->
                        <stop style="stop-color:#000000;stop-opacity:0.6;" offset="0.99" id="stopEaDusk" /> 
                        <stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator" />
                    </linearGradient>
                </defs>
    
                <g id="gEaAll" transform="scale(1.2,1.2)" >
                    <g id="gEaSunFacing" >
                        <!-- contains everything that stays oriented with the Sun -->
                        <circle
                            id="cEarthAir"
                            cx="0" cy="0" r="3339.05"
                            style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
                        <!--  overlay to give Earth a night side.  -->
                        <path id="pNight" 
                            style="stroke:none; fill:url(#lgdEaNightSide)"
                            d="M -3189.05,0 
                                A 3189.05,15945.25 0 1,1 3189.05,0
                                Z" 
                            />
                    </g>
                </g>
    
            </svg>

</div>

(如果需要,可以将其保存到 HTML(.html)文件(添加 html 和 body 标签)然后 运行 它在你的浏览器中看到它。或者将它保存到一个 SVG 文件 (.svg),并删除 div 和 br 标签,如果你想在它上面使用任何 SVG 工具.

这个伪像就发生在线性渐变停止点之一所在的位置,特别是第 33 行的 #stopEaDusk,在线性渐变 #lgdEaNightSide 中,它被用作叠加层的填充路径 #pNight。这个伪影可能看起来很微弱,但它是真实的,并且在没有缩小 300% 时更加明显。我的实际 SVG 也有一个免费的 #pDay 叠加层,我删除了它以简化代码。但是当它被添加时,我得到了三个 darkness-band 工件,一个在上面,一个在另一侧的相应位置(我有另一个 dawn/dusk 停止)和一个在中点两个叠加层相遇。当它们三个都可见时,它看起来很糟糕,所以我不能忽略它。

其他要点:

tl,dr:是什么导致了这个黑暗带,我该如何摆脱它?


这里的问题似乎有些混乱,所以请允许我澄清一下。线性渐变 lgdEaNightSide 是为了明确的效果而编写的,它似乎正在这样做,但它 产生了不受欢迎的 side-effect 即 不是,据我对 SVg 的理解,应该 NOT 会发生。

想要的效果是 pNight 叠加层添加: 1. 70% 的黑暗,在弧的顶部(非常高 off-screen),在向下 99% 的位置平滑地过渡到 60% 的黑暗。 2. 然后从 60% 黑暗度 99% 到 50% 黑暗度 100% 的过程中快速着色。这是为了提供一个dusk-like过渡区。

然而,除此之外,它还增加了不良影响: - 在下降的大约 98.8% 处,天色突然变暗 - 然后在下降的大约 99.2% 处,它又突然变轻了。

这不应该发生,据我所知,我写的 SVG 应该不会这样做。

我正在寻找的是如何保持第一个(想要的)效果,同时摆脱第二个(不想要的)效果。

我不需要被告知是第二站导致了问题。我知道,我在上面已经说过,我在代码注释中指出了这一点。我不需要答案来告诉我我已经知道的事情。我要找的是:

  1. 如何在保留我最初编写它的视觉效果的同时修复它,以及

  2. 为什么会发生,当 AFAIK 时,它应该不会发生。

这是因为你在叠加的渐变中使用了额外的停止值。当你在叠加层中使用线性渐变(lgdEaNightSide)来给地球一个夜晚的一面时,如下所示。

您可以看到您使用了两个偏移值,一个 at 0.99 和另一个 at 1。这就是你的 horizon 波段变暗的原因。

<linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%" spreadMethod="pad">
  <stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight" />
  <!-- this stop seems to cause the artifact -->
  <stop style="stop-color:#000000;stop-opacity:0.6;" offset="0.99" id="stopEaDusk" />
  <stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator" />
</linearGradient>

只需删除一个额外的停止值即可,请在下面找到工作代码:

<div style="position:absolute; z-index:1; margin:15px; 
    width:640px; height:640px; 
    background-color:black">

  <svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
      <!-- NOTE: All internal units are in KM (or %)  -->

      <defs id="defsEa">
        <clipPath id="svgEaClip">
          <rect width="100%" height="100%" />
        </clipPath>
        <linearGradient id="lgdEaSphere">
          <stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00"
                id="stopEarthCenter" />
          <stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30"
                id="stopEarthInner" />
          <stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31"
                id="stopEarthMid" />
          <stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264"
                id="stopEarthOuter" />
          <stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264"
                id="stopAirInner" />
          <stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00"
                id="stopAirOuter" />
        </linearGradient>
        <radialGradient id="rgdEaSphere" xlink:href="#lgdEaSphere"
                        gradientUnits="userSpaceOnUse" cx="0" cy="0" fx="0"
                        fy="0" r="3339.05" />

        <linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%"
                        spreadMethod="pad">
          <stop style="stop-color:#000000;stop-opacity:0.7;" offset="0.00"
                id="stopEaMidnight" />
          <!-- this stop seems to cause the artifact -->
          <stop style="stop-color:#000000;stop-opacity:0.5;" offset="1.00"
                id="stopEaTerminator" />
        </linearGradient>
      </defs>

      <g id="gEaAll" transform="scale(1.2,1.2)">
        <g id="gEaSunFacing">
          <!-- contains everything that stays oriented with the Sun -->
          <circle id="cEarthAir" cx="0" cy="0" r="3339.05"
                  style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
          <!--  overlay to give Earth a night side.  -->
          <path id="pNight" style="stroke:none; fill:url(#lgdEaNightSide)" d="M -3189.05,0 
                        A 3189.05,15945.25 0 1,1 3189.05,0
                        Z" />
        </g>
      </g>

    </svg>

</div>

代码没有问题。

存在条带是因为显示器没有足够的颜色来表示细微的变化。您正在尝试在大面积上更改 10% 的不透明度,这对大多数显示器来说是不可能的。

您可以通过切换屏幕来确定这是显示器问题。根据显示器的质量和像素密度,您会看到略有不同的结果。一些浏览器使用抖动,通过像素化的权衡减少色带。

解决该问题的唯一方法是像 Prince 那样引入更多颜色变化。

另一个 post 的回答很好地解释了这个概念。值得一读,但我将引用解决方案以节省人们的点击次数。

If you want to make it less obvious with your current 24-bit color monitor, you can:

  • Change your design to introduce a subtle color shift into your gradient (e.g. from dark blue to gray-green). This causes different RGB channels bits can transition at different times, breaking up your bands into a smaller differentiated colors.

  • Change your design to increase your dynamic range (e.g. from pure white to pure black) so that you have more color bars to work with.

  • Change your design to reduce the overall distance that the gradient occurs over, reducing the widths of the bands.

…or some combination of the above.

我实施了这个策略,但并不完美。如果你真的想摆脱色带,你将不得不做出其他权衡。例如,你可能无法使用纯黑色。

<div style="position:absolute; z-index:1; margin:15px; 
            width:640px; height:640px; 
            background-color:#0c0c26">

  <svg id="svgEa" style="width:100%; height:100%;" viewBox="-5000 -5000 10000 10000" preserveAspectRatio="xMidYMid meet" clip-path="url(#svgEaClip)" transform="scale(1.0,1.0)" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
                <!-- NOTE: All internal units are in KM (or %)  -->
    
                <defs id="defsEa">
                    <clipPath id="svgEaClip">
                        <rect width="100%" height="100%" />
                    </clipPath>
                    <linearGradient id="lgdEaSphere">
                        <stop style="stop-color:#ffffff;stop-opacity:1.00;" offset="0.00" id="stopEarthCenter" />
                        <stop style="stop-color:#dfffef;stop-opacity:1.00" offset="0.30" id="stopEarthInner" />
                        <stop style="stop-color:#91ffc8;stop-opacity:1.00" offset="0.31" id="stopEarthMid" />
                        <stop style="stop-color:#00A064;stop-opacity:1.00" offset="0.95264" id="stopEarthOuter" />
                        <stop style="stop-color:#44ffff;stop-opacity:0.66" offset="0.95264" id="stopAirInner" />
                        <stop style="stop-color:#44ffff;stop-opacity:0.10" offset="1.00" id="stopAirOuter"  />
                    </linearGradient>
                    <radialGradient id="rgdEaSphere"  xlink:href="#lgdEaSphere"
                        gradientUnits="userSpaceOnUse"
                        cx="0" cy="0"
                        fx="0" fy="0"
                        r="3339.05" 
                        />
    
                    <linearGradient id="lgdEaNightSide" x1="0%" y1="0%" x2="0%" y2="100%" spreadMethod="pad">
                        <stop style="stop-color:#00033e;stop-opacity:0.7;" offset="0.00" id="stopEaMidnight"></stop>
                        <!-- this stop seems to cause the artifact -->
                        <stop style="stop-color:#090d24;stop-opacity:0.6;" offset="0.99" id="stopEaDusk"></stop> 
                        <stop style="stop-color:#000;stop-opacity:0.5;" offset="1.00" id="stopEaTerminator"></stop>
                    </linearGradient>
                </defs>
    
                <g id="gEaAll" transform="scale(1.2,1.2)" >
                    <g id="gEaSunFacing" >
                        <!-- contains everything that stays oriented with the Sun -->
                        <circle
                            id="cEarthAir"
                            cx="0" cy="0" r="3339.05"
                            style="display:inline;fill-opacity:1;fill:url(#rgdEaSphere)" />
                        <!--  overlay to give Earth a night side.  -->
                        <path id="pNight" 
                            style="stroke:none; fill:url(#lgdEaNightSide)"
                            d="M -3189.05,0 
                                A 3189.05,15945.25 0 1,1 3189.05,0
                                Z" 
                            />
                    </g>
                </g>
    
            </svg>

</div>