关于 webgl-3d-object
面向网页设计的技能,指导使用 Three.js 在布局中呈现真实的 3D 物体,涵盖几何体、材质、光照、动画与清理。
主要功能
- 01Three.js 几何体与透视相机
- 02PBR 材质和光照设置
- 03轻微变换动画
- 04尺寸调整与清理指导
适用场景
- 01为主视觉区域添加 3D 物体
- 02展示多面体产品特征
- 03制作带真实光照的悬浮物体
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mengto/ skills --skill webgl-3d-object安装后在 Claude Code 中输入/webgl-3d-object确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
demo/index.htmlPROMPT.mdsource.json详细介绍
WebGL 3D 对象
适用场景
- 主视觉、功能区块或产品展示需要一个醒目的 3D 对象。
- 视觉效果应展现真实的几何形体、光照、高光和边缘。
- 多面体网格应在网页布局中轻微悬浮或旋转。
- CSS 变换、SVG 视觉效果或纯色渐变无法满足需求。
规则
- 使用真实的 3D 几何体:
IcosahedronGeometry、DodecahedronGeometry、BoxGeometry、自定义BufferGeometry或 glTF 网格。 - 使用透视相机,让对象呈现深度和比例感。
- 使用 PBR 材质:
MeshStandardMaterial或MeshPhysicalMaterial。 - 调整
metalness、roughness和emissive,以匹配品牌氛围。 - 至少使用一个方向光,并搭配环境光或半球光进行补光。
- 仅为变换添加动画:轻微旋转、上下浮动或视差。
- 处理窗口大小调整,并在销毁时释放几何体、材质和渲染器。
HTML 和 CSS
<div class="webgl-object-shell">
<canvas class="webgl-object-canvas" data-webgl-3d-object></canvas>
</div>
.webgl-object-shell {
position: relative;
width: min(100%, 720px);
aspect-ratio: 1 / 1;
}
.webgl-object-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
Three.js 对象配置示例
import * as THREE from "three";
function initWebGL3DObject(canvas, options = {}) {
if (!canvas) return () => {};
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true,
alpha: true,
});
renderer.setClearColor(0x000000, 0);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.75));
renderer.outputColorSpace = THREE.SRGBColorSpace;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = options.exposure || 1.05;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(38, 1, 0.1, 100);
camera.position.set(0, 0.15, 5.2);
const geometry = new THREE.IcosahedronGeometry(options.radius || 1.35, options.detail || 1);
const material = new THREE.MeshStandardMaterial({
color: options.color || 0x8aa4ff,
metalness: options.metalness ?? 0.48,
roughness: options.roughness ?? 0.34,
emissive: options.emissive || 0x101833,
emissiveIntensity: options.emissiveIntensity ?? 0.22,
flatShading: true,
});
const object = new THREE.Mesh(geometry, material);
object.castShadow = true;
object.receiveShadow = true;
scene.add(object);
const ambient = new THREE.AmbientLight(0xffffff, 0.38);
scene.add(ambient);
const key = new THREE.DirectionalLight(0xffffff, 2.15);
key.position.set(3.4, 4.2, 4.8);
key.castShadow = true;
key.shadow.mapSize.set(1024, 1024);
scene.add(key);
const rim = new THREE.DirectionalLight(options.rimColor || 0x7dd3fc, 0.82);
rim.position.set(-4.2, 1.2, -2.8);
scene.add(rim);
const shadowPlane = new THREE.Mesh(
new THREE.PlaneGeometry(5.2, 5.2),
new THREE.ShadowMaterial({ opacity: 0.18 })
);
shadowPlane.position.set(0, -1.65, 0);
shadowPlane.rotation.x = -Math.PI / 2;
shadowPlane.receiveShadow = true;
scene.add(shadowPlane);
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let rafId = 0;
function resize() {
const width = Math.max(1, canvas.clientWidth);
const height = Math.max(1, canvas.clientHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, options.maxDpr || 1.75));
renderer.setSize(width, height, false);
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
function render(time = 0) {
const t = time * 0.001;
object.rotation.x = -0.16 + Math.sin(t * 0.45) * 0.06;
object.rotation.y = t * 0.28;
object.rotation.z = Math.sin(t * 0.32) * 0.08;
object.position.y = reduceMotion ? 0 : Math.sin(t * 0.8) * 0.08;
renderer.render(scene, camera);
if (!reduceMotion) rafId = requestAnimationFrame(render);
}
function handleResize() {
cancelAnimationFrame(rafId);
resize();
render();
}
resize();
render();
window.addEventListener("resize", handleResize);
return () => {
cancelAnimationFrame(rafId);
window.removeEventListener("resize", handleResize);
geometry.dispose();
material.dispose();
shadowPlane.geometry.dispose();
shadowPlane.material.dispose();
renderer.dispose();
};
}
const cleanupObject = initWebGL3DObject(
document.querySelector("[data-webgl-3d-object]"),
{
color: 0x8aa4ff,
rimColor: 0x7dd3fc,
metalness: 0.48,
roughness: 0.34,
emissive: 0x101833,
}
);
材质默认值
- 高级金属质感:
metalness: 0.45-0.7、roughness: 0.25-0.45。 - 柔和陶瓷质感:
metalness: 0.0-0.15、roughness: 0.38-0.62。 - 带光晕色调的科技对象:使用较低的
emissive,并设置emissiveIntensity: 0.12-0.35。 - 多面体对象:设置
flatShading: true;平滑产品对象:设置为false。
光照默认值
- 主光:方向光,位于前侧上方,且为最强光源。
- 环境补光:强度较低,确保阴影仍然清晰可见。
- 轮廓光:从后方照射的品牌色调光或冷色光,用来凸显边缘。
- 阴影:仅在对象需要落地感时启用;阴影贴图尺寸保持适中。
动效默认值
- 旋转:缓慢、持续,且不喧宾夺主。
- 浮动:Y 轴方向移动
0.04到0.12个单位。 - 减少动态效果:渲染静止帧,或仅允许直接交互。
- 除非对象是主要交互元素,否则避免移动相机。
避免
- 用 CSS 3D 变换伪装 WebGL 效果。
- 在需求强调真实光照和深度时使用无光照材质。
- 用带渐变的平面代替真实几何体。
- 使用过强的泛光或粒子效果,以免掩盖对象形态。
- 在移动设备上使用过高的 DPR、过大的阴影贴图或过多的灯光。
- 让对象与前景文案或 CTA 争夺注意力。
快速检查
- 对象具有清晰可见的形态、边缘、高光和阴影。
- 材质使用了
metalness、roughness和可选的emissive。 - 同时设置了方向光和环境光。
- 相机使用透视投影,而非误用正交投影。
- 调整窗口大小时,对象不会变形。
- 已清理几何体、材质、事件监听器、RAF 和渲染器。
评论
支持 Markdown