Last active
September 5, 2024 00:15
-
-
Save ionox0/7037162dc3ff2d5b5b89846d4045f89c to your computer and use it in GitHub Desktop.
triplanar_mapping_threejs_nodes.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { abs, float, vec3, tslFn, vec2, vec4, sin, fract, max, normalize, mul, texture, floor, sub, min, smoothstep, mix, storage, instanceIndex, uint, uv, normalLocal, positionLocal, positionWorld, normalWorld } from '../../../three.js/examples/jsm/nodes/Nodes'; | |
import StorageInstancedBufferAttribute from '../../../three.js/examples/jsm/renderers/common/StorageInstancedBufferAttribute'; | |
function _PS(data: any, diffuseTex: THREE.TextureAtlas, noiseTexture: THREE.Texture ) { | |
const TRI_SCALE = float(10.0); | |
const TerrainBlend_4 = (d0: any, d1: any, d2: any, d3: any) => { | |
const depth = float( 0.2 ).toVar(); | |
const ma = float( max( d0.w, max( d1.w, max( d2.w, d3.w ) ) ).sub( depth ) ).toVar(); | |
const b1 = float( max( d0.w.sub( ma ), 0.0 ) ).toVar(); | |
const b2 = float( max( d1.w.sub( ma ), 0.0 ) ).toVar(); | |
const b3 = float( max( d2.w.sub( ma ), 0.0 ) ).toVar(); | |
const b4 = float( max( d3.w.sub( ma ), 0.0 ) ).toVar(); | |
const numer = vec4( d0.mul( b1 ).add( d1.mul( b2 ).add( d2.mul( b3 ).add( d3.mul( b4 ) ) ) ) ).toVar(); | |
const denom = float( b1.add( b2.add( b3.add( b4 ) ) ) ).toVar(); | |
return numer.div( denom ); | |
}; | |
const sum = ( v: any ) => { | |
return v.x.add( v.y.add( v.z ) ); | |
}; | |
const texture_UV = (x: any) => { | |
const k = float(texture(noiseTexture, mul(0.0025, x.xy)).x).toVar(); | |
const l = float( k.mul( 8.0 ) ).toVar(); | |
const f = float( fract( l ) ).toVar(); | |
const ia = float( floor( l.add( 0.5 ) ) ).toVar(); | |
const ib = float( floor( l ) ).toVar(); | |
f.assign( min( f, sub( 1.0, f ) ).mul( 2.0 ) ); | |
const offa = vec2( sin( vec2( 3.0, 7.0 ).mul( ia ) ) ).toVar(); | |
const offb = vec2( sin( vec2( 3.0, 7.0 ).mul( ib ) ) ).toVar(); | |
const cola = vec4(texture(diffuseTex, x.xy.add(offa).xy).depth(x.z)).toVar(); | |
const colb = vec4(texture(diffuseTex, x.xy.add(offb).xy).depth(x.z)).toVar(); | |
return mix(cola, colb, smoothstep(0.2, 0.8, f.sub(mul(0.1, sum(cola.xyz.sub(colb.xyz)))))); | |
}; | |
const Triplanar_UV = (pos: any, normal: any, texSlice: any) => { | |
const dx = vec4(texture_UV(vec3(pos.zy.div(TRI_SCALE), texSlice))).toVar(); | |
const dy = vec4(texture_UV(vec3(pos.xz.div(TRI_SCALE), texSlice))).toVar(); | |
const dz = vec4(texture_UV(vec3(pos.xy.div(TRI_SCALE), texSlice))).toVar(); | |
const weights = vec3( abs( normal.xyz ) ).toVar(); | |
weights.assign(weights.div(weights.x.add(weights.y.add(weights.z)))); | |
return dx.mul(weights.x).add(dy.mul(weights.y).add(dz.mul(weights.z))); | |
}; | |
const weights1BufferPadded = new Float32Array( [...data.weights1, ...new Float32Array( 200000 ) ] ) | |
const weights2BufferPadded = new Float32Array( [...data.weights2, ...new Float32Array( 200000 ) ] ) | |
const weights1Buffer = new StorageInstancedBufferAttribute( weights1BufferPadded, 4 ); | |
const weights2Buffer = new StorageInstancedBufferAttribute( weights2BufferPadded, 4 ); | |
const weights1BufferNode = storage( weights1Buffer, 'vec4', data.weights1.length * 4 ); | |
const weights2BufferNode = storage( weights2Buffer, 'vec4', data.weights2.length * 4 ); | |
const main = tslFn( (data: any) => { | |
const weights1 = weights1BufferNode.element(instanceIndex); | |
const weights2 = weights2BufferNode.element(instanceIndex); | |
const worldPosition = vec3(positionWorld).toVar(); | |
const worldSpaceNormal = vec3(normalize(normalWorld)).toVar(); | |
const weightIndices = vec4(weights1).toVar(); | |
const weightValues = vec4(weights2).toVar(); | |
const d0 = Triplanar_UV(worldPosition, worldSpaceNormal, weightIndices.element(0)); | |
d0.w.mulAssign(weightValues.element(0)); | |
const d1 = Triplanar_UV(worldPosition, worldSpaceNormal, weightIndices.element(1)); | |
d1.w.mulAssign(weightValues.element(1)); | |
const d2 = Triplanar_UV(worldPosition, worldSpaceNormal, weightIndices.element(2)); | |
d2.w.mulAssign(weightValues.element(2)); | |
const d3 = Triplanar_UV(worldPosition, worldSpaceNormal, weightIndices.element(3)); | |
d3.w.mulAssign(weightValues.element(3)); | |
const diffuseBlended = vec4(TerrainBlend_4(d0.mul(0.1), d1.mul(1.1), d2.mul(1.1), d3.mul(1.1))).toVar(); | |
const diffuse = vec3(diffuseBlended.xyz).toVar(); | |
const finalColour = vec3(diffuse).toVar(); | |
return vec4(finalColour, 1.0); | |
}); | |
return main(data); | |
} | |
// Usage: | |
geometry = new THREE.BufferGeometry(); | |
geometry.setAttribute('position', new THREE.Float32BufferAttribute(terrainData.positions, 3)); | |
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(terrainData.normals, 3)); | |
geometry.setAttribute('color', new THREE.Float32BufferAttribute(terrainData.colours, 3)); | |
geometry.setAttribute('coords', new THREE.Float32BufferAttribute(terrainData.coords, 3)); | |
geometry.setAttribute('weights1', new THREE.Float32BufferAttribute(terrainData.weights1, 4)); | |
geometry.setAttribute('weights2', new THREE.Float32BufferAttribute(terrainData.weights2, 4)); | |
geometry.computeBoundingBox(); | |
const loader = new THREE.TextureLoader(); | |
noiseTexture = loader.load('./resources/terrain/simplex-noise.png'); | |
material = new MeshBasicNodeMaterial(); | |
material.side = THREE.BackSide; | |
material.colorNode = PS( | |
terrainData, | |
diffuseTextureAtlas, | |
noiseTexture | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment