4
0
Fork 0

three js module

This commit is contained in:
Kseninia Mikhaylova 2025-03-14 11:37:31 +03:00
parent 2faf0979fd
commit e03889db97
8 changed files with 177 additions and 111 deletions

View File

@ -2,37 +2,28 @@
//@ts-ignore
import { useGLTF } from '@tresjs/cientos'
import { useTresContext } from '@tresjs/core'
import { Vector3 } from 'three'
import { ref, onMounted } from 'vue'
import { degToRad } from 'three/src/math/MathUtils.js'
const { camera, controls } = useTresContext()
const { scene: obj } = await useGLTF('/models/bench2_export-v1.glb')
const scale = ref(1) // Масштаб объекта
onMounted(() => {
if (obj && camera.value && controls.value) {
const distance = 10
const cameraOffset = { x: 1, y: 0.75, z: -1.25 } // Смещение камеры
// Вычисляем масштаб и позицию камеры
const { scale: objectScale } = calculateScaleToFit(
obj,
camera.value as any,
controls.value as any,
distance,
cameraOffset
)
scale.value = objectScale
const { scene } = await useGLTF('/models/table.glb')
const object = calculateScaleToFit(
scene,
camera,
controls,
3,
{
x: Math.sin(degToRad(-30)), // Смещение по оси X
y: 1, // Нет смещения по оси Y
z: -Math.cos(degToRad(0)) // Смещение по оси Z
}
})
)
</script>
<template>
<Suspense>
<TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
<ModelItem :model="obj" :target="[0, 0, 0]" />
<TresGroup>
<ModelItem :model="object" :target="[0, 0, 0]" />
</TresGroup>
</Suspense>
</template>
</template>

View File

@ -2,37 +2,28 @@
//@ts-ignore
import { useGLTF } from '@tresjs/cientos'
import { useTresContext } from '@tresjs/core'
import { Vector3 } from 'three'
import { ref, onMounted } from 'vue'
import { degToRad } from 'three/src/math/MathUtils.js'
const { camera, controls } = useTresContext()
const { scene: obj } = await useGLTF('/models/bench_export-v1.glb')
const scale = ref(1) // Масштаб объекта
onMounted(() => {
if (obj && camera.value && controls.value) {
const distance = 10
const cameraOffset = { x: -1, y: 0.75, z: -1.25 } // Смещение камеры
// Вычисляем масштаб и позицию камеры
const { scale: objectScale } = calculateScaleToFit(
obj,
camera.value as any,
controls.value as any,
distance,
cameraOffset
)
scale.value = objectScale
const { scene } = await useGLTF('/models/bench.glb')
const object = calculateScaleToFit(
scene,
camera,
controls,
2,
{
x: Math.sin(degToRad(30)), // Смещение по оси X
y: 1, // Нет смещения по оси Y
z: -Math.cos(degToRad(0)) // Смещение по оси Z
}
})
)
</script>
<template>
<Suspense>
<TresGroup :position-y="0" :scale="new Vector3(scale, scale, scale)">
<ModelItem :model="obj" :target="[0, 0, 0]" />
<TresGroup>
<ModelItem :model="object" :target="[0, 0, 0]" />
</TresGroup>
</Suspense>
</template>
</template>

View File

@ -1,11 +1,12 @@
<script setup lang="ts">
import {
PCFSoftShadowMap,
CineonToneMapping,
PMREMGenerator,
} from 'three';
PCFSoftShadowMap,
CineonToneMapping,
PMREMGenerator,
CanvasTexture
} from 'three'
import { GainMapLoader, } from '@monogrid/gainmap-js'
import { GainMapLoader } from '@monogrid/gainmap-js'
const { scene, renderer, camera } = useTresContext()
@ -15,26 +16,49 @@ renderer.value.toneMappingExposure = 1
renderer.value.shadowMap.enabled = true
renderer.value.shadowMap.type = PCFSoftShadowMap
const pmremGenerator = new PMREMGenerator(renderer.value);
pmremGenerator.compileEquirectangularShader();
const pmremGenerator = new PMREMGenerator(renderer.value)
pmremGenerator.compileEquirectangularShader()
function createGradientTexture () {
const canvas = document.createElement('canvas')
canvas.width = 256
canvas.height = 256
const ctx = canvas.getContext('2d')
if (!ctx) return null
// Создаем линейный градиент
const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height)
gradient.addColorStop(0, '#ff7eb3') // Верхний цвет
gradient.addColorStop(1, '#4f46e5') // Нижний цвет
// Заполняем холст градиентом
ctx.fillStyle = gradient
ctx.fillRect(0, 0, canvas.width, canvas.height)
// Создаем текстуру из холста
return new CanvasTexture(canvas)
}
onMounted(async () => {
const loader = new GainMapLoader(renderer.value)
const result = await loader.loadAsync([
'hdrmaps/hdr.webp',
'hdrmaps/hdr-gainmap.webp',
'hdrmaps/hdr.json',
])
if (renderer.value && camera.value) {
renderer.value.render(scene.value, camera.value)
}
const exrCubeRenderTarget = pmremGenerator.fromEquirectangular(result.renderTarget.texture);
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null;
const loader = new GainMapLoader(renderer.value)
const result = await loader.loadAsync([
'hdrmaps/hdr.webp',
'hdrmaps/hdr-gainmap.webp',
'hdrmaps/hdr.json'
])
if (renderer.value && camera.value) {
renderer.value.render(scene.value, camera.value)
}
const exrCubeRenderTarget = pmremGenerator.fromEquirectangular(
result.renderTarget.texture
)
const newEnvMap = exrCubeRenderTarget ? exrCubeRenderTarget.texture : null
scene.value.environment = newEnvMap
scene.value.environmentIntensity = 1.25
scene.value.environmentRotation.z = 0.25
result.renderTarget.texture.dispose();
scene.value.environment = newEnvMap
scene.value.environmentIntensity = 1.25
scene.value.environmentRotation.z = 0.25
result.renderTarget.texture.dispose()
})
</script>
<template></template>
<template></template>

