文章

JupyterLab第一次创建 Notebook 并绘图

JupyterLab第一次创建 Notebook 并绘图

JupyterLab第一次创建 Notebook 并绘图

1. 目标

完成以下操作:

  • 创建第一个 Jupyter Notebook。
  • 绘制一维函数对比图。
  • 绘制二维误差热力图。
  • 输出误差统计。
  • 绘制函数切片图。
  • 将 Notebook 整理为可复用结构。
  • 保存并退出 JupyterLab。

本文假设已完成《JupyterLab 环境安装与初次进入》。


2. 占位符说明

占位符含义本文示例
[PROJECT_ROOT]项目根目录E:\PycharmProjects\RenderingMathLab
[NOTEBOOK_NAME]Notebook 文件名brdf_basic_compare.ipynb

说明:

  • 占位符不是固定要求。
  • 执行操作时, 需要替换为自己的实际值。
  • 本文示例值仅用于演示。

3. 进入 notebooks 目录

操作:

在 JupyterLab 左侧文件列表中, 双击进入:

1
notebooks

预期结果:

当前文件列表显示的是 [PROJECT_ROOT]\notebooks 下的内容。


4. 新建 Notebook

操作:

点击左上角 + 打开 Launcher。

Notebook 区域点击:

1
Python 3

预期结果:

打开一个新的 Notebook 页面。


5. 保存并命名 Notebook

操作:

点击顶部菜单:

1
File -> Save Notebook As...

输入文件名:

1
[NOTEBOOK_NAME]

本文示例:

1
brdf_basic_compare.ipynb

预期结果:

左侧 notebooks 目录中出现:

1
brdf_basic_compare.ipynb

6. Cell 基础操作

Jupyter Notebook 常用 cell 类型:

类型用途
Code写 Python 代码
Markdown写标题和说明文字

常用操作:

操作快捷键
运行当前 cellShift + Enter
保存 NotebookCtrl + S
将 cell 改为 MarkdownEsc, 然后按 M
将 cell 改为 CodeEsc, 然后按 Y

7. 第一次绘制函数曲线

操作:

在第一个 Code cell 中输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0.0, 1.0, 200)

old = x ** 2.0
new = x ** 1.8
diff = new - old

plt.figure(figsize=(8, 4))
plt.plot(x, old, label="old")
plt.plot(x, new, label="new")
plt.plot(x, diff, label="diff")
plt.legend()
plt.grid(True)
plt.xlabel("x")
plt.ylabel("value")
plt.title("Basic Function Compare")
plt.show()

预期结果:

Notebook 下方显示三条曲线:

1
2
3
old
new
diff

8. 创建二维采样域

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
sample_count = 256

ndotv_min = 0.001
ndotv_max = 1.0

roughness_min = 0.001
roughness_max = 1.0

ndotv = np.linspace(ndotv_min, ndotv_max, sample_count)
roughness = np.linspace(roughness_min, roughness_max, sample_count)

NdotV, Roughness = np.meshgrid(ndotv, roughness)

预期结果:

该 cell 无图像输出。


9. 定义 old / new 测试函数

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
def dfg_old(ndotv, roughness):
    return (1.0 - roughness * 0.5) * np.power(1.0 - ndotv, 5.0)


def dfg_new(ndotv, roughness):
    return (1.0 - roughness * 0.35) * np.power(1.0 - ndotv, 4.5)

预期结果:

该 cell 无图像输出。

说明:

这不是正式 DFG 公式。 该函数仅用于验证 Notebook 工作流。


10. 计算 old / new / error

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
old_value = dfg_old(NdotV, Roughness)
new_value = dfg_new(NdotV, Roughness)

diff = new_value - old_value
abs_error = np.abs(diff)

预期结果:

该 cell 无图像输出。


11. 绘制误差热力图

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
plt.figure(figsize=(6, 5))
plt.imshow(
    abs_error,
    origin="lower",
    extent=[ndotv_min, ndotv_max, roughness_min, roughness_max],
    aspect="auto"
)
plt.colorbar(label="abs error")
plt.xlabel("NdotV")
plt.ylabel("roughness")
plt.title("DFG Function Absolute Error")
plt.show()

预期结果:

显示一张二维误差热力图。

图像含义:

图像元素含义
横轴NdotV
纵轴roughness
颜色dfg_newdfg_old 的绝对误差

12. 输出误差统计

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
max_abs_error = np.max(abs_error)
mean_abs_error = np.mean(abs_error)
rmse = np.sqrt(np.mean(diff ** 2))
p95_abs_error = np.percentile(abs_error, 95)
p99_abs_error = np.percentile(abs_error, 99)

