Документация Engee

Освещенность

Страница в процессе перевода.

Возможности освещенности Makie зависят от бэкендов и типов графиков. Они реализованы для типов графиков, связанных с сеткой (mesh, meshscatter, surface), их производных (например, 3D arrows) и, в некоторой степени, графиков volumecontour3d). Что касается бэкендов:

  • GLMakie реализует базовую модель освещенности и будет использоваться по умолчанию на этой странице.

  • WGLMakie реализует упрощенную версию освещенности GLMakie.

  • CairoMakie реализует ограниченную освещенность из-за своих ограниченных возможностей трехмерной отрисовки.

  • RPRMakie реализует часть модели освещения Makies, но также может использовать более сложные методы из RadeonProRender.

Атрибуты материала

В трехмерной отрисовке материал описывает, как объект реагирует на свет. Сюда могут входить цвет объекта, яркость и четкость зеркальных отражений, металлический цвет, шероховатость и многое другое. Однако в Makie модель все еще довольно проста и ограничена. В настоящее время доступны следующие атрибуты материала.

  • diffuse::Vec3f = Vec3f(1.0): определяет, насколько сильными будут диффузные отражения объекта в красном, зеленом и синем цветовом канале. Диффузное отражение — это отражение, при котором входящий свет рассеивается во всех направлениях. Сила этого отражения зависит от количества света, попадающего на поверхность, которое пропорционально dot(light_direction, -normal). Обычно это основной цвет освещенного объекта.

  • specular::Vec3f = Vec3f(0.4): определяет, насколько сильным будет зеркальное отражение в красном, зеленом и синем цветовых каналах. Зеркальное отражение — это прямое отражение света, т. е. такое, при котором входящий угол dot(light_direction, -normal) совпадает с исходящим углом dot(camera_direction, -normal). Оно отвечает за яркие пятна на объектах. Обратите внимание, что при этом не учитывается цвет объекта, поскольку зеркальные отражения обычно совпадают с цветом света.

  • shininess::Float32 = 32f0: определяет резкость зеркальных отражений. Низкий уровень блеска позволит увеличить разницу между входящим и исходящим углом, создавая более крупное и ровное яркое пятно. Высокий уровень блеска соответственно уменьшит размер яркого пятна и увеличит его резкость. Это значение должно быть положительным.

  • backlight::Real = 0: определяет, насколько сильно свет взаимодействует с задней стороной объекта. Установка значения > 0 может быть полезна при визуализации поверхности. (Точнее, расчет освещенности повторяется с инвертированными нормалями, а результат смешивается с backlight в качестве предшествующего коэффициента.)

Note RPRMakie не использует эти атрибуты материала. Он работает с системой материалов RadeonProRender, которая передается через атрибут material. Примеры можно найти на странице по RPRMakie.

Алгоритм освещенности

Управление светом осуществляется через вектор lights в scene и атрибут shading в графике. Как правило, вам не нужно устанавливать shading самостоятельно, поскольку он определяется вектором освещения. Далее приводятся возможные параметры для shading.

  • shading = NoShading отключает расчет освещенности, в результате чего отображается обычный цвет объекта.

  • shading = FastShading включает упрощенную модель освещения, которая позволяет использовать только один AmbientLight и один DirectionalLight.

  • shading = MultiLightShading является эксклюзивным параметром GLMakie, который позволяет использовать несколько источников света (как задано в ScreenConfig, по умолчанию до 64), а также PointLight и SpotLight.

  • shading = Makie.automatic выводит один из вышеперечисленных параметров на основе света в scene.lights.

Note Доступ к базовой сцене Axis3 можно получить с помощью ax.scene.

Для справки: для всех расчетов освещенности (кроме естественного освещения) в GLMakie, WGLMakie и, в некоторой степени, CairoMakie в конечном итоге используется модель отражения Блинна-Фонга, которая сводится к следующему:

function blinn_phong(
        diffuse, specular, shininess, normal, object_color,
        light_color, light_direction, camera_direction
    )
    diffuse_coefficient = max(dot(light_direction, -normal), 0.0)
    H = normalize(light_direction + camera_direction)
    specular_coefficient = max(dot(H, -normal), 0.0)^shininess
    return light_color * (
        diffuse * diffuse_coefficient * object_color +
        specular * specular_coefficient
    )
