107 lines
2.5 KiB
JavaScript
107 lines
2.5 KiB
JavaScript
|
import TempNode from '../core/TempNode.js';
|
||
|
import { add } from '../math/OperatorNode.js';
|
||
|
|
||
|
import { modelNormalMatrix } from '../accessors/ModelNode.js';
|
||
|
import { normalView } from '../accessors/NormalNode.js';
|
||
|
import { positionView } from '../accessors/PositionNode.js';
|
||
|
import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
|
||
|
import { uv } from '../accessors/UVNode.js';
|
||
|
import { faceDirection } from './FrontFacingNode.js';
|
||
|
import { addNodeClass } from '../core/Node.js';
|
||
|
import { addNodeElement, tslFn, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
|
||
|
|
||
|
import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
|
||
|
|
||
|
// Normal Mapping Without Precomputed Tangents
|
||
|
// http://www.thetenthplanet.de/archives/1180
|
||
|
|
||
|
const perturbNormal2Arb = tslFn( ( inputs ) => {
|
||
|
|
||
|
const { eye_pos, surf_norm, mapN, uv } = inputs;
|
||
|
|
||
|
const q0 = eye_pos.dFdx();
|
||
|
const q1 = eye_pos.dFdy();
|
||
|
const st0 = uv.dFdx();
|
||
|
const st1 = uv.dFdy();
|
||
|
|
||
|
const N = surf_norm; // normalized
|
||
|
|
||
|
const q1perp = q1.cross( N );
|
||
|
const q0perp = N.cross( q0 );
|
||
|
|
||
|
const T = q1perp.mul( st0.x ).add( q0perp.mul( st1.x ) );
|
||
|
const B = q1perp.mul( st0.y ).add( q0perp.mul( st1.y ) );
|
||
|
|
||
|
const det = T.dot( T ).max( B.dot( B ) );
|
||
|
const scale = faceDirection.mul( det.inverseSqrt() );
|
||
|
|
||
|
return add( T.mul( mapN.x, scale ), B.mul( mapN.y, scale ), N.mul( mapN.z ) ).normalize();
|
||
|
|
||
|
} );
|
||
|
|
||
|
class NormalMapNode extends TempNode {
|
||
|
|
||
|
constructor( node, scaleNode = null ) {
|
||
|
|
||
|
super( 'vec3' );
|
||
|
|
||
|
this.node = node;
|
||
|
this.scaleNode = scaleNode;
|
||
|
|
||
|
this.normalMapType = TangentSpaceNormalMap;
|
||
|
|
||
|
}
|
||
|
|
||
|
setup( builder ) {
|
||
|
|
||
|
const { normalMapType, scaleNode } = this;
|
||
|
|
||
|
let normalMap = this.node.mul( 2.0 ).sub( 1.0 );
|
||
|
|
||
|
if ( scaleNode !== null ) {
|
||
|
|
||
|
normalMap = vec3( normalMap.xy.mul( scaleNode ), normalMap.z );
|
||
|
|
||
|
}
|
||
|
|
||
|
let outputNode = null;
|
||
|
|
||
|
if ( normalMapType === ObjectSpaceNormalMap ) {
|
||
|
|
||
|
outputNode = modelNormalMatrix.mul( normalMap ).normalize();
|
||
|
|
||
|
} else if ( normalMapType === TangentSpaceNormalMap ) {
|
||
|
|
||
|
const tangent = builder.hasGeometryAttribute( 'tangent' );
|
||
|
|
||
|
if ( tangent === true ) {
|
||
|
|
||
|
outputNode = TBNViewMatrix.mul( normalMap ).normalize();
|
||
|
|
||
|
} else {
|
||
|
|
||
|
outputNode = perturbNormal2Arb( {
|
||
|
eye_pos: positionView,
|
||
|
surf_norm: normalView,
|
||
|
mapN: normalMap,
|
||
|
uv: uv()
|
||
|
} );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return outputNode;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export default NormalMapNode;
|
||
|
|
||
|
export const normalMap = nodeProxy( NormalMapNode );
|
||
|
|
||
|
addNodeElement( 'normalMap', normalMap );
|
||
|
|
||
|
addNodeClass( 'NormalMapNode', NormalMapNode );
|