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