TextureCubeUVNode.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /**
  2. * @author sunag / http://www.sunag.com.br/
  3. */
  4. import { TempNode } from '../core/TempNode.js';
  5. import { ConstNode } from '../core/ConstNode.js';
  6. import { StructNode } from '../core/StructNode.js';
  7. import { FunctionNode } from '../core/FunctionNode.js';
  8. import { FunctionCallNode } from '../core/FunctionCallNode.js';
  9. import { ExpressionNode } from '../core/ExpressionNode.js';
  10. import { ReflectNode } from '../accessors/ReflectNode.js';
  11. import { FloatNode } from '../inputs/FloatNode.js';
  12. import { OperatorNode } from '../math/OperatorNode.js';
  13. import { MathNode } from '../math/MathNode.js';
  14. import { CondNode } from '../math/CondNode.js';
  15. import { ColorSpaceNode } from '../utils/ColorSpaceNode.js';
  16. function TextureCubeUVNode( value, uv, bias ) {
  17. TempNode.call( this, 'v4' );
  18. this.value = value,
  19. this.uv = uv;
  20. this.bias = bias;
  21. }
  22. TextureCubeUVNode.Nodes = ( function () {
  23. var TextureCubeUVData = new StructNode(
  24. `struct TextureCubeUVData {
  25. vec4 tl;
  26. vec4 tr;
  27. vec4 br;
  28. vec4 bl;
  29. vec2 f;
  30. }` );
  31. var cubeUV_maxMipLevel = new ConstNode( `float cubeUV_maxMipLevel 8.0`, true );
  32. var cubeUV_minMipLevel = new ConstNode( `float cubeUV_minMipLevel 4.0`, true );
  33. var cubeUV_maxTileSize = new ConstNode( `float cubeUV_maxTileSize 256.0`, true );
  34. var cubeUV_minTileSize = new ConstNode( `float cubeUV_minTileSize 16.0`, true );
  35. // These shader functions convert between the UV coordinates of a single face of
  36. // a cubemap, the 0-5 integer index of a cube face, and the direction vector for
  37. // sampling a textureCube (not generally normalized).
  38. var getFace = new FunctionNode(
  39. `float getFace(vec3 direction) {
  40. vec3 absDirection = abs(direction);
  41. float face = -1.0;
  42. if (absDirection.x > absDirection.z) {
  43. if (absDirection.x > absDirection.y)
  44. face = direction.x > 0.0 ? 0.0 : 3.0;
  45. else
  46. face = direction.y > 0.0 ? 1.0 : 4.0;
  47. } else {
  48. if (absDirection.z > absDirection.y)
  49. face = direction.z > 0.0 ? 2.0 : 5.0;
  50. else
  51. face = direction.y > 0.0 ? 1.0 : 4.0;
  52. }
  53. return face;
  54. }` );
  55. getFace.useKeywords = false;
  56. var getUV = new FunctionNode(
  57. `vec2 getUV(vec3 direction, float face) {
  58. vec2 uv;
  59. if (face == 0.0) {
  60. uv = vec2(-direction.z, direction.y) / abs(direction.x);
  61. } else if (face == 1.0) {
  62. uv = vec2(direction.x, -direction.z) / abs(direction.y);
  63. } else if (face == 2.0) {
  64. uv = direction.xy / abs(direction.z);
  65. } else if (face == 3.0) {
  66. uv = vec2(direction.z, direction.y) / abs(direction.x);
  67. } else if (face == 4.0) {
  68. uv = direction.xz / abs(direction.y);
  69. } else {
  70. uv = vec2(-direction.x, direction.y) / abs(direction.z);
  71. }
  72. return 0.5 * (uv + 1.0);
  73. }` );
  74. getUV.useKeywords = false;
  75. var bilinearCubeUV = new FunctionNode(
  76. `TextureCubeUVData bilinearCubeUV(sampler2D envMap, vec3 direction, float mipInt) {
  77. float face = getFace(direction);
  78. float filterInt = max(cubeUV_minMipLevel - mipInt, 0.0);
  79. mipInt = max(mipInt, cubeUV_minMipLevel);
  80. float faceSize = exp2(mipInt);
  81. float texelSize = 1.0 / (3.0 * cubeUV_maxTileSize);
  82. vec2 uv = getUV(direction, face) * (faceSize - 1.0);
  83. vec2 f = fract(uv);
  84. uv += 0.5 - f;
  85. if (face > 2.0) {
  86. uv.y += faceSize;
  87. face -= 3.0;
  88. }
  89. uv.x += face * faceSize;
  90. if(mipInt < cubeUV_maxMipLevel){
  91. uv.y += 2.0 * cubeUV_maxTileSize;
  92. }
  93. uv.y += filterInt * 2.0 * cubeUV_minTileSize;
  94. uv.x += 3.0 * max(0.0, cubeUV_maxTileSize - 2.0 * faceSize);
  95. uv *= texelSize;
  96. vec4 tl = texture2D(envMap, uv);
  97. uv.x += texelSize;
  98. vec4 tr = texture2D(envMap, uv);
  99. uv.y += texelSize;
  100. vec4 br = texture2D(envMap, uv);
  101. uv.x -= texelSize;
  102. vec4 bl = texture2D(envMap, uv);
  103. return TextureCubeUVData( tl, tr, br, bl, f );
  104. }`, [ TextureCubeUVData, getFace, getUV, cubeUV_maxMipLevel, cubeUV_minMipLevel, cubeUV_maxTileSize, cubeUV_minTileSize ] );
  105. bilinearCubeUV.useKeywords = false;
  106. // These defines must match with PMREMGenerator
  107. var r0 = new ConstNode( `float r0 1.0`, true );
  108. var v0 = new ConstNode( `float v0 0.339`, true );
  109. var m0 = new ConstNode( `float m0 -2.0`, true );
  110. var r1 = new ConstNode( `float r1 0.8`, true );
  111. var v1 = new ConstNode( `float v1 0.276`, true );
  112. var m1 = new ConstNode( `float m1 -1.0`, true );
  113. var r4 = new ConstNode( `float r4 0.4`, true );
  114. var v4 = new ConstNode( `float v4 0.046`, true );
  115. var m4 = new ConstNode( `float m4 2.0`, true );
  116. var r5 = new ConstNode( `float r5 0.305`, true );
  117. var v5 = new ConstNode( `float v5 0.016`, true );
  118. var m5 = new ConstNode( `float m5 3.0`, true );
  119. var r6 = new ConstNode( `float r6 0.21`, true );
  120. var v6 = new ConstNode( `float v6 0.0038`, true );
  121. var m6 = new ConstNode( `float m6 4.0`, true );
  122. var defines = [ r0, v0, m0, r1, v1, m1, r4, v4, m4, r5, v5, m5, r6, v6, m6 ];
  123. var roughnessToMip = new FunctionNode(
  124. `float roughnessToMip(float roughness) {
  125. float mip = 0.0;
  126. if (roughness >= r1) {
  127. mip = (r0 - roughness) * (m1 - m0) / (r0 - r1) + m0;
  128. } else if (roughness >= r4) {
  129. mip = (r1 - roughness) * (m4 - m1) / (r1 - r4) + m1;
  130. } else if (roughness >= r5) {
  131. mip = (r4 - roughness) * (m5 - m4) / (r4 - r5) + m4;
  132. } else if (roughness >= r6) {
  133. mip = (r5 - roughness) * (m6 - m5) / (r5 - r6) + m5;
  134. } else {
  135. mip = -2.0 * log2(1.16 * roughness);// 1.16 = 1.79^0.25
  136. }
  137. return mip;
  138. }`, defines );
  139. return {
  140. bilinearCubeUV: bilinearCubeUV,
  141. roughnessToMip: roughnessToMip,
  142. m0: m0,
  143. cubeUV_maxMipLevel: cubeUV_maxMipLevel
  144. };
  145. } )();
  146. TextureCubeUVNode.prototype = Object.create( TempNode.prototype );
  147. TextureCubeUVNode.prototype.constructor = TextureCubeUVNode;
  148. TextureCubeUVNode.prototype.nodeType = "TextureCubeUV";
  149. TextureCubeUVNode.prototype.bilinearCubeUV = function ( builder, texture, uv, mipInt ) {
  150. var bilinearCubeUV = new FunctionCallNode( TextureCubeUVNode.Nodes.bilinearCubeUV, [ texture, uv, mipInt ] );
  151. this.colorSpaceTL = this.colorSpaceTL || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
  152. this.colorSpaceTL.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
  153. this.colorSpaceTL.input.parse( bilinearCubeUV.build( builder ) + '.tl' );
  154. this.colorSpaceTR = this.colorSpaceTR || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
  155. this.colorSpaceTR.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
  156. this.colorSpaceTR.input.parse( bilinearCubeUV.build( builder ) + '.tr' );
  157. this.colorSpaceBL = this.colorSpaceBL || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
  158. this.colorSpaceBL.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
  159. this.colorSpaceBL.input.parse( bilinearCubeUV.build( builder ) + '.bl' );
  160. this.colorSpaceBR = this.colorSpaceBR || new ColorSpaceNode( new ExpressionNode( '', 'v4' ) );
  161. this.colorSpaceBR.fromDecoding( builder.getTextureEncodingFromMap( this.value.value ) );
  162. this.colorSpaceBR.input.parse( bilinearCubeUV.build( builder ) + '.br' );
  163. var f = bilinearCubeUV.build( builder ) + '.f';
  164. // add a custom context for fix incompatibility with the core
  165. // include ColorSpace function only for vertex shader (in fragment shader color space functions is added automatically by core)
  166. // this should be removed in the future
  167. // context.include =: is used to include or not functions if used FunctionNode
  168. // context.ignoreCache =: not create temp variables nodeT0..9 to optimize the code
  169. var context = { include: builder.isShader( 'vertex' ), ignoreCache: true };
  170. builder.addContext( context );
  171. this.colorSpaceTLExp = new ExpressionNode( this.colorSpaceTL.build( builder, 'v4' ), 'v4' );
  172. this.colorSpaceTRExp = new ExpressionNode( this.colorSpaceTR.build( builder, 'v4' ), 'v4' );
  173. this.colorSpaceBLExp = new ExpressionNode( this.colorSpaceBL.build( builder, 'v4' ), 'v4' );
  174. this.colorSpaceBRExp = new ExpressionNode( this.colorSpaceBR.build( builder, 'v4' ), 'v4' );
  175. // end custom context
  176. builder.removeContext();
  177. // --
  178. var output = new ExpressionNode(`mix( mix( cubeUV_TL, cubeUV_TR, cubeUV.f.x ), mix( cubeUV_BL, cubeUV_BR, cubeUV.f.x ), cubeUV.f.y )`, 'v4' );
  179. output.keywords['cubeUV_TL'] = this.colorSpaceTLExp;
  180. output.keywords['cubeUV_TR'] = this.colorSpaceTRExp;
  181. output.keywords['cubeUV_BL'] = this.colorSpaceBLExp;
  182. output.keywords['cubeUV_BR'] = this.colorSpaceBRExp;
  183. output.keywords['cubeUV'] = bilinearCubeUV;
  184. return output;
  185. };
  186. TextureCubeUVNode.prototype.generate = function ( builder, output ) {
  187. if ( builder.isShader( 'fragment' ) ) {
  188. var uv = this.uv;
  189. var bias = this.bias || builder.context.roughness;
  190. var mipV = new FunctionCallNode( TextureCubeUVNode.Nodes.roughnessToMip, [ bias ] );
  191. var mip = new MathNode( mipV, TextureCubeUVNode.Nodes.m0, TextureCubeUVNode.Nodes.cubeUV_maxMipLevel, MathNode.CLAMP );
  192. var mipInt = new MathNode( mip, MathNode.FLOOR );
  193. var mipF = new MathNode( mip, MathNode.FRACT );
  194. var color0 = this.bilinearCubeUV( builder, this.value, uv, mipInt );
  195. var color1 = this.bilinearCubeUV( builder, this.value, uv, new OperatorNode(
  196. mipInt,
  197. new FloatNode( 1 ).setReadonly( true ),
  198. OperatorNode.ADD
  199. ) );
  200. var color1Mix = new MathNode( color0, color1, mipF, MathNode.MIX );
  201. /*
  202. // TODO: Optimize this in the future
  203. var cond = new CondNode(
  204. mipF,
  205. new FloatNode( 0 ).setReadonly( true ),
  206. CondNode.EQUAL,
  207. color0, // if
  208. color1Mix // else
  209. );
  210. */
  211. return builder.format( color1Mix.build( builder ), 'v4', output );
  212. } else {
  213. console.warn( "THREE.TextureCubeUVNode is not compatible with " + builder.shader + " shader." );
  214. return builder.format( 'vec4( 0.0 )', this.getType( builder ), output );
  215. }
  216. };
  217. TextureCubeUVNode.prototype.toJSON = function ( meta ) {
  218. var data = this.getJSONNode( meta );
  219. if ( ! data ) {
  220. data = this.createJSONNode( meta );
  221. data.value = this.value.toJSON( meta ).uuid;
  222. data.uv = this.uv.toJSON( meta ).uuid;
  223. data.bias = this.bias.toJSON( meta ).uuid;
  224. }
  225. return data;
  226. };
  227. export { TextureCubeUVNode };