GroundedSkybox.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import { Fn, If, vec3, float, min, cameraPosition, positionWorld } from 'three/tsl';
  2. /**
  3. * @module GroundedSkybox
  4. * @three_import import { getGroundProjectedNormal } from 'three/addons/tsl/utils/GroundedSkybox.js';
  5. */
  6. /**
  7. * Projects the world position onto a sphere whose bottom is clipped by a ground disk,
  8. * then returns a vector usable for sampling an environment cube map.
  9. *
  10. * @tsl
  11. * @function
  12. * @param {Node<float>} radiusNode - The radius of the projection sphere. Must be large enough to ensure the scene's camera stays inside.
  13. * @param {Node<float>} heightNode - The height is how far the camera that took the photo was above the ground. A larger value will magnify the downward part of the image.
  14. * @return {Node<vec3>} A direction vector for sampling the environment cube map.
  15. */
  16. export const getGroundProjectedNormal = Fn( ( [ radiusNode, heightNode ] ) => {
  17. const p = positionWorld.sub( cameraPosition ).normalize().toConst();
  18. const camPos = cameraPosition.toVar();
  19. camPos.y.subAssign( heightNode );
  20. // sphereIntersect( camPos, p, vec3( 0 ), radius )
  21. const b = camPos.dot( p ).toConst();
  22. const c = camPos.dot( camPos ).sub( radiusNode.mul( radiusNode ) ).toConst();
  23. const h = b.mul( b ).sub( c ).toConst();
  24. const intersection = h.greaterThanEqual( 0 ).select( h.sqrt().sub( b ), - 1 );
  25. const projected = vec3( 0, 1, 0 ).toVar();
  26. If( intersection.greaterThan( 0 ), () => {
  27. // diskIntersectWithBackFaceCulling( camPos, p, diskCenter, n, radius )
  28. const diskCenter = vec3( 0, heightNode.negate(), 0 ).toConst();
  29. const n = vec3( 0, 1, 0 ).toConst();
  30. const d = p.dot( n ).toConst();
  31. const intersection2 = float( 1e6 ).toVar();
  32. If( d.lessThanEqual( 0 ), () => {
  33. const o = camPos.sub( diskCenter ).toConst();
  34. const t = n.dot( o ).negate().div( d ).toConst();
  35. const q = o.add( p.mul( t ) ).toConst();
  36. If( q.dot( q ).lessThan( radiusNode.mul( radiusNode ) ), () => {
  37. intersection2.assign( t );
  38. } );
  39. } );
  40. projected.assign( camPos.add( p.mul( min( intersection, intersection2 ) ) ).div( radiusNode ) );
  41. } );
  42. return projected;
  43. } );
粤ICP备19079148号