55 image : https://avatars.githubusercontent.com/u/28663?v=4
66 links :
77 - {icon: github, href: 'https://github.com/wedesoft'}
8- description : A short introduction to LWJGL's OpenGL bindings by using it to render data from NASA's CGI Moon Kit
8+ description : Using LWJGL's OpenGL bindings and Fastmath to render data from NASA's CGI Moon Kit
99image : moon.jpg
1010type : post
1111date : ' 2025-09-24'
@@ -109,8 +109,10 @@ We read the pixels, write them to a temporary file using the STB library and the
109109 []
110110 (let [filename (tmpname )
111111 buffer (java.nio.ByteBuffer/allocateDirect (* 4 window-width window-height))]
112- (GL11/glReadPixels 0 0 window-width window-height GL11/GL_RGBA GL11/GL_UNSIGNED_BYTE buffer)
113- (STBImageWrite/stbi_write_png filename window-width window-height 4 buffer (* 4 window-width))
112+ (GL11/glReadPixels 0 0 window-width window-height
113+ GL11/GL_RGBA GL11/GL_UNSIGNED_BYTE buffer)
114+ (STBImageWrite/stbi_write_png filename window-width window-height 4
115+ buffer (* 4 window-width))
114116 (-> filename io/file (ImageIO/read ))))
115117```
116118:::
@@ -175,7 +177,7 @@ Next we need to set up OpenGL rendering for this window.
175177
176178::: {.printedClojure}
177179``` clojure
178- #object[org.lwjgl.opengl.GLCapabilities 0x6c66f8f9 " org.lwjgl.opengl.GLCapabilities@6c66f8f9 " ]
180+ #object[org.lwjgl.opengl.GLCapabilities 0x3763ef0d " org.lwjgl.opengl.GLCapabilities@3763ef0d " ]
179181
180182```
181183:::
@@ -391,9 +393,11 @@ We add a convenience function to setup VAO, VBO, and IBO.
391393 ibo (GL15/glGenBuffers )]
392394 (GL30/glBindVertexArray vao)
393395 (GL15/glBindBuffer GL15/GL_ARRAY_BUFFER vbo)
394- (GL15/glBufferData GL15/GL_ARRAY_BUFFER (make-float-buffer vertices) GL15/GL_STATIC_DRAW)
396+ (GL15/glBufferData GL15/GL_ARRAY_BUFFER (make-float-buffer vertices)
397+ GL15/GL_STATIC_DRAW)
395398 (GL15/glBindBuffer GL15/GL_ELEMENT_ARRAY_BUFFER ibo)
396- (GL15/glBufferData GL15/GL_ELEMENT_ARRAY_BUFFER (make-int-buffer indices) GL15/GL_STATIC_DRAW)
399+ (GL15/glBufferData GL15/GL_ELEMENT_ARRAY_BUFFER (make-int-buffer indices)
400+ GL15/GL_STATIC_DRAW)
397401 {:vao vao :vbo vbo :ibo ibo}))
398402```
399403:::
@@ -416,7 +420,8 @@ We need to specify the layout of the vertex buffer object so that OpenGL knows h
416420::: {.sourceClojure}
417421``` clojure
418422(do
419- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
423+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program " point" ) 3
424+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
420425 (GL20/glEnableVertexAttribArray 0 ))
421426```
422427:::
@@ -441,7 +446,8 @@ We select the program and define the uniform variable iResolution.
441446``` clojure
442447(do
443448 (GL20/glUseProgram program)
444- (GL20/glUniform2f (GL20/glGetUniformLocation program " iResolution" ) window-width window-height))
449+ (GL20/glUniform2f (GL20/glGetUniformLocation program " iResolution" )
450+ window-width window-height))
445451```
446452:::
447453
@@ -533,15 +539,17 @@ We define a function to download a file from the web.
533539:::
534540
535541
536- If it does not exist, we download the lunar color map from the [ NASA CGI Moon Kit] ( https://svs.gsfc.nasa.gov/4720/ ) /
542+ If it does not exist, we download the lunar color map from the [ NASA CGI Moon Kit] ( https://svs.gsfc.nasa.gov/4720/ ) .
537543
538544
539545::: {.sourceClojure}
540546``` clojure
541547(do
542548 (def moon-tif " src/opengl_visualization/lroc_color_poles_2k.tif" )
543549 (when (not (.exists (io/file moon-tif)))
544- (download " https://svs.gsfc.nasa.gov/vis/a000000/a004700/a004720/lroc_color_poles_2k.tif" moon-tif)))
550+ (download
551+ " https://svs.gsfc.nasa.gov/vis/a000000/a004700/a004720/lroc_color_poles_2k.tif"
552+ moon-tif)))
545553```
546554:::
547555
@@ -594,7 +602,8 @@ Then we create an OpenGL texture from the RGB data.
594602 (def texture-color (GL11/glGenTextures ))
595603 (GL11/glBindTexture GL11/GL_TEXTURE_2D texture-color)
596604 (GL11/glTexImage2D GL11/GL_TEXTURE_2D 0 GL11/GL_RGBA color-width color-height 0
597- GL11/GL_RGB GL11/GL_UNSIGNED_BYTE (make-byte-buffer (byte-array (map unchecked-byte color-pixels))))
605+ GL11/GL_RGB GL11/GL_UNSIGNED_BYTE
606+ (make-byte-buffer (byte-array (map unchecked-byte color-pixels))))
598607 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_MIN_FILTER GL11/GL_LINEAR)
599608 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_MAG_FILTER GL11/GL_LINEAR)
600609 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_WRAP_S GL11/GL_REPEAT)
@@ -682,7 +691,8 @@ We need to set up the layout of the vertex data again.
682691::: {.sourceClojure}
683692``` clojure
684693(do
685- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation tex-program " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
694+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation tex-program " point" ) 3
695+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
686696 (GL20/glEnableVertexAttribArray 0 ))
687697```
688698:::
@@ -704,7 +714,8 @@ We set the resolution and bind the texture to the texture slot number 0.
704714``` clojure
705715(do
706716 (GL20/glUseProgram tex-program)
707- (GL20/glUniform2f (GL20/glGetUniformLocation tex-program " iResolution" ) window-width window-height)
717+ (GL20/glUniform2f (GL20/glGetUniformLocation tex-program " iResolution" )
718+ window-width window-height)
708719 (GL20/glUniform1i (GL20/glGetUniformLocation tex-program " moon" ) 0 )
709720 (GL13/glActiveTexture GL13/GL_TEXTURE0)
710721 (GL11/glBindTexture GL11/GL_TEXTURE_2D texture-color))
@@ -875,8 +886,12 @@ out vec3 vpoint;
875886void main()
876887{
877888 // Rotate and translate vertex
878- mat3 rot_y = mat3(vec3(cos(alpha), 0, sin(alpha)), vec3(0, 1, 0), vec3(-sin(alpha), 0, cos(alpha)));
879- mat3 rot_x = mat3(vec3(1, 0, 0), vec3(0, cos(beta), -sin(beta)), vec3(0, sin(beta), cos(beta)));
889+ mat3 rot_y = mat3(vec3(cos(alpha), 0, sin(alpha)),
890+ vec3(0, 1, 0),
891+ vec3(-sin(alpha), 0, cos(alpha)));
892+ mat3 rot_x = mat3(vec3(1, 0, 0),
893+ vec3(0, cos(beta), -sin(beta)),
894+ vec3(0, sin(beta), cos(beta)));
880895 vec3 p = rot_x * rot_y * point + vec3(0, 0, distance);
881896
882897 // Project vertex creating normalized device coordinates
@@ -956,7 +971,8 @@ We need to set up the memory layout again.
956971::: {.sourceClojure}
957972``` clojure
958973(do
959- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
974+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3
975+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
960976 (GL20/glEnableVertexAttribArray 0 ))
961977```
962978:::
@@ -1063,7 +1079,9 @@ First we partition the vertex data and convert the triplets to 8 Fastmath vector
10631079
10641080::: {.sourceClojure}
10651081``` clojure
1066- (def points (map #(apply vec3 %) (partition 3 vertices-cube)))
1082+ (def points
1083+ (map #(apply vec3 %)
1084+ (partition 3 vertices-cube)))
10671085```
10681086:::
10691087
@@ -1097,7 +1115,9 @@ Then we use the index array to get the coordinates of the first corner of each f
10971115
10981116::: {.sourceClojure}
10991117``` clojure
1100- (def corners (map (fn [[i _ _ _]] (nth points i)) (partition 4 indices-cube)))
1118+ (def corners
1119+ (map (fn [[i _ _ _]] (nth points i))
1120+ (partition 4 indices-cube)))
11011121```
11021122:::
11031123
@@ -1129,7 +1149,9 @@ We get the first spanning vector of each face by subtracting the second corner f
11291149
11301150::: {.sourceClojure}
11311151``` clojure
1132- (def u-vectors (map (fn [[i j _ _]] (sub (nth points j) (nth points i))) (partition 4 indices-cube)))
1152+ (def u-vectors
1153+ (map (fn [[i j _ _]] (sub (nth points j) (nth points i)))
1154+ (partition 4 indices-cube)))
11331155```
11341156:::
11351157
@@ -1161,7 +1183,9 @@ We get the second spanning vector of each face by subtracting the fourth corner
11611183
11621184::: {.sourceClojure}
11631185``` clojure
1164- (def v-vectors (map (fn [[i _ _ l]] (sub (nth points l) (nth points i))) (partition 4 indices-cube)))
1186+ (def v-vectors
1187+ (map (fn [[i _ _ l]] (sub (nth points l) (nth points i)))
1188+ (partition 4 indices-cube)))
11651189```
11661190:::
11671191
@@ -1194,7 +1218,8 @@ We can now use vector math to subsample the faces and project the points onto a
11941218::: {.sourceClojure}
11951219``` clojure
11961220(defn sphere-points [n c u v]
1197- (for [j (range (inc n)) i (range (inc n))] (mult (normalize (add c (add (mult u (/ i n)) (mult v (/ j n))))) radius)))
1221+ (for [j (range (inc n)) i (range (inc n))]
1222+ (mult (normalize (add c (add (mult u (/ i n)) (mult v (/ j n))))) radius)))
11981223```
11991224:::
12001225
@@ -1231,7 +1256,10 @@ We also need a function to generate the indices for the quads.
12311256
12321257::: {.sourceClojure}
12331258``` clojure
1234- (defn sphere-indices [n face] (for [j (range n) i (range n)] (let [offset (+ (* face (inc n) (inc n)) (* j (inc n)) i)] [offset (inc offset) (+ offset n 2 ) (+ offset n 1 )])))
1259+ (defn sphere-indices [n face]
1260+ (for [j (range n) i (range n)]
1261+ (let [offset (+ (* face (inc n) (inc n)) (* j (inc n)) i)]
1262+ [offset (inc offset) (+ offset n 2 ) (+ offset n 1 )])))
12351263```
12361264:::
12371265
@@ -1265,7 +1293,8 @@ We subdivide once (n=2) and create a VAO with the data.
12651293``` clojure
12661294(do
12671295 (def n 2 )
1268- (def vertices-sphere (float-array (flatten (map (partial sphere-points n) corners u-vectors v-vectors))))
1296+ (def vertices-sphere (float-array (flatten (map (partial sphere-points n)
1297+ corners u-vectors v-vectors))))
12691298 (def indices-sphere (int-array (flatten (map (partial sphere-indices n) (range 6 )))))
12701299 (def vao-sphere (setup-vao vertices-sphere indices-sphere)))
12711300```
@@ -1287,7 +1316,8 @@ The layout needs to be configured again.
12871316::: {.sourceClojure}
12881317``` clojure
12891318(do
1290- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
1319+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3
1320+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
12911321 (GL20/glEnableVertexAttribArray 0 ))
12921322```
12931323:::
@@ -1392,7 +1422,8 @@ We set up the vertex layout again.
13921422::: {.sourceClojure}
13931423``` clojure
13941424(do
1395- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
1425+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-moon " point" ) 3
1426+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
13961427 (GL20/glEnableVertexAttribArray 0 ))
13971428```
13981429:::
@@ -1518,7 +1549,8 @@ We set up the vertex data layout again.
15181549::: {.sourceClojure}
15191550``` clojure
15201551(do
1521- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-diffuse " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
1552+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-diffuse " point" ) 3
1553+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
15221554 (GL20/glEnableVertexAttribArray 0 ))
15231555```
15241556:::
@@ -1550,14 +1582,16 @@ Before rendering we need to set up the various uniform values.
15501582``` clojure
15511583(do
15521584 (GL20/glUseProgram program-diffuse)
1553- (GL20/glUniform2f (GL20/glGetUniformLocation program-diffuse " iResolution" ) window-width window-height)
1585+ (GL20/glUniform2f (GL20/glGetUniformLocation program-diffuse " iResolution" )
1586+ window-width window-height)
15541587 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " fov" ) (to-radians 20.0 ))
15551588 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " alpha" ) (to-radians 0.0 ))
15561589 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " beta" ) (to-radians 0.0 ))
15571590 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " distance" ) (* radius 10.0 ))
15581591 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " ambient" ) 0.0 )
15591592 (GL20/glUniform1f (GL20/glGetUniformLocation program-diffuse " diffuse" ) 1.6 )
1560- (GL20/glUniform3f (GL20/glGetUniformLocation program-diffuse " light" ) (light 0 ) (light 1 ) (light 2 ))
1593+ (GL20/glUniform3f (GL20/glGetUniformLocation program-diffuse " light" )
1594+ (light 0 ) (light 1 ) (light 2 ))
15611595 (GL20/glUniform1i (GL20/glGetUniformLocation program-diffuse " moon" ) 0 )
15621596 (GL13/glActiveTexture GL13/GL_TEXTURE0)
15631597 (GL11/glBindTexture GL11/GL_TEXTURE_2D texture-color))
@@ -1630,7 +1664,8 @@ The lunar elevation data is downloaded from NASA's website.
16301664(do
16311665 (def moon-ldem " src/opengl_visualization/ldem_4.tif" )
16321666 (when (not (.exists (io/file moon-ldem)))
1633- (download " https://svs.gsfc.nasa.gov/vis/a000000/a004700/a004720/ldem_4.tif" moon-ldem)))
1667+ (download " https://svs.gsfc.nasa.gov/vis/a000000/a004700/a004720/ldem_4.tif"
1668+ moon-ldem)))
16341669```
16351670:::
16361671
@@ -1649,68 +1684,29 @@ The image is read using ImageIO and the floating point elevation data is extract
16491684
16501685::: {.sourceClojure}
16511686``` clojure
1652- (def ldem (ImageIO/read (io/file moon-ldem)))
1653- ```
1654- :::
1655-
1656-
1657-
1658- ::: {.sourceClojure}
1659- ``` clojure
1660- (def ldem-raster (.getRaster ldem))
1661- ```
1662- :::
1663-
1664-
1665-
1666- ::: {.sourceClojure}
1667- ``` clojure
1668- (def ldem-width (.getWidth ldem))
1669- ```
1670- :::
1671-
1672-
1673-
1674- ::: {.sourceClojure}
1675- ``` clojure
1676- (def ldem-height (.getHeight ldem))
1677- ```
1678- :::
1679-
1680-
1681-
1682- ::: {.sourceClojure}
1683- ``` clojure
1684- (def ldem-pixels (float-array (* ldem-width ldem-height)))
1685- ```
1686- :::
1687-
1688-
1689-
1690- ::: {.sourceClojure}
1691- ``` clojure
1692- (do (.getPixels ldem-raster 0 0 ldem-width ldem-height ldem-pixels) nil )
1687+ (do
1688+ (def ldem (ImageIO/read (io/file moon-ldem)))
1689+ (def ldem-raster (.getRaster ldem))
1690+ (def ldem-width (.getWidth ldem))
1691+ (def ldem-height (.getHeight ldem))
1692+ (def ldem-pixels (float-array (* ldem-width ldem-height)))
1693+ (do (.getPixels ldem-raster 0 0 ldem-width ldem-height ldem-pixels) nil )
1694+ (def resolution (/ (* 2.0 PI radius) ldem-width))
1695+ [ldem-width ldem-height]
1696+ )
16931697```
16941698:::
16951699
16961700
16971701
16981702::: {.printedClojure}
16991703``` clojure
1700- nil
1704+ [ 1440 720 ]
17011705
17021706```
17031707:::
17041708
17051709
1706-
1707- ::: {.sourceClojure}
1708- ``` clojure
1709- (def resolution (/ (* 2.0 PI radius) ldem-width))
1710- ```
1711- :::
1712-
1713-
17141710The floating point pixel data is converted into a texture
17151711
17161712
@@ -1723,7 +1719,8 @@ The floating point pixel data is converted into a texture
17231719 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_MAG_FILTER GL11/GL_LINEAR)
17241720 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_WRAP_S GL11/GL_REPEAT)
17251721 (GL11/glTexParameteri GL11/GL_TEXTURE_2D GL11/GL_TEXTURE_WRAP_T GL11/GL_REPEAT)
1726- (GL11/glTexImage2D GL11/GL_TEXTURE_2D 0 GL30/GL_R32F ldem-width ldem-height 0 GL11/GL_RED GL11/GL_FLOAT ldem-pixels))
1722+ (GL11/glTexImage2D GL11/GL_TEXTURE_2D 0 GL30/GL_R32F ldem-width ldem-height 0
1723+ GL11/GL_RED GL11/GL_FLOAT ldem-pixels))
17271724```
17281725:::
17291726
@@ -1857,7 +1854,8 @@ We set up the vertex data layout again.
18571854::: {.sourceClojure}
18581855``` clojure
18591856(do
1860- (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-normal " point" ) 3 GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
1857+ (GL20/glVertexAttribPointer (GL20/glGetAttribLocation program-normal " point" ) 3
1858+ GL11/GL_FLOAT false (* 3 Float/BYTES) (* 0 Float/BYTES))
18611859 (GL20/glEnableVertexAttribArray 0 ))
18621860```
18631861:::
@@ -1879,15 +1877,17 @@ Apart from the uniform values we also need to set up two textures this time: the
18791877``` clojure
18801878(do
18811879 (GL20/glUseProgram program-normal)
1882- (GL20/glUniform2f (GL20/glGetUniformLocation program-normal " iResolution" ) window-width window-height)
1880+ (GL20/glUniform2f (GL20/glGetUniformLocation program-normal " iResolution" )
1881+ window-width window-height)
18831882 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " fov" ) (to-radians 20.0 ))
18841883 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " alpha" ) (to-radians 0.0 ))
18851884 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " beta" ) (to-radians 0.0 ))
18861885 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " distance" ) (* radius 10.0 ))
18871886 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " resolution" ) resolution)
18881887 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " ambient" ) 0.0 )
18891888 (GL20/glUniform1f (GL20/glGetUniformLocation program-normal " diffuse" ) 1.6 )
1890- (GL20/glUniform3f (GL20/glGetUniformLocation program-normal " light" ) (light 0 ) (light 1 ) (light 2 ))
1889+ (GL20/glUniform3f (GL20/glGetUniformLocation program-normal " light" )
1890+ (light 0 ) (light 1 ) (light 2 ))
18911891 (GL20/glUniform1i (GL20/glGetUniformLocation program-normal " moon" ) 0 )
18921892 (GL20/glUniform1i (GL20/glGetUniformLocation program-normal " ldem" ) 1 )
18931893 (GL13/glActiveTexture GL13/GL_TEXTURE0)
0 commit comments