205205
206206; ; # Perlin noise
207207
208- (defn random-gradient
208+
209+ (defmulti random-gradient (fn [& args] (count args)))
210+
211+ (defmethod random-gradient 2
209212 [& args]
210213 (loop [args args]
211214 (let [random-vector (apply vec-n (map (fn [_x] (- (rand 2.0 ) 1.0 )) args))
215218 (recur args)))))
216219
217220
221+ (defmethod random-gradient 3
222+ [& _args]
223+ (apply vec3
224+ (rand-nth [[1 1 0 ] [-1 1 0 ] [1 -1 0 ] [-1 -1 0 ]
225+ [1 0 1 ] [-1 0 1 ] [1 0 -1 ] [-1 0 -1 ]
226+ [0 1 1 ] [ 0 -1 1 ] [0 1 -1 ] [ 0 -1 -1 ]])))
227+
228+
218229(defn roughly-vec
219230 [expected error]
220231 (fn [actual]
234245
235246
236247(facts " Random gradients"
237- (with-redefs [rand (constantly 1.5 )]
248+ (with-redefs [rand (constantly 1.5 )
249+ rand-nth (fn [x] (first x))]
238250 (dtype/shape (random-gradients {:divisions 8 :dimensions 2 })) => [8 8 ]
239251 ((random-gradients {:divisions 8 :dimensions 2 }) 0 0 ) => (roughly-vec (vec2 (sqrt 0.5 ) (sqrt 0.5 )) 1e-6 )
240252 (dtype/shape (random-gradients {:divisions 8 :dimensions 3 })) => [8 8 8 ]
241- ((random-gradients {:divisions 8 :dimensions 3 }) 0 0 0 ) => (roughly-vec ( vec3 ( sqrt ( / 1 3 )) ( sqrt ( / 1 3 )) ( sqrt ( / 1 3 ))) 1e-6 )))
253+ ((random-gradients {:divisions 8 :dimensions 3 }) 0 0 0 ) => (vec3 1 1 0 )))
242254
243255
244256(let [gradients (tensor/reshape (random-gradients (make-noise-params 256 8 2 )) [(* 8 8 )])
489501 (mapv #(/ % sum) series)))
490502
491503
504+ (octaves 4 0.5 )
505+
506+
492507(defn noise-octaves
493508 [tensor octaves low high]
494509 (tensor/clone
@@ -810,30 +825,31 @@ void main()
810825
811826; ; # Shader for light transfer through clouds
812827
813- (def cloud-mock
828+ (def fog
814829 (template/fn [v]
815830" #version 130
816- float cloud (vec3 idx)
831+ float fog (vec3 idx)
817832{
818833 return <%= v %>;
819834}" ))
820835
821836
822837(def cloud-transfer
838+ (template/fn [noise]
823839" #version 130
824- float cloud (vec3 idx);
840+ float <%= noise %> (vec3 idx);
825841vec4 cloud_transfer(vec3 origin, vec3 direction, vec2 interval, float step)
826842{
827843 vec4 result = vec4(0, 0, 0, 0);
828844 for (float t = interval.x + 0.5 * step; t < interval.y; t += step) {
829- float density = cloud (origin + direction * t);
845+ float density = <%= noise %> (origin + direction * t);
830846 float transmittance = exp(-density * step);
831847 vec3 color = vec3(1.0, 1.0, 1.0);
832848 result.rgb = result.rgb + color * (1.0 - result.a) * (1.0 - transmittance);
833849 result.a = 1.0 - (1.0 - result.a) * transmittance;
834850 };
835851 return result;
836- }" )
852+ }" ))
837853
838854
839855(def cloud-transfer-probe
@@ -859,7 +875,7 @@ void main()
859875
860876
861877(tabular " Test cloud transfer"
862- (fact (seq (render-pixel [vertex-test] [(cloud-mock ?density) cloud-transfer (cloud-transfer-probe ?a ?b ?step)]))
878+ (fact (seq (render-pixel [vertex-test] [(fog ?density) ( cloud-transfer " fog " ) (cloud-transfer-probe ?a ?b ?step)]))
863879 => (roughly-vector ?result 1e-3 ))
864880 ?a ?b ?step ?density ?result
865881 0 0 1 0.0 [0.0 0.0 0.0 0.0 ]
@@ -883,28 +899,28 @@ void main()
883899{
884900 vec3 direction = normalize(rotation * vec3(gl_FragCoord.xy - 0.5 * resolution, focal_length));
885901 vec3 origin = rotation * vec3(0, 0, -distance);
886- vec2 interval = ray_box(vec3(-1 , -1 , -1 ), vec3(1, 1, 1 ), origin, direction);
902+ vec2 interval = ray_box(vec3(-0.5 , -0.5 , -0.5 ), vec3(0.5, 0.5, 0.5 ), origin, direction);
887903 vec4 transfer = cloud_transfer(origin, direction, interval, 0.01);
888- vec3 background = vec3(0.5 , 0.5, 1.0 );
904+ vec3 background = vec3(0.125 , 0.125, 0.25 );
889905 fragColor = vec4(background * (1.0 - transfer.a) + transfer.rgb, 1.0);
890906}" )
891907
892908
893909(defn setup-fog-uniforms
894910 [program width height]
895- (let [rotation (mulm (rotation-matrix-3d-y (to-radians 30.0 )) (rotation-matrix-3d-x (to-radians -25 .0 )))
896- focal-length (/ (* 0.5 width) (tan (to-radians 30 .0 )))]
911+ (let [rotation (mulm (rotation-matrix-3d-y (to-radians 30.0 )) (rotation-matrix-3d-x (to-radians -20 .0 )))
912+ focal-length (/ (* 0.5 width) (tan (to-radians 25 .0 )))]
897913 (GL20/glUseProgram program)
898914 (GL20/glUniform2f (GL20/glGetUniformLocation program " resolution" ) width height)
899915 (GL20/glUniformMatrix3fv (GL20/glGetUniformLocation program " rotation" ) true
900916 (make-float-buffer (mat->float-array rotation)))
901917 (GL20/glUniform1f (GL20/glGetUniformLocation program " focal_length" ) focal-length)
902- (GL20/glUniform1f (GL20/glGetUniformLocation program " distance" ) 4.0 )))
918+ (GL20/glUniform1f (GL20/glGetUniformLocation program " distance" ) 2.5 )))
903919
904920
905921(defn render-fog
906922 [width height]
907- (let [fragment-sources [ray-box cloud-transfer ( cloud-mock 0.5 ) fragment-cloud]
923+ (let [fragment-sources [ray-box ( cloud-transfer " fog " ) ( fog 1.0 ) fragment-cloud]
908924 program (make-program-with-shaders [vertex-test] fragment-sources)
909925 vao (setup-quad-vao )
910926 texture (make-texture-2d width height)]
@@ -951,7 +967,7 @@ void main()
951967(def noise-shader
952968" #version 130
953969uniform sampler3D noise3d;
954- float cloud (vec3 idx)
970+ float noise (vec3 idx)
955971{
956972 return texture(noise3d, idx).r;
957973}" )
@@ -966,8 +982,8 @@ float cloud(vec3 idx)
966982
967983
968984(defn render-noise
969- [width height]
970- (let [fragment-sources [ray-box cloud-transfer noise-shader fragment-cloud]
985+ [width height & cloud-shaders ]
986+ (let [fragment-sources ( concat cloud-shaders [ray-box fragment-cloud])
971987 program (make-program-with-shaders [vertex-test] fragment-sources)
972988 vao (setup-quad-vao )
973989 texture (make-texture-2d width height)]
@@ -982,7 +998,64 @@ float cloud(vec3 idx)
982998 result)))
983999
9841000
985- (bufimg/tensor->image (rgba-array->bufimg (render-noise 640 480 ) 640 480 ))
1001+ (bufimg/tensor->image (rgba-array->bufimg (render-noise 640 480 (cloud-transfer " noise" ) noise-shader) 640 480 ))
1002+
1003+
1004+ ; ; # Remap and clamp 3D noise
1005+
1006+ (def remap-clamp
1007+ " #version 130
1008+ float remap_clamp(float value, float low1, float high1, float low2, float high2)
1009+ {
1010+ float t = (value - low1) / (high1 - low1);
1011+ return clamp(low2 + t * (high2 - low2), low2, high2);
1012+ }" )
1013+
1014+
1015+ (def remap-probe
1016+ (template/fn [value low1 high1 low2 high2]
1017+ " #version 130
1018+ out vec4 fragColor;
1019+ float remap_clamp(float value, float low1, float high1, float low2, float high2);
1020+ void main()
1021+ {
1022+ fragColor = vec4(remap_clamp(<%= value %>, <%= low1 %>, <%= high1 %>, <%= low2 %>, <%= high2 %>));
1023+ }" ))
1024+
1025+
1026+ (tabular " Remap and clamp input parameter values"
1027+ (fact (first (render-pixel [vertex-test] [remap-clamp (remap-probe ?value ?low1 ?high1 ?low2 ?high2)]))
1028+ => ?expected)
1029+ ?value ?low1 ?high1 ?low2 ?high2 ?expected
1030+ 0 0 1 0 1 0.0
1031+ 1 0 1 0 1 1.0
1032+ 0 0 1 2 3 2.0
1033+ 1 0 1 2 3 3.0
1034+ 2 2 3 0 1 0.0
1035+ 3 2 3 0 1 1.0
1036+ 1 0 2 0 4 2.0
1037+ 0 1 2 1 2 1.0
1038+ 3 1 2 1 2 2.0 )
1039+
1040+
1041+ (def remap-noise
1042+ (template/fn [base low1 high1 high2]
1043+ " #version 130
1044+ float <%= base %>(vec3 idx);
1045+ float remap_clamp(float value, float low1, float high1, float low2, float high2);
1046+ float remap_noise(vec3 idx)
1047+ {
1048+ return remap_clamp(<%= base %>(idx), <%= low1 %>, <%= high1 %>, 0.0, <%= high2 %>);
1049+ }" ))
1050+
1051+
1052+ (bufimg/tensor->image (rgba-array->bufimg (render-noise 640 480 (cloud-transfer " remap_noise" ) remap-clamp (remap-noise " noise" 0.45 0.9 1.5 ) noise-shader) 640 480 ))
1053+
1054+
1055+ ; ; # Octaves of 3D noise
1056+
1057+ (bufimg/tensor->image (rgba-array->bufimg (render-noise 640 480 (cloud-transfer " remap_noise" ) remap-clamp (remap-noise " octaves" 0.45 0.9 1.5 ) (noise-octaves (octaves 4 0.5 )) noise-shader) 640 480 ))
1058+
9861059
9871060(GL11/glDeleteTextures noise-texture)
9881061
0 commit comments