Pipeline Overview管线概览

NeoX's primary rendering path is forward shading — every visible pixel evaluates its material and all affecting lights in a single pass. This is configured by forward_shading.render (the pipeline declaration XML).

NeoX 的主要渲染路径是前向着色——每个可见像素在单次 Pass 中计算其材质和所有影响光源。这由 forward_shading.render(管线声明 XML)配置。

+=====================================================================+ | NeoX Forward Shading -- Single Pass Flow | +=====================================================================+ | | | vs_main() | | +-------------------------------------------------------------+ | | | 01 TransformCommonVertex() // model->world | | | | 02 ApplyVertexOffsetLocal() // vertex animation | | | | 03 DoSkin() // GPU skinning (optional) | | | | 04 Transform(world_mat) // world position | | | | 05 SetupFragment() // pack interpolants | | | | 06 DepthBias / Skybox / Bake // special modes | | | +-------------------------------------------------------------+ | | v rasterize | | ps_main() | | +-------------------------------------------------------------+ | | | 07 GetRawData() // sample all textures | | | | 08 SetupMaterial() // fill Material struct | | | | 09 CalcLightingData() // precompute NdotV etc. | | | | 10 for each Light: | | | | +-- Shadow sampling | | | | +-- DirectLighting() -> diffuse + specular | | | | +-- accumulate | | | | 11 EnvLighting() // SH + IBL reflection | | | | 12 Apply fog, emissive, AO | | | | 13 Tone mapping (ACES) | | | | 14 Output SV_Target0 | | | +-------------------------------------------------------------+ | +=====================================================================+

Vertex Shader Stage顶点着色器阶段

The vertex shader performs geometry transformation with support for GPU skinning (up to 90 bones), vertex animation, and special projection modes:

顶点着色器执行几何变换,支持 GPU 蒙皮(最多 90 根骨骼)、顶点动画和特殊投影模式:

01Transform Chain变换链

Fragment vs_main(Vertex input) {
  CommonVertex vert = TransformCommonVertex(input, vert_inter);
  ApplyVertexOffsetLocal(vert, vert.position);  // material-driven offset

  #if GPU_SKIN_ENABLE
    DoSkin(vert.blendWeights, vert.blendIndices,
           vert.position, vert.normal, vert.tangent);
  #endif

  float4 world_position = Transform(vert.world_mat, vert.position);
  PresetupFragment(...);
  ApplyVertexOffsetWorld(...);  // world-space WPO
  SetupFragment(...);           // pack all interpolants
}

02Special Projection Modes特殊投影模式

Mode模式MacroUse Case使用场景
IS_PARABOLOID#if IS_PARABOLOIDDual-paraboloid shadow maps for omni lights全向光源双抛物面阴影贴图
IS_SKYBOX#if IS_SKYBOXForce depth to far plane (z = w*0.9999)强制深度到远平面
TEXTURE_BAKE#if TEXTURE_BAKEUV-space rendering for lightmap bakingUV 空间渲染用于光照贴图烘焙
DEPTH_BIAS#if DEPTH_BIASManual depth offset (shadow bias)手动深度偏移(阴影偏差)

Pixel Shader Stage像素着色器阶段

The pixel shader is where all material evaluation and lighting happens. The flow follows a strict order:

像素着色器是所有材质计算和光照发生的地方。流程遵循严格顺序:

float4 ps_main(Fragment input, bool is_front_face : SV_IsFrontFace) : SV_Target0 {
  // 1. Setup
  Material mtl = (Material)0;
  View v;
  v.position = CameraPosition;
  v.direction = normalize(input.position_world - CameraPosition);
  v.distance = length(input.position_world - CameraPosition);

  // 2. Material setup (calls into material_*.hlsl)
  RawData raw_data = GetRawData(input);   // texture sampling
  SetupMaterial(input, raw_data, mtl);    // decode --> Material struct

  // 3. Lighting (calls into lighting.hlsl --> shading model)
  LightingResult result = Lighting(input, mtl, v);

  // 4. Compose final color
  float3 color = result.direct_diff + result.direct_spec
              + result.env_diff + result.env_spec
              + mtl.emissive;

  // 5. Tone mapping
  color = get_ACES_tone_mapping(color);

  return float4(color, opacity);
}

Lighting Loop — How Lights Are Evaluated光照循环 — 光源如何计算

Inside lighting.hlsl (1982 lines), the main lighting function iterates over all active lights. Each light goes through shadow evaluation, then calls the active shading model's DirectLighting():

lighting.hlsl(1982 行)中,主光照函数遍历所有活动光源。每个光源经过阴影计算,然后调用当前着色模型的 DirectLighting()

Lighting(input, mtl, v) | +-- CalcLightingData(mtl, v, data_r) // precompute NdotV, spec, diff | +-- for each Light[i]: | +-- compute n_dot_l | +-- if LightNotAffect(n_dot_l) --> skip | +-- CalcLightingData(light, mtl, v, n_dot_l, data_r, data_rw) | +-- shadow = SampleShadowMap() // CSM / paraboloid | +-- data_rw.atten = light.atten * shadow | +-- DirectLighting(input, mtl, data_r, data_rw, | --> light_diffuse, light_specular, light_transmission) | +-- LightingDiffuse() // model-specific | +-- LightingSpecular() // GGX or model-specific | +-- EnvLighting(input, mtl, v, data_r, result) | +-- SH diffuse // spherical harmonics | +-- EnvironmentSpecular() // reflection probe + IBL | +-- EnvBRDFApprox() // split-sum approximation | +-- return LightingResult { direct_diff, direct_spec, env_diff, env_spec }
Key difference from UE5: NeoX evaluates ALL lights in a single shader invocation (classic forward). UE5's forward path uses Clustered Shading — a 3D froxel grid bins lights by tile, reducing per-pixel light iteration cost. NeoX relies on the engine to limit active light count per draw call. 与 UE5 的关键差异:NeoX 在单次着色器调用中计算所有光源(经典前向)。UE5 的前向路径使用聚簇着色——3D Froxel 网格按 Tile 分箱光源,降低逐像素光源迭代开销。NeoX 依赖引擎限制每次绘制调用的活动光源数量。

Special Modes特殊模式

Mode模式MacroWhat it does功能
SKIN4S_ENABLESKIN4S_SPECULARPASS4-pass subsurface scattering (split diffuse/specular)4-Pass 次表面散射(分离漫反射/高光)
TOON_EFFECT#if TOON_EFFECTToon shading via Lerp3 color ramp通过 Lerp3 色阶实现卡通着色
IMAGE_DECAL_ENABLE#if IMAGE_DECAL_ENABLEApply projected image decals to GBuffer应用投影图片贴花到 GBuffer
DEBUG_DATA#if DEBUG_DATAOutput material properties as color for debugging输出材质属性为颜色用于调试
RECORD_MODE#if RECORD_MODERecord rendering data for replay记录渲染数据用于回放

Source Files源码文件