end

Различные источники света управляют light_direction и могут дополнительно регулировать результат этой функции. Например, SpotLight добавляет коэффициент, который уменьшает интенсивность света за пределами своей области.

Типы освещения

AmbientLight


using CairoMakie

fig = Figure(size = (600, 600))
ax11 = LScene(fig[1, 1], scenekw = (lights = [],))
ax12 = LScene(fig[1, 2], scenekw = (lights = [AmbientLight(RGBf(0, 0, 0))],))
ax21 = LScene(fig[2, 1], scenekw = (lights = [AmbientLight(RGBf(0.7, 0.7, 0.7))],))
ax22 = LScene(fig[2, 2], scenekw = (lights = [AmbientLight(RGBf(0.8, 0.3, 0))],))
for ax in (ax11, ax12, ax21, ax22)
    mesh!(ax, Sphere(Point3f(0), 1f0), color = :white)
end
fig
ed3c97e

DirectionalLight


using GLMakie

fig = Figure(size = (600, 600))
ax11 = LScene(fig[1, 1], scenekw = (lights = [DirectionalLight(RGBf(0, 0, 0), Vec3f(-1, 0, 0))],))
ax12 = LScene(fig[1, 2], scenekw = (lights = [DirectionalLight(RGBf(1, 1, 1), Vec3f(-1, 0, 0))],))
lights = [
    DirectionalLight(RGBf(0, 0, 0.7), Vec3f(-1, -1, 0)),
    DirectionalLight(RGBf(0.7, 0.2, 0), Vec3f(-1, 1, -1)),
    DirectionalLight(RGBf(0.7, 0.7, 0.7), Vec3f(1, -1, -1))
]
ax21 = LScene(fig[2, 1], scenekw = (lights = lights,))
ax22 = LScene(fig[2, 2], scenekw = (lights = [DirectionalLight(RGBf(4, 2, 1), Vec3f(0, 0, -1))],))
for ax in (ax11, ax12, ax21, ax22)
    mesh!(ax, Sphere(Point3f(0), 1f0), color = :white)
end
fig
fcc8cfa

PointLight


using GLMakie

fig = Figure(size = (600, 600))
ax = LScene(fig[1, 1], scenekw = (lights = [PointLight(RGBf(1, 1, 1), Point3f(0, 0, 0))],))
ps = [Point3f(x, y, z) for x in (-1, 0, 1) for y in (-1, 0, 1) for z in (-1, 0, 1)]
meshscatter!(ax, ps, color = :white)
fig
45e2168

using GLMakie

lights = [
    PointLight(RGBf(1, 1, 1), Point3f(0, 0, 5), 50),
    PointLight(RGBf(2, 0, 0), Point3f(-3, -3, 2), 10),
    PointLight(RGBf(0, 2, 0), Point3f(-3,  3, 2), 10),
    PointLight(RGBf(0, 0, 2), Point3f( 3,  3, 2), 10),
    PointLight(RGBf(2, 2, 0), Point3f( 3, -3, 2), 10),
]

fig = Figure(size = (600, 600))
ax = LScene(fig[1, 1], scenekw = (lights = lights,))
ps = [Point3f(x, y, 0) for x in -5:5 for y in -5:5]
meshscatter!(ax, ps, color = :white, markersize = 0.75)
scatter!(ax, map(l -> l.position[], lights), color = map(l -> l.color[], lights), strokewidth = 1, strokecolor = :black)
fig
3a01a77

Высокие значения PointLight и Attenuation позволяют создавать разные цвета на разных расстояниях.

using GLMakie
using GeometryBasics

ps = [
    Point3f(cosd(phi) * cosd(theta), sind(phi) * cosd(theta), sind(theta))
    for theta in range(-20, 20, length = 21) for phi in range(60, 340, length=30)
]
faces = [QuadFace(30j + i, 30j + mod1(i+1, 30), 30*(j+1) + mod1(i+1, 30), 30*(j+1) + i) for j in 0:19 for i in 1:29]
marker_mesh = GeometryBasics.mesh(ps, faces, normal = ps)

lights = [PointLight(RGBf(10, 4, 2), Point3f(0, 0, 0), 5)]

