Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,30 @@ export const LinearTransfer = 'linear';
*/
export const SRGBTransfer = 'srgb';

/**
* No normal map packing.
*
* @type {string}
* @constant
*/
export const NoNormalPacking = '';

/**
* Normal RG packing.
*
* @type {string}
* @constant
*/
export const NormalRGPacking = 'rg';

/**
* Normal GA packing.
*
* @type {string}
* @constant
*/
export const NormalGAPacking = 'ga';

/**
* Sets the stencil buffer value to `0`.
*
Expand Down
6 changes: 6 additions & 0 deletions src/nodes/accessors/MaterialNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ class MaterialNode extends Node {
node = normalMap( this.getTexture( 'normal' ), this.getCache( 'normalScale', 'vec2' ) );
node.normalMapType = material.normalMapType;

if ( material.normalMap.userData && material.normalMap.userData.unpackNormalMode ) {

node.unpackNormalMode = material.normalMap.userData.unpackNormalMode;

}

} else if ( material.bumpMap ) {

node = bumpMap( this.getTexture( 'bump' ).r, this.getFloat( 'bumpScale' ) );
Expand Down
39 changes: 37 additions & 2 deletions src/nodes/display/NormalMapNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { normalView, transformNormalToView } from '../accessors/Normal.js';
import { TBNViewMatrix } from '../accessors/AccessorsUtils.js';
import { nodeProxy, vec3 } from '../tsl/TSLBase.js';

import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from '../../constants.js';
import { TangentSpaceNormalMap, ObjectSpaceNormalMap, NoNormalPacking, NormalRGPacking, NormalGAPacking } from '../../constants.js';
import { directionToFaceDirection } from './FrontFacingNode.js';
import { unpackNormal } from '../utils/Packing.js';
import { error } from '../../utils.js';

/**
Expand Down Expand Up @@ -58,14 +59,48 @@ class NormalMapNode extends TempNode {
*/
this.normalMapType = TangentSpaceNormalMap;

/**
* Controls how to unpack the sampled normal map values.
*
* @type {string}
* @default NoNormalPacking
*/
this.unpackNormalMode = NoNormalPacking;

}

setup( { material } ) {

const { normalMapType, scaleNode } = this;
const { normalMapType, scaleNode, unpackNormalMode } = this;

let normalMap = this.node.mul( 2.0 ).sub( 1.0 );

if ( normalMapType === TangentSpaceNormalMap ) {

if ( unpackNormalMode == NormalRGPacking ) {

normalMap = unpackNormal( normalMap.xy );

} else if ( unpackNormalMode == NormalGAPacking ) {

normalMap = unpackNormal( normalMap.yw );

} else if ( unpackNormalMode != NoNormalPacking ) {

console.error( `THREE.NodeMaterial: Unexpected unpack normal mode: ${ unpackNormalMode }` );

}

} else {

if ( unpackNormalMode != NoNormalPacking ) {

console.error( `THREE.NodeMaterial: Normal map type '${ normalMapType }' is not compatible with unpack normal mode '${ unpackNormalMode }'` );

}

}

if ( scaleNode !== null ) {

let scale = scaleNode;
Expand Down
14 changes: 13 additions & 1 deletion src/nodes/utils/Packing.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { nodeObject } from '../tsl/TSLBase.js';
import { nodeObject, vec3, float } from '../tsl/TSLBase.js';
import { dot, sqrt, saturate } from '../math/MathNode.js';

/**
* Packs a direction vector into a color value.
Expand All @@ -19,3 +20,14 @@ export const directionToColor = ( node ) => nodeObject( node ).mul( 0.5 ).add( 0
* @return {Node<vec3>} The direction.
*/
export const colorToDirection = ( node ) => nodeObject( node ).mul( 2.0 ).sub( 1 );

/**
* Unpacks a tangent space normal, reconstructing the Z component by projecting the X,Y coordinates onto the hemisphere.
* The X,Y coordinates are expected to be in the [-1, 1] range.
*
* @tsl
* @function
* @param {Node<vec2>} xy - The X,Y coordinates of the normal.
* @return {Node<vec3>} The resulting normal.
*/
export const unpackNormal = ( xy ) => vec3( xy, sqrt( saturate( float( 1.0 ).sub( dot( xy, xy ) ) ) ) );