Unexpected gap between volume images

Hello, I’m using VTK to render multiple volumes which are sub-blocks of a huge 3D image, but I encounter some kind of alignment issue.

As demonstrated in the screenshot below, there are two cubic volumes side-by-side with size 64x64x64 each, the origins of the two volumes are spaced 64 unit in X direction, somehow there is an unexpected gap as pointed by the red arrow.

The size of gap is likely exactly 1 unit, since there is no gap if the two cubes are placed 63 unit apart. But if they are 63 unit apart, the geometry of these blocks are no longer correct, e.g. breaks exact periodic arrangement of the green bars (somewhat 1/4 period short). Feels like one should have one unit more for the boundary for shader.

I have tried using different volume mappers (FixedPointVolumeRayCastMapper, GPUVolumeRayCastMapper), also tried tuning the SetDataExtent and SetWholeExtent, both not works.

Example code:

#!/usr/bin/env python

import numpy as np

# noinspection PyUnresolvedReferences
from vtkmodules.vtkRenderingVolumeOpenGL2 import vtkOpenGLRayCastImageDisplayHelper
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonDataModel import vtkPiecewiseFunction
from vtkmodules.vtkRenderingCore import (
    vtkColorTransferFunction,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkRenderer,
    vtkVolume,
    vtkVolumeProperty,
)
from vtkmodules.vtkIOImage import (
    vtkImageImport,
)
from vtkmodules.vtkCommonCore import (
    VTK_CUBIC_INTERPOLATION,
)
from vtkmodules.vtkRenderingVolume import (
    vtkFixedPointVolumeRayCastMapper,
    vtkGPUVolumeRayCastMapper,
)
from vtkmodules.vtkInteractionStyle import (
    vtkInteractorStyleTerrain,
)

vtk_colors = vtkNamedColors()

def GenImg3d(n, h = 8):
    a = np.zeros((n,n,n), dtype = np.uint8)
    modular = 0
    for j in range(0, n-h, h):
        if modular == 0:
            u = np.ones(n)
            modular = 1
        else:
            u = (np.arange(n) // modular % 2)
        a[0, j:j+h, :] = np.uint8(u * 255)
        modular *= 2

    a[1:, :, :] = a[0, :, :]
    return a

def GenVolume(img_arr, orig_xyz):
    img_importer = vtkImageImport()
    simg = np.ascontiguousarray(img_arr, img_arr.dtype)
    img_importer.CopyImportVoidPointer(simg.data, simg.nbytes)
    if img_arr.dtype == np.uint8:
        img_importer.SetDataScalarTypeToUnsignedChar()
    else:
        raise "Unsupported format"
    img_importer.SetNumberOfScalarComponents(1)
    img_importer.SetDataExtent (0, simg.shape[2]-1, 0, simg.shape[1]-1, 0, simg.shape[0]-1)
    img_importer.SetWholeExtent(0, simg.shape[2]-1, 0, simg.shape[1]-1, 0, simg.shape[0]-1)
    img_importer.SetDataSpacing(1.0, 1.0, 1.0)

    img_importer.SetDataOrigin(orig_xyz)

    # Create transfer mapping scalar value to opacity.
    opacityTransferFunction = vtkPiecewiseFunction()
    opacityTransferFunction.AddPoint(20, 0.7)
    opacityTransferFunction.AddPoint(255, 0.8)

    # Create transfer mapping scalar value to color.
    colorTransferFunction = vtkColorTransferFunction()
    colorTransferFunction.AddRGBPoint(0.0, 0.4, 0.0, 0.0)
    colorTransferFunction.AddRGBPoint(64.0, 0.3, 0.2, 0.0)
    colorTransferFunction.AddRGBPoint(128.0, 0.0, 0.7, 0.1)
    colorTransferFunction.AddRGBPoint(255.0, 0.0, 1.0, 0.2)

    # The property describes how the data will look.
    volumeProperty = vtkVolumeProperty()
    volumeProperty.SetColor(colorTransferFunction)
    volumeProperty.SetScalarOpacity(opacityTransferFunction)
    volumeProperty.ShadeOn()
    #volumeProperty.SetInterpolationTypeToLinear()
    volumeProperty.SetInterpolationType(VTK_CUBIC_INTERPOLATION)

    volumeMapper = vtkGPUVolumeRayCastMapper()
    volumeMapper.SetInputConnection(img_importer.GetOutputPort())
    volumeMapper.SetBlendModeToMaximumIntensity()

    volume = vtkVolume()
    volume.SetMapper(volumeMapper)
    volume.SetProperty(volumeProperty)
    return volume

def main():
    ren1 = vtkRenderer()
    renWin = vtkRenderWindow()
    renWin.AddRenderer(ren1)
    iren = vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)
    iren.SetInteractorStyle(vtkInteractorStyleTerrain())

    img_sz = 64
    img3d = GenImg3d(img_sz)
    volume1 = GenVolume(img3d, (0,0,0))
    volume2 = GenVolume(img3d, (img_sz,0,0))

    ren1.AddVolume(volume1)
    ren1.AddVolume(volume2)
    ren1.SetBackground(vtk_colors.GetColor3d('Black'))

    cam = ren1.GetActiveCamera()
    cam.Azimuth(0)
    cam.Elevation(0)
    
    cam.SetParallelProjection(True)
    
    ren1.ResetCameraClippingRange()
    ren1.ResetCamera()

    renWin.SetSize(1440, 900)
    renWin.Render()

    iren.Start()

if __name__ == '__main__':
    main()