当前位置: 首页 > news >正文

用 Three.js 实现 PlayCanvas 风格 PBR 材质教程(第二篇):核心参数与光照模型

上一篇我们搭建了基础环境并实现了简单的自定义材质,本篇将基于完整代码,深入解析 PBR 的核心参数、光照模型的实现逻辑,以及如何通过代码将物理规律转化为视觉效果。

一、核心参数解析:从代码看 PBR 的物理基础

PlayCanvas 风格的 PBR 材质通过一组具有明确物理意义的参数描述材质特性,这些参数在代码中通过uniform变量传递,并在着色器中参与光照计算。

1. 基础参数与代码映射

参数名 代码变量 类型 范围 物理意义
基础色 albedo vec3 RGB 颜色 物体表面的固有颜色,非金属为漫反射色,金属为反射色
金属度 metalness float 0~1 区分金属(1)与非金属(0),控制漫反射 / 镜面反射比例
粗糙度 roughness float 0~1 控制表面光滑度,0 = 镜面,1 = 完全粗糙
折射率 ior float 1.0+ 决定光线折射 / 反射比例,计算基础反射率(F0)
镜面反射率 specularity vec3 RGB 颜色 控制非金属的高光强度,金属材质忽略此参数
环境反射强度 envMapReflectivity float 0~1 控制环境贴图的反射强度

2. 金属度(Metalness)的核心作用

金属与非金属的光学特性差异是 PBR 的核心,代码通过calcSpecularModulate函数实现这种差异:

vec3 calcSpecularModulate(in vec3 specularity, in vec3 albedo, in float metalness, in float f0) {vec3 dielectricF0 = f0 * specularity; // 非金属的基础反射率(由折射率和镜面反射率计算)return mix(dielectricF0, albedo, metalness); // 金属直接使用基础色作为反射率
}

代码逻辑解析

  • metalness=0(非金属):镜面反射率 = 折射率计算的 F0 × specularity
  • metalness=1(金属):镜面反射率 = albedo(基础色)
  • 中间值:通过mix函数平滑过渡,模拟半金属特性

同时,金属材质几乎没有漫反射,代码通过以下逻辑实现:

// 计算漫反射率:金属漫反射为0,非金属使用albedo作为漫反射率
litArgs_albedo = litArgs_albedo * (1.0 - litArgs_metalness);

3. 粗糙度(Roughness)与光泽度(Glossiness)

粗糙度描述表面的微观不平度,代码中通过calcGlossiness函数将其转换为光泽度(与粗糙度相反):

void calcGlossiness() {dGlossiness = 1.0 - roughness; // 光泽度 = 1 - 粗糙度dGlossiness += 0.0000001; // 避免零值,防止后续计算异常
}

作用

  • 光泽度越高(粗糙度越低):高光越集中,反射越清晰
  • 光泽度越低(粗糙度越高):高光越扩散,反射越模糊

在镜面反射计算中,光泽度直接影响采样的环境贴图层级(后续环境贴图章节详细讲解)。

4. 折射率(IOR)与基础反射率(F0)

折射率决定光线从空气进入材质时的反射比例,代码中通过以下公式计算垂直入射时的反射率(F0):

// 在evaluateBackend中计算F0
float f0 = litArgs_ior;
f0 = (f0 - 1.0) / (f0 + 1.0); // 折射率转反射率公式
f0 *= f0; // 垂直入射时的反射光比例

物理意义:F0 表示光线垂直入射时,反射光占总入射光的比例。例如:

  • 空气(IOR=1.0):F0=0(无反射)
  • 玻璃(IOR=1.5):F0≈0.04(4% 反射率)
  • 钻石(IOR=2.42):F0≈0.17(17% 反射率)

F0 是菲涅尔效应计算的基础,直接影响材质的反射强度。

二、光照模型的核心计算:从参数到颜色的转化

PBR 光照模型的核心是模拟光线与物体表面的交互,代码通过 “前端数据准备→后端光照计算” 的流程实现,核心逻辑集中在片元着色器的evaluateFrontendevaluateBackend函数中。

1. 前端计算(evaluateFrontend):数据准备与向量计算