View File

@ -14,15 +14,26 @@ const props = defineProps({
}
})
const camera = ref()
const controls = ref()
const controlsState = reactive({
// minDistance: 2,
// maxDistance: 10,
enablePan: false,
enableZoom: false,
maxPolarAngle: Math.PI / 2 - 0.2
minDistance: 2,
maxDistance: 20,
enablePan: false
// enableZoom: false,
// maxPolarAngle: Math.PI / 2 - 0.2
})
const toggleModal = () => {}
const changeDistance = (v = 1) => {
if (camera.value && controls.value) {
const distance = camera.value.position.distanceTo(new Vector3(0, 0, 0))
const r = distance + v
camera.value.position.normalize().multiplyScalar(r)
}
}
</script>
<template>
@ -32,14 +43,45 @@ const toggleModal = () => {}
<template #fallback>
<div class="fallback">Загрузка 3D модели</div>
</template>
<TresCanvas height="600">
<TresPerspectiveCamera />
<OrbitControls v-bind="controlsState" make-default />
<TresCanvas height="600" clear-color="#e2e8f0">
<TresPerspectiveCamera
:position="new Vector3(-7, 2, 4)"
ref="camera"
/>
<OrbitControls v-bind="controlsState" ref="controls" make-default />
<ModelEnv />
<Suspense>
<component :is="types[props.type]" />
</Suspense>
</TresCanvas>
<div class="canvas-icons">
<a
href="#"
@click.prevent="changeDistance(-0.5)"
:class="[
{
disabled: camera
? camera.position.distanceTo(new Vector3(0, 0, 0)) <= controlsState.minDistance
: null
}
]"
>
<Icon name="mdi:plus-circle-outline" />
</a>
<a
href="#"
@click.prevent="changeDistance(0.5)"
:class="[
{
disabled: camera
? camera.position.distanceTo(new Vector3(0, 0, 0)) >= controlsState.maxDistance
: null
}
]"
>
<Icon name="mdi:minus-circle-outline" />
</a>
</div>
</ClientOnly>
</div>
<button @click.prevent="toggleModal">Рассчитать</button>

