You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

391 lines
13 KiB

5 years ago
  1. Shader "Fog Volume/Skybox" {
  2. Properties {
  3. [hideininspector][KeywordEnum(None, Miranda, High Quality)] _SunDisk ("Sun", Int) = 1
  4. _SunSize ("Sun Size", Range(0,1)) = 0.04
  5. _SunDiscEdges("Sun Disk Edges", Range(.5,3)) = 1
  6. _SunIntensity("Sun Intensity", Range(0,1000)) = 40
  7. _AtmosphereThickness ("Atmoshpere Thickness", Range(0,5)) = 1.0
  8. _SkyTint ("Sky Tint", Color) = (.5, .5, .5, 1)
  9. _GroundColor ("Ground", Color) = (.369, .349, .341, 1)
  10. _Exposure("Exposure", Range(0, 8)) = 1.3
  11. }
  12. SubShader {
  13. Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
  14. Cull Off ZWrite Off
  15. Pass {
  16. CGPROGRAM
  17. #pragma vertex vert
  18. #pragma fragment frag
  19. #include "UnityCG.cginc"
  20. #include "Lighting.cginc"
  21. #pragma multi_compile _SUNDISK_NONE _SUNDISK_SIMPLE _SUNDISK_HIGH_QUALITY
  22. uniform half _Exposure; // HDR exposure
  23. uniform half3 _GroundColor;
  24. uniform half _SunSize;
  25. uniform half3 _SkyTint;
  26. uniform half _AtmosphereThickness;
  27. uniform half _SunIntensity, _SunDiscEdges;
  28. #if defined(UNITY_COLORSPACE_GAMMA)
  29. #define GAMMA 2
  30. #define COLOR_2_GAMMA(color) color
  31. #define COLOR_2_LINEAR(color) color*color
  32. #define LINEAR_2_OUTPUT(color) sqrt(color)
  33. #else
  34. #define GAMMA 2.2
  35. // HACK: to get gfx-tests in Gamma mode to agree until UNITY_ACTIVE_COLORSPACE_IS_GAMMA is working properly
  36. #define COLOR_2_GAMMA(color) ((unity_ColorSpaceDouble.r>2.0) ? pow(color,1.0/GAMMA) : color)
  37. #define COLOR_2_LINEAR(color) color
  38. #define LINEAR_2_LINEAR(color) color
  39. #endif
  40. // RGB wavelengths
  41. // .35 (.62=158), .43 (.68=174), .525 (.75=190)
  42. static const float3 kDefaultScatteringWavelength = float3(.65, .57, .475);
  43. static const float3 kVariableRangeForScatteringWavelength = float3(.15, .15, .15);
  44. #define OUTER_RADIUS 1.025
  45. static const float kOuterRadius = OUTER_RADIUS;
  46. static const float kOuterRadius2 = OUTER_RADIUS*OUTER_RADIUS;
  47. static const float kInnerRadius = 1.0;
  48. static const float kInnerRadius2 = 1.0;
  49. static const float kCameraHeight = 0.0001;
  50. #define kRAYLEIGH (lerp(0, 0.0025, pow(_AtmosphereThickness,2.5))) // Rayleigh constant
  51. #define kMIE 0.0010 // Mie constant
  52. #define kSUN_BRIGHTNESS 20.0 // Sun brightness
  53. #define kMAX_SCATTER 50.0 // Maximum scattering value, to prevent math overflows on Adrenos
  54. static const half kSunScale = 400.0 * kSUN_BRIGHTNESS;
  55. static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
  56. static const float kKm4PI = kMIE * 4.0 * 3.14159265;
  57. static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
  58. static const float kScaleDepth = 0.25;
  59. static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
  60. static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
  61. #define MIE_G (-0.990)
  62. #define MIE_G2 0.9801
  63. #define SKY_GROUND_THRESHOLD 0.02
  64. // fine tuning of performance. You can override defines here if you want some specific setup
  65. // or keep as is and allow later code to set it according to target api
  66. // if set vprog will output color in final color space (instead of linear always)
  67. // 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
  68. // #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
  69. // sun disk rendering:
  70. // no sun disk - the fastest option
  71. #define SKYBOX_SUNDISK_NONE 0
  72. // simplistic sun disk - without mie phase function
  73. #define SKYBOX_SUNDISK_SIMPLE 1
  74. // full calculation - uses mie phase function
  75. #define SKYBOX_SUNDISK_HQ 2
  76. // uncomment this line and change SKYBOX_SUNDISK_SIMPLE to override material settings
  77. // #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
  78. #ifndef SKYBOX_SUNDISK
  79. #if defined(_SUNDISK_NONE)
  80. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_NONE
  81. #elif defined(_SUNDISK_SIMPLE)
  82. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
  83. #else
  84. #define SKYBOX_SUNDISK SKYBOX_SUNDISK_HQ
  85. #endif
  86. #endif
  87. #ifndef SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  88. #if defined(SHADER_API_MOBILE)
  89. #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 1
  90. #else
  91. #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
  92. #endif
  93. #endif
  94. // Calculates the Rayleigh phase function
  95. half getRayleighPhase(half eyeCos2)
  96. {
  97. return 0.75 + 0.75*eyeCos2;
  98. }
  99. half getRayleighPhase(half3 light, half3 ray)
  100. {
  101. half eyeCos = dot(light, ray);
  102. return getRayleighPhase(eyeCos * eyeCos);
  103. }
  104. struct appdata_t
  105. {
  106. float4 vertex : POSITION;
  107. };
  108. struct v2f
  109. {
  110. float4 pos : SV_POSITION;
  111. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  112. // for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
  113. half3 vertex : TEXCOORD0;
  114. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  115. half3 rayDir : TEXCOORD0;
  116. #else
  117. // as we dont need sun disk we need just rayDir.y (sky/ground threshold)
  118. half skyGroundFactor : TEXCOORD0;
  119. #endif
  120. // calculate sky colors in vprog
  121. half3 groundColor : TEXCOORD1;
  122. half3 skyColor : TEXCOORD2;
  123. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  124. half3 sunColor : TEXCOORD3;
  125. #endif
  126. };
  127. float scale(float inCos)
  128. {
  129. float x = 1.0 - inCos;
  130. #if defined(SHADER_API_N3DS)
  131. // The polynomial expansion here generates too many swizzle instructions for the 3DS vertex assembler
  132. // Approximate by removing x^1 and x^2
  133. return 0.25 * exp(-0.00287 + x*x*x*(-6.80 + x*5.25));
  134. #else
  135. return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
  136. #endif
  137. }
  138. v2f vert (appdata_t v)
  139. {
  140. v2f OUT;
  141. OUT.pos = UnityObjectToClipPos(v.vertex);
  142. float3 kSkyTintInGammaSpace = COLOR_2_GAMMA(_SkyTint); // convert tint from Linear back to Gamma
  143. float3 kScatteringWavelength = lerp (
  144. kDefaultScatteringWavelength-kVariableRangeForScatteringWavelength,
  145. kDefaultScatteringWavelength+kVariableRangeForScatteringWavelength,
  146. 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
  147. float3 kInvWavelength = 1.0 / pow(kScatteringWavelength, 4);
  148. float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
  149. float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
  150. float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0); // The camera's current position
  151. // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
  152. float3 eyeRay = normalize(mul((float3x3)unity_ObjectToWorld, v.vertex.xyz));
  153. float far = 0.0;
  154. half3 cIn, cOut;
  155. if(eyeRay.y >= 0.0)
  156. {
  157. // Sky
  158. // Calculate the length of the "atmosphere"
  159. far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
  160. float3 pos = cameraPos + far * eyeRay;
  161. // Calculate the ray's starting position, then calculate its scattering offset
  162. float height = kInnerRadius + kCameraHeight;
  163. float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
  164. float startAngle = dot(eyeRay, cameraPos) / height;
  165. float startOffset = depth*scale(startAngle);
  166. // Initialize the scattering loop variables
  167. float sampleLength = far / kSamples;
  168. float scaledLength = sampleLength * kScale;
  169. float3 sampleRay = eyeRay * sampleLength;
  170. float3 samplePoint = cameraPos + sampleRay * 0.5;
  171. // Now loop through the sample rays
  172. float3 frontColor = float3(0.0, 0.0, 0.0);
  173. // Weird workaround: WP8 and desktop FL_9_1 do not like the for loop here
  174. // (but an almost identical loop is perfectly fine in the ground calculations below)
  175. // Just unrolling this manually seems to make everything fine again.
  176. // for(int i=0; i<int(kSamples); i++)
  177. {
  178. float height = length(samplePoint);
  179. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  180. float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
  181. float cameraAngle = dot(eyeRay, samplePoint) / height;
  182. float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
  183. float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  184. frontColor += attenuate * (depth * scaledLength);
  185. samplePoint += sampleRay;
  186. }
  187. {
  188. float height = length(samplePoint);
  189. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  190. float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
  191. float cameraAngle = dot(eyeRay, samplePoint) / height;
  192. float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
  193. float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  194. frontColor += attenuate * (depth * scaledLength);
  195. samplePoint += sampleRay;
  196. }
  197. // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
  198. cIn = frontColor * (kInvWavelength * kKrESun);
  199. cOut = frontColor * kKmESun;
  200. }
  201. else
  202. {
  203. // Ground
  204. far = (-kCameraHeight) / (min(-0.001, eyeRay.y));
  205. float3 pos = cameraPos + far * eyeRay;
  206. // Calculate the ray's starting position, then calculate its scattering offset
  207. float depth = exp((-kCameraHeight) * (1.0/kScaleDepth));
  208. float cameraAngle = dot(-eyeRay, pos);
  209. float lightAngle = dot(_WorldSpaceLightPos0.xyz, pos);
  210. float cameraScale = scale(cameraAngle);
  211. float lightScale = scale(lightAngle);
  212. float cameraOffset = depth*cameraScale;
  213. float temp = (lightScale + cameraScale);
  214. // Initialize the scattering loop variables
  215. float sampleLength = far / kSamples;
  216. float scaledLength = sampleLength * kScale;
  217. float3 sampleRay = eyeRay * sampleLength;
  218. float3 samplePoint = cameraPos + sampleRay * 0.5;
  219. // Now loop through the sample rays
  220. float3 frontColor = float3(0.0, 0.0, 0.0);
  221. float3 attenuate;
  222. // 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.
  223. {
  224. float height = length(samplePoint);
  225. float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
  226. float scatter = depth*temp - cameraOffset;
  227. attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
  228. frontColor += attenuate * (depth * scaledLength);
  229. samplePoint += sampleRay;
  230. }
  231. cIn = frontColor * (kInvWavelength * kKrESun + kKmESun);
  232. cOut = clamp(attenuate, 0.0, 1.0);
  233. }
  234. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  235. OUT.vertex = -v.vertex;
  236. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  237. OUT.rayDir = half3(-eyeRay);
  238. #else
  239. OUT.skyGroundFactor = -eyeRay.y / SKY_GROUND_THRESHOLD;
  240. #endif
  241. // if we want to calculate color in vprog:
  242. // 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)
  243. // 2. in case of gamma and SKYBOX_COLOR_IN_TARGET_COLOR_SPACE: do sqrt right away instead of doing that in fshader
  244. OUT.groundColor = _Exposure * (cIn + COLOR_2_LINEAR(_GroundColor) * cOut);
  245. OUT.skyColor = _Exposure * (cIn * getRayleighPhase(_WorldSpaceLightPos0.xyz, -eyeRay));
  246. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  247. OUT.sunColor = _Exposure * (cOut * _LightColor0.xyz);
  248. #endif
  249. #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  250. OUT.groundColor = sqrt(OUT.groundColor);
  251. OUT.skyColor = sqrt(OUT.skyColor);
  252. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  253. OUT.sunColor= sqrt(OUT.sunColor);
  254. #endif
  255. #endif
  256. return OUT;
  257. }
  258. // Calculates the Mie phase function
  259. half getMiePhase(half eyeCos, half eyeCos2)
  260. {
  261. half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
  262. temp = pow(temp, pow(_SunSize,0.65) * 10);
  263. temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
  264. temp = 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
  265. #if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  266. temp = pow(temp, .454545);
  267. #endif
  268. return temp;
  269. }
  270. half calcSunSpot(half3 vec1, half3 vec2)
  271. {
  272. half3 delta = vec1 - vec2;
  273. half dist = length(delta);
  274. half spot = 1.0 - smoothstep(0.0, _SunSize, dist);
  275. return kSunScale * spot * spot;
  276. }
  277. half4 frag (v2f IN) : SV_Target
  278. {
  279. half3 col = half3(0.0, 0.0, 0.0);
  280. // if y > 1 [eyeRay.y < -SKY_GROUND_THRESHOLD] - ground
  281. // if y >= 0 and < 1 [eyeRay.y <= 0 and > -SKY_GROUND_THRESHOLD] - horizon
  282. // if y < 0 [eyeRay.y > 0] - sky
  283. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
  284. half3 ray = normalize(mul((float3x3)unity_ObjectToWorld, IN.vertex));
  285. half y = ray.y / SKY_GROUND_THRESHOLD;
  286. #elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  287. half3 ray = IN.rayDir.xyz;
  288. half y = ray.y / SKY_GROUND_THRESHOLD;
  289. #else
  290. half y = IN.skyGroundFactor;
  291. #endif
  292. // if we did precalculate color in vprog: just do lerp between them
  293. col = lerp(IN.skyColor, IN.groundColor, saturate(y));
  294. #if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
  295. if(y < 0.0)
  296. {
  297. #if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
  298. half mie = calcSunSpot(_WorldSpaceLightPos0.xyz, -ray);
  299. #else // SKYBOX_SUNDISK_HQ
  300. half eyeCos = dot(_WorldSpaceLightPos0.xyz, ray);
  301. half eyeCos2 = eyeCos * eyeCos;
  302. half mie = getMiePhase(eyeCos, eyeCos2);
  303. #endif
  304. mie = pow(mie,_SunDiscEdges);
  305. mie = min(_SunIntensity, mie);
  306. col += mie * IN.sunColor;
  307. }
  308. #endif
  309. #if defined(UNITY_COLORSPACE_GAMMA) && !SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
  310. col = LINEAR_2_OUTPUT(col);
  311. #endif
  312. return half4(col,1.0);
  313. }
  314. ENDCG
  315. }
  316. }
  317. Fallback Off
  318. }