NishitaSky - 图1

    1. // Nishita Sky Adapted for Redshift
    2. // Base Nishita implementation built by Ben Simonds - https://github.com/BenSimonds/NishitaSky/blob/master/NishitaAtmos.osl
    3. /**
    4. Modified: 2021-03-23 by Nischal Pokhrel for Redshift 3D.
    5. This file is licensed under Apache 2.0 license
    6. Added: Z-Up | Light rotation inputs | Sun, Sky & Mie tint controls | Sun disc + intensity, size & tint control | Exposure, saturation & gamma control
    7. Aces mode | Faux ozone | Stars based on elevation and height.
    8. // Starfield based on shader by Thomas Dinges from https://github.com/sambler/osl-shaders
    9. **/
    10. #include <stdosl.h>
    11. int has_solutions (vector StartPoint, vector UnitVector, float Radius) {
    12. float b = 2 * dot (UnitVector, StartPoint);
    13. float bsquared = pow(b,2);
    14. float c = dot (StartPoint,StartPoint) - pow(Radius,2);
    15. float fourac = 4 * c;
    16. if (bsquared > fourac) {
    17. return 1;
    18. } else {
    19. return 0;
    20. }
    21. }
    22. int linetype (vector StartPoint, vector UnitVector, float Radius) {
    23. float b = 2 * dot (UnitVector, StartPoint);
    24. float bsquared = pow(b,2);
    25. float c = dot (StartPoint,StartPoint) - pow(Radius,2);
    26. float fourac = 4 * c;
    27. if (bsquared > fourac) {
    28. float d1 = (-b + pow(bsquared - fourac, 0.5)) / 2;
    29. float d2 = (-b - pow(bsquared - fourac, 0.5)) / 2;
    30. if (d1 < 0 && d2 < 0) {
    31. return 2;
    32. } else if (d1 < 0 || d2 < 0) {
    33. return 1;
    34. } else {
    35. return 0;
    36. }
    37. } else {
    38. return 3;
    39. }
    40. }
    41. vector lmin (vector StartPoint, vector UnitVector, float Radius) {
    42. float b = 2 * dot (UnitVector, StartPoint);
    43. float bsquared = pow(b,2);
    44. float c = dot (StartPoint,StartPoint) - pow(Radius,2);
    45. float fourac = 4 * c;
    46. if (bsquared > fourac) {
    47. float d1 = (-b + pow(bsquared - fourac, 0.5)) / 2;
    48. float d2 = (-b - pow(bsquared - fourac, 0.5)) / 2;
    49. return StartPoint + UnitVector * min(d1, d2);
    50. } else {
    51. return vector(0);
    52. }
    53. }
    54. vector lmax (vector StartPoint, vector UnitVector, float Radius) {
    55. float b = 2 * dot (UnitVector, StartPoint);
    56. float bsquared = pow(b,2);
    57. float c = dot (StartPoint,StartPoint) - pow(Radius,2);
    58. float fourac = 4 * c;
    59. if (bsquared > fourac) {
    60. float d1 = (-b + pow(bsquared - fourac, 0.5)) / 2;
    61. float d2 = (-b - pow(bsquared - fourac, 0.5)) / 2;
    62. return StartPoint + UnitVector * max(d1, d2);
    63. } else {
    64. return vector(0);
    65. }
    66. }
    67. color softLight (color a, color b){
    68. color ab = 0.0;
    69. if (length(b) < 0.5){
    70. ab = 2*(a*b) + (a*a) * (1-(2*b));
    71. }
    72. else {
    73. ab = sqrt(a) * ((2*b) - 1) + ((2*a) * (1-b));
    74. }
    75. return (ab);
    76. }
    77. struct linesample {
    78. int has_solutions;
    79. int type;
    80. vector min;
    81. vector max;
    82. };
    83. shader nishita_sky
    84. [[ string help = "Nishita Sky Model for Environments",
    85. string label = "Nishita Sky" ]]
    86. (
    87. float Intensity = 20 [[string page = "1: Color Settings"]],
    88. float Exposure = 0 [[float slidermin = -16, float slidermax = 16, int connectable = 0, string page = "1: Color Settings"]],
    89. float Gamma = 1 [[float slidermin = 0, float slidermax = 2, int connectable = 0, string page = "1: Color Settings"]],
    90. float Saturation = 1 [[float slidermin = 0, float slidermax = 1.5, int connectable = 0, string page = "1: Color Settings"]],
    91. float discIntensity = 10 [[float slidermin = 0, float slidermax = 100, string label = "Disc Intensity", string page = "1: Color Settings"]],
    92. color discColor = color(1,1,1) [[string label = "Disc Tint", string page = "1: Color Settings"]],
    93. color sunTint = color(1, 1, 1) [[string label = "Sun Halo Tint", string page = "1: Color Settings"]],
    94. color skyTint = color(1, 1, 1) [[string label = "Sky Tint", string page = "1: Color Settings"]],
    95. int convToAces = 0 [[string widget = "checkBox", string label = "ACES-AP1", int connectable = 0, string page = "1: Color Settings"]],
    96. float Scattering = 1 [[float slidermin = 0.1, float slidermax = 1.315, float slidercenter = 1, int connectable = 0, string page = "2: Control Settings"]],
    97. float ozoneDensity = 0 [[float slidermin = -3, float slidermax = 3, float slidercenter = 0, string label = "Ozone Density Multiplier", string page = "2: Control Settings"]],
    98. float Height = 200 [[string page = "2: Control Settings"]],
    99. float discSize = 1 [[float slidermin = 0, float slidermax = 100, string label = "Disc Size", string page = "2: Control Settings"]],
    100. float Azimuth = 0 [[float slidermin = 0, float slidermax = 360, int connectable = 0, string page = "2: Control Settings"]],
    101. float Elevation = 0 [[float slidermin = -40, float slidermax = 220, int connectable = 0, string page = "2: Control Settings"]],
    102. int useLight = 0 [[string widget = "checkBox", string label = "Use External Rotations", string page = "2: Control Settings"]],
    103. vector Light = vector(0, 0, 0) [[string label = "Rotation Coordinates", string page = "2: Control Settings"]],
    104. int flipAxis = 0 [[string widget = "checkBox", string label = "Z-Up", int connectable = 0, string page = "2: Control Settings"]],
    105. int useStars = 1 [[string widget = "checkBox", string label = "Enable Stars",string page = "3: Stars Settings"]],
    106. float starsIntensity = 1 [[string label = "Stars Intensity", float slidermin = 0, float slidermax = 4 ,string page = "3: Stars Settings"]],
    107. float starsFrequency = 0.5 [[string label = "Stars Frequency", float slidermin = 0, float slidermax = 1, string page = "3: Stars Settings"]],
    108. float starsWidth = 0.5 [[string label = "Stars Width", float slidermin = 0, float slidermax = 1, string page = "3: Stars Settings"]],
    109. output color Sky = 0.0)
    110. {
    111. color Rayleigh = 0.0;
    112. color Mie = 0.0;
    113. color Ozone = 0.0;
    114. color outSun = 0.0;
    115. color Stars = 0.0;
    116. //Conversion matrices
    117. matrix yUp =
    118. {1,0,0,0,
    119. 0,1,0,0,
    120. 0,0,1,0,
    121. 0,0,0,1};
    122. matrix zUp =
    123. {1,0,0,0,
    124. 0,1*flipAxis,1-flipAxis,0,
    125. 0,1-flipAxis,-1*flipAxis,0,
    126. 0,0,0,1};
    127. matrix xyz_to_rgb =
    128. {3.24096994,1.53738318,-0.49861076,0,
    129. -0.96924364,1.8759675,0.04155506,0,
    130. 0.05563008,-0.20397696,1.05697151,0,
    131. 0,0,0,1
    132. };
    133. //Variables:
    134. float Re = 6360e3; // Radius of the earth
    135. float Ra = 6460e3; // Radius of the atmosphere
    136. float Hr = 8000; // Rayleigh scale height
    137. float Hm = 1200; // Mie scale height
    138. float Ho = 10000; // Ozone scale height
    139. float g = 0.76 * clamp(Scattering, 0.1, 1.315); // Anisotropy term for Mie scattering.
    140. float oD = 0.00000022512612292678; // Ozone Coef at wavelength 18
    141. vector BetaR = vector(5.8e-6,13.0e-6,22.4e-6);
    142. vector BetaM_max = vector(9.73e-6);
    143. vector BetaM_min = vector(13.28e-6);
    144. vector BetaM = vector(20e-6);
    145. vector BetaO = vector(3.808e-6);
    146. //Clean up inputs:
    147. vector SunDirection = vector(0, 0, 1);
    148. SunDirection = normalize(SunDirection);
    149. vector Direction = normalize(I);
    150. //Sun azimuth, elevation and misc light logic
    151. if (useLight == 1){
    152. if (flipAxis == 0){
    153. SunDirection = transform(yUp, SunDirection);
    154. SunDirection = rotate(SunDirection, radians(360) - radians(Light[0]), vector(-1,0,0));
    155. SunDirection = rotate(SunDirection, radians(Light[1]) + radians(180), vector(0,-1,0));
    156. }
    157. if (flipAxis == 1){
    158. SunDirection = rotate(SunDirection, radians(270) - radians(Light[0]), vector(1,0,0));
    159. SunDirection = rotate(SunDirection, radians(Light[2]), vector(0,0,-1));
    160. }
    161. }
    162. else {
    163. if (flipAxis == 0){
    164. SunDirection = transform(yUp, SunDirection);
    165. if (Elevation >= -40){
    166. SunDirection = rotate(SunDirection, radians(360) - radians(Elevation), vector(1,0,0));
    167. }
    168. if (Azimuth >= 0){
    169. SunDirection = rotate(SunDirection, radians(Azimuth), vector(0,1,0));
    170. }
    171. }
    172. if (flipAxis == 1){
    173. if (Elevation >= -40){
    174. SunDirection = rotate(SunDirection, radians(270) - radians(Elevation), vector(1,0,0));
    175. }
    176. if (Azimuth >= 0){
    177. SunDirection = rotate(SunDirection, radians(Azimuth), vector(0,0,-1));
    178. }
    179. }
    180. }
    181. //Calcualte Rayleigh and mie phases.
    182. float mu = dot(Direction, SunDirection);
    183. float phaseR = (3/(16 * M_PI)) * (1 + mu*mu);
    184. float phaseM = (3/(8 * M_PI)) * ((1 - g*g) * (1 + mu*mu) / ((2 + g*g) * pow(1 + g*g - 2 * g * mu, 1.5)));
    185. // Sun Disc
    186. float sunDiameter = discSize * 0.53 * M_PI / 180.0;
    187. float sunAmount = step(cos(sunDiameter / 2.0), mu);
    188. //Stuff that will eventually help form my output.
    189. vector SumR = vector(0);
    190. vector SumM = vector(0);
    191. vector SumO = vector(0);
    192. //Set up start and end points for my integrals.
    193. vector Pu = vector(0,0,0);
    194. vector Pv = vector(0,0,0);
    195. //Get start and end point for View Line.
    196. if (Height > 0) {
    197. vector Pc = vector (0, 0, Re + Height);
    198. if (flipAxis == 0){
    199. Pc = transform(yUp, Pc);
    200. Pc = rotate(Pc, radians(-90), vector(1,0,0));
    201. }
    202. if (flipAxis == 1){
    203. Pc = transform(zUp, Pc);
    204. }
    205. Pu = Pc;
    206. //Ozone
    207. oD = 0;
    208. if (ozoneDensity != 0){
    209. if (Height >= 0.0) {
    210. oD = (ozoneDensity * 1000 / -15000.0);
    211. oD = clamp(oD, -2000, 3000);
    212. }
    213. }
    214. //Stars stuff
    215. point PP = I;
    216. float val;
    217. float sf = starsFrequency;
    218. sf /= 300;
    219. val = smoothstep(1 - starsWidth, 2, noise("perlin", PP / sf));
    220. vector cosSunGround = dot(normalize(Pu), normalize(SunDirection));
    221. vector sunAngle = acos(cosSunGround);
    222. float sunAngleDeg = degrees(sunAngle[0]);
    223. float sunAngleDeg_N = (sunAngleDeg - 87) / (135 - 87);
    224. if (useStars == 1){
    225. if (Height <= 30000){
    226. Stars = val * pow(4, clamp(2.5 * starsIntensity, 0, 4)) * sunAngleDeg_N;
    227. Stars = -sunAmount + Stars;
    228. }
    229. else {
    230. Stars = val * pow(1.8, clamp(2.5 * starsIntensity, 0, 4));
    231. }
    232. Stars = clamp(Stars, 0, 4);
    233. }
    234. linesample groundline;
    235. groundline.has_solutions = has_solutions(Pc, Direction, Re);
    236. groundline.type = linetype(Pc, Direction, Re);
    237. groundline.min = lmin(Pc, Direction, Re);
    238. groundline.max = lmax(Pc, Direction, Re);
    239. linesample atmosphereline;
    240. atmosphereline.has_solutions = has_solutions(Pc, Direction, Ra);
    241. atmosphereline.type = linetype(Pc, Direction, Ra);
    242. atmosphereline.min = lmin(Pc, Direction, Ra);
    243. atmosphereline.max = lmax(Pc, Direction, Ra);
    244. Pv = atmosphereline.max;
    245. if (atmosphereline.has_solutions && atmosphereline.type == 0) {
    246. //Line starts outside atmoshere, so Pu becomes Line.min
    247. Pu = atmosphereline.min;
    248. }
    249. if (groundline.has_solutions && groundline.type == 0) {
    250. //Line hits ground, Pv becomes point at which line hits ground, which will be Line.min
    251. Pv = groundline.min;
    252. sunAmount = 0;
    253. Stars = 0;
    254. }
    255. }
    256. // Create samples along view ray.
    257. int numSamples = 16;
    258. int numSamplesL = 8;
    259. float segmentLength = distance (Pu, Pv) / numSamples;
    260. float opticalDepthR = 0;
    261. float opticalDepthM = 0;
    262. float opticalDepthO = 0;
    263. for (int i = 0; i < numSamples; i++) {
    264. vector Px = Pu + segmentLength * Direction * (i + 0.5);
    265. float sampleHeight = length(Px) - Re;
    266. /* Get optical depth for light at this sample: */
    267. float Hr_sample = exp(-sampleHeight/Hr) * segmentLength;
    268. float Hm_sample = exp(-sampleHeight/Hm) * segmentLength;
    269. float Ho_sample = exp(-sampleHeight/Ho) * segmentLength;
    270. opticalDepthR += Hr_sample;
    271. opticalDepthM += Hm_sample;
    272. opticalDepthO += Ho_sample;
    273. //Get light depth to sun at this sample.
    274. float opticalDepthLR = 0;
    275. float opticalDepthLM = 0;
    276. float opticalDepthLO = 0;
    277. linesample atmosphereline_sample;
    278. atmosphereline_sample.has_solutions = has_solutions(Px, SunDirection, Ra);
    279. atmosphereline_sample.type = linetype(Px, SunDirection, Ra);
    280. atmosphereline_sample.min = lmin(Px, SunDirection, Ra);
    281. atmosphereline_sample.max = lmax(Px, SunDirection, Ra);
    282. linesample groundline_sample;
    283. groundline_sample.has_solutions = has_solutions(Px, SunDirection, Re);
    284. groundline_sample.type = linetype(Px, SunDirection, Re);
    285. groundline_sample.min = lmin(Px, SunDirection, Re);
    286. groundline_sample.max = lmax(Px, SunDirection, Re);
    287. vector Ps = atmosphereline_sample.max;
    288. int j = 0;
    289. if (groundline_sample.has_solutions && groundline_sample.type == 0) {
    290. //Light is below horizon from this point. No sample needed.
    291. opticalDepthLR += 0;
    292. opticalDepthLM += 0;
    293. opticalDepthLO += 0;
    294. } else {
    295. float segmentLengthL = distance(Px, Ps) / numSamplesL;
    296. for (j = 0; j < numSamplesL; j++) {
    297. vector Pl = Px + segmentLengthL * SunDirection * (j + 0.5);
    298. float sampleHeightL = length(Pl) - Re;
    299. if (sampleHeightL < 0) break;
    300. // ignore sampleheights < 0 they're inside the planet...
    301. float Hlr_sample = exp(-sampleHeightL/Hr)*segmentLengthL;
    302. float Hlm_sample = exp(-sampleHeightL/Hm)*segmentLengthL;
    303. float Hlo_sample = exp(-sampleHeightL/Ho)*segmentLengthL;
    304. opticalDepthLR += Hlr_sample;
    305. opticalDepthLM += Hlm_sample;
    306. opticalDepthLO += Hlo_sample;
    307. }
    308. }
    309. // With our light samples done we can calculate the attenuation.
    310. // Only include a sample if we reached j (so not if we hit the break clause for rays that hit the ground in finding the sun)
    311. if (j == numSamplesL) {
    312. vector tauR = BetaR * (opticalDepthR + opticalDepthLR);
    313. vector tauM = BetaM * 1.1 * (opticalDepthM + opticalDepthLM);
    314. vector tauO = BetaO * (opticalDepthO + opticalDepthLO);
    315. vector tau = tauR + tauM + tauO;
    316. vector attnR = vector (exp(-tauR[0]), exp(-tauR[1]), exp(-tauR[2]));
    317. vector attnM = vector (exp(-tauM));
    318. vector attenuation = vector (exp(-tau[0]), exp(-tau[1]), exp(-tau[2]));
    319. SumR += Hr_sample * attenuation;
    320. SumM += Hm_sample * attenuation;
    321. SumO += Ho_sample * attenuation;
    322. }
    323. }
    324. matrix srgbToAcesAP1 = {
    325. 0.6131, 0.0701, 0.0206, 0,
    326. 0.3395, 0.9164, 0.1096, 0,
    327. 0.0474, 0.0135, 0.8698, 0,
    328. 0, 0, 0, 1 };
    329. Rayleigh = color (SumR * phaseR * BetaR) * Intensity * clamp(skyTint, 0, 1);
    330. Mie = color (SumM * phaseM * BetaM) * Intensity * clamp(sunTint, 0, 1);
    331. Ozone = color (SumO * BetaO * oD) * Intensity * clamp(sunTint, 0, 1);
    332. outSun = color (SumM * phaseM * BetaM * discColor) * sunAmount * pow(2, discIntensity);
    333. // Subtly merge the rayleigh colors with the stars, cull very dim stars
    334. // Merge the rest of the atmosphere
    335. color atmosMix = (softLight((Stars * Stars), Rayleigh) + Rayleigh + Mie + Ozone);
    336. Sky = (atmosMix + outSun);
    337. color toHSV = transformc("hsv", Sky);
    338. toHSV[1] = toHSV[1] * Saturation;
    339. color toRGB = transformc("hsv", "rgb", toHSV);
    340. Sky = toRGB;
    341. Sky = pow(Sky, clamp(Gamma, 0, 2)) * pow(2.0, Exposure);
    342. float sR = Sky[0];
    343. float sG = Sky[1];
    344. float sB = Sky[2];
    345. if (convToAces == 1){
    346. Sky = transform(srgbToAcesAP1, vector(sR, sG, sB));
    347. Sky = pow(Sky, Gamma) * pow(2.0, Exposure);
    348. }
    349. }