[SOLVED] Incorrect Shading on Instances

I’m using my own instancing implementation (apart from InstancedNode) to do meshed particles. I’ve succeeded in making the instancing work, but for whatever reason, the shading is all messed up.

This should illustrate the problem (sorry for the poor quality).

gif of shading issue

The instances are rotating as expected, but the shading does not change with the rotation, which makes me think normals/tangents are not being properly updated for each instance.

Here’s the related code:

// in subclass of Geometry
@Override
public void updateLogicalState(float tpf) {
    if (capacity != group.capacity()) {
        initBuffers();
    }
    VertexBuffer ivb = mesh.getBuffer(VertexBuffer.Type.InstanceData);
    FloatBuffer instances = (FloatBuffer)ivb.getData();        
    instances.clear();
    if (!group.isEmpty()) {
        // write transform matrix per particle
        for (ParticleData p : group) {
            MeshUtils.writeMatrix4(instances, p.transform.toTransformMatrix(), false);
        }
    } else {
        // at least one matrix needs to be written
        MeshUtils.writeMatrix4(instances, Matrix4f.IDENTITY, false);
    }
    instances.flip();
    ivb.updateData(instances);
    mesh.updateCounts();
    mesh.updateBound();
}
private void initBuffers() {  
    capacity = group.capacity();
    FloatBuffer ib = BufferUtils.createFloatBuffer(capacity * 16);
    VertexBuffer vb = MeshUtils.initializeVertexBuffer(mesh,
        VertexBuffer.Type.InstanceData,
        VertexBuffer.Usage.Stream,
        VertexBuffer.Format.Float,
        ib, 16
    );
    vb.setInstanced(true);
}
public static VertexBuffer initializeVertexBuffer(Mesh mesh, Type type, Usage usage, Format format, Buffer data, int components) {
    VertexBuffer buf = mesh.getBuffer(type);
    if (buf != null) {
        buf.updateData(data);
    } else {
        buf = new VertexBuffer(type);
        buf.setupData(usage, components, format, data);
        mesh.setBuffer(buf);
    }
    return buf;
}
public static void writeMatrix4(FloatBuffer fb, Matrix4f matrix, boolean rowMajor) {
    float[] mat = new float[16];
    matrix.get(mat, rowMajor);
    fb.put(mat);
}

So you also have your own shaders? Or you’ve made sure your buffers are setup in a JME-compatible way?

Meaning, you expect the spheres to be lit on a constant side facing the light but the lighting is rotating with the object?

That means your shader is not rotating the normals correctly. (tangents only come into play for bumps and normal maps.) Normals need to be rotated to world space just like the other model coordinates (just without translation).

Lighting.j3md should be doing this for you automatically if you set the buffers up correctly.

1 Like

Since I’m using PBRLighting, I guess I messed up the instance buffer, since that is the only buffer I’m messing with. Am I generating the transform matrix wrong or something?

Here’s the full code and the test suite if that helps.

I’d have to look through the JME instance source code to be sure (and that’s also easy enough for you to do, too, I guess)… but I thought JME expected a quaternion and not a matrix. I may be thinking of hardware skinning.

The InstancedXXX classes or the instancing shader lib would tell you for sure.

I’ve also done instancing without using JME’s InstancedXXX classes but I was able to make it work by looking at those classes.

1 Like

I haven’t checked the full code, but what i see from the screencap is that the positions correctly appear to be in world space , while normals in model space.
model->world should be part of the instance buffer (iirc), so i think you are correctly multiplying the position but not the normal, or the buffer is wrong

EDIT:

I’ve checked your code, somehow i couldn’t figure out which build tool you used, so i’ve rebuilt it as a standard start.jmonkeyengine.org template.
Indeed your instance buffer appears to be wrong

Try this:
in MeshUtils.writeTransformMatrix add

        tempMat3.invertLocal();

after

        matrix.toRotationMatrix(tempMat3);

In InstanceParticleGeometry.updateLogicalState replace

for (ParticleData p : group) {
      MeshUtils.writeMatrix4(instances, p.transform.toTransformMatrix(),false);
}

with

for (ParticleData p : group) {
     MeshUtils.writeTransformMatrix(instances, p.transform.toTransformMatrix());
}

The rotation is supposed to be a quaternion for instancing.

Very cool project btw.

3 Likes

Thanks, that fixed it! :+1:
I suspected a quaternion was supposed to be inserted, because that’s what InstancedGeometry does, but I didn’t realize the rotation matrix needed to be inverted as well.

Sorry about that. I’m using ant, admittedly.