想象你面前有一块半透明的芒果果冻,芒果粒、奶油层和气泡悬浮其中。你想看清果冻内部的全部果粒位置,这是“透过果冻看内部”——体绘制;你又想把芒果层单独抠出来做成一个可以旋转的模型,这是“切出果核表面”——等值面提取。本文用这块果冻串起 3D 可视化的两大核心技术:光线投射如何逐层透视体数据,Marching Cubes 如何把 CT、气象等三维网格变成多边形模型,以及面对几 GB 的大块头体数据时,工程师如何分块、分级、加速。读完你就能理解医学影像 3D 重建和科学计算可视化的底层逻辑,为开发自己的可视化工具打下基础。

岗位关联

掌握体绘制与等值面提取,是进入医学影像、科学计算和 3D 数据可视化开发三大方向的核心技能,也是理解 VTK、WebGL 渲染管线的关键。

目录
  1. 可视化演示
  2. 体绘制原理
  3. 光线投射算法
  4. 传递函数设计
  5. MC网格剖分
  6. 等值面生成
  7. 大规模体数据
  8. 可视化工具链
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

体绘制原理

想象你面前有一块芒果果冻,它半透明,内部悬浮着芒果粒、奶油层和气泡。你想知道这块果冻内部的完整结构,但又不想掰开破坏它。怎么办?让光穿过它:不同密度的地方吸收光的程度不同,透过去的光就带上了内部信息。体绘制就是这种“透视果冻”的技术,它不切开数据,而是让虚拟光线穿透三维数据场,把内部结构呈现在屏幕上。

在计算机里,这块“果冻”叫体数据。CT 扫描仪把人体切成很多层,每层再分成小格子,每个格子记录一个数值——密度(CT 值)。这些格子在三维空间里排列成一个立方体网格,每个小立方体叫体素(Voxel),就像果冻里的一个小晶格。体绘制的目标就是不破坏这个网格,直接“透视”它,让医生一眼看到病灶在哪、血管怎么走。但体数据本身只是一堆数字,没有颜色,于是需要一种叫传递函数的“上色配方”,把密度值映射成颜色和透明度。当一束光线穿过多个体素时,颜色按透明度一层层叠加,最终在屏幕上形成二维图像,这就是透明度累积。

数学上,光线每穿过一个采样点,都要做一次“前向合成”(Front-to-Back Compositing),把当前采样点的颜色和之前累积的颜色按透明度混合起来:

就这样,一束光穿过一层层果冻,每经过一层就叠加一点颜色、损失一点透明度。走到最后,这束光带上了整块果冻的“味道信息”——屏幕上的每一个像素,都是一束光完整穿过果冻后的记录。

Cout=Csample×αsample+Cacc×(1−αsample)C_{out} = C_{sample} \times \alpha_{sample} + C_{acc} \times (1 - \alpha_{sample})
前向合成公式:当前颜色按自身不透明度“压”在前面,之前积累的颜色按透射比例“漏”出来。
  1. 采样颜色 C_sample当前采样点通过传递函数得到的颜色,好比这块果冻本身的颜色。
    CsampleC_{sample}
  2. 采样透明度 α_sample当前采样点的遮挡程度,1 表示完全不透明,0 表示全透明,好比果冻的浓稠度。
    αsample\alpha_{sample}
  3. 累积颜色 C_acc在这之前所有采样点已经积累的颜色,好比光已经“尝到”的前面几层果冻的味道。
    CaccC_{acc}
  4. 透射比例 1-α_sample光能穿过当前采样点的比例,果冻越透明,后面的颜色透过来越多。
    1−αsample1 - \alpha_{sample}
  5. 新累积色 C_out当前颜色和之前积累颜色按透明度加权求和,得到这束光穿过该层后的新“味觉记录”。
    CoutC_{out}

光线投射算法

阳光穿过果冻时,不是“啪”一下就到另一边,而是一步一步地走,每走一点就“尝”一口周围果冻的味道。光线投射算法模拟的正是这个精细过程:把光线想成一个小人,拿着手电筒钻进果冻,每隔一小段停下来取样,记录这一带的颜色和密度,然后继续往前走,直到走出果冻或视线被完全挡住。

