Files
METH_Transcendence/site/node_modules/three/src/nodes/display/GTAONode.js
Kum1ta 57c261c689 Site
- starting pong game 3d
2024-07-30 01:08:32 +02:00

325 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import TempNode from '../core/TempNode.js';
import { texture } from '../accessors/TextureNode.js';
import { textureSize } from '../accessors/TextureSizeNode.js';
import { uv } from '../accessors/UVNode.js';
import { addNodeElement, nodeObject, tslFn, mat3, vec2, vec3, vec4, float, int, If } from '../shadernode/ShaderNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { DataTexture } from '../../textures/DataTexture.js';
import { Vector2 } from '../../math/Vector2.js';
import { Vector3 } from '../../math/Vector3.js';
import { PI, cos, sin, pow, clamp, abs, max, mix, sqrt, acos, dot, normalize, cross } from '../math/MathNode.js';
import { div, mul, add, sub } from '../math/OperatorNode.js';
import { loop } from '../utils/LoopNode.js';
import { passTexture } from './PassNode.js';
import { RepeatWrapping } from '../../constants.js';
import QuadMesh from '../../renderers/common/QuadMesh.js';
import { RenderTarget } from '../../core/RenderTarget.js';
import { Color } from '../../math/Color.js';
const _quadMesh = /*@__PURE__*/ new QuadMesh();
const _currentClearColor = /*@__PURE__*/ new Color();
const _size = /*@__PURE__*/ new Vector2();
class GTAONode extends TempNode {
constructor( depthNode, normalNode, camera ) {
super();
this.depthNode = depthNode;
this.normalNode = normalNode;
this.radius = uniform( 0.25 );
this.resolution = uniform( new Vector2() );
this.thickness = uniform( 1 );
this.distanceExponent = uniform( 1 );
this.distanceFallOff = uniform( 1 );
this.scale = uniform( 1 );
this.noiseNode = texture( generateMagicSquareNoise() );
this.cameraProjectionMatrix = uniform( camera.projectionMatrix );
this.cameraProjectionMatrixInverse = uniform( camera.projectionMatrixInverse );
this.SAMPLES = uniform( 16 );
this._aoRenderTarget = new RenderTarget();
this._aoRenderTarget.texture.name = 'GTAONode.AO';
this._material = null;
this._textureNode = passTexture( this, this._aoRenderTarget.texture );
this.updateBeforeType = NodeUpdateType.FRAME;
}
getTextureNode() {
return this._textureNode;
}
setSize( width, height ) {
this.resolution.value.set( width, height );
this._aoRenderTarget.setSize( width, height );
}
updateBefore( frame ) {
const { renderer } = frame;
const size = renderer.getDrawingBufferSize( _size );
const currentRenderTarget = renderer.getRenderTarget();
const currentMRT = renderer.getMRT();
renderer.getClearColor( _currentClearColor );
const currentClearAlpha = renderer.getClearAlpha();
_quadMesh.material = this._material;
this.setSize( size.width, size.height );
// clear
renderer.setMRT( null );
renderer.setClearColor( 0xffffff, 1 );
// ao
renderer.setRenderTarget( this._aoRenderTarget );
_quadMesh.render( renderer );
// restore
renderer.setRenderTarget( currentRenderTarget );
renderer.setMRT( currentMRT );
renderer.setClearColor( _currentClearColor, currentClearAlpha );
}
setup( builder ) {
const uvNode = uv();
const sampleDepth = ( uv ) => this.depthNode.uv( uv ).x;
const sampleNoise = ( uv ) => this.noiseNode.uv( uv );
const getSceneUvAndDepth = tslFn( ( [ sampleViewPos ] )=> {
const sampleClipPos = this.cameraProjectionMatrix.mul( vec4( sampleViewPos, 1.0 ) );
let sampleUv = sampleClipPos.xy.div( sampleClipPos.w ).mul( 0.5 ).add( 0.5 ).toVar();
sampleUv = vec2( sampleUv.x, sampleUv.y.oneMinus() );
const sampleSceneDepth = sampleDepth( sampleUv );
return vec3( sampleUv, sampleSceneDepth );
} );
const getViewPosition = tslFn( ( [ screenPosition, depth ] ) => {
screenPosition = vec2( screenPosition.x, screenPosition.y.oneMinus() ).mul( 2.0 ).sub( 1.0 );
const clipSpacePosition = vec4( vec3( screenPosition, depth ), 1.0 );
const viewSpacePosition = vec4( this.cameraProjectionMatrixInverse.mul( clipSpacePosition ) );
return viewSpacePosition.xyz.div( viewSpacePosition.w );
} );
const ao = tslFn( () => {
const depth = sampleDepth( uvNode );
depth.greaterThanEqual( 1.0 ).discard();
const viewPosition = getViewPosition( uvNode, depth );
const viewNormal = this.normalNode.rgb.normalize();
const radiusToUse = this.radius;
const noiseResolution = textureSize( this.noiseNode, 0 );
let noiseUv = vec2( uvNode.x, uvNode.y.oneMinus() );
noiseUv = noiseUv.mul( this.resolution.div( noiseResolution ) );
const noiseTexel = sampleNoise( noiseUv );
const randomVec = noiseTexel.xyz.mul( 2.0 ).sub( 1.0 );
const tangent = vec3( randomVec.xy, 0.0 ).normalize();
const bitangent = vec3( tangent.y.mul( - 1.0 ), tangent.x, 0.0 );
const kernelMatrix = mat3( tangent, bitangent, vec3( 0.0, 0.0, 1.0 ) );
const DIRECTIONS = this.SAMPLES.lessThan( 30 ).cond( 3, 5 );
const STEPS = add( this.SAMPLES, DIRECTIONS.sub( 1 ) ).div( DIRECTIONS );
const ao = float( 0 ).toVar();
loop( { start: int( 0 ), end: DIRECTIONS, type: 'int', condition: '<' }, ( { i } ) => {
const angle = float( i ).div( float( DIRECTIONS ) ).mul( PI );
const sampleDir = vec4( cos( angle ), sin( angle ), 0., add( 0.5, mul( 0.5, noiseTexel.w ) ) );
sampleDir.xyz = normalize( kernelMatrix.mul( sampleDir.xyz ) );
const viewDir = normalize( viewPosition.xyz.negate() );
const sliceBitangent = normalize( cross( sampleDir.xyz, viewDir ) );
const sliceTangent = cross( sliceBitangent, viewDir );
const normalInSlice = normalize( viewNormal.sub( sliceBitangent.mul( dot( viewNormal, sliceBitangent ) ) ) );
const tangentToNormalInSlice = cross( normalInSlice, sliceBitangent );
const cosHorizons = vec2( dot( viewDir, tangentToNormalInSlice ), dot( viewDir, tangentToNormalInSlice.negate() ) ).toVar();
loop( { end: STEPS, type: 'int', name: 'j', condition: '<' }, ( { j } ) => {
const sampleViewOffset = sampleDir.xyz.mul( radiusToUse ).mul( sampleDir.w ).mul( pow( div( float( j ).add( 1.0 ), float( STEPS ) ), this.distanceExponent ) );
// x
const sampleSceneUvDepthX = getSceneUvAndDepth( viewPosition.add( sampleViewOffset ) );
const sampleSceneViewPositionX = getViewPosition( sampleSceneUvDepthX.xy, sampleSceneUvDepthX.z );
const viewDeltaX = sampleSceneViewPositionX.sub( viewPosition );
If( abs( viewDeltaX.z ).lessThan( this.thickness ), () => {
const sampleCosHorizon = dot( viewDir, normalize( viewDeltaX ) );
cosHorizons.x.addAssign( max( 0, mul( sampleCosHorizon.sub( cosHorizons.x ), mix( 1.0, float( 2.0 ).div( float( j ).add( 2 ) ), this.distanceFallOff ) ) ) );
} );
// y
const sampleSceneUvDepthY = getSceneUvAndDepth( viewPosition.sub( sampleViewOffset ) );
const sampleSceneViewPositionY = getViewPosition( sampleSceneUvDepthY.xy, sampleSceneUvDepthY.z );
const viewDeltaY = sampleSceneViewPositionY.sub( viewPosition );
If( abs( viewDeltaY.z ).lessThan( this.thickness ), () => {
const sampleCosHorizon = dot( viewDir, normalize( viewDeltaY ) );
cosHorizons.y.addAssign( max( 0, mul( sampleCosHorizon.sub( cosHorizons.y ), mix( 1.0, float( 2.0 ).div( float( j ).add( 2 ) ), this.distanceFallOff ) ) ) );
} );
} );
const sinHorizons = sqrt( sub( 1.0, cosHorizons.mul( cosHorizons ) ) );
const nx = dot( normalInSlice, sliceTangent );
const ny = dot( normalInSlice, viewDir );
const nxb = mul( 0.5, acos( cosHorizons.y ).sub( acos( cosHorizons.x ) ).add( sinHorizons.x.mul( cosHorizons.x ).sub( sinHorizons.y.mul( cosHorizons.y ) ) ) );
const nyb = mul( 0.5, sub( 2.0, cosHorizons.x.mul( cosHorizons.x ) ).sub( cosHorizons.y.mul( cosHorizons.y ) ) );
const occlusion = nx.mul( nxb ).add( ny.mul( nyb ) );
ao.addAssign( occlusion );
} );
ao.assign( clamp( ao.div( DIRECTIONS ), 0, 1 ) );
ao.assign( pow( ao, this.scale ) );
return vec4( vec3( ao ), 1.0 );
} );
const material = this._material || ( this._material = builder.createNodeMaterial() );
material.fragmentNode = ao().context( builder.getSharedContext() );
material.needsUpdate = true;
//
return this._textureNode;
}
dispose() {
this._aoRenderTarget.dispose();
}
}
function generateMagicSquareNoise( size = 5 ) {
const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
const magicSquare = generateMagicSquare( noiseSize );
const noiseSquareSize = magicSquare.length;
const data = new Uint8Array( noiseSquareSize * 4 );
for ( let inx = 0; inx < noiseSquareSize; ++ inx ) {
const iAng = magicSquare[ inx ];
const angle = ( 2 * Math.PI * iAng ) / noiseSquareSize;
const randomVec = new Vector3(
Math.cos( angle ),
Math.sin( angle ),
0
).normalize();
data[ inx * 4 ] = ( randomVec.x * 0.5 + 0.5 ) * 255;
data[ inx * 4 + 1 ] = ( randomVec.y * 0.5 + 0.5 ) * 255;
data[ inx * 4 + 2 ] = 127;
data[ inx * 4 + 3 ] = 255;
}
const noiseTexture = new DataTexture( data, noiseSize, noiseSize );
noiseTexture.wrapS = RepeatWrapping;
noiseTexture.wrapT = RepeatWrapping;
noiseTexture.needsUpdate = true;
return noiseTexture;
}
function generateMagicSquare( size ) {
const noiseSize = Math.floor( size ) % 2 === 0 ? Math.floor( size ) + 1 : Math.floor( size );
const noiseSquareSize = noiseSize * noiseSize;
const magicSquare = Array( noiseSquareSize ).fill( 0 );
let i = Math.floor( noiseSize / 2 );
let j = noiseSize - 1;
for ( let num = 1; num <= noiseSquareSize; ) {
if ( i === - 1 && j === noiseSize ) {
j = noiseSize - 2;
i = 0;
} else {
if ( j === noiseSize ) {
j = 0;
}
if ( i < 0 ) {
i = noiseSize - 1;
}
}
if ( magicSquare[ i * noiseSize + j ] !== 0 ) {
j -= 2;
i ++;
continue;
} else {
magicSquare[ i * noiseSize + j ] = num ++;
}
j ++;
i --;
}
return magicSquare;
}
export const ao = ( depthNode, normalNode, camera ) => nodeObject( new GTAONode( nodeObject( depthNode ), nodeObject( normalNode ), camera ) );
addNodeElement( 'ao', ao );
export default GTAONode;