summaryrefslogtreecommitdiff
path: root/shaders/vfog.glsl
blob: f638b27307910bb8ac6e314dfae2ddb148e027eb (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
#version 330 core

// --------------------------------------------------------------------------------
// Volumetric fog
//
// @model cube
// @uniform int u_mode  0 0 2
// @uniform int u_steps 8 1 64
// @uniform float u_sigma 4.0f
//
// SPDX-License-Identifier: 0BSD
// --------------------------------------------------------------------------------

// --------------------------------------------------------------------------------
// Uniforms
// --------------------------------------------------------------------------------
uniform float u_time;
uniform vec3  u_cam;
uniform int   u_mode;
uniform int   u_steps;
uniform float u_sigma;

// --------------------------------------------------------------------------------
// Vertex outputs
// --------------------------------------------------------------------------------
in vec3 v_p;
in vec2 v_t;
in vec3 v_n;

// --------------------------------------------------------------------------------
// Fragment outputs
// --------------------------------------------------------------------------------
out vec4 f_c;

// -----------------------------------------------------------------------------
// Ray-AABB intersection
// ref: https://tavianator.com/2022/ray_box_boundary.html
// -----------------------------------------------------------------------------
bool intersect(vec3 ro, vec3 rd, vec3 bbmin, vec3 bbmax, out float t0, out float t1)
{
  vec3 inv = 1.0f / rd;
  vec3 ta = (bbmin - ro) * inv;
  vec3 tb = (bbmax - ro) * inv;
  vec3 tmin = min(ta, tb);
  vec3 tmax = max(ta, tb);
  t0 = max(max(tmin.x, tmin.y), tmin.z);
  t1 = min(min(tmax.x, tmax.y), tmax.z);
  return t1 >= max(t0, 0.0f);
}

// -----------------------------------------------------------------------------
// Mode 0: Basic volumetric fog
// -----------------------------------------------------------------------------
float sample_basic(vec3 pos)
{
  return 1.0;
}

// -----------------------------------------------------------------------------
// Mode 1: Fade along y axis
// -----------------------------------------------------------------------------
float sample_vfade(vec3 pos)
{
  return 1.0f - pos.y;
}

// -----------------------------------------------------------------------------
// Mode 2: Some waves
// -----------------------------------------------------------------------------
float sample_waves(vec3 pos)
{
  // Fractal sum
  // ref: https://thebookofshaders.com/13/
  float h = 0.75f; // baseline

  // Big waves
  h += 0.08f * sin(pos.x * 2.0f + u_time);
  h += 0.08f * cos(pos.z * 2.0f + u_time * -1.1f);

  // Medium waves
  h += 0.05f * sin(pos.x * 5.0f + u_time * 1.7f);
  h += 0.05f * cos(pos.z * 4.0f + u_time * -2.0f);

  // Small but choppy waves
  h += 0.03f * sin(pos.x * 12.0f + u_time * 3.3f);
  h += 0.03f * cos(pos.z * 11.0f + u_time * -2.5f);

  return float(pos.y < h);
}

// --------------------------------------------------------------------------------
// Entry point
// --------------------------------------------------------------------------------
void main()
{
  vec3 bbmin = vec3(-1.0f);
  vec3 bbmax = vec3( 1.0f);

  vec3 ro = u_cam;
  vec3 rd = normalize(v_p - ro);

  float t0 = 0.0f;
  float t1 = 0.0f;
  if (!intersect(ro, rd, bbmin, bbmax, t0, t1)) {
    f_c = vec4(0.0f, 0.0f, 0.0f, 0.0f);
    return;
  }

  // Clamp entry point in front of camera
  float t_enter = max(t0, 0.0f);
  float t_exit  = t1;
  float t_len   = max(0.0f, t_exit - t_enter);

  float density = 0;
  for (int i = 0; i < u_steps; ++i) {
    float t = mix(t_enter, t_exit, (float(i) + 0.5f) / float(u_steps));
    vec3  p = ro + rd * t;
    switch (u_mode) {
      case 0: { density += sample_basic(p); } break;
      case 1: { density += sample_vfade(p); } break;
      case 2: { density += sample_waves(p); } break;
    }
  }
  density /= float(u_steps);

  // Beer-Lambert attenuation
  // ref: https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law
  float a = 1.0f - exp(-u_sigma * t_len * density);

  f_c = vec4(vec3(1.0f), a);
}