在代码里,这个“小人”是从相机出发的一条射线。对屏幕上的每个像素,生成一条光线(Ray),方向就是相机指向该像素的方向。光线进入体数据后以固定步长采样——每走一步就读当前所在体素的值,经传递函数换算成颜色和透明度。这里有个工程细节:采样点不一定恰好落在体素中心,需要三线性插值,读取周围 8 个格点的值做加权平均,就像小人站在果冻中某处,感觉到的是周围果粒味道的混合。

另一个关键技巧是提前终止(Early Termination)。当累积透明度接近 1 时,说明前面已经积累了大量不透明物质,后面的果冻被完全挡住,继续采样就是浪费算力,可以提前跳出循环。此外,合成顺序也有讲究:后向前合成从最远处往近处叠颜色;前向合成从近处往远处叠,后者天然支持提前终止。光线可以用参数方程简洁描述:

小人每走一步记录一点信息,直到走出果冻或视野被完全挡住。所有小人记录的信息拼在一起,就成了一张完整的二维图片——这就是光线投射体绘制最直观的图景。

P(t)=O+t⋅D\mathbf{P}(t) = \mathbf{O} + t \cdot \mathbf{D}
光线参数方程:从起点 O 出发,沿着方向 D 走 t 步,每一步的位置就是一次采样点。
  1. O 光线起点相机(眼睛)的位置,也就是小人出发的地方。
    O\mathbf{O}
  2. D 方向向量单位化后的传播方向,小人前进的方向。
    D\mathbf{D}
  3. t 步进参数离起点走了多远,单位通常是体素边长。
    tt
  4. P(t) 采样点光线在 t 时刻的位置,在这里读取体数据、合成颜色。
    P(t)\mathbf{P}(t)

传递函数设计

同样是那块芒果果冻,有人想看清奶油层,有人只想看芒果粒。为什么同一块果冻在不同人眼里不一样?因为“上色配方”不同。在体绘制里,把体数据数值翻译成颜色和透明度的配方就叫传递函数,它是整个可视化中最需要“手艺”的部分,直接决定观众能看到什么、忽略什么。

体数据只存了一堆数值,数值本身没有颜色。传递函数(Transfer Function)把标量值映射为 RGBA 四元组,即红、绿、蓝和透明度。例如 CT 中骨骼的密度值约在 1000 以上,肌肉皮肤在 100 以下。想让骨骼显眼,就把 1000 附近映射为白色不透明,其他区域映射为半透明红色。这样医生一眼就能从“果冻”里认出骨架。

设计传递函数的难点在于一维信息常常不够用:骨骼和金属可能同为高密度,仅靠密度值无法区分。更高级的传递函数把梯度(密度变化率)作为第二维输入——梯度大的地方是边界,像果冻里果粒的轮廓;梯度小的地方是均匀内部。加入梯度维度后,边界被锐化、内部更通透,病灶和器官的轮廓清晰可辨。交互式工具提供传递函数编辑器,用户用鼠标拖拽曲线就能实时调整颜色和透明度分布——像调音师推混音器,边看边调。

调传递函数就像给果冻调“显影液”:配方调得好,病灶、骨骼、血管层层分明;配方调不好,一团浆糊什么也看不清。体绘制是“拍 X 光片”,传递函数就是决定 X 光片显影效果的药水配方。

(R,G,B,A)=T(f(x),∥∇f(x)∥)(R, G, B, A) = T\left(f(\mathbf{x}), \|\nabla f(\mathbf{x})\|\right)
传递函数表达式:把位置 x 处的标量值和梯度大小映射成颜色 RGBA,控制体绘制的视觉输出。
  1. f(x) 标量值体数据在位置 x 处记录的密度值,如 CT 值。
    f(x)f(\mathbf{x})
  2. 梯度大小 ‖∇f(x)‖标量在该处变化有多剧烈,果粒边缘梯度大、内部梯度小。
    ∥∇f(x)∥\|\nabla f(\mathbf{x})\|
  3. T 传递函数把标量和梯度两个输入映射到颜色空间的映射表。
    T(⋅)T(\cdot)
  4. R,G,B 颜色值决定该处显示成什么颜色的三通道数值。
    (R,G,B)(R, G, B)
  5. A 透明度决定该处是显眼还是不显眼,是信息呈现的开关。
    AA

