我使用了Hugo来创建我的这个博客网站。为了能够在Hugo生成的页面中渲染3D模型,我使用了three.js。为了方便使用,我把代码封装成了Hugo的shortcode(保存为layouts\shortcodes\model3d.html)。
“model3d"这个shortcode有四个参数:
- src:GLTF模型路径
- ratio:视图宽高比,默认1.7778
- grid:是否显示网格,默认false
- back_light:是否显示背面的光源,默认false
{{ $_hugo_config := `{ "version": 1 }` }}
{{ $modelPath := .Get "src" }}
{{ $aspectRatio := .Get "ratio" | default 1.7778 }}
{{ $withGrid := .Get "grid" | default false }}
{{ $withBackLight := .Get "back_light" | default false }}
<div id="container_model3d">
</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.161.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.161.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { MeshoptDecoder } from 'three/addons/libs/meshopt_decoder.module.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader'
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';
let camera, scene, renderer, containerParent;
init();
render();
function init() {
const container = document.getElementById('container_model3d');
container.onload = () => {
onWindowResize();
}
// Get aspect ratio of the canvas
const aspectRatio = {{ $aspectRatio }}
// Get path of the 3d model
const model_path = {{ $modelPath }};
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(container.clientWidth, container.clientWidth / aspectRatio);
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1;
container.appendChild(renderer.domElement);
camera = new THREE.PerspectiveCamera(45, aspectRatio, 2, 100);
camera.position.set(-4, 3, 6);
const environment = new RoomEnvironment(renderer);
const pmremGenerator = new THREE.PMREMGenerator(renderer);
scene = new THREE.Scene();
scene.background = new THREE.Color(0xbbbbbb);
scene.environment = pmremGenerator.fromScene(environment).texture;
environment.dispose();
const withGrid = {{ $withGrid }};
if (withGrid) {
const grid = new THREE.GridHelper(10, 10, 0xffffff, 0xffffff);
grid.material.opacity = 0.4;
grid.material.depthWrite = false;
grid.material.transparent = true;
scene.add(grid);
}
const withBackLight = {{ $withBackLight }};
if (withBackLight) {
RectAreaLightUniformsLib.init();
const rectLight = new THREE.RectAreaLight(0xffffff, 25, 5, 5);
rectLight.position.set(-6, 6, -10);
rectLight.lookAt( 0, 0, 0 );
scene.add( rectLight );
scene.add( new RectAreaLightHelper( rectLight ) );
}
const ktx2Loader = new KTX2Loader()
.setTranscoderPath('https://unpkg.com/three@0.161.0/examples/jsm/libs/basis/')
.detectSupport(renderer);
const loader = new GLTFLoader();
loader.setKTX2Loader(ktx2Loader);
loader.setMeshoptDecoder(MeshoptDecoder);
loader.setDRACOLoader(DRACOLoader)
loader.load(model_path, function (gltf) {
gltf.scene.position.y = 0;
scene.add(gltf.scene);
render();
});
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render); // use if there is no animation loop
controls.minDistance = 4;
controls.maxDistance = 15;
controls.target.set(0, 0, 0);
controls.update();
window.addEventListener('resize', onWindowResize);
}
function onWindowResize() {
camera.aspect = aspectRatio;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientWidth / aspectRatio);
render();
}
function render() {
renderer.render(scene, camera);
}
</script>
在Markdown中使用这个shortcode的例子:
{{< model3d src="/path/to/model.glb" back_light="true" ratio="1.3333" grid="true" >}}
效果就如同在文章Blender习作集7中一样:
经过整理测试之后,我会把这个shortcode在github上开源。