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 # Various material implementations
18 intrude import depth_core
24 # Get the default blueish material
25 new do return new SmoothMaterial(
31 # Simple material with static colors
35 # Ambient color, always visible
37 # The RGB values should be premultiplied by the alpha value.
38 var ambient_color
: Array[Float] is writable
40 # Diffuse color when covered by a light source
42 # The RGB values should be premultiplied by the alpha value.
43 var diffuse_color
: Array[Float] is writable
45 # Specular color affecting reflections
47 # The RGB values should be premultiplied by the alpha value.
48 var specular_color
: Array[Float] is writable
50 redef fun draw
(actor
, model
, camera
)
52 var program
= app
.blinn_phong_program
54 program
.mvp
.uniform camera
.mvp_matrix
59 glDisableVertexAttribArray program
.translation
.location
60 glDisableVertexAttribArray program
.scale
.location
62 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
63 program
.scale
.uniform actor
.scale
64 program
.alpha
.uniform actor
.alpha
65 program
.rotation
= new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
68 program
.coord
.array_enabled
= true
69 program
.coord
.array
(mesh
.vertices
, 3)
71 program
.normal
.array_enabled
= true
72 program
.normal
.array
(mesh
.normals
, 3)
75 program
.use_map_ambient
.uniform
false
76 program
.use_map_diffuse
.uniform
false
77 program
.use_map_specular
.uniform
false
78 program
.tex_coord
.array_enabled
= false
81 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
83 # Colors from the material
84 program
.ambient_color
.uniform
(ambient_color
[0], ambient_color
[1],
85 ambient_color
[2], ambient_color
[3])
86 program
.diffuse_color
.uniform
(diffuse_color
[0], diffuse_color
[1],
87 diffuse_color
[2], diffuse_color
[3])
88 program
.specular_color
.uniform
(specular_color
[0], specular_color
[1],
89 specular_color
[2], specular_color
[3])
91 setup_lights
(camera
, program
)
94 if mesh
.indices
.is_empty
then
95 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
97 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
101 private fun setup_lights
(camera
: Camera, program
: BlinnPhongProgram)
103 # TODO use a list of lights
105 # Light, for Lambert and Blinn-Phong
106 var light
= app
.light
107 if light
isa ParallelLight then
108 program
.light_kind
.uniform
1
110 # Vector parallel to the light source
111 program
.light_center
.uniform
(
112 -light
.pitch
.sin
* light
.yaw
.sin
,
115 else if light
isa PointLight then
116 program
.light_kind
.uniform
2
118 # Position of the light source
119 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
121 program
.light_kind
.uniform
0
124 # Draw projected shadows?
125 if not light
isa LightCastingShadows or not app
.shadow_depth_texture_available
then
126 program
.use_shadows
.uniform
false
128 else program
.use_shadows
.uniform
true
130 # Light point of view
131 program
.light_mvp
.uniform light
.camera
.mvp_matrix
134 glActiveTexture gl_TEXTURE4
135 glBindTexture
(gl_TEXTURE_2D
, app
.shadow_context
.depth_texture
)
136 program
.depth_texture
.uniform
4
137 program
.depth_texture_size
.uniform app
.shadow_resolution
.to_f
138 program
.depth_texture_taps
.uniform
2 # TODO make configurable
142 # Material with potential `diffuse_texture` and `specular_texture`
143 class TexturedMaterial
146 # Texture applied to the ambient_color
147 var ambient_texture
: nullable Texture = null is writable
149 # Texture applied to the diffuse color
150 var diffuse_texture
: nullable Texture = null is writable
152 # Texture applied to the specular color
153 var specular_texture
: nullable Texture = null is writable
156 private var normals_texture
: nullable Texture = null is writable
158 redef fun draw
(actor
, model
, camera
)
160 var mesh
= model
.mesh
162 var program
= app
.blinn_phong_program
165 # One of the textures used, if any
166 var sample_used_texture
= null
168 var texture
= ambient_texture
169 if texture
!= null then
170 glActiveTexture gl_TEXTURE0
171 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
172 program
.use_map_ambient
.uniform
true
173 program
.map_ambient
.uniform
0
174 sample_used_texture
= texture
176 program
.use_map_ambient
.uniform
false
179 texture
= diffuse_texture
180 if texture
!= null then
181 glActiveTexture gl_TEXTURE1
182 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
183 program
.use_map_diffuse
.uniform
true
184 program
.map_diffuse
.uniform
1
185 sample_used_texture
= texture
187 program
.use_map_diffuse
.uniform
false
190 texture
= specular_texture
191 if texture
!= null then
192 glActiveTexture gl_TEXTURE2
193 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
194 program
.use_map_specular
.uniform
true
195 program
.map_specular
.uniform
2
196 sample_used_texture
= texture
198 program
.use_map_specular
.uniform
false
201 texture
= normals_texture
202 if texture
!= null then
203 glActiveTexture gl_TEXTURE3
204 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
205 program
.use_map_bump
.uniform
true
206 program
.map_bump
.uniform
3
207 sample_used_texture
= texture
209 program
.use_map_bump
.uniform
false
212 glDisableVertexAttribArray program
.translation
.location
213 glDisableVertexAttribArray program
.scale
.location
215 program
.mvp
.uniform camera
.mvp_matrix
216 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
217 program
.scale
.uniform actor
.scale
218 program
.alpha
.uniform actor
.alpha
220 # If using a texture, set `texture_coords`
221 program
.tex_coord
.array_enabled
= sample_used_texture
!= null
222 if sample_used_texture
!= null then
223 if sample_used_texture
isa RootTexture then
224 # Coordinates are directly valid
225 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
227 # Correlate texture coordinates from the substexture and the mesh.
228 # This is slow, but should be cached on the GPU.
229 var xa
= sample_used_texture
.offset_left
230 var xd
= sample_used_texture
.offset_right
- xa
231 var ya
= sample_used_texture
.offset_top
232 var yd
= sample_used_texture
.offset_bottom
- ya
234 var tex_coords
= new Array[Float].with_capacity
(mesh
.texture_coords
.length
)
235 for i
in [0..mesh
.texture_coords
.length
/2[ do
236 tex_coords
[i
*2] = xa
+ xd
* mesh
.texture_coords
[i
*2]
237 tex_coords
[i
*2+1] = 1.0 - (ya
+ yd
* mesh
.texture_coords
[i
*2+1])
240 program
.tex_coord
.array
(tex_coords
, 2)
244 program
.coord
.array_enabled
= true
245 program
.coord
.array
(mesh
.vertices
, 3)
247 program
.rotation
= new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
249 program
.ambient_color
.uniform
(ambient_color
[0], ambient_color
[1],
250 ambient_color
[2], ambient_color
[3])
251 program
.diffuse_color
.uniform
(diffuse_color
[0], diffuse_color
[1],
252 diffuse_color
[2], diffuse_color
[3])
253 program
.specular_color
.uniform
(specular_color
[0], specular_color
[1],
254 specular_color
[2], specular_color
[3])
256 program
.normal
.array_enabled
= true
257 program
.normal
.array
(mesh
.normals
, 3)
260 setup_lights
(camera
, program
)
263 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
265 if mesh
.indices
.is_empty
then
266 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
268 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
273 # Simple material using the normals of the surface as color
275 # Each axis composing the normals are translated to color values.
276 # This material is useful for debugging normals or display models in a colorful way.
277 class NormalsMaterial
280 redef fun draw
(actor
, model
, camera
)
282 var program
= app
.normals_program
284 program
.mvp
.uniform camera
.mvp_matrix
286 var mesh
= model
.mesh
288 # TODO apply normal map
290 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
291 program
.scale
.uniform actor
.scale
293 program
.tex_coord
.array_enabled
= true
294 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
296 program
.coord
.array_enabled
= true
297 program
.coord
.array
(mesh
.vertices
, 3)
299 program
.rotation
= new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
301 program
.normal
.array_enabled
= true
302 program
.normal
.array
(mesh
.normals
, 3)
304 if mesh
.indices
.is_empty
then
305 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
307 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
312 # Graphic program to display 3D models with Blinn-Phong specular lighting
313 class BlinnPhongProgram
314 super GamnitProgramFromSource
316 redef var vertex_shader_source
= """
317 // Vertex coordinates
318 attribute vec4 coord;
320 // Vertex translation
321 attribute vec4 translation;
324 attribute float scale;
326 attribute float alpha;
328 // Vertex coordinates on textures
329 attribute vec2 tex_coord;
332 attribute vec3 normal;
334 // Camera model view projection matrix
338 attribute vec4 rotation_row0;
339 attribute vec4 rotation_row1;
340 attribute vec4 rotation_row2;
341 attribute vec4 rotation_row3;
345 return mat4(rotation_row0, rotation_row1, rotation_row2, rotation_row3);
349 uniform lowp int light_kind;
350 uniform vec3 light_center;
351 uniform mat4 light_mvp;
353 // Coordinates of the camera
356 // Output for the fragment shader
357 varying vec2 v_tex_coord;
358 varying vec3 v_normal;
359 varying vec4 v_to_light;
360 varying vec4 v_to_camera;
361 varying vec4 v_depth_pos;
362 varying float v_alpha;
366 mat4 rotation = rotation();
367 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
368 gl_Position = pos * mvp;
369 v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
371 // Pass varyings to the fragment shader
372 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
373 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
374 v_to_camera = normalize(vec4(camera, 1.0) - pos);
376 if (light_kind == 0) {
378 } else if (light_kind == 1) {
380 v_to_light = normalize(vec4(light_center, 1.0));
382 // Point light (and others?)
383 v_to_light = normalize(vec4(light_center, 1.0) - pos);
388 """ @ glsl_vertex_shader
390 redef var fragment_shader_source
= """
391 precision mediump float;
393 // Input from the vertex shader
394 varying vec2 v_tex_coord;
395 varying vec3 v_normal;
396 varying vec4 v_to_light;
397 varying vec4 v_to_camera;
398 varying vec4 v_depth_pos;
399 varying float v_alpha;
402 uniform vec4 ambient_color;
403 uniform vec4 diffuse_color;
404 uniform vec4 specular_color;
407 uniform bool use_map_ambient;
408 uniform sampler2D map_ambient;
411 uniform bool use_map_diffuse;
412 uniform sampler2D map_diffuse;
415 uniform bool use_map_specular;
416 uniform sampler2D map_specular;
419 uniform bool use_map_bump;
420 uniform sampler2D map_bump;
423 uniform bool use_map_normal;
424 uniform sampler2D map_normal;
427 uniform lowp int light_kind;
428 uniform bool use_shadows;
429 uniform sampler2D depth_texture;
430 uniform float depth_size;
431 uniform int depth_taps;
433 // Shadow effect on the diffuse colors of the fragment at offset `x, y`
434 float shadow_lookup(vec2 depth_coord, float x, float y) {
435 float tap_width = 1.0;
436 float pixel_size = tap_width/depth_size;
438 vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
439 y * pixel_size * v_depth_pos.w);
440 depth_coord += offset;
442 float depth = v_depth_pos.z/v_depth_pos.w;
443 //vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
444 if (depth_coord.x < 0.0 || depth_coord.x > 1.0 || depth_coord.y < 0.0 || depth_coord.y > 1.0) {
445 // Out of the shadow map texture
446 //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // debug, red out of the light view
450 float shadow_depth = texture2D(depth_texture, depth_coord).r;
452 if (shadow_depth == 1.0) {
453 // Too far to be in depth texture
455 } else if (shadow_depth <= depth - bias) {
457 //gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // debug, blue shadows
458 return 0.2; // TODO replace with a configurable ambient light
461 //gl_FragColor = vec4(0.0, 1.0-(shadow_depth-depth), 0.0, 1.0); // debug, green lit surfaces
465 // Shadow effect on the diffuse colors of the fragment
467 if (!use_shadows) return 1.0;
469 vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
471 float taps = float(depth_taps);
472 float tap_step = 2.00/taps;
474 for (float x = -1.0; x <= 0.99; x += tap_step)
475 for (float y = -1.0; y <= 0.99; y += tap_step)
476 sum += shadow_lookup(depth_coord, x, y);
477 return sum / taps / taps;
483 vec3 normal = v_normal;
486 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
490 vec4 ambient = ambient_color * v_alpha;
491 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
493 if (light_kind == 0) {
494 // No light, show diffuse and ambient
496 vec4 diffuse = diffuse_color * v_alpha;
497 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
499 gl_FragColor = ambient + diffuse;
501 // Parallel light or point light (1 or 2)
503 // Diffuse Lambert light
504 vec3 to_light = v_to_light.xyz;
505 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
507 vec4 diffuse = lambert * diffuse_color;
508 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
510 // Specular Phong light
514 vec3 l = reflect(-to_light, normal);
515 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
516 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
522 vec4 specular = s * specular_color * v_alpha;
523 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
525 gl_FragColor = ambient + diffuse + specular;
528 if (gl_FragColor.a < 0.01) discard;
530 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug normals
532 """ @ glsl_fragment_shader
534 # Vertices coordinates
535 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
537 # Should this program use the texture `map_ambient`?
538 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
540 # Ambient texture unit
541 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
543 # Should this program use the texture `map_diffuse`?
544 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
546 # Diffuse texture unit
547 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
549 # Should this program use the texture `map_specular`?
550 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
552 # Specularity texture unit
553 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
555 # Should this program use the texture `map_bump`?
556 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
559 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
562 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
564 # Coordinates on the textures, per vertex
565 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
568 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
571 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
574 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
576 # Kind of lights: 0 -> no light, 1 -> parallel, 2 -> point
577 var light_kind
= uniforms
["light_kind"].as(UniformInt) is lazy
579 # Center position of the light *or* vector to parallel light source
580 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
582 # Light model view projection matrix
583 var light_mvp
= uniforms
["light_mvp"].as(UniformMat4) is lazy
585 # Should shadow be drawn? Would use `depth_texture` and `light_mvp`.
586 var use_shadows
= uniforms
["use_shadows"].as(UniformBool) is lazy
588 # Diffuse texture unit
589 var depth_texture
= uniforms
["depth_texture"].as(UniformSampler2D) is lazy
591 # Size, in pixels, of `depth_texture`
592 var depth_texture_size
= uniforms
["depth_size"].as(UniformFloat) is lazy
594 # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
595 var depth_texture_taps
= uniforms
["depth_taps"].as(UniformInt) is lazy
598 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
600 # Translation applied to each vertex
601 var translation
= attributes
["translation"].as(AttributeVec4) is lazy
# TODO attribute
603 # Set `mat` at the uniform rotation matrix
604 fun rotation
=(mat
: Matrix)
607 for r
in [rotation_row0
, rotation_row1
, rotation_row2
, rotation_row3
] do
609 glDisableVertexAttribArray r
.location
610 r
.uniform
(mat
[0, i
], mat
[1, i
], mat
[2, i
], mat
[3, i
])
614 var gl_error
= glGetError
615 assert gl_error
== gl_NO_ERROR
else print_error gl_error
618 # Rotation matrix, row0
619 var rotation_row0
= attributes
["rotation_row0"].as(AttributeVec4) is lazy
621 # Rotation matrix, row 1
622 var rotation_row1
= attributes
["rotation_row1"].as(AttributeVec4) is lazy
624 # Rotation matrix, row 2
625 var rotation_row2
= attributes
["rotation_row2"].as(AttributeVec4) is lazy
627 # Rotation matrix, row 3
628 var rotation_row3
= attributes
["rotation_row3"].as(AttributeVec4) is lazy
631 var scale
= attributes
["scale"].as(AttributeFloat) is lazy
634 var alpha
= attributes
["alpha"].as(AttributeFloat) is lazy
636 # Camera model view projection matrix
637 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
640 # Program to color objects from their normal vectors
642 # May be used in place of `BlinnPhongProgram` for debugging or effect.
644 super BlinnPhongProgram
646 redef var fragment_shader_source
= """
647 precision mediump float;
649 // Input from the vertex shader
650 varying vec3 v_normal;
654 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
656 """ @ glsl_fragment_shader
660 private var blinn_phong_program
= new BlinnPhongProgram is lazy
662 private var normals_program
= new NormalProgram is lazy