Megatexture.glsl 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // See Octree.glsl for the definitions of SampleData and intMod
  2. /* Megatexture defines (set in Scene/VoxelRenderResources.js)
  3. #define SAMPLE_COUNT ###
  4. #define NEAREST_SAMPLING
  5. #define PADDING
  6. */
  7. uniform ivec2 u_megatextureSliceDimensions; // number of slices per tile, in two dimensions
  8. uniform ivec2 u_megatextureTileDimensions; // number of tiles per megatexture, in two dimensions
  9. uniform vec2 u_megatextureVoxelSizeUv;
  10. uniform vec2 u_megatextureSliceSizeUv;
  11. uniform vec2 u_megatextureTileSizeUv;
  12. uniform ivec3 u_dimensions; // does not include padding
  13. #if defined(PADDING)
  14. uniform ivec3 u_paddingBefore;
  15. uniform ivec3 u_paddingAfter;
  16. #endif
  17. // Integer min, max, clamp: For WebGL1 only
  18. int intMin(int a, int b) {
  19. return a <= b ? a : b;
  20. }
  21. int intMax(int a, int b) {
  22. return a >= b ? a : b;
  23. }
  24. int intClamp(int v, int minVal, int maxVal) {
  25. return intMin(intMax(v, minVal), maxVal);
  26. }
  27. vec2 index1DTo2DTexcoord(int index, ivec2 dimensions, vec2 uvScale)
  28. {
  29. int indexX = intMod(index, dimensions.x);
  30. int indexY = index / dimensions.x;
  31. return vec2(indexX, indexY) * uvScale;
  32. }
  33. /*
  34. How is 3D data stored in a 2D megatexture?
  35. In this example there is only one loaded tile and it has 2x2x2 voxels (8 voxels total).
  36. The data is sliced by Z. The data at Z = 0 is placed in texels (0,0), (0,1), (1,0), (1,1) and
  37. the data at Z = 1 is placed in texels (2,0), (2,1), (3,0), (3,1).
  38. Note that there could be empty space in the megatexture because it's a power of two.
  39. 0 1 2 3
  40. +---+---+---+---+
  41. | | | | | 3
  42. +---+---+---+---+
  43. | | | | | 2
  44. +-------+-------+
  45. |010|110|011|111| 1
  46. |--- ---|--- ---|
  47. |000|100|001|101| 0
  48. +-------+-------+
  49. When doing linear interpolation the megatexture needs to be sampled twice: once for
  50. the Z slice above the voxel coordinate and once for the slice below. The two slices
  51. are interpolated with fract(coord.z - 0.5). For example, a Z coordinate of 1.0 is
  52. halfway between two Z slices so the interpolation factor is 0.5. Below is a side view
  53. of the 3D voxel grid with voxel coordinates on the left side.
  54. 2 +---+
  55. |001|
  56. 1 +-z-+
  57. |000|
  58. 0 +---+
  59. When doing nearest neighbor the megatexture only needs to be sampled once at the closest Z slice.
  60. */
  61. Properties getPropertiesFromMegatexture(in SampleData sampleData) {
  62. vec3 tileUv = clamp(sampleData.tileUv, vec3(0.0), vec3(1.0)); // TODO is the clamp necessary?
  63. int tileIndex = sampleData.megatextureIndex;
  64. vec3 voxelCoord = tileUv * vec3(u_dimensions);
  65. ivec3 voxelDimensions = u_dimensions;
  66. #if defined(PADDING)
  67. voxelDimensions += u_paddingBefore + u_paddingAfter;
  68. voxelCoord += vec3(u_paddingBefore);
  69. #endif
  70. #if defined(NEAREST_SAMPLING)
  71. // Round to the center of the nearest voxel
  72. voxelCoord = floor(voxelCoord) + vec3(0.5);
  73. #endif
  74. // Tile location
  75. vec2 tileUvOffset = index1DTo2DTexcoord(tileIndex, u_megatextureTileDimensions, u_megatextureTileSizeUv);
  76. // Slice location
  77. float slice = voxelCoord.z - 0.5;
  78. int sliceIndex = int(floor(slice));
  79. int sliceIndex0 = intClamp(sliceIndex, 0, voxelDimensions.z - 1);
  80. vec2 sliceUvOffset0 = index1DTo2DTexcoord(sliceIndex0, u_megatextureSliceDimensions, u_megatextureSliceSizeUv);
  81. // Voxel location
  82. vec2 voxelUvOffset = clamp(voxelCoord.xy, vec2(0.5), vec2(voxelDimensions.xy) - vec2(0.5)) * u_megatextureVoxelSizeUv;
  83. // Final location in the megatexture
  84. vec2 uv0 = tileUvOffset + sliceUvOffset0 + voxelUvOffset;
  85. #if defined(NEAREST_SAMPLING)
  86. return getPropertiesFromMegatextureAtUv(uv0);
  87. #else
  88. float sliceLerp = fract(slice);
  89. int sliceIndex1 = intMin(sliceIndex + 1, voxelDimensions.z - 1);
  90. vec2 sliceUvOffset1 = index1DTo2DTexcoord(sliceIndex1, u_megatextureSliceDimensions, u_megatextureSliceSizeUv);
  91. vec2 uv1 = tileUvOffset + sliceUvOffset1 + voxelUvOffset;
  92. Properties properties0 = getPropertiesFromMegatextureAtUv(uv0);
  93. Properties properties1 = getPropertiesFromMegatextureAtUv(uv1);
  94. return mixProperties(properties0, properties1, sliceLerp);
  95. #endif
  96. }
  97. // Convert an array of sample datas to a final weighted properties.
  98. Properties accumulatePropertiesFromMegatexture(in SampleData sampleDatas[SAMPLE_COUNT]) {
  99. #if (SAMPLE_COUNT == 1)
  100. return getPropertiesFromMegatexture(sampleDatas[0]);
  101. #else
  102. // When more than one sample is taken the accumulator needs to start at 0
  103. Properties properties = clearProperties();
  104. for (int i = 0; i < SAMPLE_COUNT; ++i) {
  105. float weight = sampleDatas[i].weight;
  106. // Avoid reading the megatexture when the weight is 0 as it can be costly.
  107. if (weight > 0.0) {
  108. Properties tempProperties = getPropertiesFromMegatexture(sampleDatas[i]);
  109. tempProperties = scaleProperties(tempProperties, weight);
  110. properties = sumProperties(properties, tempProperties);
  111. }
  112. }
  113. return properties;
  114. #endif
  115. }