print(f"Max Abs Error  : {max_abs_error:.6f}")
print(f"Mean Abs Error : {mean_abs_error:.6f}")
print(f"RMSE           : {rmse:.6f}")
print(f"P95 Abs Error  : {p95_abs_error:.6f}")
print(f"P99 Abs Error  : {p99_abs_error:.6f}")

预期结果:

输出类似:

1
2
3
4
5
Max Abs Error  : 0.149575
Mean Abs Error : 0.025009
RMSE           : 0.038519
P95 Abs Error  : 0.086077
P99 Abs Error  : 0.118000

说明:

该 cell 只输出文本, 不绘图。


13. 绘制三图对比

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
value_min = min(np.min(old_value), np.min(new_value))
value_max = max(np.max(old_value), np.max(new_value))

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

heatmaps = [
    (old_value, "Old Function", value_min, value_max),
    (new_value, "New Function", value_min, value_max),
    (abs_error, "Absolute Error", None, None),
]

for ax, (data, title, vmin, vmax) in zip(axes, heatmaps):
    im = ax.imshow(
        data,
        origin="lower",
        extent=[ndotv_min, ndotv_max, roughness_min, roughness_max],
        aspect="auto",
        vmin=vmin,
        vmax=vmax
    )

    ax.set_title(title)
    ax.set_xlabel("NdotV")
    ax.set_ylabel("roughness")
    fig.colorbar(im, ax=ax)

plt.tight_layout()
plt.show()

预期结果:

显示三张图:

1
2
3
Old Function
New Function
Absolute Error

说明:

Old FunctionNew Function 使用相同色阶, 便于对比。


14. 绘制 old / new 切片曲线

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
roughness_slices = [0.05, 0.25, 0.5, 0.75, 1.0]

plt.figure(figsize=(10, 6))

for r in roughness_slices:
    old_slice = dfg_old(ndotv, r)
    new_slice = dfg_new(ndotv, r)

    plt.plot(ndotv, old_slice, linestyle="--", label=f"old r={r}")
    plt.plot(ndotv, new_slice, linestyle="-", label=f"new r={r}")

plt.xlabel("NdotV")
plt.ylabel("value")
plt.title("Old vs New Function Slices")
plt.grid(True)
plt.legend()
plt.show()

预期结果:

显示多条曲线。 虚线表示 old function, 实线表示 new function。


15. 绘制差异切片曲线

操作:

新建一个 Code cell, 输入以下代码并运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plt.figure(figsize=(10, 5))

for r in roughness_slices:
    old_slice = dfg_old(ndotv, r)
    new_slice = dfg_new(ndotv, r)
    diff_slice = new_slice - old_slice

    plt.plot(ndotv, diff_slice, label=f"r={r}")

plt.axhline(0.0, linewidth=1)
plt.xlabel("NdotV")
plt.ylabel("new - old")
plt.title("Difference Slices")
plt.grid(True)
plt.legend()
plt.show()

预期结果:

显示 new - old 的差异曲线。


16. 整理 Notebook 结构

操作:

在对应位置插入 Markdown cell, 将 Notebook 整理为以下结构:

1
2
3
4
5
6
7
8
1. BRDF / DFG Function Comparison
2. Imports and Settings
3. Function Definitions
4. Sampling Domain
5. Heatmap Comparison
6. Error Statistics
7. Old vs New Slices
8. Difference Slices

建议整理方式:

章节Cell 类型
标题说明Markdown
Imports and SettingsCode
Function DefinitionsCode
Sampling DomainCode
Heatmap ComparisonCode
Error StatisticsCode
Old vs New SlicesCode
Difference SlicesCode

17. 整理后的 Notebook 内容

17.1 标题说明

Cell 类型: Markdown

输入以下内容:

1
2
3
4
5
6
7
8
9
10
# BRDF / DFG Function Comparison

This notebook is used to compare old and new rendering math functions.

Current scope:
- Compare old and new function values.
- Visualize absolute error heatmap.
- Print error statistics.
- Inspect fixed roughness slices.
- Inspect difference slices.

17.2 Imports and Settings

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
import numpy as np
import matplotlib.pyplot as plt

sample_count = 256

ndotv_min = 0.001
ndotv_max = 1.0

roughness_min = 0.001
roughness_max = 1.0

17.3 Function Definitions

Cell 类型: Code

1
2
3
4
5
6
def dfg_old(ndotv, roughness):
    return (1.0 - roughness * 0.5) * np.power(1.0 - ndotv, 5.0)


def dfg_new(ndotv, roughness):
    return (1.0 - roughness * 0.35) * np.power(1.0 - ndotv, 4.5)

17.4 Sampling Domain

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
ndotv = np.linspace(ndotv_min, ndotv_max, sample_count)
roughness = np.linspace(roughness_min, roughness_max, sample_count)

