Skip to content

Commit

Permalink
feat: contact-shadows implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarosabu committed May 9, 2023
1 parent d7ce5d5 commit 81baf86
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 12 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"devDependencies": {
"@alvarosabu/prettier-config": "^1.3.0",
"@release-it/conventional-changelog": "^5.1.1",
"@tresjs/core": "2.0.0-rc.3",
"@tweakpane/plugin-essentials": "^0.1.8",
"@types/three": "^0.152.0",
"@typescript-eslint/eslint-plugin": "^5.59.2",
Expand All @@ -74,7 +75,6 @@
"vitepress": "1.0.0-alpha.75"
},
"dependencies": {
"@tresjs/core": "2.0.0-rc.2",
"@vueuse/core": "^10.1.2",
"three": "^0.152.2",
"three-stdlib": "^2.21.10",
Expand Down
1 change: 1 addition & 0 deletions playground/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
AkuAku: typeof import('./src/components/AkuAku.vue')['default']
ContactShadowsDemo: typeof import('./src/components/ContactShadowsDemo.vue')['default']
Gltf: typeof import('./src/components/gltf/index.vue')['default']
LeviosoDemo: typeof import('./src/components/LeviosoDemo.vue')['default']
MapControlsDemo: typeof import('./src/components/MapControlsDemo.vue')['default']
Expand Down
62 changes: 62 additions & 0 deletions playground/src/components/ContactShadowsDemo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls, useTweakPane, ContactShadows, TorusKnot } from '@cientos'
import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
import { shallowRef } from 'vue'
import { watchEffect } from 'vue'
import { shallowReactive } from 'vue'
const gl = {
clearColor: '#82DBC5',
shadows: true,
alpha: false,
shadowMapType: BasicShadowMap,
outputColorSpace: SRGBColorSpace,
toneMapping: NoToneMapping,
}
const { pane } = useTweakPane()
const leviosoState = shallowReactive({
speed: 5,
rotationFactor: 1,
floatFactor: 1,
range: [-0.1, 0.1],
})
pane.addInput(leviosoState, 'speed', {
step: 1,
min: 0,
max: 100,
})
pane.addInput(leviosoState, 'rotationFactor', {
step: 1,
min: 0,
max: 10,
})
pane.addInput(leviosoState, 'floatFactor', {
step: 1,
min: 0,
max: 10,
})
const groupRef = shallowRef()
watchEffect(() => {
console.log(groupRef)
})
</script>

<template>
<TresCanvas v-bind="gl">
<TresPerspectiveCamera :position="[11, 11, 11]" />
<OrbitControls />

<TorusKnot :position="[0, 4, 0]">
<TresMeshNormalMaterial />
</TorusKnot>
<ContactShadows />
<TresAmbientLight :intensity="1" />
</TresCanvas>
</template>
4 changes: 2 additions & 2 deletions playground/src/pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script setup lang="ts">
import ModelsDemo from '../components/ModelsDemo.vue'
import ContactShadowsDemo from '../components/ContactShadowsDemo.vue'
</script>
<template>
<Suspense>
<ModelsDemo />
<ContactShadowsDemo />
</Suspense>
</template>
15 changes: 7 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

