资讯详情

基于sketch-threejs的金属与玻璃材质PBR渲染与光影调校实战

📅 2026/11/6 4:21:14 | 华诺云谱 👁 阅读
基于sketch-threejs的金属与玻璃材质PBR渲染与光影调校实战
做三维渲染这行久了你会发现一个挺有意思的现象很多人能把模型建得很精细拓扑干净、布线合理但一到材质和光影环节就卡壳渲出来的东西总有一股“塑料感”——金属不像金属玻璃不像玻璃光打上去要么死黑一片要么灰蒙蒙没有层次。这个问题在基于 sketch-threejs 这类轻量级三维展示方案里尤其常见因为大家往往把注意力放在几何体和交互上材质和光照随便给个默认值就交差了。我这次想聊的就是怎么在 sketch-threejs 这套体系里把光影效果真正做出来重点是金属和玻璃这两类最考验功底的材质。核心关键词就三个金属材质渲染、玻璃材质渲染、光影效果调校。这套东西能解决什么问题简单说就是让你的三维展示从“能看”变成“耐看”从“模型预览”变成“产品级视觉”。适合谁参考前端三维方向的同学、做产品可视化展示的开发者、以及那些用 three.js 做创意页面的朋友哪怕你之前只调过基础材质跟着思路走也能上手。先说清楚一个前提sketch-threejs 本质上是把 three.js 的渲染能力封装进了一个更轻的交互框架里光影的底层逻辑还是 three.js 那一套——PBR基于物理的渲染管线、环境贴图、光源类型、色调映射。所以下面聊的所有东西你放到原生 three.js 里同样成立只是调用方式略有差异。我不打算堆一堆 API 文档式的罗列而是按“为什么这么选、怎么调、踩过什么坑”的顺序来讲尽量让你看完能直接抄作业。1. 光影效果的整体设计思路拆解1.1 为什么金属和玻璃必须走 PBR 路线很多人一开始会想用 MeshPhongMaterial 或者 MeshLambertMaterial 凑合毕竟它们参数少、上手快。但我实测下来这两类材质做金属和玻璃基本是灾难。原因在于它们用的是经验光照模型高光是个近似值反射靠一个固定的 specular 颜色硬凑根本没有“环境反射”的概念。金属之所以像金属核心是它对环境的镜面反射——你看到的不只是高光点而是周围环境的倒影被拉伸、模糊、染色。玻璃之所以像玻璃核心是折射加菲涅尔反射——正面看透明斜着看反光强。PBR 管线里的 MeshStandardMaterial 和 MeshPhysicalMaterial 就是为这个设计的。前者覆盖金属、粗糙非金属后者在它基础上加了 clearcoat清漆层、transmission透射、ior折射率、thickness厚度这些专门伺候玻璃和涂层表面的参数。所以选型上没什么好犹豫的金属用 MeshStandardMaterial玻璃用 MeshPhysicalMaterial这是底线。这里有个容易被忽略的点PBR 的“物理正确”是建立在能量守恒基础上的也就是说材质的 metalness、roughness、color 三者是联动的。你把 metalness 拉到 1color 就不再是漫反射颜色而是变成了反射的染色F0 反射率。很多人金属渲出来发黑就是因为 metalness 给了 1 但 color 给了纯黑那自然什么都不反。这个逻辑后面会细讲。1.2 光照体系的分层设计光影效果不是“打一盏灯”就完事。我的习惯是把光照拆成三层环境层、主光层、补光层。环境层负责提供全局的间接光照和反射来源最省事也最有效的做法是用环境贴图envMap。在 sketch-threejs 里通常通过 RGBELoader 或 PMREMGenerator 加载一张 HDR 图生成预过滤的环境贴图。这一层决定了金属反射的“内容”和玻璃折射的“背景”是质感的地基。没有环境贴图的金属就像一面没有景色的镜子怎么调都是死的。主光层是一盏方向光或者聚光灯负责塑造形体和投影给出明确的光源方向感。补光层则用低强度的点光或半球光填充暗部避免阴影死黑。三层配合画面才有纵深。我见过不少人只打一盏方向光然后抱怨金属没有反射——方向光只提供高光不提供环境反射这是两码事。环境贴图才是反射的主角方向光只是点睛。1.3 色调映射与输出色彩空间这一块是“隐形杀手”。three.js 从 r152 左右开始默认走 sRGB 输出色彩空间如果你的色调映射toneMapping没配对金属会过曝成一片白玻璃会灰得没有通透感。我的常规配置是renderer.toneMapping THREE.ACESFilmicToneMapping; renderer.toneMappingExposure 1.0; renderer.outputColorSpace THREE.SRGBColorSpace;ACES 的好处是高光滚降柔和金属的强反射不会直接切到纯白保留细节。exposure 从 1.0 起步根据场景亮度微调一般不超过 1.5否则整体发灰。这个配置是后面所有材质调校的前提先把它定死再谈材质。2. 金属材质渲染的核心细节与实操要点2.1 metalness 与 roughness 的取值逻辑金属材质的两个命门参数就是 metalness 和 roughness。理论上 metalness 非 0 即 1现实中的材质要么是金属要么不是中间值只用于做旧、脏污、氧化过渡。但实操中我建议纯金属抛光铝、不锈钢metalness 1.0roughness 0.05~0.2拉丝金属metalness 1.0roughness 0.3~0.5配合各向异性贴图更佳氧化/磨砂金属metalness 0.8~0.95roughness 0.5~0.7带漆金属metalness 0.0~0.2漆层是非金属靠 clearcoat 出光泽roughness 是控制反射“锐利度”的。数值越低环境倒影越清晰像镜子越高越模糊像喷砂。这里有个实操心得roughness 不要给 0哪怕抛光金属也给 0.05 左右。因为完全为 0 的粗糙度在实时渲染里会产生极锐利的高光容易出现锯齿和闪烁尤其在低分辨率环境贴图下。给一点点粗糙度反而更真实。2.2 环境贴图的分辨率与预过滤金属反射的质量八成取决于环境贴图。这里有个关键概念叫 PMREM预过滤 mipmap 辐射环境贴图。简单说就是引擎提前把环境图按不同粗糙度模糊成多个层级粗糙度高的采样模糊层低的采样清晰层。这样不同粗糙度的金属能采到对应清晰度的反射。在 sketch-threejs 里加载 HDR 的典型写法import { RGBELoader } from three/examples/jsm/loaders/RGBELoader.js; const pmremGenerator new THREE.PMREMGenerator(renderer); pmremGenerator.compileEquirectangularShader(); new RGBELoader().load(/env/studio.hdr, (texture) { const envMap pmremGenerator.fromEquirectangular(texture).texture; scene.environment envMap; texture.dispose(); pmremGenerator.dispose(); });注意scene.environment和scene.background是两回事。前者只影响材质的环境光照和反射后者是可见背景。我一般把 environment 设上background 单独用纯色或渐变避免 HDR 背景抢戏。环境贴图分辨率不是越高越好。4K 的 HDR 加载慢、显存占用高而经过 PMREM 预过滤后实际采样的是降采样版本2K 甚至 1K 在很多场景下已经够用。我踩过的坑是拿了一张 8K 的室内 HDR结果首屏加载卡了三秒用户直接划走了。后来换成 1K 的 studio 环境图反射质量肉眼几乎无差加载快了一个数量级。2.3 金属的颜色与 F0 反射率前面提过metalness 1 时材质的 color 变成了反射染色。不同金属的反射色是有物理依据的金属类型推荐 color 值近似说明金#FFD700 附近偏暖反射偏黄银/不锈钢#F5F5F5 偏中性反射接近白铜#B87333 偏橙红反射偏暖橙铝#D6D6D6 偏冷白反射略冷钛#C0C0C0 带微蓝反射冷调这些值不是随便给的是各金属在可见光波段的反射率近似。你如果给纯白 #FFFFFF渲出来会像镀铬的塑料因为真实金属没有 100% 全波段反射的。给一点点色偏质感立刻上一个台阶。2.4 实操一个抛光金属球的完整配置拿一个最经典的测试场景——金属球——来走一遍完整配置const geometry new THREE.SphereGeometry(1, 64, 64); const material new THREE.MeshStandardMaterial({ color: 0xd6d6d6, // 铝的反射色 metalness: 1.0, roughness: 0.12, envMapIntensity: 1.2, // 环境反射强度 }); const sphere new THREE.Mesh(geometry, material); scene.add(sphere);envMapIntensity是个很实用的参数控制环境贴图对材质的贡献强度。默认 1.0金属想更亮可以拉到 1.2~1.5但别过头否则反射过曝。球体分段给到 64 是为了让高光边缘平滑低分段会在高光处看到明显的多边形棱角。再配一盏方向光做高光点睛const dirLight new THREE.DirectionalLight(0xffffff, 2.0); dirLight.position.set(5, 8, 5); scene.add(dirLight);方向光强度在 PBR 体系里建议给 1.5~3.0因为 PBR 的光照是物理量纲强度低了金属高光出不来。这个和传统 Phong 材质的强度手感完全不同别拿老经验套。3. 玻璃材质渲染的关键参数与实现路径3.1 transmission 与 opacity 的本质区别玻璃渲染最大的误区是把“透明”理解成 opacity。opacity 是简单的 alpha 混合物体变透明但不会折射背景看起来像一层半透明的塑料膜。真正的玻璃要用 transmission透射它走的是折射管线背景会被扭曲、染色这才是玻璃的灵魂。MeshPhysicalMaterial 的 transmission 参数取值 0~11 表示完全透射。但光有 transmission 不够还得配 ior折射率和 thickness厚度。ior 空气是 1.0水 1.33普通玻璃 1.5水晶 1.7~2.0。thickness 影响折射的偏移量和颜色吸收薄玻璃给 0.1~0.5厚玻璃给 1.0 以上。这里有个性能提醒transmission 会触发额外的渲染 pass透射物体需要单独渲染背景再合成开销比普通材质高不少。场景里玻璃物体多的时候要留意帧率必要时降低透射物体的分辨率或数量。3.2 菲涅尔反射与 clearcoat 的配合玻璃斜着看反光强、正面看透明这个现象叫菲涅尔效应。MeshPhysicalMaterial 内置了菲涅尔计算你只要把 reflectivity 或 ior 设对它自动处理。但想要玻璃表面那层“亮晶晶”的涂层感得靠 clearcoatconst glassMaterial new THREE.MeshPhysicalMaterial({ color: 0xffffff, metalness: 0.0, roughness: 0.05, transmission: 1.0, ior: 1.5, thickness: 0.5, clearcoat: 1.0, clearcoatRoughness: 0.05, envMapIntensity: 1.0, });clearcoat 相当于在材质表面再叠一层清漆独立计算高光。玻璃、车漆、陶瓷釉面都靠它出效果。clearcoatRoughness 给低值清漆层就锐利给高值就是磨砂玻璃的朦胧感。3.3 玻璃的颜色吸收与体积感真实玻璃不是完全无色的厚的地方会偏绿普通玻璃含铁或偏蓝光学玻璃。这个效果靠 attenuationColor 和 attenuationDistance 实现glassMaterial.attenuationColor new THREE.Color(0x88ccaa); glassMaterial.attenuationDistance 2.0;attenuationDistance 表示光在材质内走多远后颜色衰减到 attenuationColor。数值越小颜色越浓。这个参数配合 thickness 用能做出厚玻璃的“体积感”而不是一张薄片。我做过一个香水瓶的展示就是靠这组参数把瓶底的厚重感做出来的比单纯调 color 真实得多。3.4 实操玻璃球的渲染与背景配合玻璃球是最能检验玻璃材质调校的模型因为它会把背后的环境完整折射出来。配置如下const glassGeo new THREE.SphereGeometry(1, 64, 64); const glassMat new THREE.MeshPhysicalMaterial({ color: 0xffffff, metalness: 0.0, roughness: 0.02, transmission: 1.0, ior: 1.52, thickness: 0.8, clearcoat: 1.0, clearcoatRoughness: 0.03, attenuationColor: new THREE.Color(0xaaddcc), attenuationDistance: 3.0, envMapIntensity: 1.0, }); const glassBall new THREE.Mesh(glassGeo, glassMat); scene.add(glassBall);关键点玻璃球背后一定要有内容可折射。如果背景是纯色玻璃球看起来就是个透明球毫无质感。我通常会在玻璃物体后面放一些彩色几何体或者用带细节的环境贴图让折射有东西可“看”。这是很多人忽略的——玻璃的美感来自它折射的世界世界空了玻璃也就空了。4. 光影调校的实操流程与参数联动4.1 从零搭建一个金属加玻璃的展示场景把前面的东西串起来走一遍完整流程。假设要做一个“金属环托着玻璃球”的展示第一步初始化渲染器和色调映射这是地基const renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.toneMapping THREE.ACESFilmicToneMapping; renderer.toneMappingExposure 1.1; renderer.outputColorSpace THREE.SRGBColorSpace;pixelRatio 限制在 2 以内是防止高分屏上渲染压力过大实测 2 以上肉眼收益极小但性能掉得明显。第二步加载环境贴图给金属和玻璃提供反射与折射源。第三步建金属环用 TorusGeometry材质按 2.4 节的金属配置。第四步建玻璃球放在环中间材质按 3.4 节配置。第五步布光一盏方向光做主光强度 2.5位置在右上前方一盏半球光做补光强度 0.5天空色偏冷、地面色偏暖模拟自然环境的上下色温差。第六步加一个地面接收阴影用 ShadowMaterial 或者低粗糙度的深色平面让金属环的投影落下来增强空间感。这套组合下来金属环会有清晰的环境反射和方向光高光玻璃球会折射出环和背景整体层次就出来了。4.2 参数联动的调试顺序调材质最忌讳东调一下西调一下最后不知道哪个参数起了作用。我的调试顺序是固定的先定色调映射和曝光这是全局基调定完不再动。再定环境贴图反射和折射的内容源定完不再动。然后调金属的 metalness 和 roughness先给极端值metalness 1、roughness 0.1看反射是否正常再微调。接着调玻璃的 transmission、ior、thickness先保证透射和折射成立再加 clearcoat 和衰减色。最后调光源强度和位置光影是最后一步的“化妆”前面材质对了光一打就出效果。这个顺序的逻辑是从全局到局部从基础到修饰。反过来先调光再调材质你会发现光怎么打材质都不对因为材质本身没配对。4.3 阴影与接触阴影的处理金属和玻璃的阴影处理有个特殊性玻璃是透射材质理论上不该投实心阴影但完全没阴影又会让物体“飘”在空中。我的做法是给玻璃物体一个低透明度的阴影或者干脆用接触阴影contact shadow——在物体正下方放一个径向渐变的暗斑贴图模拟物体与地面的接触暗部。方向光的阴影配置也要注意dirLight.castShadow true; dirLight.shadow.mapSize.set(2048, 2048); dirLight.shadow.camera.near 0.5; dirLight.shadow.camera.far 50; dirLight.shadow.bias -0.0005;shadow.bias 给负的小值是为了消除阴影痤疮shadow acne物体表面出现的条纹状自阴影噪点。这个值调过头会导致阴影脱离物体peter-panning-0.0005 到 -0.001 之间是比较稳的区间具体看场景尺度。5. 常见问题与排查技巧实录5.1 金属发黑、玻璃发灰的排查表这是最高频的两类问题我整理成速查表现象可能原因排查与解决金属整体发黑metalness1 但 color 给了纯黑或过暗把 color 调到对应金属的反射色如 #D6D6D6金属没有反射没设 scene.environment 或 envMap加载 HDR 并赋给 scene.environment金属反射模糊一片roughness 过高或环境贴图分辨率过低roughness 降到 0.2 以下换 2K 以上 HDR金属高光闪烁roughness 接近 0 且几何分段低roughness 给 0.05球体分段提到 64玻璃不透明用了 opacity 而非 transmission改用 transmission1配 ior 和 thickness玻璃发灰浑浊环境贴图太暗或色调映射曝光不足提高 envMapIntensity 或 exposure玻璃没有折射背景是纯色无内容可折射背景加几何体或换带细节的环境图玻璃边缘发黑thickness 过大或 attenuation 过强降低 thickness调大 attenuationDistance5.2 性能与画质的平衡PBR 加透射是吃性能的。我踩过的坑是在移动端跑一个含五六个玻璃物体的场景帧率直接掉到 20 出头。后来做了几件事拉回来玻璃物体数量控制在 2~3 个以内多的用普通透明材质替代。环境贴图从 4K 降到 1KPMREM 预过滤后画质差异很小。透射物体的渲染分辨率单独降级或者用renderer.setPixelRatio全局降。阴影贴图从 4096 降到 2048视觉几乎无差。这里有个经验画质提升的边际收益递减很快。从 1K 环境图到 4K反射清晰度提升有限但加载时间和显存翻几倍。找到那个“够用”的点比无脑堆参数重要得多。5.3 环境贴图与背景的分离技巧很多人把 HDR 同时设成 environment 和 background结果背景是一张模糊的室内图抢了主体风头。我的做法是 environment 用 HDR 提供反射background 用纯色、渐变或者一个精心设计的场景。这样金属反射的是丰富的环境但画面背景是干净的主体突出。如果确实想让背景也参与可以用scene.backgroundBlurriness控制背景模糊度让背景虚化主体清晰形成景深感。这个参数在 r155 之后支持给 0.5~1.0 之间效果不错。5.4 实操心得三个容易被忽略的细节第一个材质的 needsUpdate。当你动态修改 transmission、clearcoat 这类参数时某些情况下需要设material.needsUpdate true才会重新编译 shader。我遇到过改了 transmission 没反应的情况查了半天才发现是这个。第二个透明物体的渲染顺序。玻璃和金属叠在一起时渲染顺序不对会出现穿插错误。可以给透明物体设renderOrder数值大的后渲染。玻璃一般设成比金属大保证它在金属之后合成。第三个环境贴图的旋转。有时候金属反射的内容位置不对高光点不在你想要的地方可以用scene.environmentRotation旋转环境贴图把反射的“亮点”转到合适角度。这个技巧在做产品展示时特别有用能让金属的高光正好落在产品的视觉焦点上。6. 进阶让光影更有“作品感”的几个手法6.1 用区域光模拟柔光箱方向光的高光是点状的硬。产品摄影里常用柔光箱出来的高光是长条形的柔和亮带。three.js 里可以用 RectAreaLight 模拟import { RectAreaLightUniformsLib } from three/examples/jsm/lights/RectAreaLightUniformsLib.js; RectAreaLightUniformsLib.init(); const rectLight new THREE.RectAreaLight(0xffffff, 5, 4, 2); rectLight.position.set(3, 5, 3); rectLight.lookAt(0, 0, 0); scene.add(rectLight);RectAreaLight 只影响 MeshStandardMaterial 和 MeshPhysicalMaterial正好是我们要的。它能在金属表面拉出一条漂亮的长条高光质感立刻高级。注意它不支持阴影所以阴影还得靠方向光或聚光灯补。6.2 用后期处理增强光感Bloom泛光是让高光“发光”的利器金属的强反射和玻璃的亮边经过 Bloom 会有一层柔和的光晕画面立刻有电影感。用 EffectComposer 加 UnrealBloomPassimport { EffectComposer } from three/examples/jsm/postprocessing/EffectComposer.js; import { RenderPass } from three/examples/jsm/postprocessing/RenderPass.js; import { UnrealBloomPass } from three/examples/jsm/postprocessing/UnrealBloomPass.js; const composer new EffectComposer(renderer); composer.addPass(new RenderPass(scene, camera)); const bloom new UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 0.3, // strength 0.4, // radius 0.85 // threshold ); composer.addPass(bloom);threshold 是关键0.85 表示只有亮度超过这个值的像素才泛光避免整个画面糊成一片。strength 给 0.2~0.4 之间比较克制过了就假。6.3 动态光影与交互反馈静态展示做完了可以加点动态。比如让环境贴图缓慢旋转金属表面的反射就会流动起来像真的在转台上展示。或者鼠标移动时轻微改变光源位置高光跟着走交互感很强。这些细节不复杂但对“作品感”的提升很明显。我做过一个展示页就是让 RectAreaLight 随鼠标位置微调金属的高光带跟着鼠标滑动用户停留时间明显变长。这种小交互的成本很低收益却很高。7. 我个人的调校体会聊了这么多参数和流程最后说点实在的体会。金属和玻璃的渲染本质上不是“调参数”而是“理解光”。你得先想清楚这个物体在真实世界里是怎么和光互动的——金属反射环境玻璃折射环境两者都依赖一个丰富的环境作为“素材”。所以环境贴图永远是第一优先级材质参数是第二光源是第三。顺序搞反了怎么调都别扭。另一个体会是克制比堆砌重要。我早期做金属喜欢把 envMapIntensity 拉满、roughness 给到最低追求那种锃亮的镜面感结果画面又硬又假。后来学会给一点点粗糙度、控制反射强度、用柔光代替硬光反而更接近真实产品的质感。真实世界的金属很少是完美镜面总有一点瑕疵和模糊这点“不完美”才是质感的来源。还有个小技巧分享给做产品展示的朋友金属的高光位置是可以“设计”的。通过旋转环境贴图或者调整光源角度把高光落在产品的关键结构线上能引导视觉焦点。这不是玄学是产品摄影里常用的布光思路搬到实时渲染里一样管用。玻璃这块我最想强调的是“背景决定成败”。一个玻璃物体好不好看七成看它折射的背景有没有内容。所以做玻璃展示时我会花不少时间设计背景——放几个彩色几何体、用带细节的环境图、或者干脆做一个微缩场景。玻璃是“环境的放大器”环境丰富了玻璃自然就美了。这套东西后续还能往很多方向扩展比如用自定义 shader 做色散chromatic dispersion玻璃边缘的彩虹色散或者用体积雾配合玻璃做氛围。但那些属于锦上添花把上面这些基础打牢金属和玻璃的质感已经能甩开大多数“塑料感”作品一大截了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