Skybox_Proc.shader 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. Shader "GapperGames/Skybox_Proc" {
  2. Properties
  3. {
  4. _SunSize("Sun Size", Range(0,1)) = 0.04
  5. _SunSizeConvergence("Sun Size Convergence", Range(1,10)) = 5
  6. }
  7. SubShader{
  8. Tags { "Queue" = "Background" "RenderType" = "Background" "PreviewType" = "Skybox" }
  9. Cull Off ZWrite Off
  10. Pass {
  11. CGPROGRAM
  12. #pragma vertex vert
  13. #pragma fragment frag
  14. #include "UnityCG.cginc"
  15. #include "Lighting.cginc"
  16. #pragma multi_compile_local _SUNDISK_NONE _SUNDISK_SIMPLE _SUNDISK_HIGH_QUALITY
  17. uniform half _Exposure; // HDR exposure
  18. uniform half3 _GroundColor;
  19. uniform half _SunSize;
  20. uniform half _SunSizeConvergence;
  21. uniform half3 _SkyTint;
  22. uniform half _AtmosphereThickness;
  23. #if defined(UNITY_COLORSPACE_GAMMA)
  24. #define GAMMA 2
  25. #define COLOR_2_GAMMA(color) color
  26. #define COLOR_2_LINEAR(color) color*color
  27. #define LINEAR_2_OUTPUT(color) sqrt(color)
  28. #else
  29. #define GAMMA 2.2
  30. // HACK: to get gfx-tests in Gamma mode to agree until UNITY_ACTIVE_COLORSPACE_IS_GAMMA is working properly
  31. #define COLOR_2_GAMMA(color) ((unity_ColorSpaceDouble.r>2.0) ? pow(color,1.0/GAMMA) : color)
  32. #define COLOR_2_LINEAR(color) color
  33. #define LINEAR_2_LINEAR(color) color
  34. #endif
  35. // RGB wavelengths
  36. // .35 (.62=158), .43 (.68=174), .525 (.75=190)
  37. static const float3 kDefaultScatteringWavelength = float3(.65, .57, .475);
  38. static const float3 kVariableRangeForScatteringWavelength = float3(.15, .15, .15);
  39. #define OUTER_RADIUS 1.025
  40. static const float kOuterRadius = OUTER_RADIUS;
  41. static const float kOuterRadius2 = OUTER_RADIUS * OUTER_RADIUS;
  42. static const float kInnerRadius = 1.0;
  43. static const float kInnerRadius2 = 1.0;
  44. static const float kCameraHeight = 0.0001;
  45. #define kRAYLEIGH (lerp(0.0, 0.0025, pow(_AtmosphereThickness,2.5))) // Rayleigh constant
  46. #define kMIE 0.0010 // Mie constant
  47. #define kSUN_BRIGHTNESS 20.0 // Sun brightness
  48. #define kMAX_SCATTER 50.0 // Maximum scattering value, to prevent math overflows on Adrenos
  49. static const half kHDSundiskIntensityFactor = 15.0;
  50. static const half kSimpleSundiskIntensityFactor = 27.0;
  51. static const half kSunScale = 400.0 * kSUN_BRIGHTNESS;
  52. static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
  53. static const float kKm4PI = kMIE * 4.0 * 3.14159265;
  54. static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
  55. static const float kScaleDepth = 0.25;
  56. static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
  57. static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
  58. #define MIE_G (-0.990)
  59. #define MIE_G2 0.9801
  60. #define SKY_GROUND_THRESHOLD 0.02
  61. // fine tuning of performance. You can override defines here if you want some specific setup
  62. // or keep as is and allow later code to set it according to target api
  63. // if set vprog will output color in final color space (instead of linear always)
  64. // in case of rendering in gamma mode that means that we will do lerps in gamma mode too, so there will be tiny difference around horizon
  65. // #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
  66. // sun disk rendering:
  67. // no sun disk - the fastest option
  68. #define SKYBOX_SUNDISK_NONE 0
  69. // simplistic sun disk - without mie phase function
  70. #define SKYBOX_SUNDISK_SIMPLE 1
  71. // full calculation - uses mie phase function
  72. #define SKYBOX_SUNDISK_HQ 2
  73. // uncomment this line and change SKYBOX_SUNDISK_SIMPLE to override material settings
  74. // #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
  75. #ifndef SKYBOX_SUNDISK
  76. #if defined(_SUNDISK_NONE)
  77. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_NONE
  78. #elif defined(_SUNDISK_SIMPLE)
  79. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
  80. #else
  81. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_HQ
  82. #endif
  83. #endif
  84. #ifndef SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  85. #if defined(SHADER_API_MOBILE)
  86. #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 1
  87. #else
  88. #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
  89. #endif
  90. #endif
  91. // Calculates the Rayleigh phase function
  92. half getRayleighPhase(half eyeCos2)
  93. {
  94. return 0.75 + 0.75 * eyeCos2;
  95. }
  96. half getRayleighPhase(half3 light, half3 ray)
  97. {
  98. half eyeCos = dot(light, ray);
  99. return getRayleighPhase(eyeCos * eyeCos);
  100. }
  101. struct appdata_t
  102. {
  103. float4 vertex : POSITION;
  104. UNITY_VERTEX_INPUT_INSTANCE_ID
  105. };
  106. struct v2f
  107. {
  108. float4 pos : SV_POSITION;
  109. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  110. // for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
  111. float3 vertex : TEXCOORD0;
  112. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  113. half3 rayDir : TEXCOORD0;
  114. #else
  115. // as we dont need sun disk we need just rayDir.y (sky/ground threshold)
  116. half skyGroundFactor : TEXCOORD0;
  117. #endif
  118. // calculate sky colors in vprog
  119. half3 groundColor : TEXCOORD1;
  120. half3 skyColor : TEXCOORD2;
  121. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  122. half3 sunColor : TEXCOORD3;
  123. #endif
  124. UNITY_VERTEX_OUTPUT_STEREO
  125. };
  126. float scale(float inCos)
  127. {
  128. float x = 1.0 - inCos;
  129. return 0.25 * exp(-0.00287 + x * (0.459 + x * (3.83 + x * (-6.80 + x * 5.25))));
  130. }
  131. v2f vert(appdata_t v)
  132. {
  133. v2f OUT;
  134. UNITY_SETUP_INSTANCE_ID(v);
  135. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
  136. OUT.pos = UnityObjectToClipPos(v.vertex);
  137. float3 lightPos = float3(_WorldSpaceLightPos0.x, abs(_WorldSpaceLightPos0.y), _WorldSpaceLightPos0.z);
  138. float skyTime = abs(dot(_WorldSpaceLightPos0.xyz, float3(0, 1, 0)));
  139. skyTime = pow(1 - skyTime, 3);
  140. //float skyTime2 = max(dot(_WorldSpaceLightPos0.xyz, float3(0, 1, 0)), 0);
  141. //skyTime2 = pow(1 - skyTime2, 3);
  142. float skyTime2 = dot(_WorldSpaceLightPos0.xyz, float3(0, 1, 0));
  143. skyTime2 = smoothstep(-0.2, 0.4, -skyTime2);
  144. //_SkyTint = lerp(0.5, 1, skyTime);
  145. _AtmosphereThickness = lerp(1, 1.5, skyTime);
  146. //_AtmosphereThickness *= 1 - (min(_WorldSpaceCameraPos.y, 5000) / 5000);
  147. _AtmosphereThickness = max(_AtmosphereThickness, 0);
  148. _Exposure = lerp(4, 0.8, skyTime2);
  149. _SkyTint = 0.5;
  150. float3 kSkyTintInGammaSpace = COLOR_2_GAMMA(_SkyTint); // convert tint from Linear back to Gamma
  151. float3 kScatteringWavelength = lerp(
  152. kDefaultScatteringWavelength - kVariableRangeForScatteringWavelength,
  153. kDefaultScatteringWavelength + kVariableRangeForScatteringWavelength,
  154. half3(1,1,1) - kSkyTintInGammaSpace); // using Tint in sRGB gamma allows for more visually linear interpolation and to keep (.5) at (128, gray in sRGB) point
  155. float3 kInvWavelength = 1.0 / pow(kScatteringWavelength, 4);
  156. float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
  157. float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
  158. float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0); // The camera's current position
  159. // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
  160. float3 eyeRay = normalize(mul((float3x3)unity_ObjectToWorld, v.vertex.xyz));
  161. float far = 0.0;
  162. half3 cIn, cOut;
  163. if (eyeRay.y >= 0.0)
  164. {
  165. // Sky
  166. // Calculate the length of the "atmosphere"
  167. far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
  168. float3 pos = cameraPos + far * eyeRay;
  169. // Calculate the ray's starting position, then calculate its scattering offset
  170. float height = kInnerRadius + kCameraHeight;
  171. float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
  172. float startAngle = dot(eyeRay, cameraPos) / height;
  173. float startOffset = depth * scale(startAngle);
  174. // Initialize the scattering loop variables
  175. float sampleLength = far / kSamples;
  176. float scaledLength = sampleLength * kScale;
  177. float3 sampleRay = eyeRay * sampleLength;
  178. float3 samplePoint = cameraPos + sampleRay * 0.5;
  179. // Now loop through the sample rays
  180. float3 frontColor = float3(0.0, 0.0, 0.0);
  181. // Weird workaround: WP8 and desktop FL_9_3 do not like the for loop here
  182. // (but an almost identical loop is perfectly fine in the ground calculations below)
  183. // Just unrolling this manually seems to make everything fine again.
  184. // for(int i=0; i<int(kSamples); i++)
  185. {
  186. float height = length(samplePoint);
  187. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  188. float lightAngle = dot(lightPos.xyz, samplePoint) / height;
  189. float cameraAngle = dot(eyeRay, samplePoint) / height;
  190. float scatter = (startOffset + depth * (scale(lightAngle) - scale(cameraAngle)));
  191. float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  192. frontColor += attenuate * (depth * scaledLength);
  193. samplePoint += sampleRay;
  194. }
  195. {
  196. float height = length(samplePoint);
  197. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  198. float lightAngle = dot(lightPos.xyz, samplePoint) / height;
  199. float cameraAngle = dot(eyeRay, samplePoint) / height;
  200. float scatter = (startOffset + depth * (scale(lightAngle) - scale(cameraAngle)));
  201. float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  202. frontColor += attenuate * (depth * scaledLength);
  203. samplePoint += sampleRay;
  204. }
  205. // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
  206. cIn = frontColor * (kInvWavelength * kKrESun);
  207. cOut = frontColor * kKmESun;
  208. }
  209. else
  210. {
  211. // Ground
  212. far = (-kCameraHeight) / (min(-0.001, eyeRay.y));
  213. float3 pos = cameraPos + far * eyeRay;
  214. // Calculate the ray's starting position, then calculate its scattering offset
  215. float depth = exp((-kCameraHeight) * (1.0 / kScaleDepth));
  216. float cameraAngle = dot(-eyeRay, pos);
  217. float lightAngle = dot(lightPos.xyz, pos);
  218. float cameraScale = scale(cameraAngle);
  219. float lightScale = scale(lightAngle);
  220. float cameraOffset = depth * cameraScale;
  221. float temp = (lightScale + cameraScale);
  222. // Initialize the scattering loop variables
  223. float sampleLength = far / kSamples;
  224. float scaledLength = sampleLength * kScale;
  225. float3 sampleRay = eyeRay * sampleLength;
  226. float3 samplePoint = cameraPos + sampleRay * 0.5;
  227. // Now loop through the sample rays
  228. float3 frontColor = float3(0.0, 0.0, 0.0);
  229. float3 attenuate;
  230. //for(int i=0; i<int(kSamples); i++) // Loop removed because we kept hitting SM2.0 temp variable limits. Doesn't affect the image too much.
  231. {
  232. float height = length(samplePoint);
  233. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  234. float scatter = depth * temp - cameraOffset;
  235. attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  236. frontColor += attenuate * (depth * scaledLength);
  237. samplePoint += sampleRay;
  238. }
  239. cIn = frontColor * (kInvWavelength * kKrESun + kKmESun);
  240. cOut = clamp(attenuate, 0.0, 1.0);
  241. }
  242. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  243. OUT.vertex = -eyeRay;
  244. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  245. OUT.rayDir = half3(-eyeRay);
  246. #else
  247. OUT.skyGroundFactor = -eyeRay.y / SKY_GROUND_THRESHOLD;
  248. #endif
  249. // if we want to calculate color in vprog:
  250. // 1. in case of linear: multiply by _Exposure in here (even in case of lerp it will be common multiplier, so we can skip mul in fshader)
  251. // 2. in case of gamma and SKYBOX_COLOR_IN_TARGET_COLOR_SPACE: do sqrt right away instead of doing that in fshader
  252. //OUT.groundColor = _Exposure * (cIn + COLOR_2_LINEAR(_GroundColor) * cOut);
  253. OUT.groundColor = 0;
  254. //float3 lightPos = float3(_WorldSpaceLightPos0.x, abs(_WorldSpaceLightPos0.y), _WorldSpaceLightPos0.z);
  255. float brightness = dot(_WorldSpaceLightPos0.xyz, float3(0, 1, 0));
  256. //brightness += 1;
  257. //brightness *= 0.5;
  258. //brightness = smoothstep(0.4, 0.6, brightness);
  259. //brightness = smoothstep(-0.2, 0.2, -brightness);
  260. brightness = 1 - max(-brightness, 0);
  261. brightness = pow(brightness, 5);
  262. brightness *= 0.85;
  263. brightness += 0.15;
  264. OUT.skyColor = _Exposure * (cIn * getRayleighPhase(lightPos, -eyeRay)) * brightness;
  265. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  266. // The sun should have a stable intensity in its course in the sky. Moreover it should match the highlight of a purely specular material.
  267. // This matching was done using the standard shader BRDF1 on the 5/31/2017
  268. // Finally we want the sun to be always bright even in LDR thus the normalization of the lightColor for low intensity.
  269. half lightColorIntensity = clamp(length(_LightColor0.xyz), 0.25, 1);
  270. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  271. OUT.sunColor = kSimpleSundiskIntensityFactor * saturate(cOut * kSunScale) * _LightColor0.xyz / lightColorIntensity;
  272. #else // SKYBOX_SUNDISK_HQ
  273. OUT.sunColor = kHDSundiskIntensityFactor * saturate(cOut) * _LightColor0.xyz / lightColorIntensity;
  274. #endif
  275. #endif
  276. #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  277. OUT.groundColor = sqrt(OUT.groundColor);
  278. OUT.skyColor = sqrt(OUT.skyColor);
  279. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  280. OUT.sunColor = sqrt(OUT.sunColor);
  281. #endif
  282. #endif
  283. return OUT;
  284. }
  285. // Calculates the Mie phase function
  286. half getMiePhase(half eyeCos, half eyeCos2)
  287. {
  288. half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
  289. temp = pow(temp, pow(_SunSize,0.65) * 10);
  290. temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
  291. temp = 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
  292. #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  293. temp = pow(temp, .454545);
  294. #endif
  295. return temp;
  296. }
  297. // Calculates the sun shape
  298. half calcSunAttenuation(half3 lightPos, half3 ray)
  299. {
  300. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  301. half3 delta = lightPos - ray;
  302. half dist = length(delta);
  303. half spot = 1.0 - smoothstep(0.0, _SunSize, dist);
  304. return spot * spot;
  305. #else // SKYBOX_SUNDISK_HQ
  306. half focusedEyeCos = pow(saturate(dot(lightPos, ray)), _SunSizeConvergence);
  307. return getMiePhase(-focusedEyeCos, focusedEyeCos * focusedEyeCos);
  308. #endif
  309. }
  310. float random(float2 uv)
  311. {
  312. float rnd = frac(sin(dot(uv.xy, float2(12.9898, 78.233))) * 43758.5453123);
  313. return rnd;
  314. }
  315. float cellular(float2 uv, float columns, float rows)
  316. {
  317. float2 index_uv = floor(float2(uv.x * columns, uv.y * rows));
  318. float2 fract_uv = frac(float2(uv.x * columns, uv.y * rows));
  319. float minimum_dist = 1.0;
  320. [unroll]
  321. for (int y = -1; y <= 1; y++)
  322. {
  323. for (int x = -1; x <= 1; x++)
  324. {
  325. float2 neighbor = float2(float(x), float(y));
  326. float2 _point = random(index_uv + neighbor);
  327. float2 diff = neighbor + _point - fract_uv;
  328. float dist = length(diff);
  329. minimum_dist = min(minimum_dist, dist);
  330. }
  331. }
  332. return minimum_dist;
  333. }
  334. half4 frag(v2f IN) : SV_Target
  335. {
  336. half3 col = half3(0.0, 0.0, 0.0);
  337. // if y > 1 [eyeRay.y < -SKY_GROUND_THRESHOLD] - ground
  338. // if y >= 0 and < 1 [eyeRay.y <= 0 and > -SKY_GROUND_THRESHOLD] - horizon
  339. // if y < 0 [eyeRay.y > 0] - sky
  340. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  341. half3 ray = normalize(IN.vertex.xyz);
  342. half y = ray.y / SKY_GROUND_THRESHOLD;
  343. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  344. half3 ray = IN.rayDir.xyz;
  345. half y = ray.y / SKY_GROUND_THRESHOLD;
  346. #else
  347. half y = IN.skyGroundFactor;
  348. #endif
  349. float uvY = (normalize(ray).y * 0.65) + 0.35;
  350. float2 uv = normalize(ray).xz / uvY;
  351. float stars = step(cellular((uv * 0.1) + (_Time * 0.005), 10, 10), 0.01);
  352. float starTime = dot(_WorldSpaceLightPos0.xyz, float3(0, 1, 0));
  353. starTime = max(starTime, 0);
  354. starTime = pow(1 - starTime, 5);
  355. stars *= starTime;
  356. // if we did precalculate color in vprog: just do lerp between them
  357. col = lerp(IN.skyColor, IN.groundColor, saturate(y));
  358. col += stars;
  359. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  360. if (y < 0.0)
  361. {
  362. col += IN.sunColor * calcSunAttenuation(_WorldSpaceLightPos0.xyz, -ray);
  363. }
  364. #endif
  365. #if defined(UNITY_COLORSPACE_GAMMA) && !SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  366. col = LINEAR_2_OUTPUT(col);
  367. #endif
  368. return half4(col,1.0);
  369. }
  370. ENDCG
  371. }
  372. }
  373. Fallback Off
  374. }