/**
     * Выполняет растеризацию треугольника и обновляет графический контекст.
     * Добавлено простейшее диффузное освещение.
     *
     * @param points          Координаты точек треугольника на экране.
     * @param normals         Нормали для каждой вершины треугольника.
     * @param zValues         Значения глубины для каждой вершины.
     * @param zBuffer         Z-буфер для отслеживания глубины пикселей.
     * @param graphicsContext Графический контекст для рисования.
     */
    private void rasterizeTriangle(Point2f[] points, Vector3f[] normals, float[] zValues, float[][] zBuffer, GraphicsContext graphicsContext) {
        Color colorA = Color.RED;
        Color colorB = Color.BLACK;
        Color colorC = Color.BLUE;

        int minX = clamp((int) Math.floor(Math.min(points[0].getX(), Math.min(points[1].getX(), points[2].getX()))), 0, zBuffer.length - 1);
        int maxX = clamp((int) Math.ceil(Math.max(points[0].getX(), Math.max(points[1].getX(), points[2].getX()))), 0, zBuffer.length - 1);
        int minY = clamp((int) Math.floor(Math.min(points[0].getY(), Math.min(points[1].getY(), points[2].getY()))), 0, zBuffer[0].length - 1);
        int maxY = clamp((int) Math.ceil(Math.max(points[0].getY(), Math.max(points[1].getY(), points[2].getY()))), 0, zBuffer[0].length - 1);

        for (int x = minX; x <= maxX; x++) {
            for (int y = minY; y <= maxY; y++) {
                float[] barycentric = calculateBarycentricCoordinates(points, x, y);
                float lambda0 = barycentric[0];
                float lambda1 = barycentric[1];
                float lambda2 = barycentric[2];

                if (isInsideTriangle(lambda0, lambda1, lambda2)) {
                    float depth = interpolate(lambda0, lambda1, lambda2, zValues);

                    if (depth < zBuffer[x][y]) {
                        zBuffer[x][y] = depth;

                        // Интерполяция нормалей
                        Vector3f interpolatedNormal = new Vector3f(
                                lambda0 * normals[0].getX() + lambda1 * normals[1].getX() + lambda2 * normals[2].getX(),
                                lambda0 * normals[0].getY() + lambda1 * normals[1].getY() + lambda2 * normals[2].getY(),
                                lambda0 * normals[0].getZ() + lambda1 * normals[1].getZ() + lambda2 * normals[2].getZ()
                        );

                        // Вычисление освещенности
                        double l = Math.max(0.0, -interpolatedNormal.dot(lightDirection));
                        double brightness = (1 - lightingCoefficient) + lightingCoefficient * l;

                        // Интерполируем цвет на основе барицентрических координат
                       // Color interpolatedColor = interpolateColor(lambda0, lambda1, lambda2, colorA, colorB, colorC);
                        Color interpolatedColor = Color.AQUA;

                        // Применение освещения к интерполированному цвету
                        Color finalColor = new Color(
                                clamp(interpolatedColor.getRed() * brightness),
                                clamp(interpolatedColor.getGreen() * brightness),
                                clamp(interpolatedColor.getBlue() * brightness),
                                interpolatedColor.getOpacity()
                        );

                        graphicsContext.getPixelWriter().setColor(x, y, finalColor);
                    }
                }
            }
        }
    }

добавь примитивное освещение на основе сглаживания нормалей. если надо допиши доп методы