MC网格剖分

体绘制是“透视”,那如果我只想要果冻里芒果层的那个表面,做成一个 3D 模型放进游戏里呢?这时候不能用透视,得“切”。Marching Cubes(行进立方体)算法就像一个兢兢业业的厨师,把整块果冻切成无数个小立方体,逐个检查每个立方体是否切到了芒果层,凡是切到的,就记录下切口的形状。

Marching Cubes(简称 MC)是提取等值面的经典算法。等值面是体数据中所有等于某个阈值的位置连成的曲面——就像把果冻放入模具,压出芒果层所有表面“果皮”。算法遍历体数据中的每个体素立方体:立方体的 8 个顶点各有一个标量值,与阈值比较后,每个顶点只有“在表面内 / 在表面外”两种状态。8 个顶点共产生 2^8 = 256 种组合,按对称性可归纳为 15 种基本构型,预先生成一张构型查找表。运行时查表就知道等值线穿过立方体的哪些边。

确定穿边后,用线性插值求出等值点在边上的精确位置,再按构型表连接成三角形面片。三角形的法线由体数据的梯度计算,这样灯光打上去才真实。所有体素生成的三角形拼在一起,就是完整的等值面网格。MC 算法速度快、实现简单、数值稳定,是 CT 骨骼重建、气象等压面可视化的标准算法。顶点插值公式如下:

厨师耐心地把整块果冻切成小块,逐个检查,凡是碰到芒果层的都记下切口,最后把所有切口拼起来,芒果层的 3D 模型就诞生了。

Piso=P1+Viso−V1V2−V1×(P2−P1)P_{iso} = P_1 + \frac{V_{iso} - V_1}{V_2 - V_1} \times (P_2 - P_1)
顶点插值公式:根据边上两个端点的密度与阈值的比例关系,求出等值点在这条边上的精确位置。
  1. P1、P2 顶点坐标立方体某条边的两个端点的空间位置。
    P1,P2P_1, P_2
  2. V1、V2 顶点标量值两个端点处的密度值,好比果冻两侧的“浓度读数”。
    V1,V2V_1, V_2
  3. Viso 等值阈值要提取的等值面的密度值,也就是“芒果层”的判定标准。
    VisoV_{iso}
  4. 比例 (Viso - V1)/(V2 - V1)阈值在 V1 到 V2 之间的相对位置,范围 0 到 1。
    Viso−V1V2−V1\frac{V_{iso} - V_1}{V_2 - V_1}
  5. P_iso 等值点从 P1 出发、沿 P1→P2 方向按比例移动后得到的插值点,这就是切口上的精确位置。
    PisoP_{iso}

等值面生成

切出切口只是第一步,厨师还要把一个个小切口打磨光滑,拼成完整的工艺品。等值面生成的后半段就是干这个的:从插值出的等值点,到组装成网格,再到计算法线和平滑表面。MC 输出的原始结果通常是粗糙的三角形网格,直接看可能像“碎玻璃”。

为什么粗糙?有两个原因:一是插值点的位置存在数值误差,二是构型查找表可能产生拓扑不一致的三角形——相邻体素在公共面上生成的三角形对不齐,产生裂缝。要让表面平滑可渲染,需要后续处理:合并重复顶点、修复翻转法线、平滑顶点点位。这些步骤保证了网格的拓扑一致性,就像把碎玻璃拼好后再打磨棱角。

法线计算是渲染的关键。等值面的法线方向就是密度场的梯度方向:梯度指向密度变化最快的方向,光照系统靠它判断“这一面朝哪儿”。用中心差分近似梯度,比较顶点前后左右上下的密度变化,得到三维梯度向量,归一化后就是法线。有了正确的法线,渲染器才能打出明暗,让表面看起来真的像个“果壳”而不是“玻璃渣”。最后再用表面平滑迭代:把每个顶点位置朝邻接顶点的平均位置拉一点,反复几次,碎玻璃就变成光滑的果壳。梯度法线的计算公式如下:

从碎玻璃到光滑果壳,靠法线判断朝向、靠迭代平滑去除棱角,最终结果才能拿去 3D 打印或放进游戏引擎。

