Skip to content

Instantly share code, notes, and snippets.

@wedesoft
Last active November 18, 2022 21:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wedesoft/f564054003eb7350cef8a18bf250dd7e to your computer and use it in GitHub Desktop.
Save wedesoft/f564054003eb7350cef8a18bf250dd7e to your computer and use it in GitHub Desktop.
Clouds with self-shadowing using deep opacity map
(require '[clojure.core.matrix :refer (matrix div sub mul add mget mmul inverse) :as m]
'[clojure.core.matrix.linear :refer (norm)]
'[clojure.math :refer (PI sqrt pow cos sin to-radians)]
'[com.climate.claypoole :as cp]
'[gnuplot.core :as g]
'[sfsim25.quaternion :as q]
'[sfsim25.atmosphere :refer :all]
'[sfsim25.clouds :refer :all]
'[sfsim25.shaders :as s]
'[sfsim25.ray :refer :all]
'[sfsim25.render :refer :all]
'[sfsim25.sphere :refer (ray-sphere-intersection)]
'[sfsim25.interpolate :refer :all]
'[sfsim25.matrix :refer :all]
'[sfsim25.util :refer :all])
(import '[ij ImagePlus]
'[ij.process FloatProcessor])
(import '[org.lwjgl.opengl Display DisplayMode PixelFormat GL11 GL12 GL20 GL30 GL32 GL42]
'[org.lwjgl.input Keyboard]
'[org.lwjgl BufferUtils])
(Display/setTitle "scratch")
(Display/setDisplayMode (DisplayMode. 1280 720))
;(Display/setFullscreen true)
(Display/create)
(Keyboard/create)
(def z-near 50)
(def z-far 150)
(def projection (projection-matrix (Display/getWidth) (Display/getHeight) z-near z-far (to-radians 75)))
(def origin (atom (matrix [0 0 100])))
(def orientation (atom (q/rotation (to-radians 0) (matrix [1 0 0]))))
(def threshold (atom 0.5))
(def anisotropic (atom 0.3))
(def multiplier (atom 5.0))
(def light (atom (/ PI 4)))
(def keystates (atom {}))
(def vertex-shader "#version 410 core
uniform mat4 projection;
uniform mat4 transform;
in vec3 point;
out VS_OUT
{
vec3 direction;
} vs_out;
void main()
{
vs_out.direction = (transform * vec4(point, 0)).xyz;
gl_Position = projection * vec4(point, 1);
}")
(def fragment-shader
"#version 410 core
uniform sampler3D worley;
uniform sampler3D opacity;
uniform sampler2D opacity_shape;
uniform vec3 origin;
uniform vec3 light_direction;
uniform mat4 shadow;
uniform float threshold;
uniform float multiplier;
uniform int cloud_size;
uniform float cloud_scale;
uniform float depth;
uniform float thickness;
in VS_OUT
{
vec3 direction;
} fs_in;
out vec3 fragColor;
vec2 ray_box(vec3 box_min, vec3 box_max, vec3 origin, vec3 direction);
float interpolate_3d(sampler3D tex, vec3 point, vec3 box_min, vec3 box_max);
vec3 cloud_track(vec3 light_direction, vec3 origin, vec3 direction, float a, float b, vec3 incoming);
vec3 cloud_track_base(vec3 origin, vec3 light_direction, float a, float b, vec3 incoming, float lod);
float cloud_density(vec3 point, float lod)
{
float s = interpolate_3d(worley, point * cloud_size / cloud_scale, vec3(-30, -30, -30), vec3(30, 30, 30));
return max((s - threshold) * multiplier, 0);
}
vec3 cloud_shadow(vec3 point, vec3 light_direction, float lod)
{
vec4 p = shadow * vec4(point, 1);
float offset = texture(opacity_shape, p.xy).r;
float z = (1 - p.z - offset) * depth / thickness;
vec3 idx = vec3(p.xy, z);
float o = texture(opacity, idx).r;
return vec3(o, o, o);
}
vec3 transmittance_track(vec3 p, vec3 q)
{
return vec3(1, 1, 1);
}
vec3 ray_scatter_track(vec3 light_direction, vec3 p, vec3 q)
{
return vec3(0, 0, 0);
}
void main()
{
vec3 direction = normalize(fs_in.direction);
float cos_light = dot(light_direction, direction);
vec3 bg = 0.7 * pow(max(cos_light, 0), 1000) + vec3(0.3, 0.3, 0.5);
vec2 intersection = ray_box(vec3(-30, -30, -30), vec3(30, 30, 30), origin, direction);
fragColor = cloud_track(light_direction, origin, direction, intersection.x, intersection.x + intersection.y, bg);
}")
(def program
(make-program :vertex [vertex-shader]
:fragment [fragment-shader s/ray-box s/convert-3d-index s/interpolate-3d phase-function cloud-track-base
cloud-track exponential-sampling s/is-above-horizon]))
(def indices [0 1 3 2])
(def vertices (map #(* % z-far) [-4 -4 -1, 4 -4 -1, -4 4 -1, 4 4 -1]))
(def vao (make-vertex-array-object program indices vertices [:point 3]))
(def size 128)
;(def values (worley-noise 12 size true))
;(spit-floats "values.raw" (float-array values))
(def values (slurp-floats "data/worley.raw"))
(def worley (make-float-texture-3d {:width size :height size :depth size :data values}))
(use-program program)
(uniform-sampler program :worley 0)
(uniform-sampler program :opacity 1)
(uniform-sampler program :opacity_shape 2)
(def svertex-shader
"#version 410 core
uniform mat4 iprojection;
in vec3 point;
out VS_OUT
{
vec3 origin;
} vs_out;
void main()
{
gl_Position = vec4(point, 1);
vec4 point = iprojection * vec4(point, 1);
vs_out.origin = point.xyz / point.w;
}")
(def sfragment-shader
"#version 410 core
uniform sampler3D worley;
uniform vec3 light_direction;
uniform float depth;
uniform float separation;
uniform float threshold;
uniform float anisotropic;
uniform float multiplier;
uniform int cloud_size;
uniform float cloud_scale;
uniform int cloud_base_samples;
uniform float cloud_scatter_amount;
in VS_OUT
{
vec3 origin;
} fs_in;
layout (location = 0) out float opacity1;
layout (location = 1) out float opacity2;
layout (location = 2) out float opacity3;
layout (location = 3) out float opacity4;
layout (location = 4) out float opacity5;
layout (location = 5) out float opacity6;
layout (location = 6) out float opacity7;
layout (location = 7) out float opacity_shape;
vec2 ray_box(vec3 box_min, vec3 box_max, vec3 origin, vec3 direction);
float interpolate_3d(sampler3D table, vec3 point, vec3 box_min, vec3 box_max);
float phase(float g, float mu);
float cloud_density(vec3 point, float lod)
{
float s = interpolate_3d(worley, point * cloud_size / cloud_scale, vec3(-30, -30, -30), vec3(30, 30, 30));
return max((s - threshold) * multiplier, 0);
}
void main()
{
vec2 intersection = ray_box(vec3(-30, -30, -30), vec3(30, 30, 30), fs_in.origin, -light_direction);
float scatter_amount = (anisotropic * phase(0.76, -1) + 1 - anisotropic) * cloud_scatter_amount;
float stepsize = 60.0 / cloud_base_samples;
int steps = int(ceil(intersection.y / stepsize));
stepsize = intersection.y / steps;
float previous_transmittance = 1.0;
float previous_depth = intersection.x - stepsize;
float start_depth = 0.0;
int filled = 0;
for (int i=0; i<steps; i++) {
float dist = intersection.x + (i + 0.5) * stepsize;
float depth = intersection.x + (i + 1) * stepsize;
vec3 point = fs_in.origin - light_direction * dist;
float density = cloud_density(point, 0.0);
float transmittance;
if (previous_transmittance == 1.0) {
start_depth = intersection.x + i * stepsize;
};
if (density > 0) {
float transmittance_step = exp((scatter_amount - 1) * density * stepsize);
transmittance = previous_transmittance * transmittance_step;
} else {
transmittance = previous_transmittance;
};
if (previous_depth < start_depth + 0 * separation && depth >= start_depth + 0 * separation) {
opacity1 = 1.0;
filled = 1;
};
if (previous_depth < start_depth + 1 * separation && depth >= start_depth + 1 * separation) {
float s = (start_depth + 1 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity2 = t;
filled = 2;
};
if (previous_depth < start_depth + 2 * separation && depth >= start_depth + 2 * separation) {
float s = (start_depth + 2 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity3 = t;
filled = 3;
};
if (previous_depth < start_depth + 3 * separation && depth >= start_depth + 3 * separation) {
float s = (start_depth + 3 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity4 = t;
filled = 4;
};
if (previous_depth < start_depth + 4 * separation && depth >= start_depth + 4 * separation) {
float s = (start_depth + 4 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity5 = t;
filled = 5;
};
if (previous_depth < start_depth + 5 * separation && depth >= start_depth + 5 * separation) {
float s = (start_depth + 5 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity6 = t;
filled = 6;
};
if (previous_depth < start_depth + 6 * separation && depth >= start_depth + 6 * separation) {
float s = (start_depth + 6 * separation - previous_depth) / (depth - previous_depth);
float t = s * transmittance + (1 - s) * previous_transmittance;
opacity7 = t;
filled = 7;
};
previous_depth = depth;
previous_transmittance = transmittance;
};
if (filled <= 0) opacity1 = previous_transmittance;
if (filled <= 1) opacity2 = previous_transmittance;
if (filled <= 2) opacity3 = previous_transmittance;
if (filled <= 3) opacity4 = previous_transmittance;
if (filled <= 4) opacity5 = previous_transmittance;
if (filled <= 5) opacity6 = previous_transmittance;
if (filled <= 6) opacity7 = previous_transmittance;
opacity_shape = start_depth / depth;
}")
(def sprogram
(make-program :vertex [svertex-shader]
:fragment [sfragment-shader s/ray-box s/interpolate-3d phase-function s/convert-3d-index]))
(use-program sprogram)
(uniform-sampler sprogram :worley 0)
(def indices [0 1 3 2])
(def vertices [-1 -1 1, 1 -1 1, -1 1 1, 1 1 1]) ; quad near to light in NDCs
(def vao2 (make-vertex-array-object sprogram indices vertices [:point 3]))
(def opacity (GL11/glGenTextures))
(def opacity-shape (GL11/glGenTextures))
(GL11/glBindTexture GL12/GL_TEXTURE_3D opacity)
(GL42/glTexStorage3D GL12/GL_TEXTURE_3D 1 GL30/GL_R32F 512 512 7)
(GL11/glTexParameteri GL12/GL_TEXTURE_3D GL11/GL_TEXTURE_WRAP_S GL12/GL_CLAMP_TO_EDGE)
(GL11/glTexParameteri GL12/GL_TEXTURE_3D GL11/GL_TEXTURE_WRAP_T GL12/GL_CLAMP_TO_EDGE)
(GL11/glTexParameteri GL12/GL_TEXTURE_3D GL12/GL_TEXTURE_WRAP_R GL12/GL_CLAMP_TO_EDGE)
(GL11/glTexParameteri GL12/GL_TEXTURE_3D GL11/GL_TEXTURE_MIN_FILTER GL11/GL_LINEAR)
(GL11/glTexParameteri GL12/GL_TEXTURE_3D GL11/GL_TEXTURE_MAG_FILTER GL11/GL_LINEAR)
(GL11/glBindTexture GL11/GL_TEXTURE_2D opacity-shape)
(GL42/glTexStorage2D GL11/GL_TEXTURE_2D 1 GL30/GL_R32F 512 512)
(GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_WRAP_S GL12/GL_CLAMP_TO_EDGE)
(GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_WRAP_T GL12/GL_CLAMP_TO_EDGE)
(GL11/glTexParameteri GL12/GL_TEXTURE_2D GL11/GL_TEXTURE_MIN_FILTER GL11/GL_LINEAR)
(GL11/glTexParameteri GL12/GL_TEXTURE_2D GL11/GL_TEXTURE_MAG_FILTER GL11/GL_LINEAR)
(def t0 (atom (System/currentTimeMillis)))
(def tf @t0)
(def n (atom 0))
(while (not (Display/isCloseRequested))
(while (Keyboard/next)
(let [state (Keyboard/getEventKeyState)
event-key (Keyboard/getEventKey)]
(swap! keystates assoc event-key state)))
(let [t1 (System/currentTimeMillis)
dt (- t1 @t0)
ra (if (@keystates Keyboard/KEY_NUMPAD8) 0.001 (if (@keystates Keyboard/KEY_NUMPAD2) -0.001 0))
rb (if (@keystates Keyboard/KEY_NUMPAD4) 0.001 (if (@keystates Keyboard/KEY_NUMPAD6) -0.001 0))
rc (if (@keystates Keyboard/KEY_NUMPAD3) 0.001 (if (@keystates Keyboard/KEY_NUMPAD1) -0.001 0))
tr (if (@keystates Keyboard/KEY_Q) 0.001 (if (@keystates Keyboard/KEY_A) -0.001 0))
ts (if (@keystates Keyboard/KEY_W) 0.001 (if (@keystates Keyboard/KEY_S) -0.001 0))
tm (if (@keystates Keyboard/KEY_E) 0.001 (if (@keystates Keyboard/KEY_D) -0.001 0))
ti (if (@keystates Keyboard/KEY_R) 0.001 (if (@keystates Keyboard/KEY_F) -0.001 0))
l (if (@keystates Keyboard/KEY_ADD) 0.0005 (if (@keystates Keyboard/KEY_SUBTRACT) -0.0005 0))]
(swap! orientation q/* (q/rotation (* dt ra) (matrix [1 0 0])))
(swap! orientation q/* (q/rotation (* dt rb) (matrix [0 1 0])))
(swap! orientation q/* (q/rotation (* dt rc) (matrix [0 0 1])))
(swap! threshold + (* dt tr))
(swap! anisotropic + (* dt ts))
(swap! multiplier + (* dt tm))
(reset! origin (mmul (quaternion->matrix @orientation) (matrix [0 0 100])))
(swap! light + (* l dt))
(swap! t0 + dt)
(swap! n + 1)
(print "\rthreshold" (format "%.3f" @threshold)
"anisotropic" (format "%.3f" @anisotropic)
"multiplier" (format "%.3f" @multiplier)
"fps" (format "%.3f" (/ (* @n 1000.0) (- t1 tf)))))
(let [light-direction (matrix [0 (cos @light) (sin @light)])
transform (transformation-matrix (quaternion->matrix @orientation) @origin)
shadow-mat (shadow-matrices projection transform light-direction 0)]
(let [fbo (GL30/glGenFramebuffers)]
(GL30/glBindFramebuffer GL30/GL_FRAMEBUFFER fbo)
(doseq [i (range 7)]
(GL30/glFramebufferTextureLayer GL30/GL_FRAMEBUFFER (+ GL30/GL_COLOR_ATTACHMENT0 i) opacity 0 i))
(GL30/glFramebufferTexture2D GL30/GL_FRAMEBUFFER GL30/GL_COLOR_ATTACHMENT7 GL11/GL_TEXTURE_2D opacity-shape 0)
(GL20/glDrawBuffers (make-int-buffer (int-array (for [i (range 8)] (+ GL30/GL_COLOR_ATTACHMENT0 i)))))
(setup-rendering 512 512 false)
(use-program sprogram)
(use-textures worley)
(uniform-matrix4 sprogram :iprojection (inverse (:shadow-ndc-matrix shadow-mat)))
(uniform-vector3 sprogram :light_direction light-direction)
(uniform-float sprogram :depth (:depth shadow-mat))
(uniform-float sprogram :separation 10.0)
(uniform-float sprogram :threshold @threshold)
(uniform-float sprogram :anisotropic @anisotropic)
(uniform-float sprogram :multiplier (* 0.1 @multiplier))
(uniform-float sprogram :cloud_scatter_amount 1.0)
(uniform-int sprogram :cloud_base_samples 64)
(uniform-int sprogram :cloud_size size)
(uniform-float sprogram :cloud_scale 350)
(render-quads vao2)
(GL30/glBindFramebuffer GL30/GL_FRAMEBUFFER 0))
(onscreen-render (Display/getWidth) (Display/getHeight)
(clear (matrix [0 0 0]))
(use-program program)
(use-textures worley
{:texture opacity :target GL12/GL_TEXTURE_3D}
{:texture opacity-shape :target GL11/GL_TEXTURE_2D})
(uniform-matrix4 program :projection projection)
(uniform-matrix4 program :transform transform)
(uniform-matrix4 program :shadow (:shadow-map-matrix shadow-mat))
(uniform-vector3 program :origin @origin)
(uniform-float program :threshold @threshold)
(uniform-float program :anisotropic @anisotropic)
(uniform-float program :cloud_scatter_amount 1.0)
(uniform-int program :cloud_min_samples 1)
(uniform-int program :cloud_max_samples 64)
(uniform-int program :cloud_size size)
(uniform-float program :cloud_scale 350)
(uniform-float program :cloud_max_step 1.05)
(uniform-int program :cloud_base_samples 64)
(uniform-float program :multiplier (* 0.1 @multiplier))
(uniform-vector3 program :light_direction light-direction)
(uniform-float program :depth (:depth shadow-mat))
(uniform-float program :thickness (* 7 10.0))
(render-quads vao))))
(destroy-texture {:texture opacity :target GL12/GL_TEXTURE_3D})
(destroy-texture {:texture opacity-shape :target GL11/GL_TEXTURE_2D})
(GL30/glDeleteFramebuffers fbo)
(destroy-vertex-array-object vao2)
(destroy-texture worley)
(destroy-vertex-array-object vao)
(destroy-program program)
(destroy-program sprogram)
(Display/destroy)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment