summaryrefslogtreecommitdiffstats
path: root/fog.cginc
blob: 6635ab739fd3b2dbde2988a0000cfabfc3bf2f9e (plain)
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
#include "globals.cginc"
#include "interpolators.cginc"
#include "math.cginc"
#include "noise.cginc"
#include "cnlohr.cginc"

#ifndef __FOG_INC
#define __FOG_INC

#if defined(_GIMMICK_FOG_00)

struct Fog00PBR {
  float4 albedo;
  float3 normal;
  float depth;
  float ao;
};

float map(float3 p) {
  float density = 0;
  float t = _Time[1] * 0.5;
  float radius = saturate(_Gimmick_Fog_00_Radius - length(p));
  float tmp;
  tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 3.1 + t) * radius * 0.5;
  density += tmp;
  tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 1.7 + t) * radius * 0.5;
  density *= 0.5;
  density += tmp;
  tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 1.0 + t) * radius * 0.5;
  density *= 0.5;
  density += tmp;

  density = pow(density, _Gimmick_Fog_00_Noise_Exponent);

  // Note: this term annihilates performance by creating large open areas. Long
  // avgerage view ray = bad perf!
  #if 1
  tmp = perlin_noise_3d(p * _Gimmick_Fog_00_Noise_Scale * 0.167 + t/4) * radius - 0.5;
  tmp *= 0.2;
  density += tmp;
  #endif

  return saturate(density);
}

float3 get_normal(float3 p, float map_p) {
  float3 e = float3(0.001, 0, 0);
  float center = map_p;

  // Prevent NaN
  float e2 = 1E-9;
  return normalize(float3(
      map(p + e.xyz) - center,
      map(p + e.yxz) - center,
      map(p + e.zyx) - center) + e2);
}

Fog00PBR getFog00(v2f i) {

  float3 cam_pos = _WorldSpaceCameraPos;
  float3 obj_pos = i.worldPos;

  float3 world_pos_depth_hit;
  float2 screen_uv;
  {
    float3 full_vec_eye_to_geometry = i.worldPos - _WorldSpaceCameraPos;
    float3 world_dir = normalize(i.worldPos - _WorldSpaceCameraPos);
    float perspective_divide = 1.0 / i.pos.w;
    float perspective_factor = length(full_vec_eye_to_geometry * perspective_divide);
    screen_uv = i.screenPos.xy * perspective_divide;
    float eye_depth_world =
      GetLinearZFromZDepth_WorksWithMirrors(
          SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screen_uv),
          screen_uv) * perspective_factor;
    world_pos_depth_hit = _WorldSpaceCameraPos + eye_depth_world * world_dir;
  }

  float3 rd = normalize(obj_pos - cam_pos);
  float3 ro = cam_pos;

  bool no_intersection = false;
  if (length(ro) > _Gimmick_Fog_00_Radius) {
    float3 l = ro;
    float a = 1;
    float b = 2 * dot(rd, l);
    float c = dot(l, l) - _Gimmick_Fog_00_Radius * _Gimmick_Fog_00_Radius;
    float t0, t1;
    if (solveQuadratic(a, b, c, t0, t1)) {
      no_intersection = (t0 < 0) * (t1 < 0);
      ro += min(max(t0, 0), max(t1, 0)) * rd;
    } else {
      no_intersection = true;
    }
  }

  // Factor of 10 on `screen_uv*10` eliminates visible striping artifact that
  // is visible with no factor.
  float dither = rand2(screen_uv*10) * _Gimmick_Fog_00_Step_Size * _Gimmick_Fog_00_Ray_Origin_Randomization;
  ro += rd * (0.1 + dither);

  float world_pos_depth_hit_l = length(world_pos_depth_hit - ro);

  float4 acc = 0;
  uint step_count = floor(min(
        _Gimmick_Fog_00_Max_Ray / _Gimmick_Fog_00_Step_Size,
        world_pos_depth_hit_l / _Gimmick_Fog_00_Step_Size));
  step_count *= (1 - no_intersection);

  float3 normal = i.normal;
  float ao = 0;
  for (uint ii = 0; ii < step_count; ii++) {
    float3 p = ro + (rd * _Gimmick_Fog_00_Step_Size) * ii;

    float col_gray = 0.3;
    const float map_p = map(p);
    float4 c = float4(col_gray, col_gray, col_gray, map_p);
    c.a = saturate(c.a * _Gimmick_Fog_00_Density * _Gimmick_Fog_00_Step_Size);

#if defined(_GIMMICK_FOG_00_EMITTER_TEXTURE)
    // Project onto plane
    float3 p_to_emitter = p - _Gimmick_Fog_00_Emitter_Location;
    float3 emitter_normal = normalize(_Gimmick_Fog_00_Emitter_Normal);
    float2 emitter_scale = float2(_Gimmick_Fog_00_Emitter_Scale_X, _Gimmick_Fog_00_Emitter_Scale_Y);

    float t = dot(p_to_emitter, emitter_normal);
    float3 p_projected = p - t * emitter_normal;

    p_projected -= _Gimmick_Fog_00_Emitter_Location;
    bool in_range = (abs(p_projected.x) < emitter_scale.x) * (abs(p_projected.y) < emitter_scale.y) * (t > 0);

    float2 emitter_uv = clamp(p_projected.xy, -emitter_scale, emitter_scale) / emitter_scale;
    emitter_uv /= 2.0;
    emitter_uv += 0.5;
    float3 emitter_color = _Gimmick_Fog_00_Emitter_Texture.SampleLevel(linear_repeat_s, emitter_uv, 0);
    emitter_color *= _Gimmick_Fog_00_Emitter_Brightness;
    float emitter_dist = in_range ? abs(t) : 1000;
    // Inverse square is physically accurate, but this looks better.
    float emitter_falloff = min(1, rcp(pow(emitter_dist, 1.0)));
#if 1
    c.rgb = lerp(c.rgb, emitter_color, in_range * emitter_falloff);
#else
    c.rgb = emitter_color;
#endif
#endif

    acc += c * (1.0 - acc.a);

    const float ao_str = 0.3;
    float cur_ao = saturate(length(ro) / _Gimmick_Fog_00_Radius) * ao_str + (1.0 - ao_str);
    ao = cur_ao * (1.0 - acc.a) + acc.a * ao;

    // Performance hack: stop blending normals after enough accumulation.
    if (acc.a < _Gimmick_Fog_00_Normal_Cutoff) {
      float3 n = get_normal(p, map_p);
      float n_interp = saturate(c.a * (1.0 - acc.a) * rcp(_Gimmick_Fog_00_Normal_Cutoff));
      normal = MY_BLEND_NORMALS(normal, n, n_interp);
    }
    if (acc.a > _Gimmick_Fog_00_Albedo_Cutoff) {
      acc /= acc.a;
      break;
    }
    // Performance hack: stop iterating if we go outside of the sphere.
    if (dot(p, p) > _Gimmick_Fog_00_Radius * _Gimmick_Fog_00_Radius) {
      break;
    }
  }

  Fog00PBR pbr;
  pbr.albedo.rgb = acc.rgb;
  pbr.albedo.a = saturate(acc.a);
  pbr.ao = ao;

#if 1
  pbr.normal = normalize(normal);
#else
  pbr.normal = i.normal;
#endif

  float4 clip_pos = mul(UNITY_MATRIX_VP, float4(ro, 1.0));
  pbr.depth = clip_pos.z / clip_pos.w;

  return pbr;
}

#endif  // _GIMMICK_FOG_00

#endif  // __FOG_INC