├── data ├── grass.jpg ├── rock.jpg ├── checked.jpg ├── cracks.jpg └── heightmap.jpg ├── .github ├── Screenshot-1.png ├── Screenshot-2.png └── Screenshot-3.png ├── LICENSE ├── main.py └── README.md /data/grass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/data/grass.jpg -------------------------------------------------------------------------------- /data/rock.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/data/rock.jpg -------------------------------------------------------------------------------- /data/checked.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/data/checked.jpg -------------------------------------------------------------------------------- /data/cracks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/data/cracks.jpg -------------------------------------------------------------------------------- /data/heightmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/data/heightmap.jpg -------------------------------------------------------------------------------- /.github/Screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/.github/Screenshot-1.png -------------------------------------------------------------------------------- /.github/Screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/.github/Screenshot-2.png -------------------------------------------------------------------------------- /.github/Screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/szabolcsdombi/heightmap-multitexture-terrain/HEAD/.github/Screenshot-3.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Szabolcs Dombi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import math 2 | import struct 3 | 4 | import GLWindow 5 | import ModernGL 6 | from PIL import Image 7 | from pyrr import Matrix44 8 | 9 | wnd = GLWindow.create_window() 10 | ctx = ModernGL.create_context() 11 | 12 | prog = ctx.program([ 13 | ctx.vertex_shader(''' 14 | #version 330 15 | 16 | uniform mat4 Mvp; 17 | uniform sampler2D Heightmap; 18 | 19 | in vec2 vert; 20 | 21 | out vec2 v_text; 22 | 23 | void main() { 24 | vec4 vertex = vec4(vert - 0.5, texture(Heightmap, vert).r * 0.2, 1.0); 25 | gl_Position = Mvp * vertex; 26 | v_text = vert; 27 | } 28 | '''), 29 | ctx.fragment_shader(''' 30 | #version 330 31 | 32 | uniform sampler2D Heightmap; 33 | 34 | uniform sampler2D Color1; 35 | uniform sampler2D Color2; 36 | 37 | uniform sampler2D Cracks; 38 | uniform sampler2D Darken; 39 | 40 | in vec2 v_text; 41 | 42 | out vec4 f_color; 43 | 44 | void main() { 45 | float height = texture(Heightmap, v_text).r; 46 | float border = smoothstep(0.5, 0.7, height); 47 | 48 | vec3 color1 = texture(Color1, v_text * 7.0).rgb; 49 | vec3 color2 = texture(Color2, v_text * 6.0).rgb; 50 | 51 | vec3 color = color1 * (1.0 - border) + color2 * border; 52 | 53 | color *= 0.8 + 0.2 * texture(Darken, v_text * 3.0).r; 54 | color *= 0.5 + 0.5 * texture(Cracks, v_text * 5.0).r; 55 | color *= 0.5 + 0.5 * height; 56 | 57 | f_color = vec4(color, 1.0); 58 | } 59 | '''), 60 | ]) 61 | 62 | img0 = Image.open('data/heightmap.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 63 | img1 = Image.open('data/grass.jpg').convert('RGB').transpose(Image.FLIP_TOP_BOTTOM) 64 | img2 = Image.open('data/rock.jpg').convert('RGB').transpose(Image.FLIP_TOP_BOTTOM) 65 | img3 = Image.open('data/cracks.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 66 | img4 = Image.open('data/checked.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 67 | 68 | tex0 = ctx.texture(img0.size, 1, img0.tobytes()) 69 | tex1 = ctx.texture(img1.size, 3, img1.tobytes()) 70 | tex2 = ctx.texture(img2.size, 3, img2.tobytes()) 71 | tex3 = ctx.texture(img3.size, 1, img3.tobytes()) 72 | tex4 = ctx.texture(img4.size, 1, img4.tobytes()) 73 | 74 | tex0.build_mipmaps() 75 | tex1.build_mipmaps() 76 | tex2.build_mipmaps() 77 | tex3.build_mipmaps() 78 | tex4.build_mipmaps() 79 | 80 | tex0.use(0) 81 | tex1.use(1) 82 | tex2.use(2) 83 | tex3.use(3) 84 | tex4.use(4) 85 | 86 | prog.uniforms['Heightmap'].value = 0 87 | prog.uniforms['Color1'].value = 1 88 | prog.uniforms['Color2'].value = 2 89 | prog.uniforms['Cracks'].value = 3 90 | prog.uniforms['Darken'].value = 4 91 | 92 | index = 0 93 | vertices = bytearray() 94 | indices = bytearray() 95 | 96 | for i in range(64 - 1): 97 | for j in range(64): 98 | vertices += struct.pack('2f', i / 64, j / 64) 99 | indices += struct.pack('i', index) 100 | index += 1 101 | 102 | vertices += struct.pack('2f', (i + 1) / 64, j / 64) 103 | indices += struct.pack('i', index) 104 | index += 1 105 | 106 | indices += struct.pack('i', -1) 107 | 108 | vbo = ctx.buffer(vertices) 109 | ibo = ctx.buffer(indices) 110 | 111 | vao = ctx.vertex_array(prog, [(vbo, '2f', ['vert'])], ibo) 112 | 113 | while wnd.update(): 114 | angle = wnd.time * 0.5 115 | width, height = wnd.size 116 | proj = Matrix44.perspective_projection(45.0, width / height, 0.01, 10.0) 117 | look = Matrix44.look_at((math.cos(angle), math.sin(angle), 0.8), (0.0, 0.0, 0.1), (0.0, 0.0, 1.0)) 118 | prog.uniforms['Mvp'].write((proj * look).astype('float32').tobytes()) 119 | 120 | ctx.enable(ModernGL.DEPTH_TEST) 121 | ctx.viewport = wnd.viewport 122 | ctx.clear(1.0, 1.0, 1.0) 123 | vao.render(ModernGL.TRIANGLE_STRIP) 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multitexture Terrain rendering using [ModernGL](https://github.com/cprogrammer1994/ModernGL) 2 | Small project using [ModernGL](https://github.com/cprogrammer1994/ModernGL) for terrain rendering. 3 | 4 | This example is using **ModernGL 4.x.x** please install with 5 | 6 | ```sh 7 | pip install "ModernGL<5.0.0" 8 | ``` 9 | 10 | ## Screenshots 11 | 12 | [![Screenshot-1.png](.github/Screenshot-1.png)](#) 13 | [![Screenshot-2.png](.github/Screenshot-2.png)](#) 14 | [![Screenshot-3.png](.github/Screenshot-3.png)](#) 15 | 16 | ## VertexShader 17 | 18 | ```glsl 19 | #version 330 20 | 21 | uniform mat4 Mvp; 22 | uniform sampler2D Heightmap; 23 | 24 | in vec2 vert; 25 | 26 | out vec2 v_text; 27 | 28 | void main() { 29 | vec4 vertex = vec4(vert - 0.5, texture(Heightmap, vert).r * 0.2, 1.0); 30 | gl_Position = Mvp * vertex; 31 | v_text = vert; 32 | } 33 | ``` 34 | 35 | ## Fragment Shader 36 | 37 | ```glsl 38 | #version 330 39 | 40 | uniform sampler2D Heightmap; 41 | 42 | uniform sampler2D Color1; 43 | uniform sampler2D Color2; 44 | 45 | uniform sampler2D Cracks; 46 | uniform sampler2D Darken; 47 | 48 | in vec2 v_text; 49 | 50 | out vec4 f_color; 51 | 52 | void main() { 53 | float height = texture(Heightmap, v_text).r; 54 | float border = smoothstep(0.5, 0.7, height); 55 | 56 | vec3 color1 = texture(Color1, v_text * 7.0).rgb; 57 | vec3 color2 = texture(Color2, v_text * 6.0).rgb; 58 | 59 | vec3 color = color1 * (1.0 - border) + color2 * border; 60 | 61 | color *= 0.8 + 0.2 * texture(Darken, v_text * 3.0).r; 62 | color *= 0.5 + 0.5 * texture(Cracks, v_text * 5.0).r; 63 | color *= 0.5 + 0.5 * height; 64 | 65 | f_color = vec4(color, 1.0); 66 | } 67 | ``` 68 | 69 | ## The entire code 70 | 71 | ```python 72 | import math 73 | import struct 74 | 75 | import GLWindow 76 | import ModernGL 77 | from PIL import Image 78 | from pyrr import Matrix44 79 | 80 | wnd = GLWindow.create_window() 81 | ctx = ModernGL.create_context() 82 | 83 | prog = ctx.program([ 84 | ctx.vertex_shader(''' 85 | #version 330 86 | 87 | uniform mat4 Mvp; 88 | uniform sampler2D Heightmap; 89 | 90 | in vec2 vert; 91 | 92 | out vec2 v_text; 93 | 94 | void main() { 95 | vec4 vertex = vec4(vert - 0.5, texture(Heightmap, vert).r * 0.2, 1.0); 96 | gl_Position = Mvp * vertex; 97 | v_text = vert; 98 | } 99 | '''), 100 | ctx.fragment_shader(''' 101 | #version 330 102 | 103 | uniform sampler2D Heightmap; 104 | 105 | uniform sampler2D Color1; 106 | uniform sampler2D Color2; 107 | 108 | uniform sampler2D Cracks; 109 | uniform sampler2D Darken; 110 | 111 | in vec2 v_text; 112 | 113 | out vec4 f_color; 114 | 115 | void main() { 116 | float height = texture(Heightmap, v_text).r; 117 | float border = smoothstep(0.5, 0.7, height); 118 | 119 | vec3 color1 = texture(Color1, v_text * 7.0).rgb; 120 | vec3 color2 = texture(Color2, v_text * 6.0).rgb; 121 | 122 | vec3 color = color1 * (1.0 - border) + color2 * border; 123 | 124 | color *= 0.8 + 0.2 * texture(Darken, v_text * 3.0).r; 125 | color *= 0.5 + 0.5 * texture(Cracks, v_text * 5.0).r; 126 | color *= 0.5 + 0.5 * height; 127 | 128 | f_color = vec4(color, 1.0); 129 | } 130 | '''), 131 | ]) 132 | 133 | img0 = Image.open('data/heightmap.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 134 | img1 = Image.open('data/grass.jpg').convert('RGB').transpose(Image.FLIP_TOP_BOTTOM) 135 | img2 = Image.open('data/rock.jpg').convert('RGB').transpose(Image.FLIP_TOP_BOTTOM) 136 | img3 = Image.open('data/cracks.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 137 | img4 = Image.open('data/checked.jpg').convert('L').transpose(Image.FLIP_TOP_BOTTOM) 138 | 139 | tex0 = ctx.texture(img0.size, 1, img0.tobytes()) 140 | tex1 = ctx.texture(img1.size, 3, img1.tobytes()) 141 | tex2 = ctx.texture(img2.size, 3, img2.tobytes()) 142 | tex3 = ctx.texture(img3.size, 1, img3.tobytes()) 143 | tex4 = ctx.texture(img4.size, 1, img4.tobytes()) 144 | 145 | tex0.build_mipmaps() 146 | tex1.build_mipmaps() 147 | tex2.build_mipmaps() 148 | tex3.build_mipmaps() 149 | tex4.build_mipmaps() 150 | 151 | tex0.use(0) 152 | tex1.use(1) 153 | tex2.use(2) 154 | tex3.use(3) 155 | tex4.use(4) 156 | 157 | prog.uniforms['Heightmap'].value = 0 158 | prog.uniforms['Color1'].value = 1 159 | prog.uniforms['Color2'].value = 2 160 | prog.uniforms['Cracks'].value = 3 161 | prog.uniforms['Darken'].value = 4 162 | 163 | index = 0 164 | vertices = bytearray() 165 | indices = bytearray() 166 | 167 | for i in range(64 - 1): 168 | for j in range(64): 169 | vertices += struct.pack('2f', i / 64, j / 64) 170 | indices += struct.pack('i', index) 171 | index += 1 172 | 173 | vertices += struct.pack('2f', (i + 1) / 64, j / 64) 174 | indices += struct.pack('i', index) 175 | index += 1 176 | 177 | indices += struct.pack('i', -1) 178 | 179 | vbo = ctx.buffer(vertices) 180 | ibo = ctx.buffer(indices) 181 | 182 | vao = ctx.vertex_array(prog, [(vbo, '2f', ['vert'])], ibo) 183 | 184 | while wnd.update(): 185 | angle = wnd.time * 0.5 186 | width, height = wnd.size 187 | proj = Matrix44.perspective_projection(45.0, width / height, 0.01, 10.0) 188 | look = Matrix44.look_at((math.cos(angle), math.sin(angle), 0.8), (0.0, 0.0, 0.1), (0.0, 0.0, 1.0)) 189 | prog.uniforms['Mvp'].write((proj * look).astype('float32').tobytes()) 190 | 191 | ctx.enable(ModernGL.DEPTH_TEST) 192 | ctx.viewport = wnd.viewport 193 | ctx.clear(1.0, 1.0, 1.0) 194 | vao.render(ModernGL.TRIANGLE_STRIP) 195 | ``` 196 | 197 | ## Textures 198 | 199 | ### Heightmap 200 | [![heightmap.jpg](data/heightmap.jpg)](#) 201 | 202 | ### Grass 203 | [![grass.jpg](data/grass.jpg)](#) 204 | 205 | ### Rock 206 | [![rock.jpg](data/rock.jpg)](#) 207 | 208 | ### Checked 209 | [![checked.jpg](data/checked.jpg)](#) 210 | 211 | ### Cracks 212 | [![cracks.jpg](data/cracks.jpg)](#) 213 | 214 | --------------------------------------------------------------------------------