nx≈f(x+1,y,z)−f(x−1,y,z)2Δxn_x \approx \frac{f(x+1,y,z) - f(x-1,y,z)}{2 \Delta x}
中心差分公式:用左右邻居的密度差除以距离,近似得到 x 方向的梯度分量,三个方向合起来就是法线。
  1. f(x+1,y,z) 右侧值x 方向右邻居处的标量密度。
    f(x+1,y,z)f(x+1,y,z)
  2. f(x-1,y,z) 左侧值x 方向左邻居处的标量密度。
    f(x−1,y,z)f(x-1,y,z)
  3. Δx 体素间距相邻两个体素之间的距离。
    Δx\Delta x
  4. 差值除以 2Δxx 方向的平均变化率,也就是梯度的 x 分量。
    f(x+1,y,z)−f(x−1,y,z)2Δx\frac{f(x+1,y,z) - f(x-1,y,z)}{2\Delta x}
  5. 法线 n对 y、z 方向同样计算后组合成梯度向量,归一化即得表面法线。
    n=∇fn = \nabla f

大规模体数据

冰箱里那块果冻不是一盒,而是一整块长 3 米、重 500 公斤的巨型蛋糕。厨师如果还是切成小方块逐个处理,内存早就爆了。大规模体数据——比如一整组胸部 CT 扫描,动辄几百 MB 到几 GB——不能一口气全塞进内存,得像吃自助餐一样,想吃哪盘取哪盘。

分块(Block / Chunking)把体数据切成很多小方块(如 64³ 一块),按需加载。再配上八叉树:整块体积是根节点,分成 8 个子块,每个子块再分,直到最小数据块。每个块额外存一份粗略摘要(最大值、最小值),渲染时先看摘要,判断当前视角下这个块是否值得加载精细数据。LOD(Level of Detail,多细节层次)进一步按距离分级:离相机近的块加载精细数据,远的用粗糙版本——就像远处看果冻只能看到整体轮廓,走近才能看清果粒。

GPU 并行是另一大杀器。光线投射天然适合并行,每个像素的光线互不干扰,可以同时在 GPU 的几千个核心上运行。CUDA 与 OpenGL 的 3D 纹理技术把体数据直接存储在显存中,渲染时由硬件做三线性插值采样,速度比 CPU 快几十倍。内存仍不够时启用流式加载:随着视角移动,预加载将要进入视野的块,卸载远离视野的块,让内存占用始终稳定。子块误差度量决定一个块该用多粗糙的级别:

巨型果冻蛋糕不用一次吃光。分块、LOD、GPU、流式加载,就是一套“按需取用”的组合策略,让千万级体素也能在普通电脑上流畅旋转缩放。

E=max⁡v∈block∣ffull(v)−fapprox(v)∣E = \max_{v \in \text{block}} \left| f_{full}(v) - f_{approx}(v) \right|
误差度量公式:比较某个块中原始数据与近似数据在每个体素上的差异,取最大值作为该块的误差,决定 LOD 级别。
  1. f_full(v) 原始值完整分辨率数据在体素 v 处的密度。
    ffull(v)f_{full}(v)
  2. f_approx(v) 近似值降采样或压缩后的粗略近似值。
    fapprox(v)f_{approx}(v)
  3. 遍历块内所有体素 v逐个检查这个块的每一个体素。
    v∈blockv \in \text{block}
  4. 取差值绝对值每个体素上原始值与近似值的差距。
    ∣ffull(v)−fapprox(v)∣\left| f_{full}(v) - f_{approx}(v) \right|
  5. E 最大误差块内所有体素差距的最大值,误差小于阈值就用粗糙版,否则加载精细版。
    EE

可视化工具链

有了刀法和配方还不够,专业厨房还得有一套趁手厨具。在可视化领域,这套“厨具”就是工具链——你不必从零实现光线投射和 Marching Cubes,直接用现成的开源库,几十行代码就能渲染出医学影像。

VTK(Visualization Toolkit)是开源界最常用的 3D 可视化库,内置了体绘制、Marching Cubes、传递函数编辑器等全套功能。开发者调用 vtkVolume、vtkMarchingCubes 等类即可完成从体数据到图像的整个流程。在医学影像领域,CT/MRI 的 DICOM 数据经 VTK/ITK 处理,重建出骨骼、器官、血管的 3D 模型,辅助手术规划和教学——这是体绘制和等值面提取最大的应用战场。