BIN
public/models/bench.glb Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,38 +1,54 @@
import { Box3, PerspectiveCamera, Vector3 } from 'three'
import { degToRad } from 'three/src/math/MathUtils.js';
import { Box3, PerspectiveCamera, Vector3, Mesh, Camera } from 'three'
import { degToRad } from 'three/src/math/MathUtils.js'
/**
* Вычисляет масштаб объекта и позицию камеры, чтобы объект полностью помещался в видимой области.
* @param object - Объект Three.js (например, загруженная модель).
* @param camera - Экземпляр камеры.
* @param controls - Экземпляр OrbitControls.
* @param distance - Расстояние от камеры до объекта.
* @param cameraOffset - Коэффициенты смещения камеры (x, y, z).
*/
export default function (
export default function calculateScaleToFit(
object: any,
camera: PerspectiveCamera,
camera: ComputedRef<Camera | undefined>,
controls: any,
distance: number,
cameraOffset: { x: number; y: number; z: number }
) {
// Создаем новый BoundingBox
const bbox = new Box3()
// Обновляем мировую матрицу объекта
object.updateWorldMatrix(true, true)
const fov = camera.fov // Угол обзора камеры
const aspect = camera.aspect // Соотношение сторон
// Перебираем все дочерние элементы объекта
object.traverse((child: any) => {
if (child.isMesh) {
// Добавляем геометрию каждого меша в bounding box
const geometry = child.geometry
if (geometry) {
geometry.computeBoundingBox() // Убедимся, что bounding box геометрии вычислен
const meshBBox = geometry.boundingBox.clone()
if (meshBBox) {
// Применяем мировую матрицу к bounding box
meshBBox.applyMatrix4(child.matrixWorld)
bbox.union(meshBBox) // Объединяем с общим bounding box
}
}
}
})
const bbox = new Box3().setFromObject(object) // Вычисляем bounding box
// Вычисляем центр и размер bounding box
const center = new Vector3()
bbox.getCenter(center) // Центр объекта
bbox.getCenter(center)
const size = new Vector3()
bbox.getSize(size) // Размеры объекта
bbox.getSize(size)
// Рассчитываем масштаб, чтобы объект полностью помещался в видимой области
const vFOV = degToRad(fov) // Угол обзора в радианах
const visibleHeight = 2 * Math.tan(vFOV / 2) * distance // Видимая высота
const visibleWidth = visibleHeight * aspect // Видимая ширина
// Центрируем объект
object.position.sub(center)
object.updateWorldMatrix(true, true)
// Рассчитываем масштаб
const fov = (camera.value as PerspectiveCamera).fov
const aspect = (camera.value as PerspectiveCamera).aspect
const vFOV = degToRad(fov)
const visibleHeight = 2 * Math.tan(vFOV / 2) * distance
const visibleWidth = visibleHeight * aspect
const scaleX = visibleWidth / size.x
const scaleY = visibleHeight / size.y
@ -40,18 +56,20 @@ export default function (
const scale = Math.min(scaleX, scaleY, scaleZ)
// Позиционируем камеру с учетом смещения
camera.position.set(
center.x + distance * cameraOffset.x,
center.y + distance * cameraOffset.y,
center.z + distance * cameraOffset.z
)
// Масштабируем объект
object.scale.setScalar(scale)
object.updateWorldMatrix(true, true)
// Устанавливаем цель для OrbitControls
if (controls) {
controls.target.set(center.x, center.y, center.z)
controls.update() // Обновляем контроллы
if (camera.value) {
const [x, y, z] = Object.values(cameraOffset).map(el => el * distance);
camera.value.position.set(x, y, z);
}
return { scale, center }
// Настройка OrbitControls
if (controls.value) {
controls.value.target.set(...Object.values(center.multiplyScalar(1)))
controls.value.update()
}
return object
}