JDK-8318985 : [macos] Incorrect 3D lighting on macOS 14 and later
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: jfx21,jfx24
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: os_x
  • CPU: aarch64
  • Submitted: 2023-10-20
  • Updated: 2025-04-24
  • Resolved: 2025-04-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
jfx24.0.2Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
macOS X 14.0 / JDK 21 / JavaFX 21 / M1 Macbook Pro

A DESCRIPTION OF THE PROBLEM :
After updating an M1 mac to macOS 14.0 the lighting in JavaFX 3D is broken. The behavior seems to be independent of JDK and JavaFX version, and does not happen on older versions of macOS (tested with 12.0).

See https://github.com/ennerf/lighting-issue/issues/1 for screenshots.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
git clone https://github.com/ennerf/lighting-issue.git
mvn javafx:run

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The same result as on Windows, Linux, and macOS 12.0
ACTUAL -
Very large shadows in the demo, and many resulting shading artifacts in actual applications

---------- BEGIN SOURCE ----------
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Box;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;


public class Lighting3d extends Application {

    public void start(Stage stage) {
        stage.setTitle(String.format("%s (%s) + Java %s + JavaFX %s",
                System.getProperty("os.name"),
                System.getProperty("os.version"),
                System.getProperty("java.version"),
                System.getProperty("javafx.version")
        ));

        var root = new Group();
        int n = 200;
        double margin = 10;
        double size = 100;
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < n; col++) {
//                var obj = new Box(size, size,  size);
                var obj = new Sphere(size / 2);
                obj.setTranslateX(row * (size + margin));
                obj.setTranslateY(col * (size + margin));
                root.getChildren().add(obj);
            }
        }

        Scene scene = new Scene(root, 1024, 1024, true, SceneAntialiasing.BALANCED);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
---------- END SOURCE ----------

FREQUENCY : always



Comments
A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jfx24u/pull/25 Date: 2025-04-24 14:03:58 +0000
24-04-2025

[jfx24u-fix-request] Approval Request from Jayathirth Rao D V Fix is trivial and it resolves specular power issue on OpenGL pipeline for 3D primitives
24-04-2025

Changeset: 2617ff5c Branch: master Author: Jayathirth D V <jdv@openjdk.org> Date: 2025-04-24 11:40:05 +0000 URL: https://git.openjdk.org/jfx/commit/2617ff5c891ba182581d323d8b424e4b8a6a6b63
24-04-2025

This is a similar problem I had with D3D12 with Ensemble8 3DSphere demo as well - mentioned in comments to JDK-8350773 I think this propagated from D3D backend's Shaders when developing 3D in other backends - in D3D and HLSLv3 pow(0, 0) is resolved as 0 instead of -NaN so it caused no issues there. However, other runtimes treat it as undefined behavior so with different platforms and drivers it can have different results.
23-04-2025

Also verified that using proper default specular power resolves PointLightIlluminationTest system test failures. Patch is also tested using Ensemble8 demo and fx83dfeatures
23-04-2025

As captured previously on Java side we set (1, 1, 1) for specular color and 32 for specular power as default. But when no specular color is set we use specular_none.frag pixel shader for specular data. Here we have vec4(0.0,0.0,0.0,0.0) where vec4.a is specular power which is 0.0. And this value is used in pow() function is shader. Results under this condition are undefined as mentioned at : https://registry.khronos.org/OpenGL-Refpages/es3.0/html/pow.xhtml. Previously similar issue was fixed under https://bugs.openjdk.org/browse/JDK-8094279 but it applies only when we set specular power explicitly and not when we use default values. So we need to use default value for specular power when no specular color is set. With this update the issue captured in this bug will be resolved.
23-04-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jfx/pull/1791 Date: 2025-04-23 12:13:52 +0000
23-04-2025

After debugging with some default values in shaders, it looks like this issue is related to how to we default specular values in shaders. From the Java side even when we don't have specular parameters we set (1, 1, 1, 32) value and in case of OpenGL shader when specular is not set we use (0, 0, 0, 0). If we use 32 specular power with 0 attenuation (0, 0, 0, 32) we see appropriate output. Looks like some of these default values are interpreted differently in aarch64 vs x64 scenario.
07-02-2024

This is still seen on macOS 14.2.1. So this is almost certainly our bug, exposed by changes in the OpenGL drivers for Mac / arm in Sonoma.
23-01-2024

NOTE: the failing system tests are now skipped (see JDK-8321636). As part of fixing this bug, the tests should be re-enabled.
11-12-2023

Update: We are not seeing same issue in under development Metal pipeline on macOS 14. Visually verified what difference we have between Metal and OpenGL shaders for 3D and updated OpenGL shader to match Metal/D3D shader logic. But still i continue to see issue on OpenGL.
15-11-2023

This seems to be limited to aarch64 systems (e.g., M1 or M2). This problem does not reproduce on an Intel x64 system.
27-10-2023

This problem can also be reproduced using the 3D lighting test program in tests/performance/3DLighting. Additionally, the following automated system tests fail: PointLightIlluminationTest > sphereLowerRightPixelColorShouldBeDarkRed FAILED junit.framework.AssertionFailedError: expected:rgba(139,0,0,255) but was:rgba(0,0,0,255) at test.robot.testharness.VisualTestBase.assertColorEquals(VisualTestBase.java:173) at test.robot.test3d.PointLightIlluminationTest.lambda$sphereLowerRightPixelColorShouldBeDarkRed$4(PointLightIlluminationTest.java:115) PointLightIlluminationTest > sphereUpperRightPixelColorShouldBeDarkRed FAILED junit.framework.AssertionFailedError: expected:rgba(139,0,0,255) but was:rgba(0,0,0,255) at test.robot.testharness.VisualTestBase.assertColorEquals(VisualTestBase.java:173) at test.robot.test3d.PointLightIlluminationTest.lambda$sphereUpperRightPixelColorShouldBeDarkRed$3(PointLightIlluminationTest.java:107) PointLightIlluminationTest > sphereUpperLeftPixelColorShouldBeDarkRed FAILED junit.framework.AssertionFailedError: expected:rgba(139,0,0,255) but was:rgba(0,0,0,255) at test.robot.testharness.VisualTestBase.assertColorEquals(VisualTestBase.java:173) at test.robot.test3d.PointLightIlluminationTest.lambda$sphereUpperLeftPixelColorShouldBeDarkRed$2(PointLightIlluminationTest.java:99) PointLightIlluminationTest > sphereLowerLeftPixelColorShouldBeDarkRed FAILED junit.framework.AssertionFailedError: expected:rgba(139,0,0,255) but was:rgba(0,0,0,255) at test.robot.testharness.VisualTestBase.assertColorEquals(VisualTestBase.java:173) at test.robot.test3d.PointLightIlluminationTest.lambda$sphereLowerLeftPixelColorShouldBeDarkRed$5(PointLightIlluminationTest.java:123)
27-10-2023

I have also seen this and was planning to file a bug. We'll use this one instead.
26-10-2023