十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Julia实战:从零实现3D Gaussian Splatting渲染与优化

Julia实战:从零实现3D Gaussian Splatting渲染与优化 3D Gaussian Splatting3DGS是当前三维视觉和实时渲染领域关注度很高的方向它的核心思路是用一组带位置、形状、不透明度和颜色的三维高斯原语表达场景通过可微光栅化把三维场景投到二维图像上再根据真实照片反向优化这组高斯原语的参数。相比传统 NeRF它的训练速度快、渲染速度快因而在三维重建、新视角合成、动态场景编辑等场景里被广泛应用。Julia 是一门兼顾动态语言开发效率和静态性能的编程语言它适合用来实现 3DGS 的原理验证、课程作业、小型实验以及探索算法改进。这篇文章会从 3DGS 的核心原理讲起再带你在 Julia 里搭建环境、设计数据结构、实现最小可运行的光栅化和训练流程然后深入分析 Julia 性能优化和内存管理需要注意的关键点最后给出调试排查路径和生产环境建议。全文面向有一定 Julia 基础、正在学习 3DGS 原理或者想把 3DGS 官方 CUDA 实现迁移到 Julia 进行算法实验的开发者。阅读完你可以掌握 3DGS 的渲染链路理解 Julia 中“类型稳定”“减少堆分配”“StaticArrays 使用”这些优化手段在图形学代码里的实际作用并具备继续扩展成一个完整可训练项目的能力。1. 3D Gaussian Splatting 到底做了什么1.1 一句话说清楚核心思路3D Gaussian Splatting 要解决的核心问题是如何用一组可微分的三维高斯分布来表示一个场景并且让这组分布在给定相机视角下能够快速渲染成一张高质量图像。如果把场景比作一幅拼图那么传统点云用离散的点描述表面NeRF 用神经网络隐式描述连续空间而 3DGS 用的是无数个小椭圆体。每个椭圆体都有自己的中心位置、三轴缩放、旋转角度、不透明度和颜色。渲染时从相机位置观察这些椭圆体把它们投影到屏幕上形成半透明的二维椭圆最后按深度从小到大做 alpha 混合得到最终像素颜色。这个过程和传统 Splatting 思路一脉相承但有两个重要改进所有参数都可以微分因此可以直接用梯度下降优化光栅化时做了基于 tile 的前向排序和 alpha 合成让渲染速度达到实时。理解了这一点后续看代码和参数就不会迷路。1.2 渲染流程拆解初始化、优化、密度控制3DGS 的完整训练循环可以拆成四个阶段初始化从稀疏点云或随机位置生成初始高斯原语每个高斯带一个协方差矩阵、不透明度、RGB 颜色和球谐系数。前向渲染把三维高斯协方差投影到二维图像平面结合相机内外参计算每个高斯在屏幕上的椭圆位置和大小。损失计算比较渲染图像与真实照片的 L1、SSIM 或两者组合损失。反向更新梯度回传到每个高斯的位置、协方差、不透明度、颜色同时每隔一定迭代步执行密度控制删除不透明度过低的高斯克隆位置梯度大的高斯分裂尺度较大的高斯。这个流程和 NeRF 的端到端训练类似但区别在于 3DGS 没有神经网络编码器它直接用可优化参数作为场景表示。因此每一轮迭代都是在直接调整“椭圆体的摆放、大小、颜色和透明度”。1.3 为什么选择 Julia 而不是 Python/CUDA 原型官方 3DGS 实现基于 CUDA 和 PyTorch性能很高但对想理解算法细节的开发者来说代码里大量并行逻辑会和核心数学表达式混在一起。Python 纯 NumPy 版本容易看懂但逐像素光栅化太慢实验迭代很不舒服。Julia 在表现力上接近 Python在数值计算的运行时性能上又接近 C。它的多重派发特性很适合为不同数据类型写同一套算法CPU 实验用 Float32 数组GPU 实验用 CuArray算法主体可以保持一致。对于 3DGS 这种“数学表达简单、工程实现复杂”的算法Julia 是很好的实验语言先写 CPU 版本验证公式再逐步替换为 GPU kernel 或 CUDA.jl 扩展。需要注意Julia 不是 3DGS 官方生态的主流语言很多现成工具如相机参数读取、COLMAP 点云转换、网格导出都需要自己封装。文章后面的示例定位是“学习用最小实现”面向公式验证和算法研究生产级大规模训练仍要考虑 CUDA 实现或调用官方工程链路。2. 搭建 Julia 实验环境2.1 Julia 版本与关键依赖建议安装 Julia 1.9 以上版本。较早版本虽然也能运行但泛型多重派发、任务并行和包解析行为与当前版本有差异排查问题时会增加不必要的干扰。核心依赖按使用场景划分如下依赖包用途说明StaticArrays.jl固定长度数组高斯位置、旋转、协方差矩阵使用SVector、SMatrix减少堆分配LinearAlgebra矩阵乘法、特征值分解Julia 标准库Printf日志输出训练过程中打印损失值Zygote.jl自动微分可选用于训练循环的反向传播CUDA.jlGPU 光栅化可选生产环境或大规模实验使用GLMakie / Plots.jl二维可视化可选用于调试渲染中间结果在 Julia 中创建项目并添加依赖julia -e using Pkg; Pkg.activate(gaussian_splatting_julia); Pkg.add([StaticArrays, Zygote, CUDA, Plots])说明这里使用Pkg.activate创建独立环境避免污染全局环境。如果网络环境下载慢可以先只添加必要依赖CUDA 等扩展包放到 GPU 实验阶段再安装。2.2 最小项目结构推荐按模块拆分文件每个文件只负责一个职责gaussian_splatting_julia/ ├── Project.toml ├── src/ │ ├── GaussianSplatting.jl │ ├── gaussian.jl │ ├── camera.jl │ └── rasterizer.jl ├── test/ │ └── run_basic.jl └── data/ └── 示例图片或点云Project.toml由Pkg.generate创建或者直接用文本编辑器维护。最小内容示例name GaussianSplattingJulia uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx authors [yourname] version 0.1.0 [deps] LinearAlgebra 37e2e46d-f89d-539d-b4ee-838fcccc9c8e StaticArrays 90137ffa-7385-5640-81b9-e52037218182 Plots 91a5bcdd-55d7-5caf-9e0b-520d859cae80 Zygote e88e6eb3-aa80-5325-afca-941959d7151f注意uuid必须在Pkg.generate时自动生成不要手写固定值。如果手动维护Project.toml请用Pkg.add保持格式一致。2.3 环境检查清单开始写算法前先确认环境是否正常Julia 能正常启动终端执行julia --version。包能正确解析在项目目录执行julia --project -e using StaticArrays, LinearAlgebra。自动微分包能加载执行using Zygote确认没有依赖冲突。CUDA 包如需要能识别显卡执行using CUDA; CUDA.functional()。后面所有代码默认在--project.激活环境下运行。如果加载某个包报错最优先检查Project.toml里依赖列表和Manifest.toml是否一致命令是julia --project -e using Pkg; Pkg.resolve(); Pkg.instantiate()3. 用 Julia 实现一个最小 Gaussian Splatting 流程这一节会实现一个可运行的最小流程重点展示 3DGS 的数学链路和 Julia 的数据结构设计。这里不追求与官方 CUDA 实现完全一致而是用可读的 Julia 代码跑通“定义高斯 - 投影 - 光栅化 - 计算损失”的闭环。3.1 高斯原语的数据结构设计每个三维高斯可以用以下参数描述mean三维中心点。scale三轴缩放对应协方差矩阵的特征值平方根。rotation旋转四元数 (w, x, y, z)。opacity不透明度经 sigmoid 映射到 (0, 1) 区间。colorRGB 颜色。sh_coeffs球谐系数学习阶段可选。在 Julia 中用具体结构体而非抽象字段定义 Gaussianusing StaticArrays struct Gaussian3D{T:Real} mean::SVector{3,T} scale::SVector{3,T} rotation::SVector{4,T} opacity::T color::SVector{3,T} end这里把字段类型写死为SVector{3,T}关键目的是保证 Julia 编译器能推断出结构体内存的精确布局。如果把字段类型写成Vector或AbstractVector每个字段都是一个需要动态分配内存的数组性能和类型稳定性都会变差。从四元数构造旋转矩阵function quaternion_to_matrix(q::SVector{4,T}) where T w, x, y, z q return SMatrix [ 1 - 2*(y*y z*z) 2*(x*y - z*w) 2*(x*z y*w) 2*(x*y z*w) 1 - 2*(x*x z*z) 2*(y*z - x*w) 2*(x*z - y*w) 2*(y*z x*w) 1 - 2*(x*x y*y) ] end旋转矩阵作用于原有缩放矩阵后得到三维协方差矩阵function cov_matrix(g::Gaussian3D{T}) where T R quaternion_to_matrix(g.rotation) S Diagonal(g.scale) Σ R * S * S * R return Σ end这里把协方差矩阵拆成旋转和缩放两个部分比直接优化协方差矩阵的六个独立元素更稳定。直接优化协方差矩阵容易产生非半正定矩阵渲染时会出现椭圆面积异常甚至 NaN。3.2 相机模型与投影3D 协方差到二维椭圆相机模型使用简化的针孔模型。相机参数包括位置、从世界坐标系到相机坐标系的旋转矩阵、焦距和主点struct PinholeCamera{T} position::SVector{3,T} orientation::SMatrix{3,3,T} fx::T fy::T cx::T cy::T end把三维高斯投影到二维屏幕坐标的步骤把世界坐标系中的高斯中心点转换到相机坐标系。用透视投影公式计算屏幕坐标。用仿射变换近似透视投影的雅可比矩阵把三维协方差投影成二维协方差。实现代码function project_gaussian(g::Gaussian3D{T}, cam::PinholeCamera{T}) where T cam_center cam.orientation * (g.mean - cam.position) depth cam_center[3] depth 0 return nothing u cam.fx * cam_center[1] / depth cam.cx v cam.fy * cam_center[2] / depth cam.cy J SMatrix [ cam.fx/depth 0.0 -(cam.fx * cam_center[1]) / (depth^2) 0.0 cam.fy/depth -(cam.fy * cam_center[2]) / (depth^2) ] world_to_cam cam.orientation cov3d cov_matrix(g) cov2d J * world_to_cam * cov3d * world_to_cam * J return (uu, vv, cov2dcov2d, depthdepth, opacityg.opacity, colorg.color) end需要注意depth 0的剔除是必要步骤。在针孔模型中位于相机后方的高斯不能投影到成像平面不剔除会导致屏幕上出现反向矩阵或错误叠加。3.3 逐像素 alpha blending 光栅化得到每个高斯的二维椭圆参数后就可以把椭圆叠加到图像上。真实 3DGS 使用基于 tile 的并行排序光栅化理解起来较复杂。学习用最小版本采用逐像素 alpha blending逻辑更直观。首先计算二维高斯在某个像素处的权重function gaussian_weight_2d(dx::T, dy::T, cov2d::SMatrix{2,2,T}) where T det cov2d[1,1] * cov2d[2,2] - cov2d[1,2] * cov2d[2,1] det eps(T) return zero(T) inv_det inv(det) a cov2d[2,2] * inv_det b -cov2d[1,2] * inv_det c cov2d[1,1] * inv_det exponent -0.5 * (a * dx^2 2 * b * dx * dy c * dy^2) return clamp(exponent, -30, 30) | exp end这里省略了归一化系数因为后续 alpha 混合还会做归一化保持相对权重即可。clamp(exponent, -30, 30)防止指数溢出这是数值稳定性的关键。光栅化主循环struct ProjectedGaussian{T} u::T v::T cov2d::SMatrix{2,2,T} depth::T opacity::T color::SVector{3,T} end function rasterize_frame(gaussians::Vector{Gaussian3D{T}}, cam::PinholeCamera{T}; width::Int, height::Int) where T projected ProjectedGaussian{T}[] for g in gaussians proj project_gaussian(g, cam) proj nothing continue push!(projected, ProjectedGaussian(proj.u, proj.v, proj.cov2d, proj.depth, proj.opacity, proj.color)) end sort!(projected, byp - p.depth, revtrue) image zeros(RGB{Float64}, height, width) for py in 1:height for px in 1:width acc RGB{Float64}(0, 0, 0) alpha_accum 0.0 for p in projected alpha_accum 0.999 break dx px - 0.5 - p.u dy py - 0.5 - p.v contrib gaussian_weight_2d(dx, dy, p.cov2d) alpha p.opacity * contrib acc acc (1 - alpha_accum) * alpha * p.color alpha_accum (1 - alpha_accum) * alpha end image[py, px] acc end end return image end这个代码块有三个关键点sort!(projected, byp - p.depth, revtrue)表示从远到近排序这样近处的高斯后绘制能正确遮挡远处高斯。alpha 混合公式里(1 - alpha_accum)表示当前像素还剩多少透明度没有被覆盖。dx px - 0.5 - p.u中的 0.5 是像素中心偏移习惯用像素中心作为采样点能避免半像素偏移误差。3.4 最小训练循环与损失函数在 3DGS 中颜色参数和球谐系数可以直接优化位置、缩放、旋转、透明度也可以用梯度更新。最小示例使用 MSE 损失并借助 Zygote 做反向传播using Zygote function loss_function(gaussians::Vector{Gaussian3D{T}}, cam::PinholeCamera{T}, target::Matrix{RGB{Float64}}; width::Int, height::Int) where T rendered rasterize_frame(gaussians, cam; widthwidth, heightheight) diff Float64.(red.(rendered)) .- Float64.(red.(target)) return sum(diff .^ 2) / length(diff) end如果用 Zygote 计算梯度需要注意rasterize_frame中所有操作必须可微。StaticArrays 和广播运算在 Zygote 下兼容性较好但涉及到排序的判断分支可能产生梯度中断。学习阶段的替代方案是手写梯度或简化光栅化细节。一个简化训练循环function train_step!(gaussians::Vector{Gaussian3D{Float32}}, cam::PinholeCamera{Float32}, target::Matrix{RGB{Float32}}; lr0.01) grads Zygote.gradient(g - loss_function(g, cam, target, width256, height256), gaussians) for (g, grad) in zip(gaussians, grads[1]) g.mean .- lr .* grad.mean g.scale .- lr .* grad.scale g.rotation .- lr .* grad.rotation g.opacity - lr * grad.opacity g.color .- lr .* grad.color end end完整训练还需要加入熵正则、透明度截断、密度控制等策略这里先用最简流程验证公式是否正确。把训练迭代和渲染结果保存下来观察损失是否下降、图像是否逐渐接近目标即可。4. 深入优化 Julia 性能与内存管理3DGS 的核心计算是大量三维高斯的投影和像素合成。用 Julia 实现时性能瓶颈往往不是语言本身而是代码没有做到类型稳定、分配了过多临时数组、或者在没有必要的地方用了抽象类型。这一节是 Julia 侧性能优化的重点。4.1 类型稳定是第一优先级Julia 能接近 C 性能的前提是类型稳定编译器在运行时能够推断每个变量的类型从而生成高效的机器码。类型不稳定在 3DGS 代码里最常见的表现是结构体字段是抽象类型例如Vector{Real}。函数返回类型在分支中不一致例如一个分支返回SVector另一个分支返回nothing。全局变量的类型不稳定。以ProjectedGaussian{T}为例之所以把cov2d写成SMatrix{2,2,T}而不是写Matrix就是为了让光栅化循环能够精确知道协方差矩阵的大小和元素类型。一旦cov2d是动态大小的矩阵每个gaussian_weight_2d调用都可能触发动态分派性能会明显下降。检查类型稳定性的命令using InteractiveUtils code_warntype project_gaussian(example_gaussian, example_camera)如果输出里有红色Union或Any说明返回类型没有被完全推断需要检查分支条件或字段类型。4.2 用 StaticArrays 消除堆分配Julia 的普通数组Vector{Float64}在堆上分配字段访问和矩阵运算会产生指针间接引用。对于每帧数千到数百万个高斯的数据结构堆分配会带来大量垃圾回收压力。SVector和SMatrix是固定长度、固定类型的栈上数组。对于三维点、四元数、2x2 协方差矩阵这种固定大小结构使用SVector是最合适的选择。错误写法# 不推荐每个字段都独立分配 struct BadGaussian mean::Vector{Float32} scale::Vector{Float32} rotation::Vector{Float32} color::Vector{Float32} end推荐写法见 3.1 节。使用SVector后结构体本身可以通过malloc或栈分配并且整个数组可以作为连续内存块存储。检查垃圾回收压力的方法time rasterize_frame(gaussians, cam; width256, height256)输出中allocations如果非常大说明有大量临时数组被创建。重点检查SMatrix乘法、Diagonal构造和sort是否创建了不必要的大对象。4.3 避免临时数组与 in-place 优化光栅化主循环里最常见的问题是坐标和颜色计算产生临时数组。例如delta SVector(dx, dy) # 固定的 SVector可以 weight gaussian_weight_2d(delta[1], delta[2], cov2d) tmp_color (1 - alpha_accum) * alpha * p.color # 仍是 SVector可以但如果写成tmp Vector{Float64}(undef, 2) tmp[1] dx tmp[2] dy就会在每次像素循环里分配数组光栅化图像越大性能越差。对图像缓冲区可以先预分配一次而不是在每次光栅化时重新创建function rasterize_frame!(buffer::Matrix{RGB{Float64}}, gaussians::Vector{Gaussian3D{T}}, cam::PinholeCamera{T}) where T # 直接在 buffer 中写入像素 end这样训练循环中同一块缓冲区反复使用减少分配。多线程化是另一个方向。逐像素循环天然可并行可以使用Threads.threads把像素行拆分Threads.threads for py in 1:height for px in 1:width # ... end end使用多线程前确保 Julia 以JULIA_NUM_THREADS4或julia -t 4启动。线程数不是越多越好要根据 CPU 核心数和缓存情况调整。4.4 广播融合与 SIMDJulia 的广播语法可以将多个数组操作融合成单个循环而不是为每个中间结果生成临时数组。例如计算协方差矩阵投影时cov2d . J * world_to_cam * cov3d * world_to_cam * J如果cov2d是固定大小的SMatrix编译器更容易做优化。广播语法.避免在嵌套调用中产生中间临时矩阵。Simd 指令由编译器自动生成但前提是数据在内存中连续并且循环边界明确。也就是说应当尽量让所有高斯数据存储在Vector{Gaussian3D{Float32}}这种同构连续数组里而不是Vector{Any}或Vector{Gaussian3D}的非具体参数化版本。4.5 GPU 路径CUDA.jl 的接入思路如果实验规模变大CPU 光栅化会成为瓶颈。CUDA.jl 提供了 Julia 直接编写 CUDA kernel 的能力。接入思路通常分两步把高斯参数转换为适合 GPU 并行处理的结构例如CuVector{NTuple{12,Float32}}或拆成多个CuVector{Float32}。在 GPU kernel 中实现透视投影和 tile 级并行光栅化用CuArray存渲染结果。基本示例using CUDA function gpu_rasterize_wrapper(gaussians_cu::CuDeviceArray, params..., out_cu::CuDeviceArray) i (blockIdx().x - 1) * blockDim().x threadIdx().x if i length(out_cu) # 读取高斯、投影、写像素 end return nothing end这一部分比 CPU 版本复杂很多。初学者建议先完成 CPU 版本并验证公式正确再迁移 GPU。不要一开始就写 CUDA否则调试成本和测试成本都会成倍增加。5. 调试、验证与常见问题排查5.1 可视化三个关键中间结果调试 3DGS 时最怕看到一张全黑或全白的图却不知道问题出在哪一层。推荐按以下顺序检查中间结果屏幕坐标投影画出每个高斯的(u, v)分布确认点云是否正确投影到画面范围内。二维协方差椭圆在图上叠加二维椭圆确认长轴、短轴和旋转方向是否符合相机视角。alpha 累积图单独输出每个像素的累计 alpha 值正常情况下应该是一张平滑的深度遮罩。用 Plots 画投影点using Plots function visualize_projection(gaussians, cam; width800, height600) xs Float64[] ys Float64[] for g in gaussians proj project_gaussian(g, cam) proj nothing continue push!(xs, proj.u) push!(ys, proj.v) end scatter(xs, ys, markersize1, labelprojected centers) end如果投影点大量堆在图像边界或者出现镜像分布优先检查相机朝向矩阵和景深方向。5.2 常见错误与排查顺序问题现象常见原因检查方式处理建议渲染图像全黑透明度全部太小或颜色全为 0打印 opacity、color 的均值初始化时设置合理 opacity例如 0.5 到 1.0渲染图像全白alpha 累积太快后面的高斯没有遮挡查看 depth 排序是否正确sort!必须使用revtrue且要处理depth 0投影点大量跑到屏幕外相机参数或旋转矩阵错误单独调用project_gaussian检查相机朝向和世界坐标转换顺序协方差矩阵行列式为负旋转矩阵或缩放矩阵构造错误打印cov_matrix的特征值用isposdef检查每一个高斯训练时损失出现 NaN学习率过大或协方差过小导致爆炸打印梯度范数降低学习率、对 scale 加下限保护类型推断失败性能骤降结构体字段为抽象类型code_warntype查看字段写成具体类型使用SVector内存分配过多循环里创建临时数组time观察 allocations预分配缓冲区使用views和广播语法排查顺序建议检查输入高斯初值是否合理。检查投影结果是否在合理范围内。检查二维协方差矩阵是否半正定。检查排序方向是否正确。检查 alpha 混合公式是否写对。如果这些都正确再往训练和梯度方向排查。不要在损失不降时就怀疑自动微分先用可视化确认前向链路没问题。5.3 学习环境与生产环境的差异学习环境只需要验证公式和流程跑一个小场景即可。通常可以用 256x256 图像和几百个高斯做实验即使 CPU 版本非常慢也不会影响学习节奏。生产环境如果要处理真实照片、几十万高斯和实时渲染还需要额外考虑环节学习环境生产环境光栅化逐像素 CPU 循环CUDA tile 并行光栅化数据结构Vector{Gaussian3D}拆分连续数组结合 SoA 布局自动微分Zygote 可接受手写梯度或专用 CUDA kernel数据加载简单图像COLMAP 稀疏重建结果日志监控println结构化日志、损失曲线、梯度统计参数保存无checkpoint 定期保存安全检查无校验协方差半正定、NaN 检测、异常退出恢复学习环境里用一行println足够但生产环境必须考虑异常恢复。建议训练过程中每 N 步保存一次高斯参数至少记录损失、梯度范数和有效高斯数量。6. 优化扩展与实践建议6.1 工程上值得做的改进当前最小实现仍有大量可以改进的性能点使用 tile 光栅化把图像分成固定大小的 tile每个 tile 只处理与其覆盖区域有交集的高斯避免全图逐像素扫描全部高斯。高斯排序优化按 tile 分组排序而不是全屏排序全屏排序在大规模高斯下会浪费大量计算。SoA 布局把结构体数组 AoS 改成数组结构 SoA即所有高斯的 mean 单独一个矩阵、所有 opacity 单独一个向量。GPU 环境下 SoA 内存访问更友好。混合精度训练使用 Float32 训练必要时用 Float16 存储中间量降低显存占用。球谐系数增加球谐阶数后颜色表达从固定 RGB 变成视角相关可用于复杂反射和新视角一致性优化。这些点和 Julia 本身的性能优化不冲突CPU 版本先把类型稳定和预分配做好GPU 版本再考虑并行度和内存布局。6.2 学习路径建议从原理速通到源码阅读如果刚开始接触 3DGS推荐以下顺序先跑通本文最小实现确认前向链路和 alpha 混合理解无误。在平面上验证单个高斯的投影改变相机位置观察椭圆大小、方向和叠加关系。阅读官方 3DGS 论文原图理解 tile 排序、密度控制和球谐系数的损失函数。阅读开源实现中关于光栅化和 backward kernel 的部分重点看协方差反向传播公式。尝试在 Julia 里用几万高斯做小规模训练看看密度控制、学习率衰减和损失函数的配合效果。这个顺序比直接读大型代码库更平滑。了解原理后“迭代参数与渲染”就不再是黑盒而是能根据现象定位问题。6.3 可复用清单发布前或进入下一阶段实验前建议对照以下清单检查[ ] 高斯结构体字段是否全部具体类型无抽象类型字段。[ ] 投影函数是否处理了depth 0的剔除。[ ] 协方差矩阵是否经过半正定检查。[ ] 排序方向是否正确远到近排序。[ ] 光栅化是否预分配了图像缓冲区。[ ]code_warntype是否显示无红色Union输出。[ ]time是否观察到异常大量 allocations。[ ] 训练时是否记录损失、梯度范数、有效高斯数量。[ ] 是否定期保存 checkpoint。[ ] 是否对 NaN 和高斯透明度爆炸做了保护。这份清单同样适合以后迁移到 CUDA 或加入球谐扩展时复用。技术方案会变但类型稳定、数值稳定、异常可排查这三个原则不会变。3D Gaussian Splatting 把三维场景表示从离散点云推进到了可微高斯原语Julia 的高性能数值计算特性让研究者可以快速验证算法公式并优化实现。后续往真实场景扩展时最值得优先投入的方向是 tile 光栅化和 CUDA kernel其次是密度控制策略和球谐颜色表达。建议初学者先把本文的最小 CPU 流程跑通理解每个中间矩阵的来源再决定是否向 GPU 和完整训练流程推进。
返回列表