5 changed files with 733 additions and 1 deletions
|
After Width: | Height: | Size: 346 KiB |
@ -0,0 +1,321 @@ |
|||
<template> |
|||
<div class="model3d-view"> |
|||
<el-space :gutter="10" class="toolbar"> |
|||
<el-upload :on-change="test1" |
|||
:show-file-list="false" accept=".fbx,.obj,.mtl,.3ds" action="" :auto-upload="false"> |
|||
<el-button type="primary">测试1</el-button> |
|||
</el-upload> |
|||
<div class="demo-color-block"> |
|||
<span class="demonstration">物体数:<el-text type="danger">{{ restate.objects }}</el-text></span> |
|||
<span class="demonstration"> 顶点数:<el-text type="danger">{{ restate.vertices }}</el-text></span> |
|||
<span class="demonstration"> 三角形数:<el-text type="danger">{{ restate.faces }}</el-text></span> |
|||
</div> |
|||
</el-space> |
|||
<div class="main-content"> |
|||
<div class="canvas-container" ref="canvasContainer" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import * as THREE from 'three' |
|||
import { getCurrentInstance, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue' |
|||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js' |
|||
import Stats from 'three/examples/jsm/libs/stats.module' |
|||
|
|||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls' |
|||
import * as dat from 'three/examples/jsm/libs/lil-gui.module.min' |
|||
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader' |
|||
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader' |
|||
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader' |
|||
import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader' |
|||
|
|||
const canvasContainer = ref(null) |
|||
let resizeObserver: ResizeObserver | null = null |
|||
let scene: THREE.Scene | null = null |
|||
let renderer: THREE.WebGLRenderer | null = null |
|||
let viewerDom: HTMLElement | null = null |
|||
let camera: THREE.PerspectiveCamera | THREE.OrthographicCamera | null = null |
|||
let controls: OrbitControls | null = null |
|||
let axesHelper: THREE.AxesHelper | null = null |
|||
let gridHelper: THREE.GridHelper | null = null |
|||
let statsControls: Stats | null = null |
|||
let animationFrameId: number | null = null |
|||
let modelGroup = new THREE.Group() |
|||
|
|||
const restate = reactive({ |
|||
targetColor: '#ff0000', |
|||
loadScale: 1, |
|||
mode: 'translate', |
|||
objects: 0, |
|||
vertices: 0, |
|||
faces: 0 |
|||
}) |
|||
|
|||
// 状态变量 |
|||
const state = { |
|||
showAxesHelper: true, |
|||
showGridHelper: true, |
|||
camera: { |
|||
position: { x: 0, y: 5, z: 10 }, |
|||
rotation: { x: 0, y: 0, z: 0 } |
|||
} |
|||
} |
|||
onMounted(() => { |
|||
nextTick(() => { |
|||
initThree() |
|||
|
|||
const viewerDom = canvasContainer.value |
|||
if (resizeObserver) { |
|||
resizeObserver.unobserve(viewerDom) |
|||
} |
|||
resizeObserver = new ResizeObserver(handleResize) |
|||
resizeObserver.observe(viewerDom) |
|||
|
|||
window['cp'] = getCurrentInstance() |
|||
}) |
|||
}) |
|||
|
|||
onBeforeUnmount(() => { |
|||
if (animationFrameId !== null) { |
|||
cancelAnimationFrame(animationFrameId) |
|||
animationFrameId = null |
|||
} |
|||
|
|||
cleanupThree() |
|||
|
|||
const viewerDom = canvasContainer.value |
|||
if (resizeObserver) { |
|||
resizeObserver.unobserve(viewerDom) |
|||
} |
|||
|
|||
window['cp'] = null |
|||
}) |
|||
|
|||
function initThree() { |
|||
viewerDom = canvasContainer.value |
|||
if (!viewerDom) { |
|||
console.error('Viewer DOM element not found') |
|||
return |
|||
} |
|||
|
|||
// 场景 |
|||
scene = new THREE.Scene() |
|||
scene.background = new THREE.Color(0xeeeeee) |
|||
|
|||
// 渲染器 |
|||
renderer = new THREE.WebGLRenderer({ |
|||
antialias: true, |
|||
alpha: true, |
|||
powerPreference: 'high-performance' |
|||
}) |
|||
renderer.clearDepth() |
|||
renderer.setPixelRatio(window.devicePixelRatio) |
|||
renderer.setSize(viewerDom.clientWidth, viewerDom.clientHeight) |
|||
viewerDom.appendChild(renderer.domElement) |
|||
|
|||
// 摄像机 |
|||
initMode3DCamera() |
|||
|
|||
// 辅助线 |
|||
axesHelper = new THREE.AxesHelper(5) |
|||
scene.add(axesHelper) |
|||
|
|||
gridHelper = new THREE.GridHelper(1000, 1000) |
|||
scene.add(gridHelper) |
|||
|
|||
// 光照 |
|||
const ambientLight = new THREE.AmbientLight(0xffffff, 1.5) |
|||
scene.add(ambientLight) |
|||
|
|||
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5) |
|||
directionalLight.position.set(5, 5, 5).multiplyScalar(3) |
|||
directionalLight.castShadow = true |
|||
scene.add(directionalLight) |
|||
|
|||
const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1) |
|||
scene.add(hemisphereLight) |
|||
|
|||
|
|||
// 性能监控 |
|||
statsControls = new Stats() |
|||
statsControls.showPanel(0) |
|||
statsControls.dom.style.right = '0' |
|||
statsControls.dom.style.left = 'auto' |
|||
viewerDom.appendChild(statsControls.dom) |
|||
|
|||
animate() |
|||
} |
|||
|
|||
// 动画循环 |
|||
function animate() { |
|||
animationFrameId = requestAnimationFrame(animate) |
|||
renderView() |
|||
} |
|||
|
|||
function handleResize(entries) { |
|||
for (let entry of entries) { |
|||
// entry.contentRect包含了元素的尺寸信息 |
|||
console.log('Element size changed:', entry.contentRect) |
|||
|
|||
const width = entry.contentRect.width |
|||
const height = entry.contentRect.height |
|||
|
|||
if (camera instanceof THREE.PerspectiveCamera) { |
|||
camera.aspect = width / height |
|||
camera.updateProjectionMatrix() |
|||
|
|||
} else if (camera instanceof THREE.OrthographicCamera) { |
|||
camera.left = width / -2 |
|||
camera.right = width / 2 |
|||
camera.top = height / 2 |
|||
camera.bottom = height / -2 |
|||
camera.updateProjectionMatrix() |
|||
} |
|||
renderer.setPixelRatio(window.devicePixelRatio) |
|||
renderer.setSize(width, height) |
|||
break |
|||
} |
|||
} |
|||
|
|||
function initMode3DCamera() { |
|||
if (camera) { |
|||
scene.remove(camera) |
|||
} |
|||
|
|||
const viewerDom = canvasContainer.value |
|||
|
|||
// ============================ 创建透视相机 |
|||
const cameraNew = new THREE.PerspectiveCamera(25, viewerDom.clientWidth / viewerDom.clientHeight, 0.1, 2000) |
|||
cameraNew.position.set(5, 5, 5) |
|||
cameraNew.lookAt(0, 0, 0) |
|||
|
|||
camera = cameraNew |
|||
scene.add(camera) |
|||
|
|||
const controlsNew = new OrbitControls(camera, viewerDom) |
|||
controlsNew.mouseButtons = { LEFT: THREE.MOUSE.PAN, RIGHT: THREE.MOUSE.ROTATE } // 鼠标中键平移 |
|||
controlsNew.enableDamping = false |
|||
controlsNew.screenSpacePanning = false // 定义平移时如何平移相机的位置 控制不上下移动 |
|||
controlsNew.minDistance = 2 |
|||
controlsNew.addEventListener('change', syncCameraState) |
|||
controls = controlsNew |
|||
controls.update() |
|||
|
|||
camera.updateProjectionMatrix() |
|||
|
|||
syncCameraState() |
|||
} |
|||
|
|||
/** |
|||
* 重新加载相机状态到全局状态 |
|||
*/ |
|||
function syncCameraState() { |
|||
if (camera) { |
|||
state.camera.position.x = camera.position.x |
|||
state.camera.position.y = camera.position.y |
|||
state.camera.position.z = camera.position.z |
|||
|
|||
state.camera.rotation.x = camera.rotation.x |
|||
state.camera.rotation.y = camera.rotation.y |
|||
state.camera.rotation.z = camera.rotation.z |
|||
} |
|||
} |
|||
|
|||
function renderView() { |
|||
statsControls?.update() |
|||
renderer?.render(scene, camera) |
|||
} |
|||
|
|||
|
|||
function cleaupModel() { |
|||
if (modelGroup) { |
|||
scene.remove(modelGroup) |
|||
} |
|||
tcontrols.detach() |
|||
transformEditCtl.value.detach() |
|||
} |
|||
|
|||
function cleanupThree() { |
|||
// 移除旧模型 |
|||
if (scene) { |
|||
scene.traverse((obj: THREE.Mesh) => { |
|||
// 释放几何体 |
|||
if (obj.geometry) { |
|||
obj.geometry.dispose() |
|||
} |
|||
|
|||
// 释放材质 |
|||
if (obj.material) { |
|||
if (Array.isArray(obj.material)) { |
|||
obj.material.forEach(m => m.dispose()) |
|||
} else { |
|||
obj.material.dispose() |
|||
} |
|||
} |
|||
|
|||
// 释放纹理 |
|||
if (obj.texture) { |
|||
obj.texture.dispose() |
|||
} |
|||
|
|||
// 释放渲染目标 |
|||
if (obj.renderTarget) { |
|||
obj.renderTarget.dispose() |
|||
} |
|||
|
|||
// 移除事件监听(如 OrbitControls) |
|||
if (obj.dispose) { |
|||
obj.dispose() |
|||
} |
|||
}) |
|||
|
|||
if (modelGroup) { |
|||
scene.remove(modelGroup) |
|||
} |
|||
// 清空场景 |
|||
scene.children = [] |
|||
|
|||
modelGroup = null |
|||
} |
|||
|
|||
if (statsControls) { |
|||
statsControls.dom.remove() |
|||
} |
|||
|
|||
if (renderer) { |
|||
renderer.dispose() |
|||
renderer.forceContextLoss() |
|||
console.log('WebGL disposed, memory:', renderer.info.memory) |
|||
renderer.domElement = null |
|||
} |
|||
} |
|||
|
|||
|
|||
</script> |
|||
<style scoped lang="less"> |
|||
.model3d-view { |
|||
display: flex; |
|||
flex-direction: column; |
|||
flex-grow: 1; |
|||
overflow: hidden; |
|||
|
|||
.main-content { |
|||
display: flex; |
|||
flex: 1; |
|||
overflow: hidden; |
|||
|
|||
.model3d-content { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
} |
|||
|
|||
.canvas-container { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
</style> |
|||
@ -0,0 +1,390 @@ |
|||
<template> |
|||
<div class="model3d-view"> |
|||
<el-space :gutter="10" class="toolbar"> |
|||
<el-upload :on-change="test1" |
|||
:show-file-list="false" accept=".fbx,.obj,.mtl,.3ds" action="" :auto-upload="false"> |
|||
<el-button type="primary">测试1</el-button> |
|||
</el-upload> |
|||
<div class="demo-color-block"> |
|||
<span class="demonstration">物体数:<el-text type="danger">{{ restate.objects }}</el-text></span> |
|||
<span class="demonstration"> 顶点数:<el-text type="danger">{{ restate.vertices }}</el-text></span> |
|||
<span class="demonstration"> 三角形数:<el-text type="danger">{{ restate.faces }}</el-text></span> |
|||
</div> |
|||
</el-space> |
|||
<div class="main-content"> |
|||
<div class="canvas-container" ref="canvasContainer" /> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
<script setup lang="ts"> |
|||
import * as THREE from 'three' |
|||
import { renderIcon } from '@/utils/webutils.ts' |
|||
import { nextTick, onMounted, reactive, ref } from 'vue' |
|||
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js' |
|||
import Plastic_Rough_JPG from '@/assets/Models/Plastic_Rough.jpg' |
|||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js' |
|||
import Stats from 'three/examples/jsm/libs/stats.module' |
|||
import { HorizontalBlurShader } from 'three/addons/shaders/HorizontalBlurShader.js' |
|||
import { VerticalBlurShader } from 'three/addons/shaders/VerticalBlurShader.js' |
|||
|
|||
const restate = reactive({ |
|||
targetColor: '#ff0000', |
|||
loadScale: 1, |
|||
mode: 'translate', |
|||
objects: 0, |
|||
vertices: 0, |
|||
faces: 0, |
|||
shadow: { |
|||
blur: 0.1, |
|||
darkness: 0.3, |
|||
opacity: 1 |
|||
}, |
|||
plane: { |
|||
color: '#ffffff', |
|||
opacity: 0 |
|||
}, |
|||
showWireframe: false |
|||
}) |
|||
|
|||
const canvasContainer = ref(null) |
|||
let resizeObserver: ResizeObserver | null = null |
|||
let scene: THREE.Scene | null = null |
|||
let renderer: THREE.WebGLRenderer | null = null |
|||
let viewerDom: HTMLElement | null = null |
|||
let camera: THREE.PerspectiveCamera | THREE.OrthographicCamera | null = null |
|||
let controls: OrbitControls | null = null |
|||
let stats: any = null |
|||
let shadowGroup: THREE.Group | null = null |
|||
let renderTarget: THREE.WebGLRenderTarget | null = null |
|||
let renderTargetBlur: THREE.WebGLRenderTarget | null = null |
|||
let plane: THREE.Mesh | null = null |
|||
let blurPlane: THREE.Mesh | null = null |
|||
let fillPlane: THREE.Mesh | null = null |
|||
let shadowCamera: THREE.OrthographicCamera | null = null |
|||
let depthMaterial: THREE.MeshDepthMaterial | null = null |
|||
let horizontalBlurMaterial: THREE.ShaderMaterial | null = null |
|||
let verticalBlurMaterial: THREE.ShaderMaterial | null = null |
|||
|
|||
const CAMERA_HEIGHT = 10 |
|||
|
|||
const datamodel = { |
|||
useShadows: true, |
|||
groundWidth: 1000, |
|||
groundHeight: 0.1, |
|||
delayAddThings: [], |
|||
staticThings: [] |
|||
} |
|||
|
|||
const globalSettings = { |
|||
useRealMaterial: true, |
|||
metalMaterialCfg: { |
|||
emissive: 0, |
|||
specular: 0x6d6d6d, |
|||
metalness: 0.8, |
|||
roughness: 0.2 |
|||
}, |
|||
paintMaterialCfg: { |
|||
emissive: 0, |
|||
specular: 0x6d6d6d, |
|||
metalness: 0.6, |
|||
roughness: 0.8 |
|||
}, |
|||
platisticMaterCfg: { |
|||
emissive: 0, |
|||
specular: 0x6d6d6d, |
|||
metalness: 0.4, |
|||
roughness: 0.8 |
|||
} |
|||
} |
|||
const globalRuntime = { |
|||
materialResources: Object.create(null) |
|||
} |
|||
|
|||
var ground = new THREE.Mesh( |
|||
new THREE.BoxGeometry(datamodel.groundWidth, 0.3, datamodel.groundHeight, 1, 1, 1), |
|||
new THREE.MeshPhongMaterial({ color: 0xb0b0b0 }) |
|||
) |
|||
ground.receiveShadow = true |
|||
|
|||
onMounted(() => { |
|||
nextTick(() => { |
|||
initThree() |
|||
}) |
|||
}) |
|||
|
|||
function test1() { |
|||
initStaticShadows(datamodel.groundWidth, datamodel.groundHeight) |
|||
} |
|||
|
|||
|
|||
function initThree() { |
|||
viewerDom = canvasContainer.value |
|||
if (!viewerDom) { |
|||
console.error('Viewer DOM element not found') |
|||
return |
|||
} |
|||
|
|||
scene = new THREE.Scene() |
|||
scene.background = new THREE.Color(0x343a40) // 0x343a40 |
|||
|
|||
renderer = new THREE.WebGLRenderer({ |
|||
antialias: true, |
|||
alpha: true, |
|||
powerPreference: 'high-performance' |
|||
}) |
|||
|
|||
renderer.setSize(viewerDom.clientWidth, viewerDom.clientHeight) |
|||
|
|||
if (resizeObserver) { |
|||
resizeObserver.unobserve(viewerDom) |
|||
} |
|||
resizeObserver = new ResizeObserver(handleResize) |
|||
resizeObserver.observe(viewerDom) |
|||
viewerDom.appendChild(renderer.domElement) |
|||
|
|||
if (datamodel.useShadows) { |
|||
renderer.shadowMap.enabled = true |
|||
renderer.shadowMap.type = THREE.PCFSoftShadowMap |
|||
} |
|||
const pmremGenerator = new THREE.PMREMGenerator(renderer) |
|||
scene.environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture |
|||
|
|||
const materialGround = new THREE.MeshPhongMaterial({ |
|||
color: 0x2b5d94, |
|||
emissive: 0, |
|||
specular: 0x909090, |
|||
shininess: 10 |
|||
}) |
|||
let loader = new THREE.TextureLoader() |
|||
loader.load(Plastic_Rough_JPG, function(texture) { |
|||
texture.wrapS = THREE.RepeatWrapping |
|||
texture.wrapT = THREE.RepeatWrapping |
|||
texture.repeat.set(datamodel.groundWidth, datamodel.groundHeight) |
|||
ground.material = materialGround |
|||
ground.material.normalMap = texture |
|||
ground.material.needsUpdate = true |
|||
ground.material.transparent = false |
|||
}) |
|||
|
|||
const hemiLight = new THREE.HemisphereLight(0xffffff, 0x8d8d8d, 3) |
|||
hemiLight.position.set(0, 15, 0) |
|||
scene.add(hemiLight) |
|||
|
|||
const staticShadowLight = new THREE.DirectionalLight(0xe0e0e0, 2) |
|||
staticShadowLight.position.set(0, 10, 0) |
|||
staticShadowLight.castShadow = true |
|||
staticShadowLight.shadow.mapSize.width = 2048 // default |
|||
staticShadowLight.shadow.mapSize.height = 2048 // default |
|||
staticShadowLight.shadow.camera.top = 80 |
|||
staticShadowLight.shadow.camera.bottom = -80 |
|||
staticShadowLight.shadow.camera.left = -80 |
|||
staticShadowLight.shadow.camera.right = 80 |
|||
staticShadowLight.shadow.camera.near = 1 |
|||
staticShadowLight.shadow.camera.far = 1000 |
|||
staticShadowLight.shadow.intensity = 0.5 |
|||
scene.add(staticShadowLight) |
|||
|
|||
scene.add(ground) |
|||
|
|||
|
|||
camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 1, 1000) |
|||
camera.position.set(0, 2, 30) |
|||
|
|||
controls = new OrbitControls(camera, renderer.domElement) |
|||
controls.enablePan = true |
|||
controls.enableZoom = true |
|||
controls.target.set(0, 1, 0) |
|||
controls.update() |
|||
|
|||
stats = new Stats() |
|||
viewerDom.appendChild(stats.dom) |
|||
stats.dom.style.left = 'auto' |
|||
stats.dom.style.right = '0px' |
|||
|
|||
renderer.setAnimationLoop(animate) |
|||
renderer.setClearColor(0x000000, 0) |
|||
renderer.setPixelRatio(window.devicePixelRatio) |
|||
} |
|||
|
|||
var renderNormalShadowMap = false |
|||
|
|||
function animate() { |
|||
if (renderNormalShadowMap == true) { |
|||
renderNormalShadowMap = false |
|||
|
|||
const initialBackground = scene.background |
|||
scene.background = null |
|||
|
|||
scene.overrideMaterial = depthMaterial |
|||
|
|||
const initialClearAlpha = renderer.getClearAlpha() |
|||
renderer.setClearAlpha(0) |
|||
|
|||
renderer.setRenderTarget(renderTarget) |
|||
renderer.render(scene, shadowCamera) |
|||
|
|||
scene.overrideMaterial = null |
|||
|
|||
blurShadow(restate.shadow.blur) |
|||
blurShadow(restate.shadow.blur * 0.4) |
|||
|
|||
renderer.setRenderTarget(null) |
|||
renderer.setClearAlpha(initialClearAlpha) |
|||
scene.background = initialBackground |
|||
|
|||
for (let item of datamodel.delayAddThings) { |
|||
scene.add(item) |
|||
} |
|||
|
|||
shadowGroup.position.y = 0.16 |
|||
|
|||
for (let item of datamodel.staticThings) { |
|||
item.traverse((e) => e.isMesh && (e.castShadow = false)) |
|||
item.castShadow = false |
|||
} |
|||
} |
|||
renderer.render(scene, camera) |
|||
stats.update() |
|||
} |
|||
|
|||
|
|||
function handleResize(entries) { |
|||
for (let entry of entries) { |
|||
const width = entry.contentRect.width |
|||
const height = entry.contentRect.height |
|||
|
|||
if (camera instanceof THREE.PerspectiveCamera) { |
|||
camera.aspect = width / height |
|||
camera.updateProjectionMatrix() |
|||
|
|||
} else if (camera instanceof THREE.OrthographicCamera) { |
|||
camera.left = width / -2 |
|||
camera.right = width / 2 |
|||
camera.top = height / 2 |
|||
camera.bottom = height / -2 |
|||
camera.updateProjectionMatrix() |
|||
} |
|||
renderer.setPixelRatio(window.devicePixelRatio) |
|||
renderer.setSize(width, height) |
|||
break |
|||
} |
|||
} |
|||
|
|||
function initStaticShadows(PLANE_WIDTH, PLANE_HEIGHT) { |
|||
shadowGroup = new THREE.Group() |
|||
shadowGroup.position.y = 0.16 |
|||
scene.add(shadowGroup) |
|||
|
|||
var ratio = PLANE_HEIGHT / PLANE_WIDTH |
|||
|
|||
renderTarget = new THREE.WebGLRenderTarget(2048, 2048 * ratio) |
|||
renderTarget.texture.generateMipmaps = false |
|||
|
|||
renderTargetBlur = new THREE.WebGLRenderTarget(2048, 2048 * ratio) |
|||
renderTargetBlur.texture.generateMipmaps = false |
|||
|
|||
const planeGeometry = new THREE.PlaneGeometry(PLANE_WIDTH, PLANE_HEIGHT).rotateX(Math.PI / 2) |
|||
const planeMaterial = new THREE.MeshBasicMaterial({ |
|||
map: renderTarget.texture, |
|||
opacity: restate.shadow.opacity, |
|||
transparent: true, |
|||
depthWrite: false |
|||
}) |
|||
plane = new THREE.Mesh(planeGeometry, planeMaterial) |
|||
|
|||
plane.renderOrder = 1 |
|||
shadowGroup.add(plane) |
|||
|
|||
plane.scale.y = -1 |
|||
|
|||
blurPlane = new THREE.Mesh(planeGeometry) |
|||
blurPlane.visible = false |
|||
shadowGroup.add(blurPlane) |
|||
|
|||
const fillPlaneMaterial = new THREE.MeshBasicMaterial({ |
|||
color: restate.plane.color, |
|||
opacity: restate.plane.opacity, |
|||
transparent: true, |
|||
depthWrite: false |
|||
}) |
|||
|
|||
fillPlane = new THREE.Mesh(planeGeometry, fillPlaneMaterial) |
|||
fillPlane.rotateX(Math.PI) |
|||
shadowGroup.add(fillPlane) |
|||
|
|||
shadowCamera = new THREE.OrthographicCamera(-PLANE_WIDTH / 2, PLANE_WIDTH / 2, PLANE_HEIGHT / 2, -PLANE_HEIGHT / 2, 0, CAMERA_HEIGHT) |
|||
shadowCamera.rotation.x = Math.PI / 2 |
|||
shadowGroup.add(shadowCamera) |
|||
|
|||
depthMaterial = new THREE.MeshDepthMaterial() |
|||
depthMaterial.userData.darkness = { value: restate.shadow.darkness } |
|||
depthMaterial.onBeforeCompile = function(shader) { |
|||
shader.uniforms.darkness = depthMaterial.userData.darkness |
|||
shader.fragmentShader = /* glsl */ ` |
|||
uniform float darkness; |
|||
${shader.fragmentShader.replace( |
|||
'gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity );', |
|||
'gl_FragColor = vec4( vec3( 0.0 ), ( 1.0 - fragCoordZ ) * darkness );' |
|||
)} |
|||
` |
|||
} |
|||
|
|||
depthMaterial.depthTest = false |
|||
depthMaterial.depthWrite = false |
|||
|
|||
horizontalBlurMaterial = new THREE.ShaderMaterial(HorizontalBlurShader) |
|||
horizontalBlurMaterial.depthTest = false |
|||
|
|||
verticalBlurMaterial = new THREE.ShaderMaterial(VerticalBlurShader) |
|||
verticalBlurMaterial.depthTest = false |
|||
} |
|||
|
|||
function blurShadow(amount) { |
|||
blurPlane.visible = true |
|||
|
|||
blurPlane.material = horizontalBlurMaterial |
|||
blurPlane.material.uniforms.tDiffuse.value = renderTarget.texture |
|||
horizontalBlurMaterial.uniforms.h.value = (amount * 1) / 256 |
|||
|
|||
renderer.setRenderTarget(renderTargetBlur) |
|||
renderer.render(blurPlane, shadowCamera) |
|||
|
|||
blurPlane.material = verticalBlurMaterial |
|||
blurPlane.material.uniforms.tDiffuse.value = renderTargetBlur.texture |
|||
verticalBlurMaterial.uniforms.v.value = (amount * 1) / 256 |
|||
|
|||
renderer.setRenderTarget(renderTarget) |
|||
renderer.render(blurPlane, shadowCamera) |
|||
|
|||
blurPlane.visible = false |
|||
} |
|||
|
|||
|
|||
</script> |
|||
<style scoped lang="less"> |
|||
.model3d-view { |
|||
display: flex; |
|||
flex-direction: column; |
|||
flex-grow: 1; |
|||
overflow: hidden; |
|||
|
|||
.main-content { |
|||
display: flex; |
|||
flex: 1; |
|||
overflow: hidden; |
|||
|
|||
.model3d-content { |
|||
height: 100%; |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
} |
|||
|
|||
.canvas-container { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
} |
|||
} |
|||
|
|||
</style> |
|||
Loading…
Reference in new issue