[返回博客](/cn/blog) # 使用环境视图背景和反射效果

Andrew Markovich 十月 20, 2022 [\#Visualize](/cn/blog/tag/visualize) [\#getting started](/cn/blog/tag/getting-started)

<a class="a2a_button_facebook"></a><a class="a2a_button_twitter"></a><a class="a2a_button_linkedin"></a>

图形场景对象的反射可以极大地提高渲染质量和真实感，特别是对于建筑和机械模型。镜子、光泽或金属表面、水、玻璃——所有这些材料只有在准确反射周围区域时才看起来符合物理原理。

在模型编辑期间通常不需要应用材质反射，但它们可以在运行时查看模型时极大地提高渲染质量（它们并非为最终场景渲染而设计）。

要在图形场景中为材质创建反射效果，请使用环境贴图反射或反射平面。这两种技术都需要 WinGL ES2 或 WinMetal 矢量化模块。环境贴图反射是真实的，并且不影响渲染性能，但它们不符合物理原理。反射平面创建符合物理原理的反射，但它们对 CPU/GPU 来说是昂贵的操作，因为它通常调用光线追踪技术，这对于实时渲染来说很困难。

#### 关于环境贴图反射

使用环境贴图来设置一个纹理，该纹理同时用于环境背景和环境反射。有关环境背景的详细信息，请参阅使用视图背景。环境反射可以应用于图形场景中的许多对象和材质，而不会损失渲染性能，并且这些“天空盒”反射为简单的图形模型和不太引人注目的图形对象创建了逼真的场景。然而，环境贴图反射是对物理正确反射的模拟——它们只提供背景的反射，不包括图形场景中对象的反射。

![cube map](/files/inline-images/ex_cubemap_background.png)*立方体贴图背景*

![cube map reflection](/files/inline-images/ex_cubemap_reflection.png)*立方体贴图反射*

#### 关于反射平面

反射平面可以创建图形场景的完整反射效果，包括场景中所有可见对象。但由于每个镜像平面的创建都需要渲染现有图形场景，因此此效果对渲染性能有显著影响。不建议为大型图形场景创建大量反射平面，因为这会显著降低渲染速度（反射平面镜像在每次帧更改时都会重新计算，因此它会持续影响实时渲染性能）。

![reflection plane](/files/inline-images/ex_reflection_plane.png)#### 使用环境贴图启用材质反射

要添加环境贴图纹理，请使用 OdDbRenderEnvironment 对象：

```
OdDbRenderEnvironmentPtr pEnv = ::oddbGetRenderEnvironmentObject(m_pDb, OdDb::kForWrite, true);
pEnv->setEnvironmentImageEnabled(true);
pEnv->setEnvironmentImageFileName(envImageFileName);
```

图像文件名在保存时存储在 .dwg/.dxf 文件中，因此下次打开文件时可以访问。该图像用于环境背景（如果可用）和环境反射（如果反射未被材质覆盖）。

##### 设置环境反射的材质

使用“reflectivity”材质属性来启用材质反射：

```
OdDbMaterialPtr pMaterial = ...;
pMaterial->setReflectivity(0.5);
```

反射系数在0-1范围内测量，并指定反射和漫反射材料颜色的混合百分比：

- 0.0 — 无材质反射（默认）。
- 0.5 — 半反射和半漫反射材质颜色（光泽表面）。
- 1.0 — 完全反射，不混合材质颜色（金属表面）。

![reflectivity](/files/inline-images/ex_reflectivity.png)默认情况下，材质使用环境纹理来实现反射效果（如果已设置），但可以为每个材质覆盖反射贴图：

```
OdGiMaterialMap reflectionMap;
reflectionMap.setSourceFileName(reflectionTextureFileName);
pMaterial->setReflection(reflectionMap);
```

每个材质都可以有自己的反射纹理。所有材质设置在保存时都存储在 .dwg/.dxf 文件中，因此客户端应用程序无需额外定制即可使用环境贴图反射。

##### 启用反射平面材质

.dwg 数据库不原生支持反射平面，需要进行定制。有两种方法可以定制 .dwg 数据库中材质的行为：

- 基于 OdDbMaterial 类创建自定义材质对象。
- 使用覆盖来修改现有数据库材质对象的行为。

自定义数据库对象的创建在 Drawing SDK 文档中有所描述：创建自定义实体。下一个示例更简单，它使用覆盖并展示了材质对象的定制以支持反射平面渲染：

```
class MaterialDrawableOverrule : public OdStaticRxObject<OdGiDrawableOverrule>
{
public:
  MaterialDrawableOverrule()
  {
    OdRxOverrule::setIsOverruling(true);
    OdRxOverrule::addOverrule(OdDbMaterial::desc(), this);
  }
  ~MaterialDrawableOverrule()
  {
    OdRxOverrule::removeOverrule(OdDbMaterial::desc(), this);
    OdRxOverrule::setIsOverruling(false);
  }

  virtual bool isApplicable(const OdRxObject* pOverruledSubject) const
  {
    return true;
  }

  virtual OdUInt32 setAttributes(const OdGiDrawable* pSubject, OdGiDrawableTraits *traits)
  {
    OdUInt32 nFlags = OdGiDrawableOverrule::setAttributes(pSubject, traits);
    OdGiMaterialTraitsPtr pMatTraits = OdGiMaterialTraits::cast(traits);
    if (pMatTraits.isNull())
      return nFlags;
    if (OdDbMaterial::cast(pSubject)->name() == OD_T("Material 4"))
    { OdGiMaterialMap mMap;
      pMatTraits->setChannelFlags((OdGiMaterialTraits::ChannelFlags)(pMatTraits->channelFlags() | OdGiMaterialTraits::kUseReflection));
      pMatTraits->setReflectivity(0.5);
      OdGiMaterialMap matMap;
      matMap.setSource(OdGiMaterialMap::kScene);
      matMap.setTexture(OdGiSceneTexture::createObject());
      pMatTraits->setReflection(matMap);
    }
    return nFlags;
  }
};
```

示例覆盖将反射平面应用于名为“Material 4”的数据库材质。与环境反射类似，设置了反射系数。与环境反射的主要区别在于使用 OdGiSceneTexture 占位符类作为材质通道纹理。这是一个非 .dwg 纹理类；它是一个特殊的材质通道，用于识别与非反射平面材质的区别。

或者（而不是使用覆盖），可以直接将 OdGiSceneTexture 占位符设置为数据库材质：

```
OdDbMaterialPtr pMtl = OdDbDictionary::cast(pDb->getMaterialDictionaryId().safeOpenObject())->getAt(OD_T("Material 4"), OdDb::kForWrite);
pMtl->setChannelFlags((OdGiMaterialTraits::ChannelFlags)(pMtl->channelFlags() | OdGiMaterialTraits::kUseReflection));
pMtl->setReflectivity(0.5);
OdGiMaterialMap matMap;
matMap.setSource(OdGiMaterialMap::kScene);
matMap.setTexture(OdGiSceneTexture::createObject());
pMtl->setReflection(matMap);
```

这种纹理不能存储在 .dwg/.dxf 文件格式中，因此反射平面只能在运行时应用于场景材质。要将其保存到 .dwg/.dxf 文件格式，客户端应用程序需要实现自定义材质对象。

##### 反射平面的柔和度参数

要创建反射表面的哑光效果，请使用反射平面材质的柔和度参数并将其应用于 OdGiSceneTexture 对象：

```
OdGiMaterialMap matMap;
matMap.setSource(OdGiMaterialMap::kScene);
OdGiSceneTexturePtr pTexture = OdGiSceneTexture::createObject();
pTexture->setSoftness(1);
matMap.setTexture(OdGiSceneTexture::createObject());
```

柔和度参数表示在将生成的场景纹理绘制到场景材质表面之前应用的模糊量（高斯模糊核大小）。较大的模糊量会提供更明显的模糊效果。空柔和度（默认值）表示不应用模糊效果。

![softness](/files/inline-images/ex_blur_amount.jpg)##### 将反射平面应用于平面

反射平面材质只能应用于平面。反射平面是使用壳几何图元中的第一个面法线生成的，如果表示曲面的壳包含不同的法线，则最终的反射效果将不正确。另一方面，如果应用了反射平面材质的实体将每个具有不同法线的面绘制为单独的壳几何图元，则最终的反射效果看起来是正确的。但是，在这种情况下，请注意每个单独的反射平面都需要大量的 GPU 资源，因为每个反射平面都表示为一个单独的 GPU 帧缓冲区，它占用 GPU 内存。此外，每个反射平面都需要自己的渲染通道，这会降低最终帧的渲染性能。避免将反射平面材质应用于大型曲面集或非平面曲面，确保反射平面材质不应用于绘图中的其他实体，并避免将反射平面材质用于块内的实体。

欲了解更多信息，请参阅 [ODA 文档](https://docs.opendesign.com/tv/gi_reflections.html)。

## 今天就开始行动

**免费试用 ODA 软件 60 天。  
无风险，无需信用卡。**

[免费试用](https://www.opendesign.com/cn/free-trial)
