feat: add Asteroid visualizer (#1577)
|
@ -34,5 +34,14 @@ export const visualizers: Visualizer[] = [
|
|||
author: 'Radik (@H2xDev)',
|
||||
url: 'https://codepen.io/H2xDev/pen/rRRGbv'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'asteroid',
|
||||
name: 'Asteroid',
|
||||
init: async (container) => (await import('@/visualizers/asteroid/scripts')).init(container),
|
||||
credits: {
|
||||
author: 'JH (@jhugheswebdev)',
|
||||
url: 'https://github.com/jhugheswebdev/sound-equalizer-threejs'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -27,8 +27,7 @@ export const audioService = {
|
|||
this.source = this.context.createMediaElementSource(this.element)
|
||||
this.analyzer = this.context.createAnalyser()
|
||||
|
||||
this.source.connect(this.analyzer)
|
||||
this.analyzer.connect(this.preampGainNode)
|
||||
this.source.connect(this.preampGainNode)
|
||||
|
||||
const config = equalizerStore.getConfig()
|
||||
|
||||
|
@ -62,7 +61,10 @@ export const audioService = {
|
|||
})
|
||||
})
|
||||
|
||||
prevFilter!.connect(this.context.destination)
|
||||
prevFilter!.connect(this.analyzer)
|
||||
|
||||
// connect the analyzer node last, so that changes to the equalizer affect the visualizer as well
|
||||
this.analyzer.connect(this.context.destination)
|
||||
|
||||
this.unlockAudioContext()
|
||||
},
|
||||
|
|
35
resources/assets/js/visualizers/asteroid/assets/index.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import nx3js from './nx_3js.jpg'
|
||||
import ny3js from './ny_3js.jpg'
|
||||
import nz3js from './nz_3js.jpg'
|
||||
import px3js from './px_3js.jpg'
|
||||
import py3js from './py_3js.jpg'
|
||||
import pz3js from './pz_3js.jpg'
|
||||
import nx from './nx.jpg'
|
||||
import ny from './ny.jpg'
|
||||
import nz from './nz.jpg'
|
||||
import px from './px.jpg'
|
||||
import py from './py.jpg'
|
||||
import pz from './pz.jpg'
|
||||
import rect from './sprite_additive_rect.png'
|
||||
import normalTexture from './normal.jpg'
|
||||
import roughnessTexture from './roughness.jpg'
|
||||
import metallicTexture from './metallic.jpg'
|
||||
|
||||
export {
|
||||
nx3js,
|
||||
ny3js,
|
||||
nz3js,
|
||||
px3js,
|
||||
py3js,
|
||||
pz3js,
|
||||
nx,
|
||||
ny,
|
||||
nz,
|
||||
px,
|
||||
py,
|
||||
pz,
|
||||
rect,
|
||||
normalTexture,
|
||||
roughnessTexture,
|
||||
metallicTexture
|
||||
}
|
BIN
resources/assets/js/visualizers/asteroid/assets/metallic.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/normal.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/nx.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/nx_3js.jpg
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/ny.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/ny_3js.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/nz.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/nz_3js.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/px.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/px_3js.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/py.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/py_3js.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/pz.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/pz_3js.jpg
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
resources/assets/js/visualizers/asteroid/assets/roughness.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 3 KiB |
|
@ -0,0 +1,85 @@
|
|||
import { audioService } from '@/services'
|
||||
|
||||
export class AudioAnalyzer {
|
||||
private bass = 0.
|
||||
private mid = 0.
|
||||
private high = 0.
|
||||
private level = 0.
|
||||
private history = 0.
|
||||
private frame = 0
|
||||
|
||||
private readonly frequencyBinCount: number
|
||||
|
||||
// [!] this can't be read-only regardless of IDE's suggestion
|
||||
private audioBuffer: Uint8Array
|
||||
|
||||
private analyzer: AnalyserNode
|
||||
|
||||
constructor () {
|
||||
this.analyzer = audioService.analyzer
|
||||
this.analyzer.fftSize = 128
|
||||
this.frequencyBinCount = this.analyzer.frequencyBinCount
|
||||
this.audioBuffer = new Uint8Array(this.frequencyBinCount)
|
||||
}
|
||||
|
||||
update () {
|
||||
this.analyzer.getByteFrequencyData(this.audioBuffer)
|
||||
let bass = 0., mid = 0., high = 0.
|
||||
|
||||
if (this.audioBuffer[0] === 0) {
|
||||
// create a "pulse" effect on audio idle
|
||||
if (this.frame % 40 == (Math.floor(Math.random() * 40.))) {
|
||||
bass = Math.random()
|
||||
mid = Math.random()
|
||||
high = Math.random()
|
||||
}
|
||||
} else {
|
||||
const passSize = this.frequencyBinCount / 3.
|
||||
|
||||
for (let i = 0; i < this.frequencyBinCount; i++) {
|
||||
const val = Math.pow(this.audioBuffer[i] / 196., 3.)
|
||||
|
||||
if (i < passSize)
|
||||
bass += val
|
||||
else if (i >= passSize && i < passSize * 2)
|
||||
mid += val
|
||||
else if (i >= passSize * 2)
|
||||
high += val
|
||||
}
|
||||
|
||||
bass /= passSize
|
||||
mid /= passSize
|
||||
high /= passSize
|
||||
}
|
||||
|
||||
this.bass = this.bass > bass ? this.bass * .96 : bass
|
||||
this.mid = this.mid > mid ? this.mid * .96 : mid
|
||||
this.high = this.high > high ? this.high * .96 : high
|
||||
|
||||
this.level = (this.bass + this.mid + this.high) / 3.
|
||||
|
||||
this.history += this.level * .01 + .005
|
||||
|
||||
this.frame++
|
||||
}
|
||||
|
||||
getBass () {
|
||||
return this.bass || 0.
|
||||
}
|
||||
|
||||
getMid () {
|
||||
return this.mid || 0.
|
||||
}
|
||||
|
||||
getHigh () {
|
||||
return this.high || 0.
|
||||
}
|
||||
|
||||
getLevel () {
|
||||
return this.level || 0.
|
||||
}
|
||||
|
||||
getHistory () {
|
||||
return this.history || 0.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export class DeviceChecker {
|
||||
isRetina () {
|
||||
const m = window.matchMedia('only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 1.3dppx)')
|
||||
return (m && m.matches || (window.devicePixelRatio > 1))
|
||||
}
|
||||
}
|
325
resources/assets/js/visualizers/asteroid/scripts/NoiseBlob.ts
Normal file
|
@ -0,0 +1,325 @@
|
|||
import * as THREE from 'three'
|
||||
import { ThreeSharedRenderer } from './ThreeSharedRenderer'
|
||||
import { ThreePBR } from './ThreePBR'
|
||||
import { ThreePointLight } from './ThreePointLight'
|
||||
import { AudioAnalyzer } from './AudioAnalyzer'
|
||||
import { nx, nx3js, ny, ny3js, nz, nz3js, px, px3js, py, py3js, pz, pz3js, rect } from '../assets'
|
||||
import { blobFrag, blobVert, skyboxFrag, skyboxVert } from '../shaders'
|
||||
|
||||
export class NoiseBlob {
|
||||
private textSprite!: THREE.Texture
|
||||
private renderer: ThreeSharedRenderer
|
||||
private isInit: boolean
|
||||
private readonly showHdr: boolean
|
||||
private w: number
|
||||
private h: number
|
||||
private shaderCubeMap!: THREE.ShaderMaterial
|
||||
private pbr!: ThreePBR
|
||||
private scene!: THREE.Scene
|
||||
private shadowScene!: THREE.Scene
|
||||
private shaderMesh!: THREE.ShaderMaterial
|
||||
private shaderWire!: THREE.ShaderMaterial
|
||||
private shaderPoints!: THREE.ShaderMaterial
|
||||
private shaderShadow!: THREE.ShaderMaterial
|
||||
private shaderPopPoints!: THREE.ShaderMaterial
|
||||
private shaderPopWire!: THREE.ShaderMaterial
|
||||
private shaderPopPointsOut!: THREE.ShaderMaterial
|
||||
private shaderPopWireOut!: THREE.ShaderMaterial
|
||||
private light: ThreePointLight
|
||||
private cubeMapB!: THREE.CubeTexture
|
||||
private cubeMap!: THREE.CubeTexture
|
||||
private analyzer: AudioAnalyzer
|
||||
private timer = 0
|
||||
|
||||
constructor (renderer: ThreeSharedRenderer, analyzer: AudioAnalyzer, light: ThreePointLight) {
|
||||
this.renderer = renderer
|
||||
this.analyzer = analyzer
|
||||
this.light = light
|
||||
|
||||
this.isInit = false
|
||||
this.showHdr = true
|
||||
|
||||
this.w = renderer.container.clientWidth
|
||||
this.h = renderer.container.clientHeight
|
||||
|
||||
this.initTexture()
|
||||
this.initShader()
|
||||
this.initScene()
|
||||
this.initCubeMap()
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.renderer.destroy()
|
||||
this.light.destroy()
|
||||
|
||||
this.textSprite?.dispose()
|
||||
this.shaderCubeMap?.dispose()
|
||||
this.shaderMesh?.dispose()
|
||||
this.shaderWire?.dispose()
|
||||
this.shaderPoints?.dispose()
|
||||
this.shaderShadow?.dispose()
|
||||
this.shaderPopPoints?.dispose()
|
||||
this.shaderPopWire?.dispose()
|
||||
this.shaderPopPointsOut?.dispose()
|
||||
this.shaderPopWireOut?.dispose()
|
||||
this.cubeMapB?.dispose()
|
||||
this.cubeMap?.dispose()
|
||||
}
|
||||
|
||||
initTexture () {
|
||||
this.textSprite = new THREE.TextureLoader().load(rect)
|
||||
this.textSprite.wrapS = THREE.ClampToEdgeWrapping
|
||||
this.textSprite.wrapT = THREE.ClampToEdgeWrapping
|
||||
this.textSprite.magFilter = THREE.LinearFilter
|
||||
this.textSprite.minFilter = THREE.LinearFilter
|
||||
}
|
||||
|
||||
initShader () {
|
||||
const screenRes = 'vec2( ' + this.w.toFixed(1) + ', ' + this.h.toFixed(1) + ')'
|
||||
|
||||
const load = (_vert, _frag) => new THREE.ShaderMaterial({
|
||||
defines: {
|
||||
SCREEN_RES: screenRes
|
||||
},
|
||||
uniforms: {
|
||||
u_t: { value: 0 },
|
||||
u_is_init: { value: false },
|
||||
u_audio_high: { value: 0. },
|
||||
u_audio_mid: { value: 0. },
|
||||
u_audio_bass: { value: 0. },
|
||||
u_audio_level: { value: 0. },
|
||||
u_audio_history: { value: 0. }
|
||||
},
|
||||
vertexShader: _vert,
|
||||
fragmentShader: _frag
|
||||
})
|
||||
|
||||
this.shaderCubeMap = new THREE.ShaderMaterial(
|
||||
{
|
||||
defines: {
|
||||
SCREEN_RES: screenRes
|
||||
},
|
||||
uniforms: {
|
||||
u_cubemap: { value: this.cubeMap },
|
||||
u_cubemap_b: { value: this.cubeMapB },
|
||||
u_exposure: { value: 2. },
|
||||
u_gamma: { value: 2.2 }
|
||||
},
|
||||
vertexShader: skyboxVert,
|
||||
fragmentShader: skyboxFrag
|
||||
})
|
||||
|
||||
this.shaderMesh = load(blobVert, blobFrag)
|
||||
this.shaderWire = load(blobVert, blobFrag)
|
||||
this.shaderPoints = load(blobVert, blobFrag)
|
||||
this.shaderShadow = load(blobVert, blobFrag)
|
||||
this.shaderPopPoints = load(blobVert, blobFrag)
|
||||
this.shaderPopWire = load(blobVert, blobFrag)
|
||||
this.shaderPopPointsOut = load(blobVert, blobFrag)
|
||||
this.shaderPopWireOut = load(blobVert, blobFrag)
|
||||
|
||||
this.shaderMesh.extensions.derivatives = true
|
||||
|
||||
this.shaderMesh.defines.IS_MESH = 'true'
|
||||
this.shaderMesh.defines.HAS_SHADOW = 'true'
|
||||
this.shaderWire.defines.IS_WIRE = 'true'
|
||||
this.shaderPoints.defines.IS_POINTS = 'true'
|
||||
this.shaderShadow.defines.IS_SHADOW = 'true'
|
||||
this.shaderPopPoints.defines.IS_POINTS = 'true'
|
||||
this.shaderPopPoints.defines.IS_POP = 'true'
|
||||
this.shaderPopWire.defines.IS_WIRE = 'true'
|
||||
this.shaderPopWire.defines.IS_POP = 'true'
|
||||
this.shaderPopPointsOut.defines.IS_POINTS = 'true'
|
||||
this.shaderPopPointsOut.defines.IS_POP_OUT = 'true'
|
||||
this.shaderPopWireOut.defines.IS_WIRE = 'true'
|
||||
this.shaderPopWireOut.defines.IS_POP_OUT = 'true'
|
||||
|
||||
const lightPosition = this.light.getLightPosition()
|
||||
lightPosition.applyMatrix4(this.renderer.camera.modelViewMatrix)
|
||||
|
||||
const shadowMatrix = new THREE.Matrix4()
|
||||
shadowMatrix.identity()
|
||||
shadowMatrix.multiplyMatrices(
|
||||
this.light.getLight().projectionMatrix,
|
||||
this.light.getLight().modelViewMatrix
|
||||
)
|
||||
|
||||
this.shaderMesh.uniforms.u_light_pos = { value: lightPosition }
|
||||
this.shaderMesh.uniforms.u_shadow_matrix = { value: shadowMatrix }
|
||||
this.shaderMesh.uniforms.u_shadow_map = { value: this.light.getShadowMap() }
|
||||
this.shaderMesh.uniforms.u_debug_shadow = { value: false }
|
||||
this.shaderPoints.uniforms.textSprite = { value: this.textSprite }
|
||||
this.shaderPopPoints.uniforms.textSprite = { value: this.textSprite }
|
||||
this.shaderPopWire.uniforms.textSprite = { value: this.textSprite }
|
||||
this.shaderPopPointsOut.uniforms.textSprite = { value: this.textSprite }
|
||||
this.shaderPopWireOut.uniforms.textSprite = { value: this.textSprite }
|
||||
|
||||
this.shaderPoints.blending = THREE.AdditiveBlending
|
||||
this.shaderWire.blending = THREE.AdditiveBlending
|
||||
this.shaderPopPoints.blending = THREE.AdditiveBlending
|
||||
this.shaderPopWire.blending = THREE.AdditiveBlending
|
||||
this.shaderPopPointsOut.blending = THREE.AdditiveBlending
|
||||
this.shaderPopWireOut.blending = THREE.AdditiveBlending
|
||||
|
||||
this.shaderWire.transparent = true
|
||||
this.shaderPoints.transparent = true
|
||||
this.shaderPopPoints.transparent = true
|
||||
this.shaderPopWire.transparent = true
|
||||
this.shaderPopPointsOut.transparent = true
|
||||
this.shaderPopWireOut.transparent = true
|
||||
|
||||
this.shaderWire.depthTest = false
|
||||
this.shaderPoints.depthTest = false
|
||||
this.shaderPopPoints.depthTest = false
|
||||
this.shaderPopWire.depthTest = false
|
||||
this.shaderPopPointsOut.depthTest = false
|
||||
this.shaderPopWireOut.depthTest = false
|
||||
}
|
||||
|
||||
initScene () {
|
||||
const _sphere_size = .7
|
||||
const _geom = new THREE.SphereGeometry(_sphere_size, 128, 128)
|
||||
const _geom_lowres = new THREE.SphereGeometry(_sphere_size, 64, 64)
|
||||
|
||||
this.scene = new THREE.Scene()
|
||||
this.shadowScene = new THREE.Scene()
|
||||
|
||||
const _mesh = new THREE.Mesh(_geom, this.shaderMesh)
|
||||
const _wire = new THREE.Line(_geom_lowres, this.shaderWire)
|
||||
const _points = new THREE.Points(_geom, this.shaderPoints)
|
||||
const _shadow_mesh = new THREE.Mesh(_geom, this.shaderShadow)
|
||||
|
||||
const _pop_points = new THREE.Points(_geom_lowres, this.shaderPopPoints)
|
||||
const _pop_wire = new THREE.Line(_geom_lowres, this.shaderPopWire)
|
||||
|
||||
const _pop_points_out = new THREE.Points(_geom_lowres, this.shaderPopPointsOut)
|
||||
const _pop_wire_out = new THREE.Line(_geom_lowres, this.shaderPopWireOut)
|
||||
|
||||
this.scene.add(_mesh)
|
||||
this.scene.add(_wire)
|
||||
this.scene.add(_points)
|
||||
|
||||
this.scene.add(_pop_points)
|
||||
this.scene.add(_pop_wire)
|
||||
this.scene.add(_pop_points_out)
|
||||
this.scene.add(_pop_wire_out)
|
||||
|
||||
this.shadowScene.add(_shadow_mesh)
|
||||
|
||||
const boxGeometry = new THREE.BoxGeometry(100, 100, 100)
|
||||
const mesh = new THREE.Mesh(boxGeometry, this.shaderCubeMap)
|
||||
|
||||
const mS = (new THREE.Matrix4()).identity()
|
||||
mS.elements[0] = -1
|
||||
mS.elements[5] = -1
|
||||
mS.elements[10] = -1
|
||||
|
||||
boxGeometry.applyMatrix4(mS)
|
||||
|
||||
this.scene.add(mesh)
|
||||
}
|
||||
|
||||
initCubeMap () {
|
||||
this.cubeMap = new THREE.CubeTextureLoader().load([
|
||||
px3js, nx3js,
|
||||
py3js, ny3js,
|
||||
pz3js, nz3js
|
||||
])
|
||||
|
||||
this.cubeMap.format = THREE.RGBAFormat
|
||||
|
||||
this.cubeMapB = new THREE.CubeTextureLoader().load([
|
||||
px, nx,
|
||||
py, ny,
|
||||
pz, nz
|
||||
])
|
||||
|
||||
this.cubeMapB.format = THREE.RGBAFormat
|
||||
|
||||
this.shaderMesh.uniforms.cubemap = { value: this.cubeMap }
|
||||
this.shaderCubeMap.uniforms.u_cubemap.value = this.cubeMap
|
||||
this.shaderMesh.uniforms.cubemap_b = { value: this.cubeMapB }
|
||||
this.shaderCubeMap.uniforms.u_cubemap_b.value = this.cubeMapB
|
||||
this.shaderCubeMap.uniforms.u_show_cubemap = { value: this.showHdr }
|
||||
this.shaderMesh.defines.HAS_CUBEMAP = 'true'
|
||||
}
|
||||
|
||||
updateCubeMap () {
|
||||
const crossFader = 0.
|
||||
this.shaderMesh.uniforms.cross_fader = { value: crossFader }
|
||||
this.shaderCubeMap.uniforms.cross_fader = { value: crossFader }
|
||||
|
||||
this.shaderCubeMap.uniforms.u_exposure.value = this.pbr.getExposure()
|
||||
this.shaderCubeMap.uniforms.u_gamma.value = this.pbr.getGamma()
|
||||
}
|
||||
|
||||
update () {
|
||||
const shaders = [
|
||||
this.shaderMesh,
|
||||
this.shaderWire,
|
||||
this.shaderPoints,
|
||||
this.shaderPopPoints,
|
||||
this.shaderPopWire,
|
||||
this.shaderPopPointsOut,
|
||||
this.shaderPopWireOut,
|
||||
this.shaderShadow
|
||||
]
|
||||
|
||||
for (let i = 0; i < shaders.length; i++) {
|
||||
shaders[i].uniforms.u_is_init.value = this.isInit
|
||||
shaders[i].uniforms.u_t.value = this.timer
|
||||
|
||||
shaders[i].uniforms.u_audio_high.value = this.analyzer.getHigh()
|
||||
shaders[i].uniforms.u_audio_mid.value = this.analyzer.getMid()
|
||||
shaders[i].uniforms.u_audio_bass.value = this.analyzer.getBass()
|
||||
shaders[i].uniforms.u_audio_level.value = this.analyzer.getLevel()
|
||||
shaders[i].uniforms.u_audio_history.value = this.analyzer.getHistory()
|
||||
}
|
||||
|
||||
this.updateCubeMap()
|
||||
|
||||
const _cam = this.renderer.getCamera()
|
||||
this.renderer.renderer.render(this.scene, _cam)
|
||||
|
||||
if (!this.isInit) {
|
||||
this.isInit = true
|
||||
}
|
||||
|
||||
this.timer = this.renderer.getTimer()
|
||||
}
|
||||
|
||||
setRetina () {
|
||||
this.w *= .5
|
||||
this.h *= .5
|
||||
}
|
||||
|
||||
setPBR (pbr: ThreePBR) {
|
||||
this.pbr = pbr
|
||||
|
||||
this.shaderMesh.uniforms.tex_normal = { value: this.pbr.getNormalMap() }
|
||||
this.shaderMesh.uniforms.tex_roughness = { value: this.pbr.getRoughnessMap() }
|
||||
this.shaderMesh.uniforms.tex_metallic = { value: this.pbr.getMetallicMap() }
|
||||
|
||||
this.shaderMesh.uniforms.u_normal = { value: this.pbr.getNormal() }
|
||||
this.shaderMesh.uniforms.u_roughness = { value: this.pbr.getRoughness() }
|
||||
this.shaderMesh.uniforms.u_metallic = { value: this.pbr.getMetallic() }
|
||||
|
||||
this.shaderMesh.uniforms.u_exposure = { value: this.pbr.getExposure() }
|
||||
this.shaderMesh.uniforms.u_gamma = { value: this.pbr.getGamma() }
|
||||
|
||||
this.shaderMesh.uniforms.u_view_matrix_inverse = { value: this.renderer.getInverseMatrix() }
|
||||
|
||||
this.shaderMesh.defines.IS_PBR = 'true'
|
||||
}
|
||||
|
||||
updatePBR () {
|
||||
this.shaderMesh.uniforms.u_normal.value = this.pbr.getNormal()
|
||||
this.shaderMesh.uniforms.u_roughness.value = this.pbr.getRoughness()
|
||||
this.shaderMesh.uniforms.u_metallic.value = this.pbr.getMetallic()
|
||||
|
||||
this.shaderMesh.uniforms.u_exposure.value = this.pbr.getExposure()
|
||||
this.shaderMesh.uniforms.u_gamma.value = this.pbr.getGamma()
|
||||
|
||||
this.shaderMesh.uniforms.u_view_matrix_inverse.value = this.renderer.getInverseMatrix()
|
||||
}
|
||||
}
|
65
resources/assets/js/visualizers/asteroid/scripts/ThreePBR.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import * as THREE from 'three'
|
||||
import { metallicTexture, normalTexture, roughnessTexture } from '../assets'
|
||||
|
||||
export class ThreePBR {
|
||||
private readonly normalMap: THREE.Texture
|
||||
private readonly roughnessMap: THREE.Texture
|
||||
private readonly metallicMap: THREE.Texture
|
||||
private readonly normal = 1.
|
||||
private readonly roughness = .0
|
||||
private readonly metallic = 1.
|
||||
private readonly exposure = 2.
|
||||
private readonly gamma = 2.2
|
||||
|
||||
constructor () {
|
||||
this.normalMap = new THREE.TextureLoader().load(normalTexture)
|
||||
this.normalMap.wrapS = THREE.ClampToEdgeWrapping
|
||||
this.normalMap.wrapT = THREE.ClampToEdgeWrapping
|
||||
this.normalMap.magFilter = THREE.LinearFilter
|
||||
this.normalMap.minFilter = THREE.LinearFilter
|
||||
|
||||
this.roughnessMap = new THREE.TextureLoader().load(roughnessTexture)
|
||||
this.roughnessMap.wrapS = THREE.ClampToEdgeWrapping
|
||||
this.roughnessMap.wrapT = THREE.ClampToEdgeWrapping
|
||||
this.roughnessMap.magFilter = THREE.LinearFilter
|
||||
this.roughnessMap.minFilter = THREE.LinearFilter
|
||||
|
||||
this.metallicMap = new THREE.TextureLoader().load(metallicTexture)
|
||||
this.metallicMap.wrapS = THREE.ClampToEdgeWrapping
|
||||
this.metallicMap.wrapT = THREE.ClampToEdgeWrapping
|
||||
this.metallicMap.magFilter = THREE.LinearFilter
|
||||
this.metallicMap.minFilter = THREE.LinearFilter
|
||||
}
|
||||
|
||||
getNormalMap () {
|
||||
return this.normalMap
|
||||
}
|
||||
|
||||
getRoughnessMap () {
|
||||
return this.roughnessMap
|
||||
}
|
||||
|
||||
getMetallicMap () {
|
||||
return this.metallicMap
|
||||
}
|
||||
|
||||
getExposure () {
|
||||
return this.exposure
|
||||
}
|
||||
|
||||
getGamma () {
|
||||
return this.gamma
|
||||
}
|
||||
|
||||
getNormal () {
|
||||
return this.normal
|
||||
}
|
||||
|
||||
getRoughness () {
|
||||
return this.roughness
|
||||
}
|
||||
|
||||
getMetallic () {
|
||||
return this.metallic
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import * as THREE from 'three'
|
||||
|
||||
export class ThreePointLight {
|
||||
private readonly shadowBuffer: THREE.WebGLRenderTarget
|
||||
private readonly light: THREE.PerspectiveCamera
|
||||
|
||||
constructor () {
|
||||
this.shadowBuffer = new THREE.WebGLRenderTarget(2048., 2048.)
|
||||
this.shadowBuffer.depthBuffer = true
|
||||
this.shadowBuffer.depthTexture = new THREE.DepthTexture(0, 0)
|
||||
|
||||
this.light = new THREE.PerspectiveCamera(35., this.shadowBuffer.width / this.shadowBuffer.height, .1, 1000.)
|
||||
this.light.lookAt(0., 0., 0.)
|
||||
}
|
||||
|
||||
ziggle (frame: number) {
|
||||
const e = frame * 10.
|
||||
|
||||
this.light.position.copy(new THREE.Vector3(
|
||||
this.light.position.x * Math.sin(e),
|
||||
this.light.position.y,
|
||||
this.light.position.z * Math.cos(e)
|
||||
))
|
||||
|
||||
this.light.lookAt(0., 0., 0.)
|
||||
this.light.updateProjectionMatrix()
|
||||
}
|
||||
|
||||
getLight () {
|
||||
return this.light
|
||||
}
|
||||
|
||||
getLightPosition () {
|
||||
return this.light.position
|
||||
}
|
||||
|
||||
getShadowMap () {
|
||||
return this.shadowBuffer.depthTexture
|
||||
}
|
||||
|
||||
destroy () {
|
||||
this.shadowBuffer.dispose()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
import * as THREE from 'three'
|
||||
|
||||
export class ThreeSharedRenderer {
|
||||
public readonly camera: THREE.PerspectiveCamera
|
||||
private timer: number
|
||||
public renderer!: THREE.WebGLRenderer
|
||||
public container: HTMLElement
|
||||
private readonly resizeHandler: () => void
|
||||
|
||||
constructor (container: HTMLElement) {
|
||||
this.container = container
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera(45, this.container.clientWidth / this.container.clientHeight, .1, 100)
|
||||
this.camera.position.z = 5
|
||||
this.camera.updateProjectionMatrix()
|
||||
|
||||
this.timer = 0
|
||||
|
||||
this.initRenderer()
|
||||
|
||||
this.resizeHandler = this.resize.bind(this)
|
||||
window.addEventListener('resize', this.resizeHandler, false)
|
||||
}
|
||||
|
||||
resize () {
|
||||
this.camera.aspect = this.container.clientWidth / this.container.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
|
||||
}
|
||||
|
||||
initRenderer () {
|
||||
this.renderer = new THREE.WebGLRenderer()
|
||||
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio)
|
||||
this.renderer.setSize(this.container.clientWidth, this.container.clientHeight)
|
||||
|
||||
this.renderer.autoClear = true
|
||||
this.renderer.shadowMap.enabled = true
|
||||
this.renderer.shadowMap.type = THREE.PCFShadowMap
|
||||
|
||||
this.container.appendChild(this.renderer.domElement)
|
||||
}
|
||||
|
||||
render (queue: Closure[]) {
|
||||
for (let i = 0; i < queue.length; i++) {
|
||||
this.renderer.clearDepth()
|
||||
queue[i]()
|
||||
}
|
||||
|
||||
this.timer += .001
|
||||
|
||||
if (this.timer > 999999.) {
|
||||
this.timer = 0.
|
||||
}
|
||||
}
|
||||
|
||||
ziggleCam (frame: number) {
|
||||
const e = frame
|
||||
const nLoc = new THREE.Vector3(
|
||||
Math.sin(e),
|
||||
Math.cos(e * .9) * Math.sin(e * .7),
|
||||
Math.cos(e)).normalize()
|
||||
|
||||
nLoc.multiplyScalar(8. + 2. * Math.sin(2. * e))
|
||||
|
||||
this.camera.position.copy(nLoc)
|
||||
this.camera.lookAt(0., 0., 0.)
|
||||
this.camera.updateProjectionMatrix()
|
||||
}
|
||||
|
||||
getInverseMatrix () {
|
||||
return this.camera.matrixWorldInverse
|
||||
}
|
||||
|
||||
getTimer () {
|
||||
return this.timer == undefined ? 0. : this.timer
|
||||
}
|
||||
|
||||
getCamera () {
|
||||
return this.camera
|
||||
}
|
||||
|
||||
destroy () {
|
||||
window.removeEventListener('resize', this.resizeHandler, false)
|
||||
this.renderer.dispose()
|
||||
}
|
||||
}
|
58
resources/assets/js/visualizers/asteroid/scripts/index.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { ThreeSharedRenderer } from './ThreeSharedRenderer'
|
||||
import { ThreePBR } from './ThreePBR'
|
||||
import { ThreePointLight } from './ThreePointLight'
|
||||
import { NoiseBlob } from './NoiseBlob'
|
||||
import { AudioAnalyzer } from './AudioAnalyzer'
|
||||
import { DeviceChecker } from './DeviceChecker'
|
||||
|
||||
let analyzer: AudioAnalyzer
|
||||
let renderer: ThreeSharedRenderer
|
||||
let renderQueue
|
||||
let blob
|
||||
let pbr
|
||||
let light
|
||||
let deviceChecker
|
||||
|
||||
export const init = (container: HTMLElement) => {
|
||||
deviceChecker = new DeviceChecker()
|
||||
const isRetina = deviceChecker.isRetina()
|
||||
|
||||
analyzer = new AudioAnalyzer()
|
||||
renderer = new ThreeSharedRenderer(container)
|
||||
|
||||
pbr = new ThreePBR()
|
||||
light = new ThreePointLight()
|
||||
|
||||
blob = new NoiseBlob(renderer, analyzer, light)
|
||||
blob.setPBR(pbr)
|
||||
|
||||
isRetina && blob.setRetina()
|
||||
|
||||
renderQueue = [
|
||||
blob.update.bind(blob)
|
||||
]
|
||||
|
||||
update()
|
||||
|
||||
return () => {
|
||||
// this will destroy the renderers and related class instances
|
||||
blob.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
const update = () => {
|
||||
requestAnimationFrame(update)
|
||||
|
||||
analyzer.update()
|
||||
|
||||
// update blob
|
||||
blob.updatePBR()
|
||||
|
||||
// update pbr
|
||||
pbr.exposure = 5. + 30. * analyzer.getLevel()
|
||||
|
||||
light.ziggle(renderer.getTimer())
|
||||
renderer.ziggleCam(renderer.getTimer())
|
||||
|
||||
renderer.render(renderQueue)
|
||||
}
|
300
resources/assets/js/visualizers/asteroid/shaders/blob.frag.ts
Normal file
|
@ -0,0 +1,300 @@
|
|||
export const blobFrag =
|
||||
`
|
||||
|
||||
varying float v_noise;
|
||||
|
||||
uniform float u_audio_high;
|
||||
uniform float u_audio_mid;
|
||||
uniform float u_audio_bass;
|
||||
uniform float u_audio_level;
|
||||
uniform float u_audio_history;
|
||||
|
||||
vec3 norm(in vec3 _v){
|
||||
return length(_v) > .0 ? normalize(_v) : vec3(.0);
|
||||
}
|
||||
|
||||
#if defined(IS_POINTS)
|
||||
uniform sampler2D tex_sprite;
|
||||
#endif
|
||||
|
||||
#if defined(IS_PBR) && defined(HAS_CUBEMAP)
|
||||
uniform samplerCube cubemap;
|
||||
uniform samplerCube cubemap_b;
|
||||
uniform float cross_fader;
|
||||
|
||||
uniform sampler2D tex_normal;
|
||||
uniform sampler2D tex_roughness;
|
||||
uniform sampler2D tex_metallic;
|
||||
|
||||
uniform float u_normal;
|
||||
uniform float u_roughness;
|
||||
uniform float u_metallic;
|
||||
uniform float u_exposure;
|
||||
uniform float u_gamma;
|
||||
|
||||
varying vec3 v_world_normal;
|
||||
varying vec3 v_object_pos;
|
||||
varying vec3 v_eye_pos;
|
||||
varying vec3 v_pos;
|
||||
varying vec3 v_normal;
|
||||
varying vec3 v_world_pos;
|
||||
varying vec2 v_uv;
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
|
||||
// Filmic tonemapping from
|
||||
// http://filmicgames.com/archives/75
|
||||
const float A = 0.15;
|
||||
const float B = 0.50;
|
||||
const float C = 0.10;
|
||||
const float D = 0.20;
|
||||
const float E = 0.02;
|
||||
const float F = 0.30;
|
||||
|
||||
vec3 Uncharted2Tonemap( vec3 x )
|
||||
{
|
||||
return ((x*(A*x+C*B)+D*E)/((x*(A*x+B)+D*F) + .00001))-E/F;
|
||||
}
|
||||
|
||||
// https://www.unrealengine.com/blog/physically-based-shading-on-mobile
|
||||
vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV )
|
||||
{
|
||||
const vec4 c0 = vec4( -1, -0.0275, -0.572, 0.022 );
|
||||
const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04 );
|
||||
vec4 r = Roughness * c0 + c1;
|
||||
float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
|
||||
vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
|
||||
return SpecularColor * AB.x + AB.y;
|
||||
}
|
||||
|
||||
|
||||
// http://the-witness.net/news/2012/02/seamless-cube-map-filtering/
|
||||
vec3 fix_cube_lookup( vec3 v, float cube_size, float lod ) {
|
||||
float M = max(max(abs(v.x), abs(v.y)), abs(v.z));
|
||||
float scale = 1. - exp2(lod) / (cube_size + .00001);
|
||||
if (abs(v.x) != M) v.x *= scale;
|
||||
if (abs(v.y) != M) v.y *= scale;
|
||||
if (abs(v.z) != M) v.z *= scale;
|
||||
return v;
|
||||
}
|
||||
|
||||
// Normal Blending
|
||||
// Source adapted from http://blog.selfshadow.com/publications/blending-in-detail/
|
||||
vec3 blendNormalsUnity( vec3 baseNormal, vec3 detailsNormal )
|
||||
{
|
||||
vec3 n1 = baseNormal;
|
||||
vec3 n2 = detailsNormal;
|
||||
mat3 nBasis = mat3(
|
||||
vec3(n1.z, n1.y, -n1.x), // +90 degree rotation around y axis
|
||||
vec3(n1.x, n1.z, -n1.y), // -90 degree rotation around x axis
|
||||
vec3(n1.x, n1.y, n1.z));
|
||||
return norm(n2.x*nBasis[0] + n2.y*nBasis[1] + n2.z*nBasis[2]);
|
||||
}
|
||||
vec3 blendNormals( vec3 n1, vec3 n2 )
|
||||
{
|
||||
return blendNormalsUnity( n1, n2 );
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
uniform sampler2D u_shadow_map;
|
||||
uniform vec3 u_light_pos;
|
||||
uniform bool u_debug_shadow;
|
||||
varying vec4 v_shadow_coord;
|
||||
|
||||
float sample_shadow( vec4 sc )
|
||||
{
|
||||
float s = 1./1024.;
|
||||
|
||||
vec2 unproj2D = vec2 (sc.s / (sc.q + .00001),
|
||||
sc.t / (sc.q + .00001));
|
||||
|
||||
float shadow = 0.0;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2(-s,-s) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2(-s, 0.) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2(-s, s) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( 0.,-s) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( 0., 0.) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( 0., s) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( s,-s) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( s, 0.) ).r;
|
||||
shadow += texture2D( u_shadow_map, unproj2D + vec2( s, s) ).r;
|
||||
|
||||
return shadow/9.0;;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void main(){
|
||||
float m_noise = v_noise;
|
||||
float m_noise_inv = 1.-v_noise;
|
||||
|
||||
vec3 m_diffuse = vec3(0.);
|
||||
m_diffuse.r += m_noise_inv + m_noise;
|
||||
m_diffuse.g += m_noise*1.5;
|
||||
//m_diffuse.b += m_noise;
|
||||
m_diffuse -= pow(abs(1.-m_noise), 4.)*.95; //<- darken peak
|
||||
m_diffuse = clamp(m_diffuse, vec3(0.), vec3(2.));
|
||||
|
||||
m_diffuse *= pow(u_audio_level, 2.);
|
||||
|
||||
vec3 m_col = m_diffuse;
|
||||
|
||||
|
||||
#if defined(IS_SHADOW)
|
||||
gl_FragColor = vec4(m_col, 1.);
|
||||
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(IS_PBR) && defined(HAS_CUBEMAP)
|
||||
vec3 N = norm( v_world_normal );
|
||||
|
||||
// blend with PBR's
|
||||
N = blendNormals( N, texture2D( tex_normal, v_uv ).xyz );
|
||||
|
||||
vec3 V = norm( v_eye_pos );
|
||||
|
||||
// fresnel
|
||||
float m_fresnel = pow(1. + dot(norm(v_world_pos - v_eye_pos), v_world_normal), 8.);
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
// Light direction
|
||||
vec3 L = norm( u_light_pos - v_world_pos.xyz );
|
||||
// Surface reflection vector
|
||||
vec3 R = norm( -reflect( L, N ) );
|
||||
#endif
|
||||
|
||||
// sample the roughness and metallic textures
|
||||
float roughnessMask = texture2D( tex_roughness, v_uv ).r;
|
||||
float metallicMask = texture2D( tex_metallic, v_uv ).r;
|
||||
|
||||
// deduce the diffuse and specular color from the baseColor and how metallic the material is
|
||||
vec3 m_specular_col = vec3(m_diffuse)*8.;
|
||||
vec3 m_diffuse_col = vec3(m_diffuse)*8.;
|
||||
vec3 diffuseColor = m_diffuse_col - m_diffuse_col * u_metallic * metallicMask;
|
||||
vec3 specularColor = mix( vec3( 0.08 * m_specular_col ), m_diffuse_col, u_metallic * metallicMask );
|
||||
|
||||
// sample the pre-filtered cubemap at the corresponding mipmap level
|
||||
int numMips = 6;
|
||||
float mip = float(numMips) - 1. + log2( u_roughness * roughnessMask );
|
||||
vec3 lookup = -reflect( V, N );
|
||||
|
||||
vec3 cube_a_rad = pow( abs(textureCube( cubemap, fix_cube_lookup( lookup, 2048., mip ) ).rgb), vec3( 2.2 ) );
|
||||
vec3 cube_b_rad = pow( abs(textureCube( cubemap_b, fix_cube_lookup( lookup, 2048., mip ) ).rgb), vec3( 2.2 ) );
|
||||
vec3 cube_a_irr = pow( abs(textureCube( cubemap, fix_cube_lookup( N, 2048., 0. ) ).rgb), vec3( 2.2 ) );
|
||||
vec3 cube_b_irr = pow( abs(textureCube( cubemap_b, fix_cube_lookup( N, 2048., 0. ) ).rgb), vec3( 2.2 ) );
|
||||
|
||||
vec3 radiance = mix(cube_a_rad, cube_b_rad, cross_fader);
|
||||
vec3 irradiance = mix(cube_a_irr, cube_b_irr, cross_fader);
|
||||
|
||||
// get the approximate reflectance
|
||||
// float NoV = saturate( dot( N, V ) );
|
||||
float NoV = clamp( dot( N, V ), 0., 1. );
|
||||
vec3 reflectance = EnvBRDFApprox( specularColor, pow( abs(u_roughness * roughnessMask), 4.0 ), NoV );
|
||||
|
||||
// combine the specular IBL and the BRDF
|
||||
vec3 diffuse = diffuseColor * radiance;
|
||||
vec3 specular = radiance * reflectance;
|
||||
m_col = (diffuse + specular)*u_audio_level*(1.-min(m_fresnel, .99));
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
// from light source
|
||||
vec3 m_light_diffuse_color = vec3(m_diffuse)*3.;
|
||||
vec3 m_light_specular_color = vec3(m_diffuse)*3.;
|
||||
float m_light_diffuse_intensity = 30.;
|
||||
float m_light_specular_intensity = 30.;
|
||||
float m_light_diffuse_pow = 150.;
|
||||
float m_light_specular_pow = 120.;
|
||||
|
||||
// Diffuse factor
|
||||
float NdotL = max( dot( N, L ), 0.0 );
|
||||
vec3 D = vec3( NdotL );
|
||||
D = pow(abs(D), vec3(m_light_diffuse_pow));
|
||||
D *= m_light_diffuse_color * m_light_diffuse_intensity;
|
||||
|
||||
// Specular factor
|
||||
vec3 S = pow( max( dot( R, V ), 0.0 ), m_light_specular_pow ) * vec3(1.);
|
||||
S *= m_light_specular_color * m_light_specular_intensity;
|
||||
|
||||
m_col += (D + S)*u_audio_level*(1.-min(m_fresnel, .99));
|
||||
|
||||
// cal shadow
|
||||
float m_shadow = 1.;
|
||||
vec4 m_shadow_coord = v_shadow_coord;
|
||||
m_shadow_coord.z += .0003; // <- bias
|
||||
|
||||
m_shadow = sample_shadow(m_shadow_coord);
|
||||
m_col *= (m_shadow + m_col*.2 + m_diffuse*.5);
|
||||
#endif
|
||||
|
||||
// add noise diffuse
|
||||
m_col += pow(abs(m_diffuse), vec3(10.))*8.;
|
||||
|
||||
// apply the tone-mapping
|
||||
m_col = Uncharted2Tonemap( m_col * u_exposure );
|
||||
// white balance
|
||||
m_col = m_col * ( 1. / (Uncharted2Tonemap( vec3( 20. ) ) + .00001) );
|
||||
|
||||
// gamma correction
|
||||
m_col = pow( abs(m_col), vec3( 1. / (u_gamma + .00001) ) );
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(IS_WIRE) || defined(IS_POINTS)
|
||||
m_col.b -= m_col.g;
|
||||
|
||||
// inner ziggle
|
||||
m_col *= .4 * pow(abs(m_noise), 6.);
|
||||
|
||||
// outter ziggle
|
||||
m_col.rg += .2 * pow(abs(m_noise_inv), 4.);
|
||||
m_col.g *= .5;
|
||||
|
||||
// treble burn
|
||||
m_col += pow(abs(u_audio_high), 3.) * 1.;
|
||||
|
||||
// on&off
|
||||
m_col *= u_audio_level;
|
||||
|
||||
#if defined(IS_WIRE)
|
||||
m_col *= .7;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
gl_FragColor = vec4(m_col, 1.);
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
if(u_debug_shadow)
|
||||
gl_FragColor = vec4(vec3(m_shadow), 1.);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(IS_POINTS)
|
||||
gl_FragColor *= texture2D(tex_sprite, gl_PointCoord);
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(IS_POP) || defined(IS_POP_OUT)
|
||||
gl_FragColor.rgb = pow(abs(gl_FragColor.rgb), vec3(1.2));
|
||||
|
||||
#if defined(IS_POINTS) && defined(IS_POP)
|
||||
gl_FragColor.rgb *= pow(u_audio_level, 2.);
|
||||
gl_FragColor.rgb *= 50.;
|
||||
#endif
|
||||
|
||||
#if defined(IS_WIRE)
|
||||
gl_FragColor.rgb *= 2.;
|
||||
|
||||
#if defined(IS_POP_OUT)
|
||||
gl_FragColor.rgb *= .2;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
`
|
266
resources/assets/js/visualizers/asteroid/shaders/blob.vert.ts
Normal file
|
@ -0,0 +1,266 @@
|
|||
export const blobVert =
|
||||
`
|
||||
uniform vec2 u_mouse;
|
||||
uniform vec2 u_mouse_delta;
|
||||
uniform float u_t;
|
||||
uniform bool u_is_init;
|
||||
|
||||
uniform float u_audio_high;
|
||||
uniform float u_audio_mid;
|
||||
uniform float u_audio_bass;
|
||||
uniform float u_audio_level;
|
||||
uniform float u_audio_history;
|
||||
|
||||
varying float v_noise;
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(IS_PBR) && defined(HAS_CUBEMAP)
|
||||
uniform mat4 u_view_matrix_inverse;
|
||||
|
||||
varying vec3 v_world_normal;
|
||||
varying vec3 v_eye_pos;
|
||||
varying vec3 v_object_pos;
|
||||
varying vec3 v_pos;
|
||||
varying vec3 v_normal;
|
||||
varying vec3 v_world_pos;
|
||||
varying vec2 v_uv;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
uniform mat4 u_shadow_matrix;
|
||||
varying vec4 v_shadow_coord;
|
||||
|
||||
const mat4 biasMat = mat4( 0.5, 0.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0 );
|
||||
#endif
|
||||
|
||||
|
||||
// (Keijiro) This shader was slightly modified from the original version.
|
||||
// It's recommended to use the original version for other purposes.
|
||||
|
||||
//
|
||||
// Description : Array and textureless GLSL 2D/3D/4D simplex
|
||||
// noise functions.
|
||||
// Author : Ian McEwan, Ashima Arts.
|
||||
// Maintainer : ijm
|
||||
// Lastmod : 20110822 (ijm)
|
||||
// License : Copyright (C) 2011 Ashima Arts. All rights reserved.
|
||||
// Distributed under the MIT License. See LICENSE file.
|
||||
// https://github.com/ashima/webgl-noise
|
||||
//
|
||||
|
||||
vec3 mod289(vec3 x)
|
||||
{
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec4 mod289(vec4 x) {
|
||||
return x - floor(x * (1.0 / 289.0)) * 289.0;
|
||||
}
|
||||
|
||||
vec4 permute(vec4 x)
|
||||
{
|
||||
return mod289((x * 34.0 + 1.0) * x);
|
||||
}
|
||||
|
||||
vec4 taylorInvSqrt(vec4 r)
|
||||
{
|
||||
return 1.79284291400159 - 0.85373472095314 * r;
|
||||
}
|
||||
|
||||
float snoise(vec3 v)
|
||||
{
|
||||
const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
|
||||
|
||||
// First corner
|
||||
vec3 i = floor(v + dot(v, C.yyy));
|
||||
vec3 x0 = v - i + dot(i, C.xxx);
|
||||
|
||||
// Other corners
|
||||
vec3 g = step(x0.yzx, x0.xyz);
|
||||
vec3 l = 1.0 - g;
|
||||
vec3 i1 = min(g.xyz, l.zxy);
|
||||
vec3 i2 = max(g.xyz, l.zxy);
|
||||
|
||||
// x1 = x0 - i1 + 1.0 * C.xxx;
|
||||
// x2 = x0 - i2 + 2.0 * C.xxx;
|
||||
// x3 = x0 - 1.0 + 3.0 * C.xxx;
|
||||
vec3 x1 = x0 - i1 + C.xxx;
|
||||
vec3 x2 = x0 - i2 + C.yyy;
|
||||
vec3 x3 = x0 - 0.5;
|
||||
|
||||
// Permutations
|
||||
i = mod289(i); // Avoid truncation effects in permutation
|
||||
vec4 p =
|
||||
permute(permute(permute(i.z + vec4(0.0, i1.z, i2.z, 1.0))
|
||||
+ i.y + vec4(0.0, i1.y, i2.y, 1.0))
|
||||
+ i.x + vec4(0.0, i1.x, i2.x, 1.0));
|
||||
|
||||
// Gradients: 7x7 points over a square, mapped onto an octahedron.
|
||||
// The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
|
||||
vec4 j = p - 49.0 * floor(p * (1.0 / 49.0)); // mod(p,7*7)
|
||||
|
||||
vec4 x_ = floor(j * (1.0 / 7.0));
|
||||
vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
|
||||
|
||||
vec4 x = x_ * (2.0 / 7.0) + 0.5 / 7.0 - 1.0;
|
||||
vec4 y = y_ * (2.0 / 7.0) + 0.5 / 7.0 - 1.0;
|
||||
|
||||
vec4 h = 1.0 - abs(x) - abs(y);
|
||||
|
||||
vec4 b0 = vec4(x.xy, y.xy);
|
||||
vec4 b1 = vec4(x.zw, y.zw);
|
||||
|
||||
//vec4 s0 = vec4(lessThan(b0, 0.0)) * 2.0 - 1.0;
|
||||
//vec4 s1 = vec4(lessThan(b1, 0.0)) * 2.0 - 1.0;
|
||||
vec4 s0 = floor(b0) * 2.0 + 1.0;
|
||||
vec4 s1 = floor(b1) * 2.0 + 1.0;
|
||||
vec4 sh = -step(h, vec4(0.0));
|
||||
|
||||
vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
|
||||
vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
|
||||
|
||||
vec3 g0 = vec3(a0.xy, h.x);
|
||||
vec3 g1 = vec3(a0.zw, h.y);
|
||||
vec3 g2 = vec3(a1.xy, h.z);
|
||||
vec3 g3 = vec3(a1.zw, h.w);
|
||||
|
||||
// Normalise gradients
|
||||
vec4 norm = taylorInvSqrt(vec4(dot(g0, g0), dot(g1, g1), dot(g2, g2), dot(g3, g3)));
|
||||
g0 *= norm.x;
|
||||
g1 *= norm.y;
|
||||
g2 *= norm.z;
|
||||
g3 *= norm.w;
|
||||
|
||||
// Mix final noise value
|
||||
vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0);
|
||||
m = m * m;
|
||||
m = m * m;
|
||||
|
||||
vec4 px = vec4(dot(x0, g0), dot(x1, g1), dot(x2, g2), dot(x3, g3));
|
||||
return (42.0 * dot(m, px) + 1.) * .5;
|
||||
}
|
||||
|
||||
vec3 norm(in vec3 _v){
|
||||
return length(_v) > .0 ? normalize(_v) : vec3(.0);
|
||||
}
|
||||
|
||||
mat4 rotationMatrix(vec3 axis, float angle)
|
||||
{
|
||||
axis = norm(axis);
|
||||
float s = sin(angle);
|
||||
float c = cos(angle);
|
||||
float oc = 1.0 - c;
|
||||
|
||||
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
|
||||
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
|
||||
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
void main(){
|
||||
float m_bass = u_audio_bass;
|
||||
float m_mid = u_audio_mid;
|
||||
float m_high = u_audio_high;
|
||||
float m_level = u_audio_level;
|
||||
float m_history = u_audio_history;
|
||||
|
||||
vec3 m_noise_seed = position.xyz;
|
||||
float m_noise_complexity = .6;
|
||||
float m_noise_time = u_audio_history * .3;
|
||||
float m_noise_scale = 1.2 + m_level;
|
||||
|
||||
vec3 m_tangent_vector = .00001 * norm(cross(position, vec3(1., 0., 0.))
|
||||
+ cross(position, vec3(0., 1., 0.)));
|
||||
vec3 m_bitangent_vector = .00001 * norm(cross(m_tangent_vector, position));
|
||||
|
||||
float m_fbm = 0.;
|
||||
float m_fbm_tangent = 0.;
|
||||
float m_fbm_bitangent = 0.;
|
||||
|
||||
const int m_noise_oct = 5;
|
||||
for(int i = 0; i < m_noise_oct; i++){
|
||||
m_fbm += snoise(
|
||||
(m_noise_seed) * m_noise_complexity * float(i) +
|
||||
m_noise_time * float(i)
|
||||
);
|
||||
m_fbm_tangent += snoise(
|
||||
(m_noise_seed + m_tangent_vector) * m_noise_complexity * float(i) +
|
||||
m_noise_time * float(i)
|
||||
);
|
||||
m_fbm_bitangent += snoise(
|
||||
(m_noise_seed + m_bitangent_vector) * m_noise_complexity * float(i) +
|
||||
m_noise_time * float(i)
|
||||
);
|
||||
}
|
||||
m_fbm /= (float(m_noise_oct));
|
||||
m_fbm_tangent /= (float(m_noise_oct));
|
||||
m_fbm_bitangent /= (float(m_noise_oct));
|
||||
|
||||
vec3 m_pos = position + norm(position) * m_fbm * m_noise_scale;
|
||||
vec3 m_pos_tangent = (position + m_tangent_vector) + norm(position + m_tangent_vector) * m_fbm * m_noise_scale;
|
||||
vec3 m_pos_bitangent = (position + m_bitangent_vector) + norm(position + m_bitangent_vector) * m_fbm * m_noise_scale;
|
||||
|
||||
vec3 m_normal = norm(cross( (m_pos_tangent - m_pos), (m_pos_bitangent - m_pos)));
|
||||
|
||||
|
||||
// get color
|
||||
float m_noise_col = pow(abs(1.-m_fbm), 3.5);
|
||||
v_noise = m_noise_col + m_noise_col * m_level * 2.2;
|
||||
|
||||
// rand direction
|
||||
float _dirx = snoise(m_pos.zyx * 4. + m_noise_time * .01);
|
||||
float _diry = snoise(m_pos.yzx * 4. + m_noise_time * .01);
|
||||
float _dirz = snoise(m_pos.zxy * 4. + m_noise_time * .01);
|
||||
vec3 _rand_point_dir = vec3(_dirx, _diry, _dirz);
|
||||
_rand_point_dir = 1.-2.*_rand_point_dir;
|
||||
|
||||
#if defined(IS_WIRE) || defined(IS_POINTS)
|
||||
// size
|
||||
gl_PointSize = pow(abs(m_fbm), 6.) * 1000. * m_high;
|
||||
|
||||
m_pos += (_rand_point_dir * .3 * m_level);
|
||||
#endif
|
||||
|
||||
#if defined(IS_POP)
|
||||
gl_PointSize *= .5;
|
||||
m_pos *= 1.1 * m_fbm;
|
||||
m_pos = vec3(rotationMatrix(vec3(.3,1.,.2), .5*m_history) * vec4(m_pos, 1.));
|
||||
#endif
|
||||
#if defined(IS_POP_OUT)
|
||||
gl_PointSize *= .5;
|
||||
m_pos *= 1.2;
|
||||
|
||||
m_pos += (_rand_point_dir*_rand_point_dir * .2 * m_high);
|
||||
m_pos = vec3(rotationMatrix(vec3(1.,.2,.3), -.5*m_history) * vec4(m_pos, 1.));
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(IS_PBR) && defined(HAS_CUBEMAP)
|
||||
vec4 _world_pos = modelMatrix * vec4(m_pos, 1.);
|
||||
vec4 _view_pos = viewMatrix * _world_pos;
|
||||
|
||||
v_object_pos = m_pos;
|
||||
v_pos = _view_pos.xyz;
|
||||
v_normal = normalMatrix * m_normal;
|
||||
v_world_pos = _world_pos.xyz;
|
||||
v_world_normal = vec3(u_view_matrix_inverse * vec4(v_normal, 0.));
|
||||
v_eye_pos = -1. * vec3(u_view_matrix_inverse * (_view_pos - vec4(0.,0.,0.,1.)) );
|
||||
v_uv = uv;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SHADOW)
|
||||
v_shadow_coord = (biasMat * u_shadow_matrix) * vec4(m_pos, 1.);
|
||||
#endif
|
||||
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(m_pos, 1.);
|
||||
}
|
||||
`
|
|
@ -0,0 +1,4 @@
|
|||
export * from './skybox.vert'
|
||||
export * from './skybox.frag'
|
||||
export * from './blob.vert'
|
||||
export * from './blob.frag'
|
|
@ -0,0 +1,41 @@
|
|||
export const skyboxFrag =
|
||||
`
|
||||
#define A 0.15
|
||||
#define B 0.50
|
||||
#define C 0.10
|
||||
#define D 0.20
|
||||
#define E 0.02
|
||||
#define F 0.30
|
||||
|
||||
uniform samplerCube u_cubemap;
|
||||
uniform samplerCube u_cubemap_b;
|
||||
uniform float cross_fader;
|
||||
uniform float u_exposure;
|
||||
uniform float u_gamma;
|
||||
uniform bool u_show_cubemap;
|
||||
|
||||
varying vec3 v_direction;
|
||||
|
||||
vec3 Uncharted2Tonemap( vec3 x ){
|
||||
return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
|
||||
}
|
||||
|
||||
void main( void ){
|
||||
vec3 cube_a = pow( abs(textureCube( u_cubemap, v_direction ).rgb), vec3( 2.2 ) );
|
||||
vec3 cube_b = pow( abs(textureCube( u_cubemap_b, v_direction ).rgb), vec3( 2.2 ) );
|
||||
|
||||
vec3 color = mix(cube_a, cube_b, cross_fader);
|
||||
|
||||
// apply the tone-mapping
|
||||
// color = Uncharted2Tonemap( color * u_exposure );
|
||||
// white balance
|
||||
// color = color * ( 1. / Uncharted2Tonemap( vec3( 20. ) ) );
|
||||
|
||||
// gamma correction
|
||||
// color = pow( color, vec3( 1. / u_gamma ) );
|
||||
|
||||
color *= u_show_cubemap ? 1. : 0.;
|
||||
|
||||
gl_FragColor = vec4( color, 1. );
|
||||
}
|
||||
`
|
|
@ -0,0 +1,8 @@
|
|||
export const skyboxVert =
|
||||
`
|
||||
varying vec3 v_direction;
|
||||
void main(){
|
||||
v_direction = position.xyz;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xyz, 1.);
|
||||
}
|
||||
`
|