Lighting on merged Object3Ds

Started by AeroShark333, December 03, 2021, 01:37:22 AM

Previous topic - Next topic

AeroShark333

Hello again,

I'm having some lighting issues when using Object3D.mergeObjects(obj1, obj2)

See image: https://imgur.com/a/aIF67LC

I believe the main cause of this is that the Object3D's overlap..?
(road and the dirt on the side are different layers for instance, at slightly different heights (dirt layer is above road layer).
When rendering it as separate Object3D's the issue is not there.

Any way to solve this without having to flatten the layers in height? I believe the merging of Object3D's messes up the normal vectors or something..? Can this be prevented so it renders similarly as if it were two separate Object3D's?

I'm having similar issues when merging other parts of the scene. Sometimes things appear too bright, sometimes there's dark areas.
It was worse like this before https://imgur.com/a/kfvdjI8 (because one Object3D accidentally had setTransparency, affecting the final merged Object3D...) Removing setTransparency improves it but sometimes there's still random brightly lit parts in the scene.. I wish to get the same visual results as if it was not merged. (Merging seems to boost FPS so that's why..)

Cheers :)

Note:
sideview of road:
dirt layer:   ____________                                _____________
road layer:          -----------------------------------------

EgonOlsen

The merging does nothing to the normals nor does an overlap, at least not on itself. Are these meshes sharing coordinates? You might want to try to call build() on the objects before merging them and not afterwards to see if that helps.

AeroShark333

#2
Quote from: EgonOlsen on December 03, 2021, 07:14:31 AM
Are these meshes sharing coordinates?
No, but they are usually very close (almost negligible), though probably they don't get merged to be using them as the same coordinates as they are still slightly different.

Quote from: EgonOlsen on December 03, 2021, 07:14:31 AM
You might want to try to call build() on the objects before merging them and not afterwards to see if that helps.
This didn't work

I fixed part of the issue by making them share coordinates (I believe the overlap makes the lower layer 'unlit..'..? or something)
But I still have the lighting issue for some other parts (but these appear too bright..? for unknown reason, they also flicker)

EDIT: Images for clarification: https://imgur.com/a/FYNF3Wm
It might not seem like a lot in the images, but it flickers from dark to bright so it is very noticable in that way

EgonOlsen

Each object will be lit by itself, no matter how they overlap with each other. Regarding the lighting, they are completely independant. Maybe it's just caused by way in which the normals are created? A vertex normal is calculated by taking the face normals of the adjacent polygons and averaging them. If you, for example, stack boxes onto another the normals on the lower corners will face something like 45° down while the ones on the upper corners will face 45° up. That will cause uneven lighting on something like a wall that consists of these boxes.


AeroShark333

Quote from: EgonOlsen on December 10, 2021, 03:34:44 PM
Each object will be lit by itself, no matter how they overlap with each other. Regarding the lighting, they are completely independant. Maybe it's just caused by way in which the normals are created? A vertex normal is calculated by taking the face normals of the adjacent polygons and averaging them. If you, for example, stack boxes onto another the normals on the lower corners will face something like 45° down while the ones on the upper corners will face 45° up. That will cause uneven lighting on something like a wall that consists of these boxes.
Is it possible to merge Object3D's without these vertices being combined (or normals being recalculated) but instead literally keep the original meshes and add them together (non-optimized), so that the normals stay as the original Object3D's? (if this makes sense)

EgonOlsen

Actually, if the normals have been calculated (or loaded from file with Config.useNormalsFromOBJ=true) already before merging, they won't be recalculated after the merge when you call build on the merged object. I'm not sure if your issue is related to merging at all, though. I rather think it's caused by normal vectors that aren't calculated in a why that fits your scene. Does the file from which these objects come includes normals? In that case, you could try the config switch mentioned above.

AeroShark333

#6
Quote from: EgonOlsen on December 11, 2021, 07:40:22 PM
Actually, if the normals have been calculated (or loaded from file with Config.useNormalsFromOBJ=true) already before merging, they won't be recalculated after the merge when you call build on the merged object. I'm not sure if your issue is related to merging at all, though. I rather think it's caused by normal vectors that aren't calculated in a why that fits your scene. Does the file from which these objects come includes normals? In that case, you could try the config switch mentioned above.
I'm not using models from files, but I create them using Object3D#addTriangle (as they are simple planes sort of...)
I don't think the Config switch would do anything for this...

