第15章:前沿技术与发展方向
15.1 实时渲染技术
15.1.1 现代渲染管线
实时渲染技术的演进
传统渲染:
光栅化 + 预计算光照 + 纹理 → 简单场景
现代渲染:
延迟渲染 + PBR + 大量后处理 → 真实感场景
延迟渲染(Deferred Rendering):
传统前向渲染:
for each 物体:
for each 光源:
计算光照 → O(物体 × 光源)
延迟渲染:
Pass 1: 渲染 G-Buffer(几何信息)
Pass 2: for each 光源:
在屏幕空间计算光照 → O(光源 × 像素)
G-Buffer 内容:
┌────────────────────────────────────────────┐
│ 位置(Position) │
├────────────────────────────────────────────┤
│ 法向量(Normal) │
├────────────────────────────────────────────┤
│ 反照率(Albedo) │
├────────────────────────────────────────────┤
│ 材质参数(Roughness, Metallic, AO...) │
└────────────────────────────────────────────┘15.1.2 屏幕空间技术
常用屏幕空间效果
1. SSAO(屏幕空间环境光遮蔽)
- 模拟物体间的环境光遮挡
- 增加深度感
原图: 加 SSAO:
┌───────────┐ ┌───────────┐
│ │ │ │▓▓▓ │ │
│ │ │ → │▓▓▓ │ │ ← 角落变暗
│────┘ │ │▓▓▓─┘ │
└───────────┘ └───────────┘
2. SSR(屏幕空间反射)
- 利用屏幕空间信息计算反射
- 比光线追踪快,但有局限
3. SSGI(屏幕空间全局光照)
- 屏幕空间的间接光照
- 近似效果
4. 运动模糊(Motion Blur)
- 基于速度缓冲
5. 景深(Depth of Field)
- 模拟相机焦外模糊
6. 抗锯齿
- TAA(时域抗锯齿)
- FXAA/SMAA(后处理抗锯齿)15.2 光线追踪
15.2.1 实时光线追踪
实时光线追踪(Real-time Ray Tracing)
硬件加速:
- NVIDIA RTX(RT Cores)
- AMD RDNA 2
- Intel Arc
光线追踪的优势:
1. 精确的反射
传统:环境贴图近似
光追:真实反射
2. 精确的折射
传统:难以实现
光追:自然支持
3. 软阴影
传统:阴影贴图 + 模糊
光追:多采样直接计算
4. 环境光遮蔽
传统:SSAO 近似
光追:准确计算
混合渲染管线:
┌─────────────────────────────────────────────┐
│ │
│ 光栅化 ──────────┬───────────────► 主要场景│
│ │ │
│ 光线追踪 ────────┼───────────────► 反射 │
│ │ │ │
│ └────────────┼───────────────► 阴影 │
│ │ │
│ └───────────────► 合成 │
│ │
└─────────────────────────────────────────────┘15.2.2 路径追踪基础
javascript
/**
* 简单路径追踪器
*/
class PathTracer {
constructor(scene, maxBounces = 5) {
this.scene = scene;
this.maxBounces = maxBounces;
}
/**
* 追踪路径
*/
tracePath(ray, depth = 0) {
if (depth >= this.maxBounces) {
return { r: 0, g: 0, b: 0 };
}
// 求交
const hit = this.scene.intersect(ray);
if (!hit) {
return this.scene.background(ray.direction);
}
const material = hit.material;
// 自发光
const emission = material.emission || { r: 0, g: 0, b: 0 };
// 俄罗斯轮盘赌终止
const p = Math.max(material.albedo.r, material.albedo.g, material.albedo.b);
if (Math.random() > p) {
return emission;
}
// 采样 BRDF
const sample = this.sampleBRDF(hit, ray.direction, material);
// 递归追踪
const newRay = {
origin: this.offsetPoint(hit.point, hit.normal),
direction: sample.direction
};
const incoming = this.tracePath(newRay, depth + 1);
// 渲染方程
const cosTheta = Math.max(0, this.dot(sample.direction, hit.normal));
return {
r: emission.r + (material.albedo.r * incoming.r * cosTheta) / (sample.pdf * p),
g: emission.g + (material.albedo.g * incoming.g * cosTheta) / (sample.pdf * p),
b: emission.b + (material.albedo.b * incoming.b * cosTheta) / (sample.pdf * p)
};
}
/**
* 采样漫反射 BRDF
*/
sampleBRDF(hit, viewDir, material) {
// 余弦加权采样
const u1 = Math.random();
const u2 = Math.random();
const phi = 2 * Math.PI * u1;
const cosTheta = Math.sqrt(1 - u2);
const sinTheta = Math.sqrt(u2);
// 局部坐标
const localDir = {
x: Math.cos(phi) * sinTheta,
y: Math.sin(phi) * sinTheta,
z: cosTheta
};
// 转换到世界坐标
const worldDir = this.localToWorld(localDir, hit.normal);
return {
direction: worldDir,
pdf: cosTheta / Math.PI
};
}
/**
* 渲染图像
*/
render(width, height, samplesPerPixel) {
const image = new Float32Array(width * height * 3);
const camera = this.scene.camera;
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let color = { r: 0, g: 0, b: 0 };
// 多次采样
for (let s = 0; s < samplesPerPixel; s++) {
const u = (x + Math.random()) / width;
const v = (y + Math.random()) / height;
const ray = camera.generateRay(u, v);
const sample = this.tracePath(ray);
color.r += sample.r;
color.g += sample.g;
color.b += sample.b;
}
// 平均
const idx = (y * width + x) * 3;
image[idx] = color.r / samplesPerPixel;
image[idx + 1] = color.g / samplesPerPixel;
image[idx + 2] = color.b / samplesPerPixel;
}
// 进度显示
console.log(`Progress: ${Math.round((y + 1) / height * 100)}%`);
}
return image;
}
// 辅助方法
dot(a, b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
offsetPoint(p, n) {
const epsilon = 0.001;
return {
x: p.x + n.x * epsilon,
y: p.y + n.y * epsilon,
z: p.z + n.z * epsilon
};
}
localToWorld(local, normal) {
// 构建正交基
const up = Math.abs(normal.y) < 0.99
? { x: 0, y: 1, z: 0 }
: { x: 1, y: 0, z: 0 };
const tangent = this.normalize(this.cross(up, normal));
const bitangent = this.cross(normal, tangent);
return {
x: local.x * tangent.x + local.y * bitangent.x + local.z * normal.x,
y: local.x * tangent.y + local.y * bitangent.y + local.z * normal.y,
z: local.x * tangent.z + local.y * bitangent.z + local.z * normal.z
};
}
}15.3 体积渲染
15.3.1 体积渲染原理
体积渲染(Volume Rendering)
用于可视化 3D 数据集(如 CT、MRI 扫描)。
体素数据:
┌─────────────────┐
╱ ╱│
╱ ╱ │
╱ ╱ │
┌─────────────────┐ │
│ ▒▒▒ │ │
│ ▒▓▓▒ │ │
│ ▒▓▓▓▒ │ ╱
│ ▒▒▒ │ ╱
└─────────────────┘╱
光线投射(Ray Casting):
对每个像素:
1. 发射光线穿过体积
2. 沿光线采样密度值
3. 应用传递函数(密度 → 颜色/不透明度)
4. 前向后或后向前合成
前向后合成:
C_out = C_in + (1 - α_in) × C_sample
α_out = α_in + (1 - α_in) × α_sample15.3.2 体积渲染实现
javascript
/**
* 简单体积渲染
*/
class VolumeRenderer {
constructor(volumeData, dimensions) {
this.data = volumeData; // Float32Array
this.dim = dimensions; // { x, y, z }
this.transferFunction = null;
}
/**
* 设置传递函数
*/
setTransferFunction(tf) {
this.transferFunction = tf;
}
/**
* 采样体积
*/
sample(x, y, z) {
// 三线性插值
const x0 = Math.floor(x);
const y0 = Math.floor(y);
const z0 = Math.floor(z);
const x1 = Math.min(x0 + 1, this.dim.x - 1);
const y1 = Math.min(y0 + 1, this.dim.y - 1);
const z1 = Math.min(z0 + 1, this.dim.z - 1);
const tx = x - x0;
const ty = y - y0;
const tz = z - z0;
const getValue = (xi, yi, zi) => {
const idx = zi * this.dim.x * this.dim.y + yi * this.dim.x + xi;
return this.data[idx];
};
// 三线性插值
const c000 = getValue(x0, y0, z0);
const c100 = getValue(x1, y0, z0);
const c010 = getValue(x0, y1, z0);
const c110 = getValue(x1, y1, z0);
const c001 = getValue(x0, y0, z1);
const c101 = getValue(x1, y0, z1);
const c011 = getValue(x0, y1, z1);
const c111 = getValue(x1, y1, z1);
const c00 = c000 * (1 - tx) + c100 * tx;
const c10 = c010 * (1 - tx) + c110 * tx;
const c01 = c001 * (1 - tx) + c101 * tx;
const c11 = c011 * (1 - tx) + c111 * tx;
const c0 = c00 * (1 - ty) + c10 * ty;
const c1 = c01 * (1 - ty) + c11 * ty;
return c0 * (1 - tz) + c1 * tz;
}
/**
* 渲染单条射线
*/
rayMarch(origin, direction, tMin, tMax, stepSize) {
let color = { r: 0, g: 0, b: 0 };
let alpha = 0;
let t = tMin;
while (t < tMax && alpha < 0.99) {
const pos = {
x: origin.x + direction.x * t,
y: origin.y + direction.y * t,
z: origin.z + direction.z * t
};
// 检查边界
if (pos.x >= 0 && pos.x < this.dim.x &&
pos.y >= 0 && pos.y < this.dim.y &&
pos.z >= 0 && pos.z < this.dim.z) {
const density = this.sample(pos.x, pos.y, pos.z);
const { color: sampleColor, alpha: sampleAlpha } =
this.transferFunction(density);
// 前向后合成
color.r += (1 - alpha) * sampleColor.r * sampleAlpha;
color.g += (1 - alpha) * sampleColor.g * sampleAlpha;
color.b += (1 - alpha) * sampleColor.b * sampleAlpha;
alpha += (1 - alpha) * sampleAlpha;
}
t += stepSize;
}
return { color, alpha };
}
}15.4 虚拟现实
15.4.1 VR/AR 技术概述
VR/AR 核心技术
┌─────────────────────────────────────────────────┐
│ VR/AR 技术栈 │
├─────────────────────────────────────────────────┤
│ │
│ 显示技术: │
│ - 头戴显示器(HMD) │
│ - 透视显示(AR) │
│ - 空间光场显示 │
│ │
│ 追踪技术: │
│ - 头部追踪(6DoF) │
│ - 手部追踪 │
│ - 眼动追踪 │
│ - SLAM(定位与建图) │
│ │
│ 渲染技术: │
│ - 立体渲染 │
│ - 时间扭曲(Time Warp) │
│ - 注视点渲染(Foveated Rendering) │
│ - 多视图渲染 │
│ │
└─────────────────────────────────────────────────┘
立体渲染:
左眼 右眼
┌─────────┐ ┌─────────┐
│ │ │ │
│ ● │ │ ● │
│ │ │ │
└─────────┘ └─────────┘
两个视角 → 深度感知
注视点渲染:
中央高分辨率,周边低分辨率:
┌─────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░│
│░░░░░░░░▓██████▓░░░░░░░░░░░░░│ ← 注视点
│░░░░░░░░▓██████▓░░░░░░░░░░░░░│
│░░░░░░░░▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░│
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
└─────────────────────────────┘
减少 50%+ 的像素计算15.4.2 WebXR API
javascript
/**
* WebXR 基础示例
*/
class WebXRExample {
constructor(canvas) {
this.canvas = canvas;
this.gl = canvas.getContext('webgl2', { xrCompatible: true });
this.xrSession = null;
this.xrRefSpace = null;
}
/**
* 检查 WebXR 支持
*/
async checkSupport() {
if (!navigator.xr) {
console.log('WebXR not supported');
return false;
}
const isSupported = await navigator.xr.isSessionSupported('immersive-vr');
console.log('Immersive VR supported:', isSupported);
return isSupported;
}
/**
* 开始 XR 会话
*/
async startXRSession() {
try {
this.xrSession = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['local-floor'],
optionalFeatures: ['hand-tracking']
});
// 设置渲染层
const glLayer = new XRWebGLLayer(this.xrSession, this.gl);
this.xrSession.updateRenderState({ baseLayer: glLayer });
// 获取参考空间
this.xrRefSpace = await this.xrSession.requestReferenceSpace('local-floor');
// 开始渲染循环
this.xrSession.requestAnimationFrame(this.onXRFrame.bind(this));
} catch (error) {
console.error('Failed to start XR session:', error);
}
}
/**
* XR 渲染帧
*/
onXRFrame(time, frame) {
const session = frame.session;
session.requestAnimationFrame(this.onXRFrame.bind(this));
const glLayer = session.renderState.baseLayer;
// 绑定帧缓冲
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, glLayer.framebuffer);
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
// 获取姿态
const pose = frame.getViewerPose(this.xrRefSpace);
if (pose) {
// 为每个视图(眼睛)渲染
for (const view of pose.views) {
const viewport = glLayer.getViewport(view);
this.gl.viewport(
viewport.x, viewport.y,
viewport.width, viewport.height
);
// 获取变换矩阵
const viewMatrix = view.transform.inverse.matrix;
const projectionMatrix = view.projectionMatrix;
// 渲染场景
this.renderScene(viewMatrix, projectionMatrix);
}
}
}
/**
* 渲染场景
*/
renderScene(viewMatrix, projectionMatrix) {
// 实际的渲染代码
// ...
}
/**
* 结束会话
*/
async endXRSession() {
if (this.xrSession) {
await this.xrSession.end();
this.xrSession = null;
}
}
}15.5 神经渲染
15.5.1 NeRF 简介
NeRF(Neural Radiance Fields)
使用神经网络表示 3D 场景。
输入:
- 3D 位置 (x, y, z)
- 视角方向 (θ, φ)
输出:
- 颜色 (r, g, b)
- 密度 σ
网络结构:
(x, y, z)
│
▼
┌─────────────┐
│ 位置编码 │
└──────┬──────┘
│
▼
┌─────────────┐
│ MLP │ ×8 层
└──────┬──────┘
│
┌─────┴─────┐
│ │
▼ ▼
密度σ 特征向量
│
├── + 视角编码
│
▼
┌─────────────┐
│ MLP │
└──────┬──────┘
│
▼
颜色(r,g,b)
体积渲染:
沿射线积分:
C = ∫ T(t) × σ(t) × c(t) dt
其中 T(t) = exp(-∫σ(s)ds) 是透射率15.5.2 3D 高斯溅射
3D Gaussian Splatting
用 3D 高斯表示场景,实现实时渲染。
每个高斯:
- 位置(均值)
- 协方差矩阵(形状、方向)
- 颜色(球谐函数)
- 不透明度
优势:
- 比 NeRF 快 100-1000 倍
- 可以实时渲染
- 质量接近 NeRF
渲染流程:
1. 将 3D 高斯投影到 2D
2. 按深度排序
3. 在图像空间进行 α 混合
3D 高斯
│
▼
┌─────────────┐
│ 投影到 2D │
└──────┬──────┘
│
▼
┌─────────────┐
│ 深度排序 │
└──────┬──────┘
│
▼
┌─────────────┐
│ α 混合 │
└──────┬──────┘
│
▼
最终图像15.6 WebGPU
15.6.1 WebGPU 概述
WebGPU:下一代 Web 图形 API
WebGL 的问题:
- 基于 OpenGL ES 2.0/3.0(老旧)
- 状态机模型
- 无法利用现代 GPU 特性
- 计算着色器支持有限
WebGPU 的优势:
- 现代设计(类似 Vulkan/Metal/DX12)
- 显式资源管理
- 计算着色器支持
- 更低的 CPU 开销
- 更好的验证
对比:
WebGL:
┌────────────────────────────────┐
│ 应用层 │
│ │ │
│ ▼ │
│ WebGL API (OpenGL ES) │
│ │ │
│ ▼ │
│ 浏览器 + ANGLE │
│ │ │
│ ▼ │
│ 原生 API (DX/Metal/Vulkan) │
└────────────────────────────────┘
WebGPU:
┌────────────────────────────────┐
│ 应用层 │
│ │ │
│ ▼ │
│ WebGPU API │
│ │ │
│ ▼ │
│ Dawn/wgpu │
│ │ │
│ ▼ │
│ 原生 API (DX/Metal/Vulkan) │
└────────────────────────────────┘15.6.2 WebGPU 基础示例
javascript
/**
* WebGPU 基础示例
*/
async function webgpuExample() {
// 1. 获取适配器和设备
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// 2. 配置画布
const canvas = document.querySelector('canvas');
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format,
alphaMode: 'opaque'
});
// 3. 创建着色器模块
const shaderModule = device.createShaderModule({
code: `
@vertex
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> @builtin(position) vec4f {
var pos = array<vec2f, 3>(
vec2f(0.0, 0.5),
vec2f(-0.5, -0.5),
vec2f(0.5, -0.5)
);
return vec4f(pos[vertexIndex], 0.0, 1.0);
}
@fragment
fn fragmentMain() -> @location(0) vec4f {
return vec4f(1.0, 0.0, 0.0, 1.0);
}
`
});
// 4. 创建渲染管线
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: shaderModule,
entryPoint: 'vertexMain'
},
fragment: {
module: shaderModule,
entryPoint: 'fragmentMain',
targets: [{ format }]
}
});
// 5. 渲染帧
function frame() {
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass({
colorAttachments: [{
view: context.getCurrentTexture().createView(),
loadOp: 'clear',
clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 },
storeOp: 'store'
}]
});
passEncoder.setPipeline(pipeline);
passEncoder.draw(3);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
requestAnimationFrame(frame);
}
frame();
}
/**
* WebGPU 计算着色器示例
*/
async function computeExample() {
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
// 创建缓冲区
const bufferSize = 1024 * 4; // 1024 个 float
const inputBuffer = device.createBuffer({
size: bufferSize,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
});
const outputBuffer = device.createBuffer({
size: bufferSize,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC
});
const readBuffer = device.createBuffer({
size: bufferSize,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
// 上传数据
const inputData = new Float32Array(1024);
for (let i = 0; i < 1024; i++) {
inputData[i] = i;
}
device.queue.writeBuffer(inputBuffer, 0, inputData);
// 创建计算着色器
const computeModule = device.createShaderModule({
code: `
@group(0) @binding(0) var<storage, read> input: array<f32>;
@group(0) @binding(1) var<storage, read_write> output: array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
output[id.x] = input[id.x] * 2.0;
}
`
});
// 创建计算管线
const computePipeline = device.createComputePipeline({
layout: 'auto',
compute: {
module: computeModule,
entryPoint: 'main'
}
});
// 创建绑定组
const bindGroup = device.createBindGroup({
layout: computePipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: inputBuffer } },
{ binding: 1, resource: { buffer: outputBuffer } }
]
});
// 执行计算
const commandEncoder = device.createCommandEncoder();
const computePass = commandEncoder.beginComputePass();
computePass.setPipeline(computePipeline);
computePass.setBindGroup(0, bindGroup);
computePass.dispatchWorkgroups(16); // 16 * 64 = 1024
computePass.end();
// 复制结果
commandEncoder.copyBufferToBuffer(outputBuffer, 0, readBuffer, 0, bufferSize);
device.queue.submit([commandEncoder.finish()]);
// 读取结果
await readBuffer.mapAsync(GPUMapMode.READ);
const result = new Float32Array(readBuffer.getMappedRange());
console.log('Result:', result.slice(0, 10));
readBuffer.unmap();
}15.7 本章小结
技术发展趋势
图形学发展方向
1. 实时光线追踪
- 硬件加速普及
- 混合渲染成为主流
2. AI 与图形学融合
- 神经渲染
- AI 超分辨率
- 程序化内容生成
3. 沉浸式体验
- VR/AR 普及
- 元宇宙应用
- 空间计算
4. Web 图形增强
- WebGPU 普及
- 浏览器中的专业应用
- 云渲染
5. 实时全局光照
- 软件光线追踪优化
- 新的 GI 近似技术学习建议
| 方向 | 推荐技术 | 学习资源 |
|---|---|---|
| 实时渲染 | Vulkan, DX12, WebGPU | 官方文档、LearnOpenGL |
| 离线渲染 | PBRT, 光线追踪 | PBRT 书籍 |
| VR/AR | WebXR, Unity XR | 官方教程 |
| AI 渲染 | NeRF, 3DGS | 论文、开源实现 |
关键要点
- 光线追踪正在从离线走向实时
- 神经渲染是重要的新方向
- WebGPU 将大幅提升 Web 图形能力
- VR/AR 需要特殊的渲染优化
- 图形学与 AI 的结合越来越紧密
系列总结
恭喜完成《计算机图形学原理》系列学习!
本系列涵盖了:
- 数学基础(向量、矩阵、变换)
- 经典算法(光栅化、裁剪、曲线)
- 渲染技术(光照、纹理、阴影)
- 高级主题(碰撞、空间索引、图像处理)
- 前沿方向(光追、神经渲染、WebGPU)
建议后续学习路径:
- 实践:实现软件光栅化器或光线追踪器
- 深入:学习一个专业方向(游戏、影视、可视化)
- 应用:参与开源项目或开发自己的项目
文档版本:v1.0
字数统计:约 10,000 字
系列完结
