跳到主要内容
rendering-effects · 代码
查看代码目录
Graphics Learning Lab Rendering Effects / Source

Observe · Change · Recompute · Explain

每个效果都要露出它的成本和边界

Read-only · exact teaching model

代码页直接展示课程使用的最小 HLSL/GLSL 结构,按传播、pass、coverage、深度、管线、体积和数值 debug 阅读。

这一课怎么学

  1. 观察
  2. 预测
  3. 改一个量
  4. 看证据
  5. 再读代码
SSS profile

传播半径与采样

把次表面散射从“加颜色”拆成可预算的传播。

// SECTION:sss
float sssProfile(float radius, int samples, float thickness) {
  float diffusion = 0.0;
  for (int i = 0; i < samples; i++) {
    diffusion += profile(radius, thickness, float(i) / float(samples));
  }
  return diffusion / float(samples);
}

ShaderLab anatomy

顶点/片元结构

Q10 的手写 shader 先读入口、资源和 pass。

// SECTION:shaderlab
Shader "TA/Learning/NormalMapped" {
  Properties { _NormalMap("Normal", 2D) = "bump" {} }
  SubShader {
    Pass { HLSLPROGRAM
      #pragma vertex vert
      #pragma fragment frag
    ENDHLSL }
  }
}

Alpha / A2C

coverage 不是 blend

透明路径的硬件 coverage 与排序成本不同。

// SECTION:alpha-coverage
float coverage = round(alpha * float(msaaSamples)) / float(msaaSamples);
float4 color = alphaMode == ATOC
  ? float4(baseColor, coverage)
  : blendSortedLayers(baseColor, alpha);

Soft occlusion

深度决定可见性

软遮挡必须说明深度来源和边界。

// SECTION:occlusion
float visibility = smoothstep(-softness, softness, sceneDepth - sampleDepth);
visibility *= depthPrepass ? 1.0 : 0.82;

Forward / Deferred

路径是资源选择

灯光、G-buffer、透明和带宽共同决定取舍。

// SECTION:forward-deferred
// Forward: light work follows each visible material.
// Deferred: write G-buffer, then light screen-space pixels.
float lightingCost = renderPath == FORWARD
  ? visiblePixels * lightCount
  : gbufferBandwidth + visiblePixels;

Volume ray march

步数×噪声

天气效果的质量和成本来自可解释的循环。

// SECTION:volume
for (int step = 0; step < volumeSteps; step++) {
  float density = sampleNoise(rayPosition, noiseOctaves);
  transmittance *= exp(-density * stepLength);
  radiance += transmittance * density * exposure;
  rayPosition += rayDirection * stepLength;
}

HDR overflow

数值也要 debug

NaN/Inf/过曝必须有 clamp 和可见证据。

// SECTION:overflow
float3 safeRadiance = clampHdr ? min(radiance, maxHdr) : radiance;
debugColor = any(isnan(safeRadiance)) || max(safeRadiance) > maxHdr
  ? float3(1.0, 0.1, 0.1)
  : safeRadiance;