Terrain.hx 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. package hrt.shader;
  2. class Terrain extends hxsl.Shader {
  3. static var SRC = {
  4. @const var SHOW_GRID : Bool;
  5. @const var SURFACE_COUNT : Int;
  6. @const var CHECKER : Bool;
  7. @const var COMPLEXITY : Bool;
  8. @const var PARALLAX : Bool;
  9. @const var VERTEX_DISPLACEMENT : Bool;
  10. @param var primSize : Vec2;
  11. @param var cellSize : Vec2;
  12. @param var heightMapSize : Vec2;
  13. @param var albedoTextures : Sampler2DArray;
  14. @param var normalTextures : Sampler2DArray;
  15. @param var pbrTextures : Sampler2DArray;
  16. /*
  17. We need to use a texture array so each blend factor is separate.
  18. This allows us to fetch in bilinear without worrying about interpolation
  19. between weights corresponding to different indexes.
  20. */
  21. @param var weightTextures : Sampler2DArray;
  22. @param var surfaceIndexMap : Sampler2D;
  23. @param var heightMap : Sampler2D;
  24. @param var normalMap : Sampler2D;
  25. @param var surfaceParams : Array<Vec4, SURFACE_COUNT>;
  26. @param var secondSurfaceParams : Array<Vec4, SURFACE_COUNT>;
  27. @param var heightBlendStrength : Float;
  28. @param var blendSharpness : Float;
  29. @param var parallaxAmount : Float;
  30. @param var minStep : Int;
  31. @param var maxStep : Int;
  32. @param var tileIndex : Vec2;
  33. var worldUV : Vec2;
  34. var calculatedUV : Vec2;
  35. var TBN : Mat3;
  36. var emissive : Float;
  37. var metalness : Float;
  38. var roughness : Float;
  39. var occlusion : Float;
  40. var tangentViewPos : Vec3;
  41. var tangentFragPos : Vec3;
  42. var transformedPosition : Vec3;
  43. var transformedNormal : Vec3;
  44. var terrainNormal : Vec3;
  45. var pixelColor : Vec4;
  46. @input var input : {
  47. var position : Vec3;
  48. var normal : Vec3;
  49. };
  50. @global var global : {
  51. @perObject var modelView : Mat4;
  52. };
  53. @global var camera : {
  54. var position : Vec3;
  55. };
  56. function vertex() {
  57. calculatedUV = input.position.xy / primSize;
  58. worldUV = transformedPosition.xy;
  59. if( VERTEX_DISPLACEMENT ) { // Use heightMap and normalMap
  60. transformedPosition += vec3(0, 0, textureLod(heightMap, calculatedUV, 0).r);
  61. terrainNormal = unpackNormal(textureLod(normalMap, calculatedUV, 0).rgba);
  62. }
  63. else { // The normal and height are in the vertex
  64. terrainNormal = normalize(input.normal * global.modelView.mat3());
  65. }
  66. // Make the TBN matrix for normal mapping and parallax
  67. var bitangent = normalize(cross(vec3(1, 0, 0), terrainNormal));
  68. var tangent = normalize(cross(terrainNormal, bitangent));
  69. TBN = mat3( vec3(tangent.x, bitangent.x, terrainNormal.x),
  70. vec3(tangent.y, bitangent.y, terrainNormal.y),
  71. vec3(tangent.z, bitangent.z, terrainNormal.z));
  72. }
  73. function getWeight( i : IVec3, uv : Vec2 ) : Vec3 {
  74. var weight = vec3(0);
  75. weight.x = weightTextures.getLod(vec3(uv, i.x), 0).r;
  76. if( i.y != i.x ) weight.y = weightTextures.getLod(vec3(uv, i.y), 0).r;
  77. if( i.z != i.x ) weight.z = weightTextures.getLod(vec3(uv, i.z), 0).r;
  78. return weight;
  79. }
  80. function getDepth( i : IVec3, uv : Vec2 ) : Vec3 {
  81. var depth = vec3(0);
  82. if( w.x > 0 ) depth.x = pbrTextures.getLod(getsurfaceUV(i.x, uv), 0).a;
  83. if( w.y > 0 ) depth.y = pbrTextures.getLod(getsurfaceUV(i.y, uv), 0).a;
  84. if( w.z > 0 ) depth.z = pbrTextures.getLod(getsurfaceUV(i.z, uv), 0).a;
  85. return 1 - depth;
  86. }
  87. var w : Vec3;
  88. var i : IVec3;
  89. function getPOMUV( i : IVec3, uv : Vec2 ) : Vec2 {
  90. var viewNS = normalize(camera.position - transformedPosition) * TBN;
  91. viewNS.xy /= viewNS.z;
  92. viewNS.x *= -1;
  93. var numLayers = mix(float(maxStep), float(minStep), viewNS.dot(terrainNormal));
  94. var layerDepth = 1.0 / numLayers;
  95. var curLayerDepth = 0.;
  96. var delta = (viewNS.xy * parallaxAmount / primSize) / numLayers;
  97. var curUV = uv;
  98. var depth = getDepth(i, curUV);
  99. var curDepth = depth.dot(w);
  100. var prevDepth = 0.;
  101. while( curLayerDepth < curDepth ) {
  102. curUV += delta;
  103. prevDepth = curDepth;
  104. i = ivec3(surfaceIndexMap.getLod(curUV, 0).rgb * 255);
  105. w = getWeight(i, curUV);
  106. depth = getDepth(i, curUV);
  107. curDepth = depth.dot(w);
  108. curLayerDepth += layerDepth;
  109. }
  110. var prevUV = curUV - delta;
  111. var after = curDepth - curLayerDepth;
  112. var before = prevDepth - curLayerDepth + layerDepth;
  113. var pomUV = mix(curUV, prevUV, after / (after - before));
  114. return pomUV;
  115. }
  116. function getsurfaceUV( id : Int, uv : Vec2 ) : Vec3 {
  117. uv = transformedPosition.xy + (uv * primSize - input.position.xy); // Local To world
  118. var angle = surfaceParams[id].w;
  119. var offset = vec2(surfaceParams[id].y, surfaceParams[id].z);
  120. var tilling = surfaceParams[id].x;
  121. var worldUV = uv * tilling + offset;
  122. var res = vec2( worldUV.x * cos(angle) - worldUV.y * sin(angle) , worldUV.y * cos(angle) + worldUV.x * sin(angle));
  123. var surfaceUV = vec3(res % 1, id);
  124. return surfaceUV;
  125. }
  126. function fragment() {
  127. if( CHECKER ) {
  128. var tile = abs(abs(floor(input.position.x)) % 2 - abs(floor(input.position.y)) % 2);
  129. pixelColor = vec4(mix(vec3(0.4), vec3(0.1), tile), 1.0);
  130. roughness = mix(0.1, 0.9, tile);
  131. metalness = mix(1.0, 0, tile);
  132. occlusion = 1;
  133. emissive = 0;
  134. }
  135. else if( COMPLEXITY ) {
  136. var blendCount = 0 + weightTextures.get(vec3(0)).r * 0;
  137. for(i in 0 ... SURFACE_COUNT)
  138. blendCount += ceil(weightTextures.get(vec3(calculatedUV, i)).r);
  139. pixelColor = vec4(mix(vec3(0,1,0), vec3(1,0,0), blendCount / 3.0) , 1);
  140. emissive = 1;
  141. roughness = 1;
  142. metalness = 0;
  143. occlusion = 1;
  144. }
  145. else {
  146. i = ivec3(surfaceIndexMap.get(calculatedUV).rgb * 255);
  147. w = getWeight(i, calculatedUV);
  148. var pomUV = PARALLAX ? getPOMUV(i, calculatedUV) : calculatedUV;
  149. if( PARALLAX ) {
  150. i = ivec3(surfaceIndexMap.get(pomUV).rgb * 255);
  151. w = getWeight(i, pomUV);
  152. }
  153. var h = vec3(0);
  154. var surfaceUV1 = getsurfaceUV(i.x, pomUV);
  155. var surfaceUV2 = getsurfaceUV(i.y, pomUV);
  156. var surfaceUV3 = getsurfaceUV(i.z, pomUV);
  157. var pbr1 = vec4(0), pbr2 = vec4(0), pbr3 = vec4(0);
  158. if( w.x > 0 ) pbr1 = pbrTextures.get(surfaceUV1).rgba;
  159. if( w.y > 0 ) pbr2 = pbrTextures.get(surfaceUV2).rgba;
  160. if( w.z > 0 ) pbr3 = pbrTextures.get(surfaceUV3).rgba;
  161. // Height Blend
  162. var h = vec3( secondSurfaceParams[i.x].x + pbr1.a * (secondSurfaceParams[i.x].y - secondSurfaceParams[i.x].x),
  163. secondSurfaceParams[i.y].x + pbr2.a * (secondSurfaceParams[i.y].y - secondSurfaceParams[i.y].x),
  164. secondSurfaceParams[i.z].x + pbr3.a * (secondSurfaceParams[i.z].y - secondSurfaceParams[i.z].x));
  165. var h = mix(vec3(1,1,1), h, heightBlendStrength);
  166. w *= h;
  167. // Sharpness
  168. var ws = mix(w, w, blendSharpness);
  169. var m = max(w.x, max(w.y, w.z));
  170. var mw = vec3(0,0,0);
  171. if( m == w.x ) mw = vec3(1,0,0);
  172. if( m == w.y ) mw = vec3(0,1,0);
  173. if( m == w.z ) mw = vec3(0,0,1);
  174. w = mix(w, mw, blendSharpness);
  175. // Blend
  176. var albedo = vec3(0);
  177. var normal = vec3(0);
  178. var pbr = vec4(0);
  179. if( w.x > 0 ) {
  180. albedo += albedoTextures.get(surfaceUV1).rgb * w.x;
  181. normal += unpackNormal(normalTextures.get(surfaceUV1).rgba) * w.x;
  182. pbr += pbr1 * w.x;
  183. }
  184. if( w.y > 0 ) {
  185. albedo += albedoTextures.get(surfaceUV2).rgb * w.y;
  186. normal += unpackNormal(normalTextures.get(surfaceUV2).rgba) * w.y;
  187. pbr += pbr2 * w.y;
  188. }
  189. if( w.z > 0 ) {
  190. albedo += albedoTextures.get(surfaceUV3).rgb * w.z;
  191. normal += unpackNormal(normalTextures.get(surfaceUV3).rgba) * w.z;
  192. pbr += pbr3 * w.z;
  193. }
  194. var wSum = w.x + w.y + w.z;
  195. albedo /= wSum;
  196. pbr /= wSum;
  197. normal /= wSum;
  198. normal = normal.normalize();
  199. // Output
  200. transformedNormal = normalize(normal * TBN);
  201. pixelColor = vec4(albedo, 1.0);
  202. roughness = 1 - pbr.g * pbr.g;
  203. metalness = pbr.r;
  204. occlusion = pbr.b;
  205. emissive = 0;
  206. }
  207. if( SHOW_GRID ) {
  208. var gridColor = vec4(1,0,0,1);
  209. var tileEdgeColor = vec4(1,1,0,1);
  210. var grid : Vec2 = ((input.position.xy.mod(cellSize.xy) / cellSize.xy ) - 0.5) * 2.0;
  211. grid = ceil(max(vec2(0), abs(grid) - 0.9));
  212. var tileEdge = max( (1 - ceil(input.position.xy / primSize - 0.1 / (primSize / cellSize) )), floor(input.position.xy / primSize + 0.1 / (primSize / cellSize)));
  213. emissive = max(max(grid.x, grid.y), max(tileEdge.x, tileEdge.y));
  214. pixelColor = mix( pixelColor, gridColor, clamp(0,1,max(grid.x, grid.y)));
  215. pixelColor = mix( pixelColor, tileEdgeColor, clamp(0,1,max(tileEdge.x, tileEdge.y)));
  216. metalness = mix(metalness, 0, emissive);
  217. roughness = mix(roughness, 1, emissive);
  218. occlusion = mix(occlusion, 1, emissive);
  219. transformedNormal = mix(transformedNormal, vec3(0,1,0), emissive);
  220. }
  221. }
  222. };
  223. }