fig = Figure(size = (600, 600), backgroundcolor = :black)
ax = LScene(fig[1, 1], scenekw = (lights = lights,), show_axis = false)
update_cam!(ax.scene, ax.scene.camera_controls, Rect3f(Point3f(-2), Vec3f(4)))
meshscatter!(
    ax, [Point3f(0) for _ in 1:14], marker = marker_mesh, markersize = 0.1:0.2:3.0,
    color = :white, backlight = 1, transparency = false)
fig
bd17a52

SpotLight


using GLMakie

lights = [
    SpotLight(RGBf(1, 0, 0), Point3f(-3, 0, 3), Vec3f(0,  0, -1), Vec2f(0.0, 0.3pi)),
    SpotLight(RGBf(0, 1, 0), Point3f( 0, 3, 3), Vec3f(0, -0.5, -1), Vec2f(0.2pi, 0.25pi)),
    SpotLight(RGBf(0, 0, 1), Point3f( 3, 0, 3), Vec3f(0,  0, -1), Vec2f(0.25pi, 0.25pi)),
]

fig = Figure(size = (600, 600))
ax = LScene(fig[1, 1], scenekw = (lights = lights,))
ps = [Point3f(x, y, 0) for x in -5:5 for y in -5:5]
meshscatter!(ax, ps, color = :white, markersize = 0.75)
scatter!(ax, map(l -> l.position[], lights), color = map(l -> l.color[], lights), strokewidth = 1, strokecolor = :black)
fig
81ba690

RectLight


using GLMakie
using FileIO, GeometryBasics, LinearAlgebra

# Создадим сетку на основе параметров RectLight
function to_mesh(l::RectLight)
    n = -normalize(cross(l.u1[], l.u2[]))
    p = l.position[] - 0.5 * l.u1[] - 0.5 * l.u2[]
    positions = [p, p + l.u1[], p + l.u2[], p + l.u1[] + l.u2[]]
    faces = GLTriangleFace[(1,2,3), (2,3,4)]
    normals = [n,n,n,n]
    return GeometryBasics.Mesh(positions, faces, normal = normals)
end

fig = Figure(backgroundcolor = :black)

# Подготовим освещение
lights = Makie.AbstractLight[
    AmbientLight(RGBf(0.1, 0.1, 0.1)),
    RectLight(RGBf(0.9, 1, 0.8), Rect2f(-1.9, -1.9, 1.8, 1.8)),
    RectLight(RGBf(0.9, 1, 0.8), Rect2f(-1.9,  0.1, 1.8, 1.8)),
    RectLight(RGBf(0.9, 1, 0.8), Rect2f( 0.1,  0.1, 1.8, 1.8)),
    RectLight(RGBf(0.9, 1, 0.8), Rect2f( 0.1, -1.9, 1.8, 1.8)),
]

for l in lights
    if l isa RectLight
        angle = pi/4
        p = l.position[]
        Makie.rotate!(l, Vec3f(0, 1, 0), angle)

        p = 3 * Vec3f(1+sin(angle), 0, cos(angle)) +
            p[1] * normalize(l.u1[]) +
            p[2] * normalize(l.u2[])
        translate!(l, p)
    end
end

# Зададим сцену
scene = LScene(
    fig[1, 1], show_axis = false,
    scenekw=(lights = lights, backgroundcolor = :black, center = false),
)

# пол
msh = mesh!(scene, Rect3f(Point3f(-10, -10, 0.01), Vec3f(20, 20, 0.02)), color = :white)
translate!(msh, 0, 0, -5)

# Кот
cat_mesh = FileIO.load(Makie.assetpath("cat.obj"))
cat_texture = FileIO.load(Makie.assetpath("diffusemap.png"))
p2 = mesh!(scene, cat_mesh, color = cat_texture)
Makie.rotate!(p2, Vec3f(1,0,0), pi/2)
translate!(p2, -2, 2, -5)
scale!(p2, Vec3f(4))

# Маркеры окон/источников света
for l in lights
    if l isa RectLight
        mesh!(to_mesh(l), color = :white, backlight = 1)
    end
end

# разместим камеру
update_cam!(scene.scene, Vec3f(1.5, -13, 2), Vec3f(1, -2, 0), Vec3f(0, 0, 1))

fig
d27f025

EnvironmentLight