气象领域同样离不开这两大方法:等压面、等温面用 Marching Cubes 提取,降水云层用体绘制半透明渲染,叠加在地球曲面上形成立体天气预报。工业上,工业 CT 扫描零件后,用体绘制查看内部裂纹,用等值面提取出缺陷区域模型,实现无损检测。如今 Web 技术也跟了上来,WebGL 结合 GPU 体绘制,浏览器里就能旋转查看 CT 数据,无需安装任何专业软件。工欲善其事,必先利其器——学会用齐备的厨具,再理解背后的刀法,才能高效做出惊艳的可视化作品。

AI 应用场景

医学影像三维重建

阶段:可视化与交互分析

解决的问题:传统诊断需要医生逐张翻看几百张 CT 切片,在脑中重建 3D 结构,效率低且主观。体绘制可将整套 CT 数据一次渲染成半透明 3D 图像,整体把握器官与病灶的位置关系;等值面提取则把骨骼或肿瘤区域变成可旋转、可测量的网格模型。

具体案例:某三甲医院骨外科用 VTK 对患者髋关节 CT 做等值面提取,生成 3D 骨骼模型,术前在模型上模拟截骨角度,并 3D 打印假体试模,手术时间缩短约 30%。

气象数据立体可视化

阶段:数据处理与可视化

解决的问题:气象雷达与大气模型输出的是多层三维网格数据,平面云图完全看不出云的垂直结构和冷暖空气交界面。体绘制把云层渲染成半透明立体云团,等值面提取显示 0°C 高度层和等压面,帮助预报员判断强对流发展。

具体案例:某省气象台将大气模型输出转为体数据,在三维地球模型上叠加体绘制云层与等温面,成功辅助了一次冰雹橙色预警的提前发布。

工业零件无损检测

阶段:采集后可视化分析

解决的问题:工业 CT 扫描铸件后会产生 512×512×1024 的高分辨率体数据,内部气孔和裂纹用手工切片查找费时费力。使用 Marching Cubes 提取缺陷区域等值面,直接生成缺陷 3D 模型并自动计算体积。

具体案例:某航空发动机叶片铸造厂用体绘制快速透视叶片内部,再用等值面提取气孔表面,质检效率提升 5 倍,缺陷定位误差从毫米级降到亚毫米级。

Python 演示

python58 行
import numpy as npimport matplotlib.pyplot as plt  def make_density_field(n=24):    """生成一个 3D 球体密度场:中心密度 1,向外衰减到 0"""    t = np.linspace(-1.0, 1.0, n)    x, y, z = np.meshgrid(t, t, t, indexing="ij")    r = np.sqrt(x**2 + y**2 + z**2)    return np.clip(1.0 - r, 0.0, 1.0)  def transfer_function(d):    """传递函数:把体数据密度映射为颜色和透明度"""    alpha = min(0.7, d * 0.8)    color = np.array([0.2, 0.6, 0.9]) * d    return color, alpha  def raycast(density, size=64):    """沿 z 轴逐像素发射光线,前向合成颜色"""    n = density.shape[0]    img = np.zeros((size, size, 3))    for i in range(size):        for j in range(size):            u = (i / (size - 1) - 0.5) * 2.0            v = (j / (size - 1) - 0.5) * 2.0            acc_c = np.zeros(3)            acc_a = 0.0            for k in range(n):                t = -1.0 + (k / (n - 1)) * 2.0                if u < -1.0 or u > 1.0 or v < -1.0 or v > 1.0:                    break                ix = int((u + 1.0) * 0.5 * (n - 1))                iy = int((v + 1.0) * 0.5 * (n - 1))                iz = int((t + 1.0) * 0.5 * (n - 1))                d = density[ix, iy, iz]                c, a = transfer_function(d)                acc_c = acc_c + (1.0 - acc_a) * a * c                acc_a = acc_a + (1.0 - acc_a) * a                if acc_a > 0.95:                    break            img[i, j] = acc_c    return img  def main():    density = make_density_field(n=24)    image = raycast(density, size=64)    plt.imshow(image)    plt.axis("off")    plt.title("Ray Casting Volume Rendering")    plt.savefig("volume_rendering.png", dpi=100)    print("Saved: volume_rendering.png")  if __name__ == "__main__":    main()
目的:用约 50 行代码实现光线投射体绘制的最小闭环——生成球体密度场、经过传递函数映射、逐像素光线步进合成,最终输出一张“透视果冻”般的二维图像。
  1. r = np.sqrt(x**2 + y**2 + z**2)计算体数据网格中每个格点到球心的距离,距离越近密度越高。
  2. np.clip(1.0 - r, 0.0, 1.0)把距离转成 0 到 1 的密度值,形成一颗实心球。
  3. alpha = min(0.7, d * 0.8)传递函数把密度映射为透明度,密度高就“更浓稠”。
  4. acc_c = acc_c + (1.0 - acc_a) * a * c前向合成核心公式,当前颜色按透明度叠加到累积颜色上。
  5. acc_a = acc_a + (1.0 - acc_a) * a同步更新累积透明度,模拟光线穿过半透明果冻被逐层吸收的过程。
  6. if acc_a > 0.95: break提前终止:光线已被充分遮挡,后面的数据不需要再采样。
  7. int((u + 1.0) * 0.5 * (n - 1))把连续坐标映射到体素网格索引,完成采样点的取值。

