-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathNPC Skybox Shader.shader
408 lines (331 loc) · 16.9 KB
/
NPC Skybox Shader.shader
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
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "Skybox/Procedural Cartoon" {
Properties {
[KeywordEnum(None, Simple, High Quality)] _SunDisk ("Sun", Int) = 2
_SunSize ("Sun Size", Range(0,1)) = 0.04
_SunSizeConvergence("Sun Size Convergence", Range(1,10)) = 5
_AtmosphereThickness ("Atmosphere Thickness", Range(0,5)) = 1.0
_SkyTint ("Sky Tint", Color) = (.5, .5, .5, 1)
_GroundColor ("Ground", Color) = (.369, .349, .341, 1)
_Exposure("Exposure", Range(0, 8)) = 1.3
}
SubShader {
Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
Cull Off ZWrite Off
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#pragma multi_compile _SUNDISK_NONE _SUNDISK_SIMPLE _SUNDISK_HIGH_QUALITY
uniform half _Exposure; // HDR exposure
uniform half3 _GroundColor;
uniform half _SunSize;
uniform half _SunSizeConvergence;
uniform half4 _SkyTint;
uniform half _AtmosphereThickness;
#if defined(UNITY_COLORSPACE_GAMMA)
#define GAMMA 2
#define COLOR_2_GAMMA(color) color
#define COLOR_2_LINEAR(color) color*color
#define LINEAR_2_OUTPUT(color) sqrt(color)
#else
#define GAMMA 2.2
// HACK: to get gfx-tests in Gamma mode to agree until UNITY_ACTIVE_COLORSPACE_IS_GAMMA is working properly
#define COLOR_2_GAMMA(color) ((unity_ColorSpaceDouble.r>2.0) ? pow(color,1.0/GAMMA) : color)
#define COLOR_2_LINEAR(color) color
#define LINEAR_2_LINEAR(color) color
#endif
// RGB wavelengths
// .35 (.62=158), .43 (.68=174), .525 (.75=190)
static const float3 kDefaultScatteringWavelength = float3(.65, .57, .475);
static const float3 kVariableRangeForScatteringWavelength = float3(.15, .15, .15);
#define OUTER_RADIUS 1.025
static const float kOuterRadius = OUTER_RADIUS;
static const float kOuterRadius2 = OUTER_RADIUS*OUTER_RADIUS;
static const float kInnerRadius = 1.0;
static const float kInnerRadius2 = 1.0;
static const float kCameraHeight = 0.0001;
#define kRAYLEIGH (lerp(0.0, 0.0025, pow(_AtmosphereThickness,2.5))) // Rayleigh constant
#define kMIE 0.0010 // Mie constant
#define kSUN_BRIGHTNESS 20.0 // Sun brightness
#define kMAX_SCATTER 50.0 // Maximum scattering value, to prevent math overflows on Adrenos
static const half kHDSundiskIntensityFactor = 15.0;
static const half kSimpleSundiskIntensityFactor = 27.0;
static const half kSunScale = 400.0 * kSUN_BRIGHTNESS;
static const float kKmESun = kMIE * kSUN_BRIGHTNESS;
static const float kKm4PI = kMIE * 4.0 * 3.14159265;
static const float kScale = 1.0 / (OUTER_RADIUS - 1.0);
static const float kScaleDepth = 0.25;
static const float kScaleOverScaleDepth = (1.0 / (OUTER_RADIUS - 1.0)) / 0.25;
static const float kSamples = 2.0; // THIS IS UNROLLED MANUALLY, DON'T TOUCH
#define MIE_G (-0.990)
#define MIE_G2 0.9801
#define SKY_GROUND_THRESHOLD 0.02
// fine tuning of performance. You can override defines here if you want some specific setup
// or keep as is and allow later code to set it according to target api
// if set vprog will output color in final color space (instead of linear always)
// 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
// #define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
// sun disk rendering:
// no sun disk - the fastest option
#define SKYBOX_SUNDISK_NONE 0
// simplistic sun disk - without mie phase function
#define SKYBOX_SUNDISK_SIMPLE 1
// full calculation - uses mie phase function
#define SKYBOX_SUNDISK_HQ 2
// uncomment this line and change SKYBOX_SUNDISK_SIMPLE to override material settings
// #define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
#ifndef SKYBOX_SUNDISK
#if defined(_SUNDISK_NONE)
#define SKYBOX_SUNDISK SKYBOX_SUNDISK_NONE
#elif defined(_SUNDISK_SIMPLE)
#define SKYBOX_SUNDISK SKYBOX_SUNDISK_SIMPLE
#else
#define SKYBOX_SUNDISK SKYBOX_SUNDISK_HQ
#endif
#endif
#ifndef SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
#if defined(SHADER_API_MOBILE)
#define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 1
#else
#define SKYBOX_COLOR_IN_TARGET_COLOR_SPACE 0
#endif
#endif
// Calculates the Rayleigh phase function
half getRayleighPhase(half eyeCos2)
{
return 0.75 + 0.75*eyeCos2;
}
half getRayleighPhase(half3 light, half3 ray)
{
half eyeCos = dot(light, ray);
return getRayleighPhase(eyeCos * eyeCos);
}
struct appdata_t
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
// for HQ sun disk, we need vertex itself to calculate ray-dir per-pixel
half3 vertex : TEXCOORD0;
#elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
half3 rayDir : TEXCOORD0;
#else
// as we dont need sun disk we need just rayDir.y (sky/ground threshold)
half skyGroundFactor : TEXCOORD0;
#endif
// calculate sky colors in vprog
half3 groundColor : TEXCOORD1;
half3 skyColor : TEXCOORD2;
#if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
half3 sunColor : TEXCOORD3;
#endif
UNITY_VERTEX_OUTPUT_STEREO
};
float scale(float inCos)
{
float x = 1.0 - inCos;
#if defined(SHADER_API_N3DS)
// The polynomial expansion here generates too many swizzle instructions for the 3DS vertex assembler
// Approximate by removing x^1 and x^2
return 0.25 * exp(-0.00287 + x*x*x*(-6.80 + x*5.25));
#else
return 0.25 * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
#endif
}
v2f vert (appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.pos = UnityObjectToClipPos(v.vertex);
float3 kSkyTintInGammaSpace = COLOR_2_GAMMA(_SkyTint.rgb); // convert tint from Linear back to Gamma
float3 kScatteringWavelength = lerp (
kDefaultScatteringWavelength-kVariableRangeForScatteringWavelength,
kDefaultScatteringWavelength+kVariableRangeForScatteringWavelength,
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
float3 kInvWavelength = 1.0 / pow(kScatteringWavelength, 4);
float kKrESun = kRAYLEIGH * kSUN_BRIGHTNESS;
float kKr4PI = kRAYLEIGH * 4.0 * 3.14159265;
float3 cameraPos = float3(0,kInnerRadius + kCameraHeight,0); // The camera's current position
// Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
float3 eyeRay = normalize(mul((float3x3)unity_ObjectToWorld, v.vertex.xyz));
float far = 0.0;
half3 cIn, cOut;
if(eyeRay.y >= 0.0)
{
// Sky
// Calculate the length of the "atmosphere"
far = sqrt(kOuterRadius2 + kInnerRadius2 * eyeRay.y * eyeRay.y - kInnerRadius2) - kInnerRadius * eyeRay.y;
float3 pos = cameraPos + far * eyeRay;
// Calculate the ray's starting position, then calculate its scattering offset
float height = kInnerRadius + kCameraHeight;
float depth = exp(kScaleOverScaleDepth * (-kCameraHeight));
float startAngle = dot(eyeRay, cameraPos) / height;
float startOffset = depth*scale(startAngle);
// Initialize the scattering loop variables
float sampleLength = far / kSamples;
float scaledLength = sampleLength * kScale;
float3 sampleRay = eyeRay * sampleLength;
float3 samplePoint = cameraPos + sampleRay * 0.5;
// Now loop through the sample rays
float3 frontColor = float3(0.0, 0.0, 0.0);
// Weird workaround: WP8 and desktop FL_9_3 do not like the for loop here
// (but an almost identical loop is perfectly fine in the ground calculations below)
// Just unrolling this manually seems to make everything fine again.
// for(int i=0; i<int(kSamples); i++)
{
float height = length(samplePoint);
float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
float cameraAngle = dot(eyeRay, samplePoint) / height;
float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
frontColor += attenuate * (depth * scaledLength);
samplePoint += sampleRay;
}
{
float height = length(samplePoint);
float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
float lightAngle = dot(_WorldSpaceLightPos0.xyz, samplePoint) / height;
float cameraAngle = dot(eyeRay, samplePoint) / height;
float scatter = (startOffset + depth*(scale(lightAngle) - scale(cameraAngle)));
float3 attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
frontColor += attenuate * (depth * scaledLength);
samplePoint += sampleRay;
}
// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
cIn = frontColor * (kInvWavelength * kKrESun);
cOut = frontColor * kKmESun;
}
else
{
// Ground
far = (-kCameraHeight) / (min(-0.001, eyeRay.y));
float3 pos = cameraPos + far * eyeRay;
// Calculate the ray's starting position, then calculate its scattering offset
float depth = exp((-kCameraHeight) * (1.0/kScaleDepth));
float cameraAngle = dot(-eyeRay, pos);
float lightAngle = dot(_WorldSpaceLightPos0.xyz, pos);
float cameraScale = scale(cameraAngle);
float lightScale = scale(lightAngle);
float cameraOffset = depth*cameraScale;
float temp = (lightScale + cameraScale);
// Initialize the scattering loop variables
float sampleLength = far / kSamples;
float scaledLength = sampleLength * kScale;
float3 sampleRay = eyeRay * sampleLength;
float3 samplePoint = cameraPos + sampleRay * 0.5;
// Now loop through the sample rays
float3 frontColor = float3(0.0, 0.0, 0.0);
float3 attenuate;
// 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.
{
float height = length(samplePoint);
float depth = exp(kScaleOverScaleDepth * (kInnerRadius - height));
float scatter = depth*temp - cameraOffset;
attenuate = exp(-clamp(scatter, 0.0, kMAX_SCATTER) * (kInvWavelength * kKr4PI + kKm4PI));
frontColor += attenuate * (depth * scaledLength);
samplePoint += sampleRay;
}
cIn = frontColor * (kInvWavelength * kKrESun + kKmESun);
cOut = clamp(attenuate, 0.0, 1.0);
}
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
OUT.vertex = -v.vertex;
#elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
OUT.rayDir = half3(-eyeRay);
#else
OUT.skyGroundFactor = -eyeRay.y / SKY_GROUND_THRESHOLD;
#endif
// if we want to calculate color in vprog:
// 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)
// 2. in case of gamma and SKYBOX_COLOR_IN_TARGET_COLOR_SPACE: do sqrt right away instead of doing that in fshader
OUT.groundColor = _Exposure * (cIn + COLOR_2_LINEAR(_GroundColor) * cOut);
OUT.skyColor = _Exposure * (cIn * getRayleighPhase(_WorldSpaceLightPos0.xyz, -eyeRay));
#if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
// The sun should have a stable intensity in its course in the sky. Moreover it should match the highlight of a purely specular material.
// This matching was done using the standard shader BRDF1 on the 5/31/2017
// Finally we want the sun to be always bright even in LDR thus the normalization of the lightColor for low intensity.
half lightColorIntensity = clamp(length(_LightColor0.xyz), 0.25, 1);
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
OUT.sunColor = kSimpleSundiskIntensityFactor * saturate(cOut * kSunScale) * _LightColor0.xyz / lightColorIntensity;
#else // SKYBOX_SUNDISK_HQ
OUT.sunColor = kHDSundiskIntensityFactor * saturate(cOut) * _LightColor0.xyz / lightColorIntensity;
#endif
#endif
#if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
OUT.groundColor = sqrt(OUT.groundColor);
OUT.skyColor = sqrt(OUT.skyColor);
#if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
OUT.sunColor= sqrt(OUT.sunColor);
#endif
#endif
return OUT;
}
// Calculates the Mie phase function
half getMiePhase(half eyeCos, half eyeCos2)
{
half temp = 1.0 + MIE_G2 - 2.0 * MIE_G * eyeCos;
temp = pow(temp, pow(_SunSize,0.65) * 10);
temp = max(temp,1.0e-4); // prevent division by zero, esp. in half precision
temp = 1.5 * ((1.0 - MIE_G2) / (2.0 + MIE_G2)) * (1.0 + eyeCos2) / temp;
#if defined(UNITY_COLORSPACE_GAMMA) && SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
temp = pow(temp, .454545);
#endif
return temp;
}
// Calculates the sun shape
half calcSunAttenuation(half3 lightPos, half3 ray)
{
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
half3 delta = lightPos - ray;
half dist = length(delta);
half spot = 1.0 - smoothstep(0.0, _SunSize, dist);
return spot * spot;
#else // SKYBOX_SUNDISK_HQ
half focusedEyeCos = pow(saturate(dot(lightPos, ray)), _SunSizeConvergence);
return getMiePhase(-focusedEyeCos, focusedEyeCos * focusedEyeCos);
#endif
}
half4 frag (v2f IN) : SV_Target
{
half3 col = half3(0.0, 0.0, 0.0);
// if y > 1 [eyeRay.y < -SKY_GROUND_THRESHOLD] - ground
// if y >= 0 and < 1 [eyeRay.y <= 0 and > -SKY_GROUND_THRESHOLD] - horizon
// if y < 0 [eyeRay.y > 0] - sky
#if SKYBOX_SUNDISK == SKYBOX_SUNDISK_HQ
half3 ray = normalize(mul((float3x3)unity_ObjectToWorld, IN.vertex));
half y = ray.y / SKY_GROUND_THRESHOLD;
#elif SKYBOX_SUNDISK == SKYBOX_SUNDISK_SIMPLE
half3 ray = IN.rayDir.xyz;
half y = ray.y / SKY_GROUND_THRESHOLD;
#else
half y = IN.skyGroundFactor;
#endif
// if we did precalculate color in vprog: just do lerp between them
col = lerp(IN.skyColor, IN.groundColor, saturate(y));
#if SKYBOX_SUNDISK != SKYBOX_SUNDISK_NONE
if(y < 0.0)
{
col += IN.sunColor * calcSunAttenuation(_WorldSpaceLightPos0.xyz, -ray);
}
#endif
#if defined(UNITY_COLORSPACE_GAMMA) && !SKYBOX_COLOR_IN_TARGET_COLOR_SPACE
col = LINEAR_2_OUTPUT(col);
#endif
half3 col2 = ((col.r+col.g+col.b)/3.0) * _SkyTint;
return half4(lerp(col,col2,_SkyTint.a),1.0);
}
ENDCG
}
}
Fallback Off
CustomEditor "SkyboxProceduralShaderGUI"
}