NdotV, Roughness = np.meshgrid(ndotv, roughness)

old_value = dfg_old(NdotV, Roughness)
new_value = dfg_new(NdotV, Roughness)

diff = new_value - old_value
abs_error = np.abs(diff)

17.5 Heatmap Comparison

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
value_min = min(np.min(old_value), np.min(new_value))
value_max = max(np.max(old_value), np.max(new_value))

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

heatmaps = [
    (old_value, "Old Function", value_min, value_max),
    (new_value, "New Function", value_min, value_max),
    (abs_error, "Absolute Error", None, None),
]

for ax, (data, title, vmin, vmax) in zip(axes, heatmaps):
    im = ax.imshow(
        data,
        origin="lower",
        extent=[ndotv_min, ndotv_max, roughness_min, roughness_max],
        aspect="auto",
        vmin=vmin,
        vmax=vmax
    )

    ax.set_title(title)
    ax.set_xlabel("NdotV")
    ax.set_ylabel("roughness")
    fig.colorbar(im, ax=ax)

plt.tight_layout()
plt.show()

17.6 Error Statistics

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
11
max_abs_error = np.max(abs_error)
mean_abs_error = np.mean(abs_error)
rmse = np.sqrt(np.mean(diff ** 2))
p95_abs_error = np.percentile(abs_error, 95)
p99_abs_error = np.percentile(abs_error, 99)

print(f"Max Abs Error  : {max_abs_error:.6f}")
print(f"Mean Abs Error : {mean_abs_error:.6f}")
print(f"RMSE           : {rmse:.6f}")
print(f"P95 Abs Error  : {p95_abs_error:.6f}")
print(f"P99 Abs Error  : {p99_abs_error:.6f}")

17.7 Old vs New Slices

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
roughness_slices = [0.05, 0.25, 0.5, 0.75, 1.0]

plt.figure(figsize=(10, 6))

for r in roughness_slices:
    old_slice = dfg_old(ndotv, r)
    new_slice = dfg_new(ndotv, r)

    plt.plot(ndotv, old_slice, linestyle="--", label=f"old r={r}")
    plt.plot(ndotv, new_slice, linestyle="-", label=f"new r={r}")

plt.xlabel("NdotV")
plt.ylabel("value")
plt.title("Old vs New Function Slices")
plt.grid(True)
plt.legend()
plt.show()

17.8 Difference Slices

Cell 类型: Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
plt.figure(figsize=(10, 5))

for r in roughness_slices:
    old_slice = dfg_old(ndotv, r)
    new_slice = dfg_new(ndotv, r)
    diff_slice = new_slice - old_slice

    plt.plot(ndotv, diff_slice, label=f"r={r}")

plt.axhline(0.0, linewidth=1)
plt.xlabel("NdotV")
plt.ylabel("new - old")
plt.title("Difference Slices")
plt.grid(True)
plt.legend()
plt.show()

18. 重新运行全部 Cell

操作:

点击顶部菜单:

1
Run -> Run All Cells

预期结果:

全部 cell 从上到下运行完成, 且没有报错。

如果需要清空旧状态后重新运行, 点击:

1
Kernel -> Restart Kernel and Run All Cells

19. 保存 Notebook

操作:

按下:

1
Ctrl + S

或点击:

1
File -> Save Notebook

预期结果:

Notebook 保存完成。


20. 退出 JupyterLab

操作:

点击 JupyterLab 页面中的:

1
Shut Down

如果通过 .bat 启动, 回到命令行窗口后可能显示:

1
请按任意键继续. . .

按任意键关闭窗口。


21. 常见问题

21.1 NameError: name 'ndotv_min' is not defined

原因:

定义 ndotv_min 的 cell 没有先运行。

处理方式:

点击:

1
Kernel -> Restart Kernel and Run All Cells

或从上到下手动运行所有 cell。


21.2 运行后没有图, 只有文字输出

原因:

当前 cell 只包含 print 输出, 不包含绘图代码。

说明:

误差统计 cell 只输出文本, 这是正常结果。


21.3 In [7] 是否表示第 7 个 cell

不是。

In [7] 表示第 7 次执行, 不表示页面中的第 7 个 cell。


21.4 修改函数后图没有变化

原因:

后续 cell 没有重新运行。

处理方式:

点击:

1
Run -> Run All Cells

或从函数定义 cell 开始, 按顺序重新运行后续 cell。


21.5 Kernel disconnected 或 Kernel dead

处理方式:

点击:

1
Kernel -> Restart Kernel and Run All Cells

如果仍然失败, 关闭 JupyterLab 后重新进入。

本文由作者按照 CC BY 4.0 进行授权