ParallaxShader.js 5.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
  1. ( function () {
  2. // Parallax Occlusion shaders from
  3. // http://sunandblackcat.com/tipFullView.php?topicid=28
  4. // No tangent-space transforms logic based on
  5. // http://mmikkelsen3d.blogspot.sk/2012/02/parallaxpoc-mapping-and-no-tangent.html
  6. var ParallaxShader = {
  7. // Ordered from fastest to best quality.
  8. modes: {
  9. none: 'NO_PARALLAX',
  10. basic: 'USE_BASIC_PARALLAX',
  11. steep: 'USE_STEEP_PARALLAX',
  12. occlusion: 'USE_OCLUSION_PARALLAX',
  13. // a.k.a. POM
  14. relief: 'USE_RELIEF_PARALLAX'
  15. },
  16. uniforms: {
  17. 'bumpMap': {
  18. value: null
  19. },
  20. 'map': {
  21. value: null
  22. },
  23. 'parallaxScale': {
  24. value: null
  25. },
  26. 'parallaxMinLayers': {
  27. value: null
  28. },
  29. 'parallaxMaxLayers': {
  30. value: null
  31. }
  32. },
  33. vertexShader: [ 'varying vec2 vUv;', 'varying vec3 vViewPosition;', 'varying vec3 vNormal;', 'void main() {', ' vUv = uv;', ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );', ' vViewPosition = -mvPosition.xyz;', ' vNormal = normalize( normalMatrix * normal );', ' gl_Position = projectionMatrix * mvPosition;', '}' ].join( '\n' ),
  34. fragmentShader: [ 'uniform sampler2D bumpMap;', 'uniform sampler2D map;', 'uniform float parallaxScale;', 'uniform float parallaxMinLayers;', 'uniform float parallaxMaxLayers;', 'varying vec2 vUv;', 'varying vec3 vViewPosition;', 'varying vec3 vNormal;', '#ifdef USE_BASIC_PARALLAX', ' vec2 parallaxMap( in vec3 V ) {', ' float initialHeight = texture2D( bumpMap, vUv ).r;', // No Offset Limitting: messy, floating output at grazing angles.
  35. //"vec2 texCoordOffset = parallaxScale * V.xy / V.z * initialHeight;",
  36. // Offset Limiting
  37. ' vec2 texCoordOffset = parallaxScale * V.xy * initialHeight;', ' return vUv - texCoordOffset;', ' }', '#else', ' vec2 parallaxMap( in vec3 V ) {', // Determine number of layers from angle between V and N
  38. ' float numLayers = mix( parallaxMaxLayers, parallaxMinLayers, abs( dot( vec3( 0.0, 0.0, 1.0 ), V ) ) );', ' float layerHeight = 1.0 / numLayers;', ' float currentLayerHeight = 0.0;', // Shift of texture coordinates for each iteration
  39. ' vec2 dtex = parallaxScale * V.xy / V.z / numLayers;', ' vec2 currentTextureCoords = vUv;', ' float heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;', // while ( heightFromTexture > currentLayerHeight )
  40. // Infinite loops are not well supported. Do a "large" finite
  41. // loop, but not too large, as it slows down some compilers.
  42. ' for ( int i = 0; i < 30; i += 1 ) {', ' if ( heightFromTexture <= currentLayerHeight ) {', ' break;', ' }', ' currentLayerHeight += layerHeight;', // Shift texture coordinates along vector V
  43. ' currentTextureCoords -= dtex;', ' heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;', ' }', ' #ifdef USE_STEEP_PARALLAX', ' return currentTextureCoords;', ' #elif defined( USE_RELIEF_PARALLAX )', ' vec2 deltaTexCoord = dtex / 2.0;', ' float deltaHeight = layerHeight / 2.0;', // Return to the mid point of previous layer
  44. ' currentTextureCoords += deltaTexCoord;', ' currentLayerHeight -= deltaHeight;', // Binary search to increase precision of Steep Parallax Mapping
  45. ' const int numSearches = 5;', ' for ( int i = 0; i < numSearches; i += 1 ) {', ' deltaTexCoord /= 2.0;', ' deltaHeight /= 2.0;', ' heightFromTexture = texture2D( bumpMap, currentTextureCoords ).r;', // Shift along or against vector V
  46. ' if( heightFromTexture > currentLayerHeight ) {', // Below the surface
  47. ' currentTextureCoords -= deltaTexCoord;', ' currentLayerHeight += deltaHeight;', ' } else {', // above the surface
  48. ' currentTextureCoords += deltaTexCoord;', ' currentLayerHeight -= deltaHeight;', ' }', ' }', ' return currentTextureCoords;', ' #elif defined( USE_OCLUSION_PARALLAX )', ' vec2 prevTCoords = currentTextureCoords + dtex;', // Heights for linear interpolation
  49. ' float nextH = heightFromTexture - currentLayerHeight;', ' float prevH = texture2D( bumpMap, prevTCoords ).r - currentLayerHeight + layerHeight;', // Proportions for linear interpolation
  50. ' float weight = nextH / ( nextH - prevH );', // Interpolation of texture coordinates
  51. ' return prevTCoords * weight + currentTextureCoords * ( 1.0 - weight );', ' #else', // NO_PARALLAX
  52. ' return vUv;', ' #endif', ' }', '#endif', 'vec2 perturbUv( vec3 surfPosition, vec3 surfNormal, vec3 viewPosition ) {', ' vec2 texDx = dFdx( vUv );', ' vec2 texDy = dFdy( vUv );', ' vec3 vSigmaX = dFdx( surfPosition );', ' vec3 vSigmaY = dFdy( surfPosition );', ' vec3 vR1 = cross( vSigmaY, surfNormal );', ' vec3 vR2 = cross( surfNormal, vSigmaX );', ' float fDet = dot( vSigmaX, vR1 );', ' vec2 vProjVscr = ( 1.0 / fDet ) * vec2( dot( vR1, viewPosition ), dot( vR2, viewPosition ) );', ' vec3 vProjVtex;', ' vProjVtex.xy = texDx * vProjVscr.x + texDy * vProjVscr.y;', ' vProjVtex.z = dot( surfNormal, viewPosition );', ' return parallaxMap( vProjVtex );', '}', 'void main() {', ' vec2 mapUv = perturbUv( -vViewPosition, normalize( vNormal ), normalize( vViewPosition ) );', ' gl_FragColor = texture2D( map, mapUv );', '}' ].join( '\n' )
  53. };
  54. THREE.ParallaxShader = ParallaxShader;
  55. } )();