前端计算的作用是将原始参数转换为光照计算所需的中间变量,包括读取材质参数、计算核心向量(法线、视线方向等)。

void evaluateFrontend() {// 1. 读取材质参数到d前缀变量(原始数据存储)calcAlpha();      // dAlpha = opacitycalcAlbedo();     // dAlbedo = albedocalcMetalness();  // dMetalness = metalnesscalcGlossiness(); // dGlossiness = 1 - roughnesscalcIor();        // dIor = iorcalcSpecularity();// dSpecularity = specularitycalcEmission();   // dEmissive = emissive// 2. 计算核心向量(用于光照计算)calcNormal();     // dNormal = 世界空间法线calcViewDir();    // dViewDir = 视线方向(相机→顶点)calcReflectDir(); // dReflectDir = 反射方向(基于法线和视线)// 3. 将d变量传递给litArgs变量(光照计算参数)litArgs_albedo = dAlbedo;litArgs_specularity = dSpecularity;litArgs_gloss = dGlossiness;litArgs_metalness = dMetalness;litArgs_ior = dIor;litArgs_worldNormal = dNormal;litArgs_viewDir = dViewDir;litArgs_reflectDir = dReflectDir;
}

核心向量解析

  • 法线向量(dNormal):normalize(vNormalW),表面朝向,决定光线反射方向
  • 视线方向(dViewDir):normalize(cameraWorldPos - vPositionW),从顶点指向相机
  • 反射方向(dReflectDir):normalize(reflect(-dViewDir, dNormal)),环境光反射的方向

2. 后端计算(evaluateBackend):光照核心逻辑

后端计算是 PBR 的 “大脑”,实现从参数到最终颜色的转化,包含 F0 计算、菲涅尔效应、漫反射与镜面反射计算等核心步骤。

步骤 1:计算基础反射率(F0)
// 根据折射率计算垂直入射的光照 反射光/总入射光的比例 f0
float f0 = litArgs_ior;
f0 = (f0 - 1.0) / (f0 + 1.0);
f0 *= f0;

这一步将折射率转换为物理上的基础反射率,是后续所有反射计算的基础。

步骤 2:修正高光反射率(考虑金属度)

// 根据f0和金属度调节高光反射率
litArgs_specularity = calcSpecularModulate(litArgs_specularity, litArgs_albedo, 
http://www.xdnf.cn/news/1164565.html

相关文章:

  • DBSCAN聚类算法
  • OpenAI Codex CLI与 Google Gemini CLI 比较
  • 关于java8里边Collectors.toMap()的空限制
  • 泛型:C#中的类型抽象艺术
  • Android NDK ffmpeg 音视频开发实战
  • 数据结构 之 【排序】(直接插入排序、希尔排序)
  • 【C++】list的模拟实现
  • 音视频学习(四十二):H264帧间压缩技术
  • 周志华《机器学习导论》第13章 半监督学习
  • [深度学习] 大模型学习3上-模型训练与微调
  • 机器学习初学者理论初解
  • MySQL:表的增删查改
  • 基于VSCode的nRF52840开发环境搭建
  • C++高性能日志库spdlog介绍
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘pywifi’问题
  • boost::asio 中 io_service与线程的关系
  • Netty中CompositeByteBuf 的addComponents方法解析
  • React-useEffect的闭包陷阱(stale closure)
  • CentOS 系统上部署一个简单的 Web 应用程序
  • 关键成功因素法(CSF)深度解析:从战略目标到数据字典
  • AK视频下载工具:免费高效,多平台支持
  • 计算机网络:概述层---计算机网络的性能指标
  • 【c++】leetcode438 找到字符串中所有字母异位词
  • 易语言+懒人精灵/按键中控群控教程(手机、主板机、模拟器通用)
  • Three.js 从零入门:构建你的第一个 Web 3D 世界
  • 2025最新版PyCharm for Mac统一版安装使用指南
  • 树链剖分-苹果树
  • Java基础教程(010):面向对象中的this和就近原则
  • 图片转 PDF三个免费方法总结
  • 解决win10下Vmware虚拟机在笔记本睡眠唤醒后ssh连接不上的问题