第09章:材质、光照与渲染
在第02章中我们认识了 GeometricElement——所有三维可见元素的基类。它除了定义几何形状(通过 Representation + SolidOperation),还需要材质来赋予表面外观,需要光照来照亮场景。本章从 Material 类的完整属性出发,逐步介绍颜色系统、内置材质、灯光类型,最终落到 glTF 导出与逐顶点修饰的实战应用。
9.1 Material 材质
Material 继承自 Element,因此它拥有 Id(GUID 唯一标识)和 Name,可以在 Model 中作为独立元素存储和引用。核心属性如下:
public class Material : Element
{
public Color Color { get; set; } = Colors.Gray; // RGBA 颜色 [Required]
public double SpecularFactor { get; set; } = 0.1; // 高光强度 [0, 1]
public double GlossinessFactor { get; set; } = 0.1; // 光泽度 [0, 1]
public bool Unlit { get; set; } = false; // 无光照(自发光/常量色)
public string Texture { get; set; } // 纹理图片路径
public string NormalTexture { get; set; } // 法线贴图路径
public string EmissiveTexture { get; set; } // 自发光纹理路径
public double EmissiveFactor { get; set; } // 自发光强度 [0, 1]
public bool DoubleSided { get; set; } = false; // 双面渲染
public bool RepeatTexture { get; set; } = true; // 纹理重复/拉伸
public bool InterpolateTexture { get; set; } = true; // 纹理插值(关闭为硬像素)
public bool DrawInFront { get; set; } = false; // 总在最前渲染
public EdgeDisplaySettings EdgeDisplaySettings { get; set; } // 边线显示设置
}
9.1.1 Color:RGBA 颜色
Color 是一个结构体(struct),位于 Elements.Geometry 命名空间下。每个分量的取值范围是 [0.0, 1.0](0 为全暗/全透明,1 为最亮/完全不透明):
public struct Color : IEquatable<Color>
{
public double Red { get; set; } // 红色分量 [0, 1]
public double Green { get; set; } // 绿色分量 [0, 1]
public double Blue { get; set; } // 蓝色分量 [0, 1]
public double Alpha { get; set; } // 透明度分量 [0, 1]
}
创建颜色的几种方式:
// 直接指定 RGBA 浮点值
var c1 = new Color(1.0, 0.0, 0.0, 1.0); // 红色
// 隐式转换:从 (R, G, B) 元组(Alpha 自动为 1)
Color c2 = (1.0, 0.5, 0.0); // 橙色
// 隐式转换:从 (R, G, B, A) 元组
Color c3 = (0.0, 1.0, 0.0, 0.5); // 半透明绿色
// 隐式转换:从十六进制字符串
Color c4 = "#FF5733";
// 隐式转换:从 X11 颜色名(如 "Goldenrod", "DarkSlateGray" 等)
Color c5 = "SteelBlue";
Color 支持加减乘运算符和线性插值:
var blended = c1.Lerp(c2, 0.3); // 30% c1 + 70% c2
var darkened = 0.5 * c1; // 亮度减半
var additive = c1 + c2; // 加法混合(自动钳位到 [0,1])
9.1.2 高光与光泽:SpecularFactor 与 GlossinessFactor
Elements 的材质使用 Specular-Glossiness 工作流(对应 glTF 已弃用但 Elements 仍在使用的 PBR 变体):
- SpecularFactor(高光强度):值越大,高光越明显。金属材质可设 0.5~1.0,混凝土/木材设 0.0。
- GlossinessFactor(光泽度):值越大,高光范围越小(越”亮面”)。1.0 为完美镜面反射效果,0.0 为完全漫反射。
两者的取值范围均为 [0.0, 1.0],构造时超出范围将抛出 ArgumentOutOfRangeException。
9.1.3 无光照模式:Unlit
当 Unlit = true 时,材质不响应场景光照,始终以 Color 常量值渲染。适合分析可视化、标注线、轴指示器等不需要明暗变化的元素。
var unlitRed = new Material("analysis", Colors.Red, unlit: true);
9.1.4 纹理映射:Texture / NormalTexture / EmissiveTexture
三个纹理属性都接受相对文件路径(.jpg / .png),构造时会检查文件是否存在——若文件不存在,该属性被静默置为 null:
var texturedMaterial = new Material(
name: "brick-wall",
color: Colors.White,
texture: "textures/brick_diffuse.png",
normalTexture: "textures/brick_normal.png",
repeatTexture: true
);
- Texture:基础颜色纹理(漫反射贴图),与
Color相乘得到最终颜色。 - NormalTexture:法线贴图,增加表面凹凸细节。
- EmissiveTexture:自发光纹理,配合
EmissiveFactor控制发光强度。 - RepeatTexture:
true时纹理在 UV 范围外重复;false时钳制到边缘。 - InterpolateTexture:
true时纹理像素间线性插值(平滑);false时显示硬像素边缘。
9.1.5 双面渲染:DoubleSided
默认情况下,三角面片的法线朝外那一面才可见。当 DoubleSided = true 时,面片的背面也会被渲染。这在以下场景有用:
- 单层曲面(如幕墙玻璃、薄膜结构)
- 没有厚度的开口
- 半透明材质需要从内部看到
9.1.6 边线控制:EdgeDisplaySettings
EdgeDisplaySettings 控制曲线和线段的显示样式(不影响网格/实体渲染):
public class EdgeDisplaySettings
{
public double LineWidth { get; set; } = 1; // 线宽
public EdgeDisplayWidthMode WidthMode { get; set; } // 屏幕单位 或 世界单位
public EdgeDisplayDashMode DashMode { get; set; } // 虚线模式
public double DashSize { get; set; } = 1; // 虚线长度
public double? GapSize { get; set; } = 1; // 虚线间隔
}
9.1.7 构造器一览
Elements 提供了多个 Material 构造器,按需选用:
// 1. 默认构造:灰色材质
var m1 = new Material();
// 2. 仅命名
var m2 = new Material("my-material");
// 3. 命名 + 颜色 + 可选参数(最常用)
var m3 = new Material("concrete", Colors.Gray, specularFactor: 0.0, glossinessFactor: 0.0);
// 4. 命名 + 颜色 + 纹理(完整参数)
var m4 = new Material(
name: "glass-panel",
color: new Color(1, 1, 1, 0.3),
specularFactor: 1.0,
glossinessFactor: 1.0,
texture: null,
unlit: false,
doubleSided: true,
repeatTexture: true
);
// 5. 完整参数构造器(含所有属性)
var m5 = new Material(
color: Colors.White,
specularFactor: 0.1,
glossinessFactor: 0.5,
unlit: false,
texture: "wood.png",
doubleSided: false,
repeatTexture: true,
normalTexture: null,
interpolateTexture: true,
emissiveTexture: null,
emissiveFactor: 0.0,
drawInFront: false
);
9.2 标准颜色预设:Colors 常量类
Colors 静态类提供了 30 种常用颜色预设,值域均为 RGB [0, 1] + Alpha 1.0:
| 属性 | R | G | B | 视觉 |
|---|---|---|---|---|
Colors.Red |
1.0 | 0.0 | 0.0 | 纯红 |
Colors.Green |
0.0 | 1.0 | 0.0 | 纯绿 |
Colors.Blue |
0.0 | 0.0 | 1.0 | 纯蓝 |
Colors.White |
1.0 | 1.0 | 1.0 | 纯白 |
Colors.Black |
0.0 | 0.0 | 0.0 | 纯黑 |
Colors.Gray |
0.5 | 0.5 | 0.5 | 中灰 |
Colors.Darkgray |
0.2 | 0.2 | 0.2 | 深灰 |
Colors.Yellow |
1.0 | 0.9 | 0.1 | 黄色 |
Colors.Orange |
1.0 | 0.5 | 0.1 | 橙色 |
Colors.Cyan |
0.3 | 0.9 | 0.9 | 青色 |
Colors.Purple |
0.7 | 0.1 | 1.0 | 紫色 |
Colors.Brown |
0.6 | 0.4 | 0.2 | 棕色 |
Colors.Pink |
1.0 | 0.3 | 0.5 | 粉色 |
| … | … | … | … | … |
完整清单还包括 Aqua、Beige、Cobalt、Coral、Crimson、Emerald、Granite、Lavender、Lime、Magenta、Maroon、Mint、Navy、Olive、Sand、Stone、Teal。快速创建带颜色的材质:
var redMaterial = new Material("red", Colors.Red);
var semiTransparent = new Material("glass", new Color(1, 1, 1, 0.3), specularFactor: 1.0, glossinessFactor: 1.0);
9.3 材质与 GeometricElement 的关联
GeometricElement 上的 Material 属性决定了元素的基础材质。每个建筑元素类型(Wall、Beam、Column、Floor 等)在构造时都可以传入 Material 参数:
// Profile 和 Line 的用法参见第03章
var profile = Polygon.Rectangle(10, 10);
// 创建材质
var concreteMaterial = new Material("concrete", Colors.Gray, specularFactor: 0.0, glossinessFactor: 0.0);
// 将材质绑定到楼板
var floor = new Floor(profile, concreteMaterial, transform: new Transform(0, 0, 3));
9.3.1 RepresentationInstance 中的材质覆盖
当建筑元素使用 RepresentationInstances(如幕墙中的嵌板、门窗实例)时,每个 RepresentationInstance 可以携带自己的 Material,覆盖基础元素的材质。换言之:
GeometricElement.Material → 默认材质(所有表现实例的 fallback)
RepresentationInstance.Material → 实例级覆盖材质(优先级更高)
这种两级材质体系使得同一个元素定义可以产生不同外观的实例。例如一面标准墙体定义可以生成不同楼层、不同颜色的墙段——无需为每种颜色创建新元素。
9.3.2 元素定义与实例共享
当 GeometricElement.IsElementDefinition = true 时,该元素本身不在场景中渲染,而是作为模板供其他 ElementInstance 引用。实例通过 CreateInstance(transform, name) 创建:
// 定义模板(不渲染)
var columnDef = new Column(Vector3.Origin, 3.0, Polygon.Rectangle(0.3, 0.3),
material: BuiltInMaterials.Steel, isElementDefinition: true);
// 在不同位置创建实例
var instance1 = columnDef.CreateInstance(new Transform(0, 0, 0), "col-1");
var instance2 = columnDef.CreateInstance(new Transform(5, 0, 0), "col-2");
model.AddElements(columnDef, instance1, instance2);
9.4 Light 灯光体系
Light 是所有灯光的抽象基类,继承自 Element。灯光朝向由 Transform 的 -Z 轴 定义(即灯光朝 Transform 局部坐标系的 -Z 方向照射)。
public abstract class Light : Element
{
public LightType LightType { get; set; } // 灯光类型枚举
public double Intensity { get; set; } // 光照强度
public Color Color { get; set; } // 光照颜色
public Transform Transform { get; set; } // 位置+方向(朝 -Z)
}
三种具体灯光类型,对应枚举 LightType:
9.4.1 DirectionalLight:平行光(太阳光)
平行光模拟无限远处的光源(如太阳),所有光线方向平行。强度以 lux(勒克斯)为单位,默认名称 "Sun":
var sun = new DirectionalLight(
color: Colors.White,
transform: new Transform(Vector3.Origin, new Vector3(1, 1, 1)),
intensity: 100000, // 晴天日光约 100000 lux
name: "Sun"
);
要改变光线方向,调整 Transform 的朝向即可。例如从东南方 45 度角照射:
var sunDir = new Vector3(1, -1, 1).Unitized(); // 东南方向,往下
var sunTransform = new Transform(Vector3.Origin, sunDir);
var sun = new DirectionalLight(Colors.White, sunTransform, intensity: 80000);
顺便提一句:Elements 右手坐标系中 +Z 向上,而灯光的 -Z 是照射方向。平行光的位置(Transform 的平移分量)不影响渲染,只影响照射方向。
9.4.2 PointLight:点光源
点光源从一个位置向所有方向均匀辐射。强度以 candela(坎德拉)为单位:
var bulb = new PointLight(
color: new Color(1.0, 0.95, 0.8, 1.0), // 暖白
transform: new Transform(5, 5, 3), // 房间中央
intensity: 500,
name: "ceiling-light"
);
9.4.3 SpotLight:聚光灯
聚光灯从一点向锥形区域发射光线,有内锥和外锥两个角度参数(单位弧度):
var spotlight = new SpotLight(
color: Colors.White,
transform: new Transform(new Vector3(0, 0, 5), Vector3.ZAxis.Negate()), // 从上往下照
intensity: 2000,
innerConeAngle: Math.PI / 8, // 内锥 22.5 度(完全亮区域)
outerConeAngle: Math.PI / 4, // 外锥 45 度(衰减区域边界)
name: "track-light"
);
约束条件:
innerConeAngle和outerConeAngle必须在[0, PI/2]范围内innerConeAngle必须 ≤outerConeAngle- 内锥与外锥之间为光线衰减区(半影)
9.5 命名颜色与 BuiltInMaterials 内置材质
BuiltInMaterials 是一个静态工具类,提供了一套带有固定 GUID 的预设材质,适用于 AEC 常见场景。因为 GUID 固定,序列化到 JSON 时材质引用保持一致:
| 材质 | 颜色 | Specular | Glossiness | 用途 |
|---|---|---|---|---|
BuiltInMaterials.Glass |
白色, α=0.2 | 1.0 | 1.0 | 玻璃/幕墙 |
BuiltInMaterials.Steel |
暖灰色 | 0.1 | 0.4 | 钢结构 |
BuiltInMaterials.Concrete |
中灰 | 0.0 | 0.0 | 混凝土 |
BuiltInMaterials.Wood |
木色 (224,206,155)/255 | 0.0 | 0.0 | 木材 |
BuiltInMaterials.Mass |
淡蓝, α=0.2 | 0.0 | 0.0 | 体量模型 |
BuiltInMaterials.Default |
白色 | 0.0 | 0.0 | 默认材质 |
BuiltInMaterials.Black |
黑色 | 0.0 | 0.0 | 纯黑 |
BuiltInMaterials.Edges |
深灰 Unlit | 0.0 | 0.0 | 模型边线 |
BuiltInMaterials.Points |
红色 | 0.0 | 0.0 | 点标记 |
BuiltInMaterials.Topography |
卡其色 | 0.0 | 0.0 | 地形 |
BuiltInMaterials.Trans |
全透明 | 0.0 | 0.0 | 完全透明 |
BuiltInMaterials.Void |
石灰绿, α=0.1 | 0.1 | 0.1 | 减除体示意 |
BuiltInMaterials.XAxis / YAxis / ZAxis |
红/绿/蓝 Unlit | 0.1 | 0.1 | 坐标轴指示 |
使用方式:
var beam = new Beam(line, profile, BuiltInMaterials.Steel);
var floor = new Floor(profile, BuiltInMaterials.Concrete, transform: new Transform(0, 0, 3));
这些内置材质可以按需修改(因为是 Material 实例的属性是可读写的),但不建议这样做——它们被多个元素共享,修改一处会影响所有引用者。如果需要变体,最好创建自己的 Material 实例。
9.6 渲染管线:Material 到 glTF PBR 的导出
Elements 的 Model.ToGlTF() 方法(位于 Elements.Serialization.glTF 命名空间)将模型中的所有几何元素、材质和灯光导出为 glTF 2.0 格式(.glb 或 .gltf)。
9.6.1 导出方法
using Elements.Serialization.glTF;
// 导出为二进制 GLB
model.ToGlTF("output.glb");
// 导出为 JSON + 外部 buffer
model.ToGlTF("output.gltf", useBinarySerialization: false);
// 带边线绘制
model.ToGlTF("output.glb", drawEdges: true);
// 导出到字节数组(适合 Web API 返回)
byte[] glbBytes = model.ToGlTF(drawEdges: false);
// 导出到流
using var stream = new MemoryStream();
model.ToGlTF(stream);
// 导出为 Base64 字符串(嵌入 HTML)
string base64 = model.ToBase64String();
9.6.2 材质转换映射
导出管线将 Elements 的 Specular-Glossiness 材质自动转换为 glTF 的 PBR Metallic-Roughness 标准:
| Elements 属性 | glTF 输出 | 转换逻辑 |
|---|---|---|
Color |
pbrMetallicRoughness.baseColorFactor |
RGBA 数组,sRGB→线性空间 |
GlossinessFactor |
pbrMetallicRoughness.roughnessFactor |
roughness = 1.0 - glossiness |
SpecularFactor |
KHR_materials_specular.specularFactor |
扩展属性,直接映射 |
Unlit = true |
KHR_materials_unlit |
激活 Unlit 扩展 |
DoubleSided |
material.doubleSided |
直接映射 |
Texture |
pbrMetallicRoughness.baseColorTexture |
图片嵌入 buffer |
NormalTexture |
material.normalTexture |
图片嵌入 buffer |
EmissiveTexture |
material.emissiveTexture |
图片嵌入 buffer |
EmissiveFactor |
material.emissiveFactor |
三个分量相同 |
Color.Alpha < 1 |
alphaMode: "BLEND" |
启用混合模式 |
注意:MetallicFactor 固定为 0(转换自 Spec-Gloss 工作流时无金属度信息)。
9.6.3 灯光导出
所有 Light 子类通过 KHR_lights_punctual 扩展写入 glTF。灯光节点的变换矩阵从 Transform 计算,颜色和强度直接映射:
// glTF 中的灯光结构(由导出管线自动生成)
{
"extensions": {
"KHR_lights_punctual": {
"lights": [
{
"color": [1.0, 1.0, 1.0],
"type": "directional",
"intensity": 100000,
"name": "Sun"
}
]
}
}
}
9.6.4 完整导出示例
以下代码创建一个包含楼板、柱子和灯光的场景,并导出为 glTF:
using Elements;
using Elements.Geometry;
using Elements.Serialization.glTF;
// 创建模型
var model = new Model();
// 创建材质
var concreteMat = new Material("concrete", Colors.Gray, specularFactor: 0.0, glossinessFactor: 0.0);
var steelMat = new Material("steel", new Color(0.6, 0.5, 0.5, 1.0), specularFactor: 0.1, glossinessFactor: 0.4);
// 创建楼板
var floor = new Floor(
Polygon.Rectangle(10, 8),
concreteMat,
transform: new Transform(0, 0, 0)
);
// 创建柱子
var colProfile = Polygon.Rectangle(0.3, 0.3);
for (int x = 0; x <= 10; x += 5)
{
for (int y = 0; y <= 8; y += 4)
{
var col = new Column(
new Vector3(x, y, 0),
3.5,
colProfile,
steelMat
);
model.AddElement(col);
}
}
model.AddElement(floor);
// 添加平行光(太阳)
var sun = new DirectionalLight(
Colors.White,
new Transform(Vector3.Origin, new Vector3(1, 1, 1)),
intensity: 100000
);
model.AddElement(sun);
// 添加点光源
var pointLight = new PointLight(
new Color(1.0, 0.95, 0.8),
new Transform(5, 4, 2.5),
intensity: 300
);
model.AddElement(pointLight);
// 导出
model.ToGlTF("building.glb");
9.7 ModifyVertexAttributes:逐顶点修改钩子
GeometricElement 上有一个委托属性 ModifyVertexAttributes,在三角剖分(Tessellation)过程中逐顶点调用。其签名为:
public Func<
(Vector3 position, Vector3 normal, UV uv, Color? color), // 输入
(Vector3 position, Vector3 normal, UV uv, Color? color) // 输出
> ModifyVertexAttributes { get; set; }
每次剖分生成一个顶点时,该函数接收顶点的位置、法线、UV 和可选颜色,返回修改后的版本。你可以利用它:
- 分析可视化:根据顶点位置计算分析值,映射颜色
- 顶点位移:实现地形起伏、波浪效果
- 程序化 UV:动态计算纹理坐标
9.7.1 分析可视化示例
以下示例创建一个楼板,并根据每个顶点到中心点的距离映射红-蓝渐变色:
var floorProfile = Polygon.Rectangle(20, 20);
var floor = new Floor(floorProfile, new Material("heatmap", Colors.White, unlit: true));
// 中心点
var center = new Vector3(10, 10, 0);
double maxDist = center.DistanceTo(new Vector3(0, 0, 0)); // 对角线半长
floor.ModifyVertexAttributes = (input) =>
{
var dist = input.position.DistanceTo(center);
var t = Math.Min(dist / maxDist, 1.0); // 归一化 [0, 1]
// 靠近中心为蓝色,远离中心为红色
var color = new Color(t, 0.0, 1.0 - t, 1.0);
return (input.position, input.normal, input.uv, color);
};
9.7.2 地形顶点位移示例
// 假设有一个 Mass 作为地形
var terrain = new Mass(Polygon.Rectangle(50, 50), 10.0);
terrain.ModifyVertexAttributes = (input) =>
{
var pos = input.position;
// 使用正弦波模拟地形起伏
var displacement = Math.Sin(pos.X * 0.3) * Math.Cos(pos.Y * 0.3) * 2.0;
// 海拔越高颜色越浅
var h = (displacement + 2.0) / 4.0; // 归一化
var color = new Color(1 - h * 0.5, 1 - h * 0.3, 1 - h * 0.5, 1.0);
var newPos = new Vector3(pos.X, pos.Y, pos.Z + displacement);
return (newPos, input.normal, input.uv, color);
};
注意:ModifyVertexAttributes 只影响三角剖分阶段的顶点颜色属性(glTF 中的 COLOR_0)。材质自身的 Color 属性仍然作用于渲染管线。如需完全掌控颜色,建议同时设置 unlit: true 关闭光照,避免顶点颜色与材质颜色相乘。
9.7.3 内置分析可视化:AnalysisMesh
Elements 内置了一个更高级的分析可视化方案:AnalysisMesh(位于 Elements.Analysis 命名空间)。它将多边形区域划分为网格单元,对每个单元中心调用分析函数,用颜色梯度显示结果:
using Elements.Analysis;
var perimeter = Polygon.Rectangle(30, 20);
var colorScale = new ColorScale(new[] { Colors.Blue, Colors.Green, Colors.Red }, 5);
var analysisMesh = new AnalysisMesh(
perimeter: perimeter,
uLength: 0.5,
vLength: 0.5,
colorScale: colorScale,
analyze: (point) =>
{
// 假设分析函数:距中心点的距离
return point.DistanceTo(new Vector3(15, 10, 0));
},
name: "distance-analysis"
);
analysisMesh.Analyze();
model.AddElement(analysisMesh);
ColorScale 根据分析值自动从颜色梯度中选择对应颜色。
9.8 实战示例:建筑外立面材质与光照
以下完整示例创建一个简化的三层办公楼外立面:混凝土楼板、玻璃幕墙、钢柱,加上阳光和环境点光源,导出为可直接在 Web 查看的 .glb 文件。
using System;
using Elements;
using Elements.Geometry;
using Elements.Serialization.glTF;
class BuildingFacade
{
static void Main()
{
var model = new Model();
double floorHeight = 3.6;
int floorCount = 3;
double width = 15;
double depth = 10;
// ===== 材质 =====
var concreteSlab = new Material("concrete-slab",
new Color(0.7, 0.7, 0.7, 1.0),
specularFactor: 0.0, glossinessFactor: 0.05);
var glassCurtain = new Material("glass-curtain",
new Color(0.6, 0.75, 0.9, 0.35),
specularFactor: 1.0, glossinessFactor: 0.95,
doubleSided: true);
var steelColumn = new Material("steel-column",
new Color(0.35, 0.35, 0.35, 1.0),
specularFactor: 0.3, glossinessFactor: 0.6);
// ===== 结构 =====
var slabProfile = Polygon.Rectangle(width, depth);
var colProfile = Polygon.Rectangle(0.25, 0.25);
for (int f = 0; f < floorCount; f++)
{
double z = f * floorHeight;
// 楼板
var slab = new Floor(slabProfile, concreteSlab,
transform: new Transform(0, 0, z + 0.2));
model.AddElement(slab);
// 四角柱子
foreach (var corner in new[]
{
(0.15, 0.15), (width - 0.15, 0.15),
(0.15, depth - 0.15), (width - 0.15, depth - 0.15)
})
{
var col = new Column(
new Vector3(corner.Item1, corner.Item2, z),
floorHeight, colProfile, steelColumn);
model.AddElement(col);
}
}
// 幕墙(简化:用 Floor 表达玻璃面板)
var glassPanelEast = new Floor(
Polygon.Rectangle(0.02, depth),
glassCurtain,
transform: new Transform(new Vector3(width, 0, 1.8),
new Vector3(0, -1, 0), // 旋转 90 度竖直
Vector3.ZAxis));
glassPanelEast.Transform.Scale(new Vector3(1, 1, floorCount * floorHeight));
model.AddElement(glassPanelEast);
// ===== 光照 =====
// 太阳(西南方向,45度仰角)
var sunDir = new Vector3(1, -1, 1).Unitized();
var sun = new DirectionalLight(
Colors.White,
new Transform(Vector3.Origin, sunDir),
intensity: 90000,
name: "Sun");
model.AddElement(sun);
// 室内点光源
for (int f = 0; f < floorCount; f++)
{
var roomLight = new PointLight(
new Color(1.0, 0.92, 0.8),
new Transform(width / 2, depth / 2, f * floorHeight + 2.8),
intensity: 400,
name: $"interior-light-{f + 1}");
model.AddElement(roomLight);
}
// ===== 导出 =====
model.ToGlTF("office-building.glb", drawEdges: false);
Console.WriteLine("已导出 office-building.glb");
}
}
运行以上代码生成的 office-building.glb 可以在 glTF Viewer 或任何支持 glTF 的引擎中直接打开,看到带有正确 PBR 材质和灯光的建筑模型。
9.9 小结
本章覆盖了 Elements 中材质与光照的完整体系:
- Material 是继承
Element的一等公民,携带 RGBA 颜色、Specular-Glossiness PBR 参数、纹理路径等 12 个属性 - Colors 提供 30 种常用颜色;BuiltInMaterials 提供 17 种 AEC 标准材质
- Light 有三个子类:
DirectionalLight(太阳)、PointLight(点光)、SpotLight(聚光),通过Transform控制位置和方向 - glTF 导出自动将 Spec-Gloss 转换为 Metallic-Roughness PBR,并支持纹理、灯光、双面渲染
- ModifyVertexAttributes 提供逐顶点插值钩子,用于分析可视化和顶点位移
下一章将讨论 Elements 的多格式序列化能力:JSON、glTF、IFC 的导入与导出。