常见误区

误区我一直以为体绘制和等值面提取是两种不同的叫法,反正都是把三维数据渲染成图片,应该是一回事吧。

正解它们是两种完全不同的可视化策略。体绘制像“透视果冻”,输出的是二维图像,视线穿过半透明体数据、逐层累积颜色;等值面提取像“切出模具”,输出的是三维三角网格模型,只保留等于某个阈值的表面。前者保整体、显通透,后者保边界、出模型,应用场景也不同。

为何常见:二者输入都是体数据,最终都让人看到三维结构,很多软件界面同时提供这两种功能,用户只点按钮不关心背后的算法差异,自然容易混淆。

误区Marching Cubes 跑完一遍就输出了一个完整光滑的三维模型,拿来就可以直接放到游戏里用。

正解MC 的原始输出是粗糙的三角形网格,顶点位置来自线性插值、法线尚未修正,常伴随重复顶点、拓扑裂缝和表面棱角。必须做后续处理——合并顶点、修复法线、平滑迭代,才能得到可渲染、可打印的光滑模型。

为何常见:教程和案例展示的多是漂亮的最终效果,很少展示算法的中间产物,给大家造成“一步到位”的错觉。

误区传递函数不就是把数据映射成好看的颜色?调整配色让画面漂亮就行了。

正解传递函数同时决定颜色和透明度,而透明度往往比颜色更关键。不透明度设置不当,会把病灶完全遮住,或让骨骼在肌肉里隐形。高级传递函数还把梯度作为第二维输入来区分边界与内部。它决定的是“能不能看到信息”,不只是“好不好看”。

为何常见:视觉上“上色”最直观,许多案例以炫酷配色为卖点,让人忽略了透明度对信息可见性的决定性作用。

误区体绘制反正都是逐像素光线投射,数据大就慢、数据小就快,没啥优化空间。

正解实际工程中有大量提速手段:提前终止跳过被遮挡区域,空间跳跃跳过无数据的空区域(如肺部的空气),LOD 让远处用低分辨率,GPU 并行把每根光线分配到独立核心。合理优化后的渲染速度可能比朴素实现快几十倍。

为何常见:初学时实现的是小数据朴素版本,性能尚可接受,从未体验过几十 GB 体数据在普通电脑上转圈等待的痛,对优化必要性缺乏认识。

面试题

Q1. 体绘制与等值面提取的本质区别是什么?

单选题 · easy

  • A.体绘制输出二维图像结果← 正确答案
  • B.体绘制快但质量较差
  • C.等值面只用于医学影像
  • D.两者效果与输出相同

答案:A。体绘制像“透视果冻”,光线穿过体数据累积颜色,输出二维图像;等值面提取像“切出模具”,输出三维三角网格模型。B 说体绘制快没有依据,C 说等值面只用于医学影像以偏概全,D 把两者混为一谈。

Q2. 体数据中最基本的单元是什么?

单选题 · easy

  • A.像素 Pixel
  • B.体素 Voxel← 正确答案
  • C.顶点 Vertex
  • D.面片 Triangle