I was able to create a test case, however... (it doesn't look amazing but it shows the problem I guess...)
final GLSurfaceView.Renderer testRenderer = new GLSurfaceView.Renderer() {
                private FrameBuffer frameBuffer;
                private World world;
                private Object3D master;
                private Object3D plane1;
                private Object3D plane2;
                private Object3D plane3;
                private Camera camera;

                @Override
                public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

                }

                @Override
                public void onSurfaceChanged(GL10 gl10, int w, int h) {
                    Config.nearPlane = 1f;
                    Config.glIgnoreNearPlane = false;

                    frameBuffer = new FrameBuffer(w, h);
                    world = new World();
                    world.setAmbientLight(0, 0, 0);
                    Light l1 = new Light(world);
                    l1.setPosition(new SimpleVector(0f, 6f, 0f));
                    camera = world.getCamera();

                    final Texture sphereTexture = new Texture(2, 2, new RGBColor(75, 0, 0));
                    final Texture overlayTexture = new Texture(2, 2, new RGBColor(0, 0, 75));
                    final Texture planeTexture = new Texture(2, 2, new RGBColor(0, 75, 0));

                    final TextureManager textureManager = TextureManager.getInstance();
                    textureManager.addTexture("tex0", sphereTexture);
                    textureManager.addTexture("tex1", overlayTexture);
                    textureManager.addTexture("tex2", planeTexture);

                    master = Object3D.createDummyObj();

                    plane1 = new Object3D(4);
                    plane2 = new Object3D(8);
                    plane3 = new Object3D(8);

                    plane1.addTriangle(new SimpleVector(-5.55, -0.941941941, -1.0), 0.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f);
                    plane1.addTriangle(new SimpleVector(5.55, -0.941941941, 1.0), 1.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f);
                    //roadMaster.setAdditionalColor(100, 100, 100);

                    plane1.setAdditionalColor(100, 100, 100);
                    plane1.compile(false, true);
                    plane1.strip();
                    plane1.build();
                    plane1.setTexture("tex0");

                    plane2.addTriangle(new SimpleVector(-6.6, -0.939, -1.02), 0.5f, 0.0f, new SimpleVector(-6.6, -0.939, 1.02), 0.5f, 1.0f, new SimpleVector(-5.55, -0.941941941, -1.02), 1.0f, 0.0f);
                    plane2.addTriangle(new SimpleVector(-5.55, -0.941941941, 1.02), 1.0f, 1.0f, new SimpleVector(-5.55, -0.941941941, -1.02), 1.0f, 0.0f, new SimpleVector(-6.6, -0.939, 1.02), 0.5f, 1.0f);
                    plane2.addTriangle(new SimpleVector(5.55, -0.941941941, -1.02), 0.0f, 0.0f, new SimpleVector(5.55, -0.941941941, 1.02), 0.0f, 1.0f, new SimpleVector(6.6, -0.939, -1.02), 0.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(6.6, -0.939, 1.02), 0.5f, 1.0f, new SimpleVector(6.6, -0.939, -1.02), 0.5f, 0.0f, new SimpleVector(5.55, -0.941941941, 1.02), 0.0f, 1.0f);

                    plane2.setAdditionalColor(100, 100, 100);
                    plane2.compile(false, true);
                    plane2.strip();
                    plane2.build();
                    plane2.setTexture("tex1");

                    float heightMultiplier = 333f;
                    float lastHeightLeft = 0.03f;
                    float newHeightLeft = 0.024f;
                    float newHeightRight = 0.012f;
                    float lastHeightRight = 0.01f;

                    // base

                    plane3.setAdditionalColor(100, 100, 100);
                    plane3.addTriangle(new SimpleVector(-11.4f, lastHeightLeft * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(-6.6f, -0.939, -1.02), 1.0f, 0.0f);
                    plane3.addTriangle(new SimpleVector(-6.6f, -0.939, 1.02), 1.0f, 1.0f, new SimpleVector(-6.6f, -0.939, -1.02), 1.0f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f);
                    plane3.addTriangle(new SimpleVector(6.6f, -0.939, -1.02), 0.0f, 0.0f, new SimpleVector(6.6f, -0.939, 1.02), 0.0f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(11.4f, newHeightRight * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(6.6f, -0.939, 1.02), 0.0f, 1.0f);
                    plane3.setTexture("tex2");
                    plane3.build();

                    boolean merge = false;

                    if(merge){
                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        world.addObject( mergeObj);
                    }else {
                        world.addObject(plane1);
                        world.addObject(plane2);
                        world.addObject(plane3);

                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        world.addObject( mergeObj);
                        mergeObj.setSpecularLighting(false);
                        mergeObj.translate(0,0,3f);
                    }
                }

                @Override
                public void onDrawFrame(GL10 gl10) {
                    frameBuffer.clear();
                    final float maxDistance = 150f;
                    camera.setPosition(-(0.5f * ((float) Math.abs(Math.cos(world.getFrameCounter() * 0.03f))) + 0.25f) * maxDistance, 30f, 0f);
                    camera.lookAt(new SimpleVector(0,0,0));
                    world.renderScene(frameBuffer);
                    world.draw(frameBuffer);
                    frameBuffer.display();
                }
            };

            // Attach to some GLSurfaceView...
            viewer.setRenderer(testRenderer);


Results: https://imgur.com/a/KffOw9m (left = merged object, right = not merged objects)
The first two images show the same but at a slightly different camera distance/angle, and you can see that the green tile on the left object is slightly darker than the in the second image.
In the last image, you can see some shading pattern on the red and green on the left object.

These do share coordinates however... maybe that nullifies the build() effect...

EDIT:
Okay... apparently it still happens when the coordinates are NOT shared...
So now I don't understand why build() didn't work...

Code snippet for non-sharing coordinates (replace these lines in the test case...):
plane2.addTriangle(new SimpleVector(-6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f);
                    plane2.addTriangle(new SimpleVector(-5.55, - 0.9, 1.02), 1.0f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f);
                    plane2.addTriangle(new SimpleVector(5.55, - 0.9, -1.02), 0.0f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f);

                    plane2.setAdditionalColor(100, 100, 100);
                    plane2.compile(false, true);
                    plane2.strip();
                    plane2.build();
                    plane2.setTexture("tex1");

                    float heightMultiplier = 333f;
                    float lastHeightLeft = 0.03f;
                    float newHeightLeft = 0.024f;
                    float newHeightRight = 0.012f;
                    float lastHeightRight = 0.01f;

                    // base

                    plane3.setAdditionalColor(100, 100, 100);
                    plane3.addTriangle(new SimpleVector(-11.4f, lastHeightLeft * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f);
                    plane3.addTriangle(new SimpleVector(-6.6f, -0.941941941, 1.02), 1.0f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f);
                    plane3.addTriangle(new SimpleVector(6.6f, -0.941941941, -1.02), 0.0f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(11.4f, newHeightRight * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f);
                    plane3.setTexture("tex2");
                    plane3.build();


EDIT2: I forgot to set the additionalColor on the merged object... Just forget everything above...
Here is a test case without any additionalColor:
final GLSurfaceView.Renderer testRenderer = new GLSurfaceView.Renderer() {
                private FrameBuffer frameBuffer;
                private World world;
                private Object3D master;
                private Object3D plane1;
                private Object3D plane2;
                private Object3D plane3;
                private Camera camera;

                @Override
                public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

                }

                @Override
                public void onSurfaceChanged(GL10 gl10, int w, int h) {
                    Config.nearPlane = 1f;
                    Config.glIgnoreNearPlane = false;

                    frameBuffer = new FrameBuffer(w, h);
                    world = new World();
                    world.setAmbientLight(0, 0, 0);
                    Light l1 = new Light(world);
                    l1.setPosition(new SimpleVector(0f, 6f, 0f));
                    camera = world.getCamera();

                    final Texture sphereTexture = new Texture(2, 2, new RGBColor(75, 0, 0));
                    final Texture overlayTexture = new Texture(2, 2, new RGBColor(0, 0, 75));
                    final Texture planeTexture = new Texture(2, 2, new RGBColor(0, 75, 0));

                    final TextureManager textureManager = TextureManager.getInstance();
                    textureManager.addTexture("tex0", sphereTexture);
                    textureManager.addTexture("tex1", overlayTexture);
                    textureManager.addTexture("tex2", planeTexture);

                    master = Object3D.createDummyObj();

                    plane1 = new Object3D(4);
                    plane2 = new Object3D(8);
                    plane3 = new Object3D(8);

                    plane1.addTriangle(new SimpleVector(-5.55, -0.941941941, -1.0), 0.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f);
                    plane1.addTriangle(new SimpleVector(5.55, -0.941941941, 1.0), 1.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f);
                    //roadMaster.setAdditionalColor(100, 100, 100);

                    //plane1.setAdditionalColor(100, 100, 100);
                    plane1.compile(false, true);
                    plane1.strip();
                    plane1.build();
                    plane1.setTexture("tex0");

                    plane2.addTriangle(new SimpleVector(-6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f);
                    plane2.addTriangle(new SimpleVector(-5.55, - 0.9, 1.02), 1.0f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f);
                    plane2.addTriangle(new SimpleVector(5.55, - 0.9, -1.02), 0.0f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f);

                    //plane2.setAdditionalColor(100, 100, 100);
                    plane2.compile(false, true);
                    plane2.strip();
                    plane2.build();
                    plane2.setTexture("tex1");

                    float heightMultiplier = 333f;
                    float lastHeightLeft = 0.03f;
                    float newHeightLeft = 0.024f;
                    float newHeightRight = 0.012f;
                    float lastHeightRight = 0.01f;

                    // base

                    //plane3.setAdditionalColor(100, 100, 100);
                    plane3.addTriangle(new SimpleVector(-11.4f, lastHeightLeft * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f);
                    plane3.addTriangle(new SimpleVector(-6.6f, -0.941941941, 1.02), 1.0f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f);
                    plane3.addTriangle(new SimpleVector(6.6f, -0.941941941, -1.02), 0.0f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(11.4f, newHeightRight * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f);
                    plane3.setTexture("tex2");
                    plane3.build();

                    boolean merge = false;

                    if(merge){
                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        world.addObject( mergeObj);
                    }else {
                        world.addObject(plane1);
                        world.addObject(plane2);
                        world.addObject(plane3);

                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        world.addObject( mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        mergeObj.translate(0,0,3f);
                        plane1.translate(0,0,-3f);
                        plane2.translate(0,0,-3f);
                        plane3.translate(0,0,-3f);
                    }
                }

                @Override
                public void onDrawFrame(GL10 gl10) {
                    frameBuffer.clear();
                    final float maxDistance = 150f;
                    camera.setPosition(-(0.5f * ((float) Math.abs(Math.cos(world.getFrameCounter() * 0.03f))) + 0.25f) * maxDistance, 30f, 0f);
                    camera.lookAt(new SimpleVector(0,0,0));
                    world.renderScene(frameBuffer);
                    world.draw(frameBuffer);
                    frameBuffer.display();
                }
            };

            // Attach to some GLSurfaceView...
            viewer.setRenderer(testRenderer);


EDIT3: An animated image for the last test case: https://imgur.com/a/wR8MqgG
I believe I get similar-ish results but it gets brighter for me randomly but I guess it's a similar idea

EDIT4: I have a slight feeling this issue is there because the merged Object3D is multi-textured..? I'm not entirely sure...
The issue seems to disappear if it has 1 or 2 different textures only (but when you add the third, the lighting seems to behave differently)

EgonOlsen

Honestly, I'm not sure what I'm supposed to see on these images!? The last one (the one with the animation) doesn't work for me on the desktop. I can view it on my phone though, but all I'm seeing are two bouncing planes...I'm not sure what to look out for, they look fine (albeit highly compressed) to me.

Anyway, the lighting calculation done by the default shaders doesn't depend on the number of texture stages. It just multiplies the calculated light color/intensity with the pixel after all texture stages have been blended together. If one texture is very dark or the blending mode is some unusual one, the results might not be what one expects, but I don't think that this is the case here.

Looking at your code, you:


  • create the plane/object
  • compile it (not needed if you merge them anyway)
  • strip it (not sure, if that's a good idea to this before calling build(), but if you merge later, it's not needed anyway)
  • build it. This can be done here, but actually doesn't have to.

I suggest to do this instead:


  • create the plane
  • merge
  • build()
  • compile (if needed, actually I don't see why it should)
  • strip()

I don't think that this will change something but it's actually the way it's intended to be done and maybe your way has some effect on something that I don't see ATM.

AeroShark333

I changed the code to your suggestions (see below):

Here are the animations:
https://imgur.com/a/Jk8zt2g
https://imgur.com/a/Zwxem7I

Code:
final GLSurfaceView.Renderer testRenderer = new GLSurfaceView.Renderer() {
                private FrameBuffer frameBuffer;
                private World world;
                private Object3D master;
                private Object3D plane1;
                private Object3D plane2;
                private Object3D plane3;
                private Camera camera;

                @Override
                public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

                }

                @Override
                public void onSurfaceChanged(GL10 gl10, int w, int h) {
                    Config.nearPlane = 1f;
                    Config.glIgnoreNearPlane = false;

                    frameBuffer = new FrameBuffer(w, h);
                    world = new World();
                    world.setAmbientLight(0, 0, 0);
                    Light l1 = new Light(world);
                    l1.setPosition(new SimpleVector(0f, 6f, 0f));
                    camera = world.getCamera();

                    final Texture sphereTexture = new Texture(2, 2, new RGBColor(75, 0, 0));
                    final Texture overlayTexture = new Texture(2, 2, new RGBColor(0, 0, 75));
                    final Texture planeTexture = new Texture(2, 2, new RGBColor(0, 75, 0));

                    final TextureManager textureManager = TextureManager.getInstance();
                    textureManager.addTexture("tex0", sphereTexture);
                    textureManager.addTexture("tex1", overlayTexture);
                    textureManager.addTexture("tex2", planeTexture);

                    master = Object3D.createDummyObj();

                    plane1 = new Object3D(4);
                    plane2 = new Object3D(8);
                    plane3 = new Object3D(8);

                    plane1.addTriangle(new SimpleVector(-5.55, -0.941941941, -1.0), 0.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f);
                    plane1.addTriangle(new SimpleVector(5.55, -0.941941941, 1.0), 1.0f, 1.0f, new SimpleVector(5.55, -0.941941941, -1.0), 1.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 1.0), 0.0f, 1.0f);
                    //roadMaster.setAdditionalColor(100, 100, 100);

                    //plane1.setAdditionalColor(100, 100, 100);
                    //plane1.compile(false, true);
                    //plane1.strip();
                    //plane1.build();
                    plane1.setTexture("tex0");

                    plane2.addTriangle(new SimpleVector(-6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f);
                    plane2.addTriangle(new SimpleVector(-5.55, - 0.9, 1.02), 1.0f, 1.0f, new SimpleVector(-5.55, - 0.9, -1.02), 1.0f, 0.0f, new SimpleVector(-6.6, - 0.9, 1.02), 0.5f, 1.0f);
                    plane2.addTriangle(new SimpleVector(5.55, - 0.9, -1.02), 0.0f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(6.6, - 0.9, 1.02), 0.5f, 1.0f, new SimpleVector(6.6, - 0.9, -1.02), 0.5f, 0.0f, new SimpleVector(5.55, - 0.9, 1.02), 0.0f, 1.0f);

                    //plane2.setAdditionalColor(100, 100, 100);
                    //plane2.compile(false, true);
                    //plane2.strip();
                    //plane2.build();
                    plane2.setTexture("tex1");

                    float heightMultiplier = 333f;
                    float lastHeightLeft = 0.03f;
                    float newHeightLeft = 0.024f;
                    float newHeightRight = 0.012f;
                    float lastHeightRight = 0.01f;

                    // base

                    //plane3.setAdditionalColor(100, 100, 100);
                    plane3.addTriangle(new SimpleVector(-11.4f, lastHeightLeft * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f);
                    plane3.addTriangle(new SimpleVector(-6.6f, -0.941941941, 1.02), 1.0f, 1.0f, new SimpleVector(-6.6f, -0.941941941, -1.02), 1.0f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 1.02), 0.5f, 1.0f);
                    plane3.addTriangle(new SimpleVector(6.6f, -0.941941941, -1.02), 0.0f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(11.4f, newHeightRight * heightMultiplier / 3f, 1.02), 0.5f, 1.0f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -1.02), 0.5f, 0.0f, new SimpleVector(6.6f, -0.941941941, 1.02), 0.0f, 1.0f);
                    plane3.setTexture("tex2");
                    //plane3.build();

                    boolean merge = false;

                    if(merge){
                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        mergeObj.build();
                        mergeObj.compile(false, true);
                        mergeObj.strip();
                        world.addObject( mergeObj);
                    }else {
                        world.addObject(plane1);
                        world.addObject(plane2);
                        world.addObject(plane3);

                        Object3D mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        world.addObject( mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        mergeObj.translate(0,0,3f);
                        plane1.translate(0,0,-3f);
                        plane2.translate(0,0,-3f);
                        plane3.translate(0,0,-3f);
                    }
                }

                @Override
                public void onDrawFrame(GL10 gl10) {
                    frameBuffer.clear();
                    final float maxDistance = 150f;
                    camera.setPosition(-(0.5f * ((float) Math.abs(Math.cos(world.getFrameCounter() * 0.03f))) + 0.25f) * maxDistance, 30f, 0f);
                    camera.lookAt(new SimpleVector(0,4.5f,0));
                    world.renderScene(frameBuffer);
                    world.draw(frameBuffer);
                    frameBuffer.display();
                }
            };

            // Attach to some GLSurfaceView...
            viewer.setRenderer(testRenderer);


If I do:
plane2.setTexture("tex2");
the problem disappears..?  ???

EgonOlsen

Have you tried to create larger textures? Maybe it's a rendering glitch caused by the small size and mip-mapping somehow?

AeroShark333

Quote from: EgonOlsen on December 17, 2021, 05:29:42 PM
Have you tried to create larger textures? Maybe it's a rendering glitch caused by the small size and mip-mapping somehow?
I tried:
final Texture sphereTexture = new Texture(2048, 2048, new RGBColor(75, 0, 0));
                    final Texture overlayTexture = new Texture(2048, 2048, new RGBColor(0, 0, 75));
                    final Texture planeTexture = new Texture(2048, 2048, new RGBColor(0, 75, 0));

                    sphereTexture.setMipmap(false);
                    overlayTexture.setMipmap(false);
                    planeTexture.setMipmap(false);

Both true and false for mip-mapping. Seems to behave as before.

EgonOlsen

I'm not sure if what's going on then, but then again, I'm not sure what the actual issue really is. The videos don't really make it clear to me. Can you create something that is more, well, obvious?

AeroShark333

#12
Quote from: EgonOlsen on December 30, 2021, 04:41:12 PM
I'm not sure if what's going on then, but then again, I'm not sure what the actual issue really is. The videos don't really make it clear to me. Can you create something that is more, well, obvious?
I'm not sure if this is any helpful but I enlarged the shapes..? Well... here goes:

Explanation:
If the sphere on the right is visible, then it will only render the merged Object3D.
If the sphere on the right is NOT visible, then it will only render the unmerged Object3Ds.
The merged Object3D consists of the unmerged Object3Ds.

Expectation:
Similar visuals for both scenarios

Result:
Not similar visuals; there seem to be lighting differences (bugs?)
Only when all of the unmerged Object3Ds use 1 texture (instead of 2 or 3 different textures), the bug seems to disappear.

Video proof:
https://imgur.com/a/Ox51NRL
https://imgur.com/a/Ag76Sml (different texture variations)

Test code (for reproducing):
final GLSurfaceView.Renderer testRenderer = new GLSurfaceView.Renderer() {
                private FrameBuffer frameBuffer;
                private World world;
                private Object3D master;
                private Object3D plane1;
                private Object3D plane2;
                private Object3D plane3;
                private Camera camera;
                private boolean offset = false;
                private Object3D mergeObj;

                @Override
                public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

                }

                @Override
                public void onSurfaceChanged(GL10 gl10, int w, int h) {
                    Config.nearPlane = 1f;
                    Config.glIgnoreNearPlane = false;

                    frameBuffer = new FrameBuffer(w, h);
                    world = new World();
                    world.setAmbientLight(0, 0, 0);
                    Light l1 = new Light(world);
                    l1.setPosition(new SimpleVector(0f, 6f, 0f));
                    camera = world.getCamera();

                    final Texture sphereTexture = new Texture(2, 2, new RGBColor(75, 0, 0));
                    final Texture overlayTexture = new Texture(2, 2, new RGBColor(0, 0, 75));
                    final Texture planeTexture = new Texture(2, 2, new RGBColor(0, 75, 0));
                    final Texture whiteTexture = new Texture(2,2, new RGBColor(255,255,255));

                    final TextureManager textureManager = TextureManager.getInstance();
                    textureManager.addTexture("tex0", sphereTexture);
                    textureManager.addTexture("tex1", overlayTexture);
                    textureManager.addTexture("tex2", planeTexture);
                    textureManager.addTexture("tex3", whiteTexture);

                    //master = Object3D.createDummyObj();

                    plane1 = new Object3D(4);
                    plane2 = new Object3D(8);
                    plane3 = new Object3D(8);

                    master = ExtendedPrimitives.createSphere(3.0f,60);
                    master.setTexture("tex3");

                    plane1.addTriangle(new SimpleVector(-5.55, -0.941941941, -4.5), 0.0f, 0.0f, new SimpleVector(-5.55, -0.941941941, 4.5), 0.0f, 4.5f, new SimpleVector(5.55, -0.941941941, -4.5), 4.5f, 0.0f);
                    plane1.addTriangle(new SimpleVector(5.55, -0.941941941, 4.5), 4.5f, 4.5f, new SimpleVector(5.55, -0.941941941, -4.5), 4.5f, 0.0f, new SimpleVector(-5.55, -0.941941941, 4.5), 0.0f, 4.5f);
                    //roadMaster.setAdditionalColor(100, 100, 100);

                    //plane1.setAdditionalColor(100, 100, 100);
                    //plane1.compile(false, true);
                    //plane1.strip();
                    //plane1.build();
                    plane1.setTexture("tex0");

                    plane2.addTriangle(new SimpleVector(-6.6, -0.9, -4.52), 0.5f, 0.0f, new SimpleVector(-6.6, -0.9, 4.52), 0.5f, 4.5f, new SimpleVector(-5.55, -0.9, -4.52), 4.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(-5.55, -0.9, 4.52), 4.5f, 4.5f, new SimpleVector(-5.55, -0.9, -4.52), 4.5f, 0.0f, new SimpleVector(-6.6, -0.9, 4.52), 0.5f, 4.5f);
                    plane2.addTriangle(new SimpleVector(5.55, -0.9, -4.52), 0.0f, 0.0f, new SimpleVector(5.55, -0.9, 4.52), 0.0f, 4.5f, new SimpleVector(6.6, -0.9, -4.52), 0.5f, 0.0f);
                    plane2.addTriangle(new SimpleVector(6.6, -0.9, 4.52), 0.5f, 4.5f, new SimpleVector(6.6, -0.9, -4.52), 0.5f, 0.0f, new SimpleVector(5.55, -0.9, 4.52), 0.0f, 4.5f);

                    //plane2.setAdditionalColor(100, 100, 100);
                    //plane2.compile(false, true);
                    //plane2.strip();
                    //plane2.build();
                    plane2.setTexture("tex1");

                    float heightMultiplier = 333f;
                    float lastHeightLeft = 0.03f;
                    float newHeightLeft = 0.024f;
                    float newHeightRight = 0.012f;
                    float lastHeightRight = 0.01f;

                    // base

                    //plane3.setAdditionalColor(100, 100, 100);
                    plane3.addTriangle(new SimpleVector(-11.4f, lastHeightLeft * heightMultiplier / 3f, -4.52), 0.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 4.52), 0.5f, 4.5f, new SimpleVector(-6.6f, -0.941941941, -4.52), 4.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(-6.6f, -0.941941941, 4.52), 4.5f, 4.5f, new SimpleVector(-6.6f, -0.941941941, -4.52), 4.5f, 0.0f, new SimpleVector(-11.4f, newHeightLeft * heightMultiplier / 3f, 4.52), 0.5f, 4.5f);
                    plane3.addTriangle(new SimpleVector(6.6f, -0.941941941, -4.52), 0.0f, 0.0f, new SimpleVector(6.6f, -0.941941941, 4.52), 0.0f, 4.5f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -4.52), 0.5f, 0.0f);
                    plane3.addTriangle(new SimpleVector(11.4f, newHeightRight * heightMultiplier / 3f, 4.52), 0.5f, 4.5f, new SimpleVector(11.4f, lastHeightRight * heightMultiplier / 3f, -4.52), 0.5f, 0.0f, new SimpleVector(6.6f, -0.941941941, 4.52), 0.0f, 4.5f);
                    plane3.setTexture("tex2");
                    //plane3.build();

                    boolean merge = false;

                    if (merge) {
                        mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        mergeObj.build();
                        mergeObj.compile(false, true);
                        mergeObj.strip();
                        world.addObject(mergeObj);
                    } else {
                        world.addObject(plane1);
                        world.addObject(plane2);
                        world.addObject(plane3);

                        mergeObj = Object3D.mergeObjects(plane1, plane2);
                        mergeObj = Object3D.mergeObjects(plane3, mergeObj);
                        world.addObject(mergeObj);
                        //mergeObj.setAdditionalColor(100, 100, 100);
                        if(offset) {
                            mergeObj.translate(0, 0, 6f);
                            plane1.translate(0, 0, -6f);
                            plane2.translate(0, 0, -6f);
                            plane3.translate(0, 0, -6f);
                        }else{
                            mergeObj.translate(0, 0, 6f);
                            plane1.translate(0, 0, 6f);
                            plane2.translate(0, 0, 6f);
                            plane3.translate(0, 0, 6f);
                            world.addObject(master);
                            master.translate(0,6f,-6f);
                        }
                        mergeObj.build();
                    }
                }

                @Override
                public void onDrawFrame(GL10 gl10) {
                    frameBuffer.clear();
                    final float maxDistance = 105f;
                    if(!offset){
                        if(Math.cos(world.getFrameCounter() * 0.03f)<0.0f){
                            plane3.setVisibility(false);
                            plane2.setVisibility(false);
                            plane1.setVisibility(false);
                            mergeObj.setVisibility(true);
                            master.setVisibility(true);
                        }else{
                            plane3.setVisibility(true);
                            plane2.setVisibility(true);
                            plane1.setVisibility(true);
                            mergeObj.setVisibility(false);
                            master.setVisibility(false);
                        }
                    }
                    camera.setPosition(-(0.5f * ((float) Math.abs(Math.cos(world.getFrameCounter() * 0.03f))) + 0.25f) * maxDistance, 30f, 0f);
                    camera.lookAt(new SimpleVector(0, 6f, 0f));
                    world.renderScene(frameBuffer);
                    world.draw(frameBuffer);
                    frameBuffer.display();
                }
            };

            // Attach to some GLSurfaceView...
            viewer.setRenderer(testRenderer);

EgonOlsen

I made myself a test case from your example. On a Samsung S7, there's no issue. The lighting is consistent whether the merged object is visible or the unmerged ones are. On an old LG G4, I can see the issue, though. I'll have a look...

EgonOlsen

#14
Ok, it's cleary some kind of shader bug (...edit: or maybe not...see post below...). Not in the shader code itself but in the shader compiler of some devices. Or in the GPU...I don't know...

Here's what it does (that's not the exact same code that's used by the engine, but it has the same issue):


vec3 specPart = specularColors * pow(max(0.0, dot(normalize(-vertexPos.xyz), reflect(-surface2Light, normalEye))), shininess);
vertexColor += vec4((diffuseColors * angle + specPart)*(1.0/(1.0+length(lightPositions - vertexPos.xyz)*attenuation)), 1);


As you can see, it calculates a vertex' color. For this, it also takes the specular color into account. If there is none (i.e. specular on the object is off, which is the default), shininess is 1 and specularColors is vec3(0.0, 0.0, 0.0), i.e. zeroed out. A zero vector multiplied by some value (in this case the result of the pow()-calculation) should be...a zero vector. Because, as we all know, 0 times something is still 0. Turns out, that some devices beg to differ.

We can modify the code to make this more clear:


vec3 specPart = vec3(0.0, 0.0, 0.0) * pow(max(0.0, dot(normalize(-vertexPos.xyz), reflect(-surface2Light, normalEye))), shininess);
vertexColor += vec4((diffuseColors * angle + specPart)*(1.0/(1.0+length(lightPositions - vertexPos.xyz) * attenuation)), 1);


Here, specularColors is gone and has been replaced by a static zero vector. Clearly, specPart has to be a zero vector as well in all cases. But it's not. shininess on the other hand is set to 1. However, it too seems to fluctuate in value as well while the shader is executing, which is actually impossible. It's supposed to stay fixed once the shader is executed for a given mesh, hence the name "uniform". Or maybe it doesn't fluctuate but evaluating it in the shader goes wrong for the same reasons that 0 times something is something else rather than 0.

Anyway, if we do this:


vec3 specPart = vec3(0.0, 0.0, 0.0) * pow(max(0.0, dot(normalize(-vertexPos.xyz), reflect(-surface2Light, normalEye))), 1.0);
vertexColor += vec4((diffuseColors * angle + specPart)*(1.0/(1.0+length(lightPositions - vertexPos.xyz)*attenuation)), 1);


i.e. replace shininess with a fixed value (doesn't matter which one, 0,1,10...they all work and give the same results), all of a sudden, 0 times something is 0 again.

However, we can't do that because we need both uniforms in case somebody is acutally using specular lighting. I also tried to split the term into smaller parts, which helped in similar situations in the past, but to no avail.

The fact that the behaviour changes when using a single texture is a red herring IMHO. It also changes/works if you are assigning the red texture to the ball instead of the white one.

I'm not sure what to do here. Are you by any chance already using your own shaders? I that case, we could just cut the specular part (if not used, that is) and call it a day. If not, it might be worth considering to do so.

To be sure: Which device are you using that has this issue? As said, I tried it on a S7 and it works fine. It also works fine in the emulator. I fails on the LG G4.