Getting ShaderTerrainMesh and BulletHeightfieldShape to match

I can’t seem to solve this. The heightfields of these two don’t seem to match. Have a look:

It doesn’t feel like just a different position would fix it, like is claimed here, but who knows how weirdly my eyes are working.

I have a simplified example on how to produce this issue here.

So, is this a bug or can someone point out to me how to get the two heightfields to match?

I haven’t used either tool, confessedly, but from the screenshot it looks like the two heightfields have different vertical extents. Without scaling the collision-node (which may interfere with its working, I fear), is there a means of adjusting their heights?

I don’t know what I should change to test your hypothesis, but on the background on the right edge of the screenshot you can see another area with a flatly rising terrain. The height of that is reproduced “exactly”, except at the edge where the texture goes above the collision mesh until it reaches the “center of the edge”, and then the collision mesh is the one that is above, until they converge to the same height. So I don’t think it’s a scaling issue.

I am afraid that 100 identities cannot be achieved, it may be a matter of error or in the shader algorithm.

The maximum possible fit is something like this.

from direct.showbase.ShowBase import ShowBase
from panda3d.core import ShaderTerrainMesh, Shader
from panda3d.core import SamplerState

from panda3d.bullet import BulletHeightfieldShape, BulletDebugNode, BulletWorld, ZUp, BulletTriangleMesh, BulletRigidBodyNode

from panda3d.core import Filename, PNMImage


class ShaderTerrainDemo(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)

        debugNode = BulletDebugNode('Debug')
        debugNP = render.attachNewNode(debugNode)
        debugNP.show()

        self.world = BulletWorld()
        self.world.setDebugNode(debugNP.node())

        shape = BulletHeightfieldShape(PNMImage(Filename('heightfield1.png')), 10, ZUp)

        node = BulletRigidBodyNode('Ground')
        node.addShape(shape)
        
        np = render.attachNewNode(node)
        self.world.attachRigidBody(node)

        self.camLens.set_fov(90)
        self.camLens.set_near_far(0.1, 50000)

        self.terrain_node = ShaderTerrainMesh()

        heightfield = self.loader.loadTexture("heightfield.png")
        heightfield.wrap_u = SamplerState.WM_clamp
        heightfield.wrap_v = SamplerState.WM_clamp
        self.terrain_node.heightfield = heightfield

        self.terrain_node.target_triangle_width = 10.0

        self.terrain_node.generate()

        self.terrain = self.render.attach_new_node(self.terrain_node)
        self.terrain.set_scale(128, 128, 10)
        self.terrain.set_pos(-64, -64, -5)

        terrain_shader = Shader.load(Shader.SL_GLSL, "terrain.vert.glsl", "terrain.frag.glsl")
        self.terrain.set_shader(terrain_shader)
        self.terrain.set_shader_input("camera", self.camera)

        self.accept("f3", self.toggleWireframe)

        grass_tex = self.loader.loadTexture("textures/grass.png")
        grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear)
        grass_tex.set_anisotropic_degree(16)
        self.terrain.set_texture(grass_tex)

        taskMgr.add(self.update, 'update')
        
    def update(self, task):
       self.world.doPhysics(globalClock.getDt())
       return task.cont  

ShaderTerrainDemo().run()

Also note that the image that you pass to BulletHeightfieldShape needs to be enlarged by 1 pixel. So ShaderTerrainMesh will do it automatically.

Example to run: shader-terrain.zip (135.2 KB)

2 Likes

Hmm… I see. At a guess, it might be a difference in the points being sampled by each system, then. But what to do about that–if indeed it is the problem–I don’t know, I’m afraid!

Hey, thanks for the tip to enlarge the BulletHeightfieldShape’s image by 1 pixel. I now do it with the Pillow library using the Image.BILINEAR algorithm for pretty good results, I feel.

1 Like