答案:B。体数据是三维网格上的标量场,最小单元是体素 Voxel,相当于二维图像中像素的立体对应物。像素是二维图像单元,顶点和面片是网格模型的组成元素,都不是体数据的基本单元。

Q3. 光线投射中前向合成的正确公式是?

单选题 · mid

  • A.Cout = Cacc + Csample
  • B.Cout = Cs×α + Ca×(1-α)← 正确答案
  • C.Cout = Cacc×α + Csample
  • D.Cout = max(Cs, Cacc)

答案:B。前向合成要把当前采样颜色按透明度加权,再加上之前积累的颜色乘以透射比例 (1-α)。这个公式模拟了光线穿过半透明果冻时逐层叠加颜色的物理过程。A 缺少权重,C 权重用错对象,D 取最大值完全不符合物理。

Q4. 传递函数的输入可以包括哪些?

多选题 · mid

  • A.标量密度值 f(x)← 正确答案
  • B.梯度大小与方向← 正确答案
  • C.像素屏幕坐标值
  • D.体素法线三维向量

答案:AB。传递函数把体数据标量值映射为 RGBA,也可以加入梯度等派生特征来增强边界。像素屏幕坐标是输出图像的属性,不是体数据的输入;法线是从梯度推导出的结果,不直接作为传递函数输入。

Q5. Marching Cubes 中一个体素立方体的顶点状态组合有多少种?

单选题 · mid

  • A.8 种
  • B.15 种
  • C.256 种← 正确答案
  • D.512 种

答案:C。立方体有 8 个顶点,每个顶点只有“高于阈值 / 低于阈值”两种状态,因此总组合数为 2^8 = 256 种。按旋转对称和互补对称归纳后可压缩为 15 种基本构型,但原始组合数仍是 256。

Q6. Marching Cubes 顶点插值公式计算的是什么?

单选题 · mid

  • A.求立方体表面颜色值
  • B.等值点在边上的位置← 正确答案
  • C.计算体素的重心坐标
  • D.面片顶点颜色插值值

答案:B。公式 P_iso = P1 + (Viso-V1)/(V2-V1)(P2-P1) 根据边两端点的标量值和阈值的关系,线性插值出等值点在边上的精确位置。它不计算颜色、重心或顶点色,而是确定“切口”的几何位置。

Q7. 处理大规模体数据常用的策略有哪些?

多选题 · mid

  • A.分块按需加载数据← 正确答案
  • B.八叉树管理细节层次← 正确答案
  • C.一次性载入全部数据
  • D.GPU 并行加速渲染← 正确答案

答案:ABD。大规模体数据必须分块按需加载、用八叉树管理 LOD,并借助 GPU 并行加速。一次性载入全部数据在内存不足时直接崩溃,不是可行策略。

Q8. 等值面的法线通常如何计算?

单选题 · easy

  • A.用密度场的梯度近似← 正确答案
  • B.用随机数生成法线
  • C.用顶点颜色平均获取
  • D.直接设为固定垂直方向

答案:A。等值面的法线方向与密度场梯度方向一致,用中心差分求梯度再归一化即可得到法线。光照渲染依赖正确的法线来表现明暗。随机数、颜色平均和固定方向都无法反映真实表面朝向。

Q9. 体绘制中“提前终止”的判定条件是什么?

单选题 · mid

  • A.光线到达图像边界处
  • B.累积不透明度接近一← 正确答案
  • C.采样次数固定为百次
  • D.当前颜色变成纯黑色

答案:B。前向合成中累积不透明度接近 1 时,说明光线已被前面物体完全遮挡,后续采样对最终颜色几乎没有贡献,可以提前跳出循环。这是一个性能优化手段,不是几何边界或颜色阈值问题。

Q10. 下列哪些是体绘制的典型应用场景?

多选题 · hard

  • A.医学 CT 影像三维重建← 正确答案
  • B.气象云层立体渲染展示← 正确答案
  • C.工业零件内部缺陷检测← 正确答案
  • D.音乐播放器的频谱显示

答案:ABC。体绘制把三维体数据可视化为直观图像,广泛用于医学影像重建、气象云层渲染和工业 CT 缺陷检测。音乐频谱是二维信号可视化,与三维体数据无关。