162 changes: 162 additions & 0 deletions src/core/abstractions/ContactShadows.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<script setup lang="ts">
import { TresColor, useRenderLoop } from '@tresjs/core'
import {
Color,
ColorRepresentation,
Material,
Mesh,
MeshDepthMaterial,
OrthographicCamera,
PlaneGeometry,
ShaderMaterial,
Texture,
WebGLRenderTarget,
} from 'three'
import { computed, shallowRef } from 'vue'
import { useCientos } from '../useCientos'
import { HorizontalBlurShader, VerticalBlurShader } from 'three-stdlib'
export interface ContactShadowsProps {
opacity?: number
width?: number
height?: number
blur?: number
far?: number
smooth?: boolean
resolution?: number
frames?: number
scale?: number | [x: number, y: number]
color?: TresColor
depthWrite?: boolean
}
const props = withDefaults(defineProps<ContactShadowsProps>(), {
scale: 10,
frames: Infinity,
opacity: 1,
width: 1,
height: 1,
blur: 1,
far: 10,
resolution: 512,
smooth: true,
color: '#000000',
depthWrite: false,
})
const groupRef = shallowRef()
const shadowRef = shallowRef<OrthographicCamera>()
defineExpose({
value: groupRef,
})
const { state } = useCientos()
const cameraW = computed(() => props.width * (Array.isArray(props.scale) ? props.scale[0] : props.scale || 1))
const cameraH = computed(() => props.height * (Array.isArray(props.scale) ? props.scale[1] : props.scale || 1))
// the render target that will show the shadows in the plane texture and
// the target that we will use to blur the first render target
let renderTarget = new WebGLRenderTarget(props.resolution, props.resolution)
let renderTargetBlur = new WebGLRenderTarget(props.resolution, props.resolution)
renderTargetBlur.texture.generateMipmaps = renderTarget.texture.generateMipmaps = false
// make a plane and make it face up
const planeGeometry = new PlaneGeometry(props.width, props.height).rotateX(Math.PI / 2)
const blurPlane = new Mesh(planeGeometry)
const depthMaterial = new MeshDepthMaterial()
depthMaterial.depthTest = depthMaterial.depthWrite = false
// Overwrite depthMaterial sahders
depthMaterial.onBeforeCompile = shader => {
shader.uniforms = {
...shader.uniforms,
ucolor: { value: new Color(props.color as ColorRepresentation) },
}
shader.fragmentShader = shader.fragmentShader.replace(
`void main() {`, //
`uniform vec3 ucolor;
void main() {
`,
)
shader.fragmentShader = shader.fragmentShader.replace(
'vec4( vec3( 1.0 - fragCoordZ ), opacity );',
// Colorize the shadow, multiply by the falloff so that the center can remain darker
'vec4( ucolor * fragCoordZ * 2.0, ( 1.0 - fragCoordZ ) * 1.0 );',
)
}
// Initialize the blur shaders
const horizontalBlurMaterial = new ShaderMaterial(HorizontalBlurShader)
const verticalBlurMaterial = new ShaderMaterial(VerticalBlurShader)
verticalBlurMaterial.depthTest = horizontalBlurMaterial.depthTest = false
// Blur the shadow
function blurShadows(blur: number) {
if (!state.renderer || !shadowRef.value) return
blurPlane.visible = true
blurPlane.material = horizontalBlurMaterial
horizontalBlurMaterial.uniforms.tDiffuse.value = renderTarget.texture
horizontalBlurMaterial.uniforms.h.value = blur / props.resolution
state.renderer.setRenderTarget(renderTargetBlur)
state.renderer.render(blurPlane, shadowRef.value)
blurPlane.material = verticalBlurMaterial
verticalBlurMaterial.uniforms.tDiffuse.value = renderTargetBlur.texture
verticalBlurMaterial.uniforms.v.value = blur / props.resolution
state.renderer.setRenderTarget(renderTarget)
state.renderer.render(blurPlane, shadowRef.value)
blurPlane.visible = false
}
const { onLoop } = useRenderLoop()
let count = 0
let initialBackground: Color | Texture | null
let initialOverrideMaterial: Material | null
onLoop(() => {
if (!shadowRef.value || state.scene === undefined || state.renderer === undefined) return
if (frames === Infinity || count < props.frames) {
count++
// Save the initial background and override material
initialBackground = state.scene.background
initialOverrideMaterial = state.scene.overrideMaterial
// Render the shadows
groupRef.value.visible = false
state.scene.background = null
state.scene.overrideMaterial = depthMaterial
state.renderer.setRenderTarget(renderTarget)
state.renderer.render(state.scene, shadowRef.value)
// Blur the shadows
blurShadows(props.blur)
if (props.smooth) {
blurShadows(props.blur * 0.6)
}
// Restore the initial background and override material
state.scene.background = initialBackground
state.scene.overrideMaterial = initialOverrideMaterial
groupRef.value.visible = true
}
})
</script>
<template>
<TresGroup ref="groupRef" :rotation-x="Math.PI / 2">
<TresMesh :geometry="planeGeometry" :scale="[1, -1, 1]" :rotation="[-Math.PI / 2, 0, 0]">
<TresMeshBasicMaterial transparent :map="renderTarget.texture" :opacity="opacity" :depth-write="depthWrite" />
</TresMesh>
<TresOrthographicCamera :args="[-cameraW / 2, cameraW / 2, cameraH / 2, -cameraH / 2, 0, far]" />
</TresGroup>
</template>
3 changes: 2 additions & 1 deletion src/core/abstractions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { PamCameraMouse } from './usePamCameraMouse/component'
import Stars from './Stars.vue'
import Smoke from './Smoke.vue'
import Levioso from './Levioso.vue'
import ContactShadows from './ContactShadows.vue'

export * from './usePamCameraMouse'
export * from './useEnvironment'
export { Text3D, useAnimations, Environment, PamCameraMouse, Stars, Smoke, Levioso }
export { Text3D, useAnimations, Environment, PamCameraMouse, Stars, Smoke, Levioso, ContactShadows }

0 comments on commit 81baf86

Please sign in to comment.