1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Shadow mapping using a depth texture
17 # The default light does not cast any shadows. It can be changed to a
18 # `ParallelLight` in client games to cast sun-like shadows:
23 # var sun = new ParallelLight
30 intrude import gamnit
::depth_core
34 # Resolution of the shadow texture, defaults to 4096 pixels
36 # TODO make configurable / ask the hardware for gl_MAX_TEXTURE_SIZE
37 var shadow_resolution
= 4096
39 # Are shadows supported by the current hardware configuration?
41 # The implementation may change in the future, but it currently relies on
42 # the GL extension `GL_EOS_depth_texture`.
43 var supports_shadows
: Bool is lazy
do
44 return display
.as(not null).gl_extensions
.has
("GL_OES_depth_texture")
47 # Is `shadow_context.depth_texture` ready to be used?
48 fun shadow_depth_texture_available
: Bool
49 do return supports_shadows
and shadow_context
.depth_texture
!= -1
51 private var shadow_depth_program
= new ShadowDepthProgram
53 private var perf_clock_shadow
= new Clock is lazy
55 redef fun create_gamnit
59 var program
= shadow_depth_program
60 program
.compile_and_link
61 var error
= program
.error
62 assert error
== null else print_error error
65 private var shadow_context
: ShadowContext = create_shadow_context
is lazy
67 private fun create_shadow_context
: ShadowContext
70 assert display
!= null
72 var context
= new ShadowContext
73 context
.prepare_once
(display
, shadow_resolution
)
77 # Update the depth texture from the light point of view
79 # This method updates `shadow_context.depth_texture`.
80 protected fun frame_core_shadow_prep
(display
: GamnitDisplay)
82 if not supports_shadows
then return
85 if not light
isa LightCastingShadows then return
87 # Make sure there's no errors pending
88 assert glGetError
== gl_NO_ERROR
90 # Bind the framebuffer and make sure it is OK
91 glBindFramebuffer
(gl_FRAMEBUFFER
, shadow_context
.light_view_framebuffer
)
92 assert glGetError
== gl_NO_ERROR
93 assert glCheckFramebufferStatus
(gl_FRAMEBUFFER
) == gl_FRAMEBUFFER_COMPLETE
95 # Draw to fill the depth texture and only the depth
96 glViewport
(0, 0, shadow_resolution
, shadow_resolution
)
97 glColorMask
(false, false, false, false)
98 glClear gl_COLOR_BUFFER_BIT
| gl_DEPTH_BUFFER_BIT
99 assert glGetError
== gl_NO_ERROR
101 # Update light position
102 var camera
= light
.camera
103 camera
.position
.x
= app
.world_camera
.position
.x
104 camera
.position
.y
= app
.world_camera
.position
.y
105 camera
.position
.z
= app
.world_camera
.position
.z
108 for actor
in actors
do
109 for leaf
in actor
.model
.leaves
do
110 leaf
.material
.draw_depth
(actor
, leaf
, camera
)
114 # Take down, bring back default values
115 glBindFramebuffer
(gl_FRAMEBUFFER
, shadow_context
.screen_framebuffer
)
116 glColorMask
(true, true, true, true)
120 # Debug: show light view in the bottom left of the screen
122 # Lazy load the debugging program
123 private var shadow_debug_program
: LightPointOfViewProgram is lazy
do
124 var program
= new LightPointOfViewProgram
125 program
.compile_and_link
126 var error
= program
.error
127 assert error
== null else print_error error
131 # Draw the light view in the bottom left of the screen, for debugging only
133 # The shadow depth texture is a square that can be deformed by this projection.
134 protected fun frame_core_shadow_debug
(display
: GamnitDisplay)
136 if not supports_shadows
then
137 print_error
"Error: Shadows are not supported by the current hardware configuration"
141 perf_clock_shadow
.lapse
143 var program
= shadow_debug_program
145 glBindBuffer
(gl_ARRAY_BUFFER
, shadow_context
.buffer_array
)
146 glViewport
(0, 0, display
.width
/3, display
.height
/3)
147 glClear gl_DEPTH_BUFFER_BIT
151 glActiveTexture gl_TEXTURE0
152 glBindTexture
(gl_TEXTURE_2D
, shadow_context
.depth_texture
)
153 program
.texture
.uniform
0
156 var sizeof_gl_float
= 4
158 glEnableVertexAttribArray program
.coord
.location
159 glVertexAttribPointeri
(program
.coord
.location
, n_floats
, gl_FLOAT
, false, 0, 0)
160 var offset
= 4 * n_floats
* sizeof_gl_float
163 glEnableVertexAttribArray program
.tex_coord
.location
164 glVertexAttribPointeri
(program
.tex_coord
.location
, n_floats
, gl_FLOAT
, false, 0, offset
)
165 var gl_error
= glGetError
166 assert gl_error
== gl_NO_ERROR
else print_error gl_error
169 glDrawArrays
(gl_TRIANGLE_STRIP
, 0, 4)
170 gl_error
= glGetError
171 assert gl_error
== gl_NO_ERROR
else print_error gl_error
174 glBindBuffer
(gl_ARRAY_BUFFER
, 0)
175 gl_error
= glGetError
176 assert gl_error
== gl_NO_ERROR
else print_error gl_error
178 sys
.perfs
["gamnit shadow debug"].add app
.perf_clock_shadow
.lapse
182 # Handles to reused GL buffers and texture
183 private class ShadowContext
185 # Real screen framebuffer
186 var screen_framebuffer
: Int = -1
188 # Framebuffer for the light point of view
189 var light_view_framebuffer
: Int = -1
191 # Depth attached to `light_view_framebuffer`
192 var depth_texture
: Int = -1
194 # Buffer name for vertex data
195 var buffer_array
: Int = -1
197 # Prepare all attributes once per resolution change
198 fun prepare_once
(display
: GamnitDisplay, shadow_resolution
: Int)
200 assert display
.gl_extensions
.has
("GL_OES_depth_texture")
202 # Set aside the real screen framebuffer name
203 var screen_framebuffer
= glGetIntegerv
(gl_FRAMEBUFFER_BINDING
, 0)
204 self.screen_framebuffer
= screen_framebuffer
207 var framebuffer
= glGenFramebuffers
(1).first
208 glBindFramebuffer
(gl_FRAMEBUFFER
, framebuffer
)
209 assert glIsFramebuffer
(framebuffer
)
210 self.light_view_framebuffer
= framebuffer
211 var gl_error
= glGetError
212 assert gl_error
== gl_NO_ERROR
else print_error gl_error
214 # Depth & texture/color
215 var textures
= glGenTextures
(1)
216 self.depth_texture
= textures
[0]
217 gl_error
= glGetError
218 assert gl_error
== gl_NO_ERROR
else print_error gl_error
220 resize
(display
, shadow_resolution
)
221 assert glCheckFramebufferStatus
(gl_FRAMEBUFFER
) == gl_FRAMEBUFFER_COMPLETE
224 buffer_array
= glGenBuffers
(1).first
225 glBindBuffer
(gl_ARRAY_BUFFER
, buffer_array
)
226 assert glIsBuffer
(buffer_array
)
227 gl_error
= glGetError
228 assert gl_error
== gl_NO_ERROR
else print_error gl_error
231 var data
= new Array[Float]
232 data
.add_all
([-1.0, -1.0, 0.0,
237 data
.add_all
([0.0, 0.0,
241 var c_data
= new GLfloatArray.from
(data
)
242 glBufferData
(gl_ARRAY_BUFFER
, data
.length
*4, c_data
.native_array
, gl_STATIC_DRAW
)
244 glBindBuffer
(gl_ARRAY_BUFFER
, 0)
246 gl_error
= glGetError
247 assert gl_error
== gl_NO_ERROR
else print_error gl_error
250 # Init size or resize `depth_texture`
251 fun resize
(display
: GamnitDisplay, shadow_resolution
: Int)
253 glBindFramebuffer
(gl_FRAMEBUFFER
, light_view_framebuffer
)
254 var gl_error
= glGetError
255 assert gl_error
== gl_NO_ERROR
else print_error gl_error
258 var depth_texture
= self.depth_texture
259 glActiveTexture gl_TEXTURE0
260 glBindTexture
(gl_TEXTURE_2D
, depth_texture
)
261 glTexParameteri
(gl_TEXTURE_2D
, gl_TEXTURE_MIN_FILTER
, gl_LINEAR
)
262 glTexParameteri
(gl_TEXTURE_2D
, gl_TEXTURE_MAG_FILTER
, gl_NEAREST
)
263 glTexParameteri
(gl_TEXTURE_2D
, gl_TEXTURE_WRAP_S
, gl_CLAMP_TO_EDGE
)
264 glTexParameteri
(gl_TEXTURE_2D
, gl_TEXTURE_WRAP_T
, gl_CLAMP_TO_EDGE
)
265 gl_error
= glGetError
266 assert gl_error
== gl_NO_ERROR
else print_error gl_error
268 # TODO support hardware shadows with GL ES 3.0 or GL_EXT_shadow_samplers
269 #glTexParameteri(gl_TEXTURE_2D, gl_TEXTURE_COMPARE_MODE, ...)
271 glTexImage2D
(gl_TEXTURE_2D
, 0, gl_DEPTH_COMPONENT
,
272 shadow_resolution
, shadow_resolution
,
273 0, gl_DEPTH_COMPONENT
, gl_UNSIGNED_SHORT
, new Pointer.nul
)
274 gl_error
= glGetError
275 assert gl_error
== gl_NO_ERROR
else print_error gl_error
277 glFramebufferTexture2D
(gl_FRAMEBUFFER
, gl_DEPTH_ATTACHMENT
, gl_TEXTURE_2D
, depth_texture
, 0)
278 gl_error
= glGetError
279 assert gl_error
== gl_NO_ERROR
else print_error gl_error
281 # Check if the framebuffer is complete and valid
282 assert glCheckFramebufferStatus
(gl_FRAMEBUFFER
) == gl_FRAMEBUFFER_COMPLETE
285 glBindTexture
(gl_TEXTURE_2D
, 0)
286 glBindFramebuffer
(gl_FRAMEBUFFER
, 0)
287 gl_error
= glGetError
288 assert gl_error
== gl_NO_ERROR
else print_error gl_error
291 var destroyed
= false
295 if destroyed
then return
299 glDeleteBuffers
([buffer_array
])
300 var gl_error
= glGetError
301 assert gl_error
== gl_NO_ERROR
else print_error gl_error
304 # Free the array and framebuffer plus its attachments
305 glDeleteBuffers
([buffer_array
])
306 glDeleteFramebuffers
([light_view_framebuffer
])
307 glDeleteTextures
([depth_texture
])
312 # Optimized draw of `model`, a part of `actor`, from the view of `camera`
314 # This drawing should only produce usable depth data. The default behavior,
315 # uses `shadow_depth_program`.
316 protected fun draw_depth
(actor
: Actor, model
: LeafModel, camera
: Camera)
318 var program
= app
.shadow_depth_program
320 program
.mvp
.uniform camera
.mvp_matrix
322 var mesh
= model
.mesh
324 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
325 program
.scale
.uniform actor
.scale
326 program
.use_map_diffuse
.uniform
false
328 program
.tex_coord
.array_enabled
= true
329 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
331 program
.coord
.array_enabled
= true
332 program
.coord
.array
(mesh
.vertices
, 3)
334 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
336 if mesh
.indices
.is_empty
then
337 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
339 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
345 # Efficiently draw actors from the light view
346 class ShadowDepthProgram
347 super GamnitProgramFromSource
349 redef var vertex_shader_source
= """
350 // Vertex coordinates
351 attribute vec4 coord;
353 // Vertex translation
354 uniform vec4 translation;
359 // Vertex coordinates on textures
360 attribute vec2 tex_coord;
363 attribute vec3 normal;
365 // Model view projection matrix
369 uniform mat4 rotation;
371 // Output for the fragment shader
372 varying vec2 v_tex_coord;
376 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
377 gl_Position = pos * mvp;
379 // Pass varyings to the fragment shader
380 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
382 """ @ glsl_vertex_shader
384 redef var fragment_shader_source
= """
385 precision mediump float;
388 uniform bool use_map_diffuse;
389 uniform sampler2D map_diffuse;
391 varying vec2 v_tex_coord;
395 if (use_map_diffuse && texture2D(map_diffuse, v_tex_coord).a <= 0.01) {
399 """ @ glsl_fragment_shader
401 # Vertices coordinates
402 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
404 # Should this program use the texture `map_diffuse`?
405 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
407 # Diffuse texture unit
408 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
410 # Coordinates on the textures, per vertex
411 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
414 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
416 # Translation applied to each vertex
417 var translation
= uniforms
["translation"].as(UniformVec4) is lazy
420 var rotation
= uniforms
["rotation"].as(UniformMat4) is lazy
423 var scale
= uniforms
["scale"].as(UniformFloat) is lazy
425 # Model view projection matrix
426 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
429 # Draw the camera point of view on screen
430 private class LightPointOfViewProgram
431 super GamnitProgramFromSource
433 redef var vertex_shader_source
= """
434 // Vertex coordinates
435 attribute vec3 coord;
437 // Vertex coordinates on textures
438 attribute vec2 tex_coord;
440 // Output to the fragment shader
441 varying vec2 v_coord;
445 gl_Position = vec4(coord, 1.0);
448 """ @ glsl_vertex_shader
450 redef var fragment_shader_source
= """
451 precision mediump float;
453 // Virtual screen texture / color attachment
454 uniform sampler2D texture0;
456 // Input from the vertex shader
457 varying vec2 v_coord;
461 gl_FragColor = texture2D(texture0, v_coord);
463 """ @ glsl_fragment_shader
465 # Vertices coordinates
466 var coord
= attributes
["coord"].as(AttributeVec3) is lazy
468 # Coordinates on the textures, per vertex
469 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
472 var texture
= uniforms
["texture0"].as(UniformSampler2D) is lazy