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
)
100 assert glGetError
== gl_NO_ERROR
103 private fun setup_lights
(camera
: Camera, program
: BlinnPhongProgram)
105 # TODO use a list of lights
107 # Light, for Lambert and Blinn-Phong
108 var light
= app
.light
109 if light
isa ParallelLight then
110 program
.light_kind
.uniform
1
112 # Vector parallel to the light source
113 program
.light_center
.uniform
(
114 -light
.pitch
.sin
* light
.yaw
.sin
,
117 else if light
isa PointLight then
118 program
.light_kind
.uniform
2
120 # Position of the light source
121 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
123 program
.light_kind
.uniform
0
126 # Draw projected shadows?
127 if not light
isa LightCastingShadows or not app
.shadow_depth_texture_available
then
128 program
.use_shadows
.uniform
false
130 else program
.use_shadows
.uniform
true
132 # Light point of view
133 program
.light_mvp
.uniform light
.camera
.mvp_matrix
136 glActiveTexture gl_TEXTURE4
137 glBindTexture
(gl_TEXTURE_2D
, app
.shadow_context
.depth_texture
)
138 program
.depth_texture
.uniform
4
139 program
.depth_texture_size
.uniform app
.shadow_resolution
.to_f
140 program
.depth_texture_taps
.uniform
2 # TODO make configurable
144 # Material with potential `diffuse_texture` and `specular_texture`
145 class TexturedMaterial
148 # Texture applied to the ambient_color
149 var ambient_texture
: nullable Texture = null is writable
151 # Texture applied to the diffuse color
152 var diffuse_texture
: nullable Texture = null is writable
154 # Texture applied to the specular color
155 var specular_texture
: nullable Texture = null is writable
158 private var normals_texture
: nullable Texture = null is writable
160 redef fun draw
(actor
, model
, camera
)
162 var mesh
= model
.mesh
164 var program
= app
.blinn_phong_program
167 # One of the textures used, if any
168 var sample_used_texture
= null
170 var texture
= ambient_texture
171 if texture
!= null then
172 glActiveTexture gl_TEXTURE0
173 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
174 program
.use_map_ambient
.uniform
true
175 program
.map_ambient
.uniform
0
176 sample_used_texture
= texture
178 program
.use_map_ambient
.uniform
false
181 texture
= diffuse_texture
182 if texture
!= null then
183 glActiveTexture gl_TEXTURE1
184 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
185 program
.use_map_diffuse
.uniform
true
186 program
.map_diffuse
.uniform
1
187 sample_used_texture
= texture
189 program
.use_map_diffuse
.uniform
false
192 texture
= specular_texture
193 if texture
!= null then
194 glActiveTexture gl_TEXTURE2
195 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
196 program
.use_map_specular
.uniform
true
197 program
.map_specular
.uniform
2
198 sample_used_texture
= texture
200 program
.use_map_specular
.uniform
false
203 texture
= normals_texture
204 if texture
!= null then
205 glActiveTexture gl_TEXTURE3
206 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
207 program
.use_map_bump
.uniform
true
208 program
.map_bump
.uniform
3
209 sample_used_texture
= texture
211 program
.use_map_bump
.uniform
false
214 glDisableVertexAttribArray program
.translation
.location
215 glDisableVertexAttribArray program
.scale
.location
217 program
.mvp
.uniform camera
.mvp_matrix
218 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
219 program
.scale
.uniform actor
.scale
220 program
.alpha
.uniform actor
.alpha
222 # If using a texture, set `texture_coords`
223 program
.tex_coord
.array_enabled
= sample_used_texture
!= null
224 if sample_used_texture
!= null then
225 if sample_used_texture
isa RootTexture then
226 # Coordinates are directly valid
227 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
229 # Correlate texture coordinates from the substexture and the mesh.
230 # This is slow, but should be cached on the GPU.
231 var xa
= sample_used_texture
.offset_left
232 var xd
= sample_used_texture
.offset_right
- xa
233 var ya
= sample_used_texture
.offset_top
234 var yd
= sample_used_texture
.offset_bottom
- ya
236 var tex_coords
= new Array[Float].with_capacity
(mesh
.texture_coords
.length
)
237 for i
in [0..mesh
.texture_coords
.length
/2[ do
238 tex_coords
[i
*2] = xa
+ xd
* mesh
.texture_coords
[i
*2]
239 tex_coords
[i
*2+1] = 1.0 - (ya
+ yd
* mesh
.texture_coords
[i
*2+1])
242 program
.tex_coord
.array
(tex_coords
, 2)
246 program
.coord
.array_enabled
= true
247 program
.coord
.array
(mesh
.vertices
, 3)
249 program
.rotation
= new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
251 program
.ambient_color
.uniform
(ambient_color
[0], ambient_color
[1],
252 ambient_color
[2], ambient_color
[3])
253 program
.diffuse_color
.uniform
(diffuse_color
[0], diffuse_color
[1],
254 diffuse_color
[2], diffuse_color
[3])
255 program
.specular_color
.uniform
(specular_color
[0], specular_color
[1],
256 specular_color
[2], specular_color
[3])
258 program
.normal
.array_enabled
= true
259 program
.normal
.array
(mesh
.normals
, 3)
262 setup_lights
(camera
, program
)
265 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
267 if mesh
.indices
.is_empty
then
268 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
270 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
275 # Simple material using the normals of the surface as color
277 # Each axis composing the normals are translated to color values.
278 # This material is useful for debugging normals or display models in a colorful way.
279 class NormalsMaterial
282 redef fun draw
(actor
, model
, camera
)
284 var program
= app
.normals_program
286 program
.mvp
.uniform camera
.mvp_matrix
288 var mesh
= model
.mesh
290 # TODO apply normal map
292 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
293 program
.scale
.uniform actor
.scale
295 program
.tex_coord
.array_enabled
= true
296 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
298 program
.coord
.array_enabled
= true
299 program
.coord
.array
(mesh
.vertices
, 3)
301 program
.rotation
= new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
303 program
.normal
.array_enabled
= true
304 program
.normal
.array
(mesh
.normals
, 3)
306 if mesh
.indices
.is_empty
then
307 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
309 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
314 # Graphic program to display 3D models with Blinn-Phong specular lighting
315 class BlinnPhongProgram
316 super GamnitProgramFromSource
318 redef var vertex_shader_source
= """
319 // Vertex coordinates
320 attribute vec4 coord;
322 // Vertex translation
323 attribute vec4 translation;
326 attribute float scale;
328 attribute float alpha;
330 // Vertex coordinates on textures
331 attribute vec2 tex_coord;
334 attribute vec3 normal;
336 // Camera model view projection matrix
340 attribute vec4 rotation_row0;
341 attribute vec4 rotation_row1;
342 attribute vec4 rotation_row2;
343 attribute vec4 rotation_row3;
347 return mat4(rotation_row0, rotation_row1, rotation_row2, rotation_row3);
351 uniform lowp int light_kind;
352 uniform vec3 light_center;
353 uniform mat4 light_mvp;
355 // Coordinates of the camera
358 // Output for the fragment shader
359 varying vec2 v_tex_coord;
360 varying vec3 v_normal;
361 varying vec4 v_to_light;
362 varying vec4 v_to_camera;
363 varying vec4 v_depth_pos;
364 varying float v_alpha;
368 mat4 rotation = rotation();
369 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
370 gl_Position = pos * mvp;
371 v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
373 // Pass varyings to the fragment shader
374 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
375 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
376 v_to_camera = normalize(vec4(camera, 1.0) - pos);
378 if (light_kind == 0) {
380 } else if (light_kind == 1) {
382 v_to_light = normalize(vec4(light_center, 1.0));
384 // Point light (and others?)
385 v_to_light = normalize(vec4(light_center, 1.0) - pos);
390 """ @ glsl_vertex_shader
392 redef var fragment_shader_source
= """
393 precision mediump float;
395 // Input from the vertex shader
396 varying vec2 v_tex_coord;
397 varying vec3 v_normal;
398 varying vec4 v_to_light;
399 varying vec4 v_to_camera;
400 varying vec4 v_depth_pos;
401 varying float v_alpha;
404 uniform vec4 ambient_color;
405 uniform vec4 diffuse_color;
406 uniform vec4 specular_color;
409 uniform bool use_map_ambient;
410 uniform sampler2D map_ambient;
413 uniform bool use_map_diffuse;
414 uniform sampler2D map_diffuse;
417 uniform bool use_map_specular;
418 uniform sampler2D map_specular;
421 uniform bool use_map_bump;
422 uniform sampler2D map_bump;
425 uniform bool use_map_normal;
426 uniform sampler2D map_normal;
429 uniform lowp int light_kind;
430 uniform bool use_shadows;
431 uniform sampler2D depth_texture;
432 uniform float depth_size;
433 uniform int depth_taps;
435 // Shadow effect on the diffuse colors of the fragment at offset `x, y`
436 float shadow_lookup(vec2 depth_coord, float x, float y) {
437 float tap_width = 1.0;
438 float pixel_size = tap_width/depth_size;
440 vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
441 y * pixel_size * v_depth_pos.w);
442 depth_coord += offset;
444 float depth = v_depth_pos.z/v_depth_pos.w;
445 //vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
446 if (depth_coord.x < 0.0 || depth_coord.x > 1.0 || depth_coord.y < 0.0 || depth_coord.y > 1.0) {
447 // Out of the shadow map texture
448 //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // debug, red out of the light view
452 float shadow_depth = texture2D(depth_texture, depth_coord).r;
454 if (shadow_depth == 1.0) {
455 // Too far to be in depth texture
457 } else if (shadow_depth <= depth - bias) {
459 //gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // debug, blue shadows
460 return 0.2; // TODO replace with a configurable ambient light
463 //gl_FragColor = vec4(0.0, 1.0-(shadow_depth-depth), 0.0, 1.0); // debug, green lit surfaces
467 // Shadow effect on the diffuse colors of the fragment
469 if (!use_shadows) return 1.0;
471 vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
473 float taps = float(depth_taps);
474 float tap_step = 2.00/taps;
476 for (float x = -1.0; x <= 0.99; x += tap_step)
477 for (float y = -1.0; y <= 0.99; y += tap_step)
478 sum += shadow_lookup(depth_coord, x, y);
479 return sum / taps / taps;
485 vec3 normal = v_normal;
488 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
492 vec4 ambient = ambient_color * v_alpha;
493 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
495 if (light_kind == 0) {
496 // No light, show diffuse and ambient
498 vec4 diffuse = diffuse_color * v_alpha;
499 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
501 gl_FragColor = ambient + diffuse;
503 // Parallel light or point light (1 or 2)
505 // Diffuse Lambert light
506 vec3 to_light = v_to_light.xyz;
507 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
509 vec4 diffuse = lambert * diffuse_color;
510 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
512 // Specular Phong light
516 vec3 l = reflect(-to_light, normal);
517 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
518 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
524 vec4 specular = s * specular_color * v_alpha;
525 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
527 gl_FragColor = ambient + diffuse + specular;
530 if (gl_FragColor.a < 0.01) discard;
532 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug normals
534 """ @ glsl_fragment_shader
536 # Vertices coordinates
537 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
539 # Should this program use the texture `map_ambient`?
540 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
542 # Ambient texture unit
543 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
545 # Should this program use the texture `map_diffuse`?
546 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
548 # Diffuse texture unit
549 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
551 # Should this program use the texture `map_specular`?
552 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
554 # Specularity texture unit
555 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
557 # Should this program use the texture `map_bump`?
558 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
561 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
564 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
566 # Coordinates on the textures, per vertex
567 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
570 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
573 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
576 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
578 # Kind of lights: 0 -> no light, 1 -> parallel, 2 -> point
579 var light_kind
= uniforms
["light_kind"].as(UniformInt) is lazy
581 # Center position of the light *or* vector to parallel light source
582 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
584 # Light model view projection matrix
585 var light_mvp
= uniforms
["light_mvp"].as(UniformMat4) is lazy
587 # Should shadow be drawn? Would use `depth_texture` and `light_mvp`.
588 var use_shadows
= uniforms
["use_shadows"].as(UniformBool) is lazy
590 # Diffuse texture unit
591 var depth_texture
= uniforms
["depth_texture"].as(UniformSampler2D) is lazy
593 # Size, in pixels, of `depth_texture`
594 var depth_texture_size
= uniforms
["depth_size"].as(UniformFloat) is lazy
596 # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
597 var depth_texture_taps
= uniforms
["depth_taps"].as(UniformInt) is lazy
600 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
602 # Translation applied to each vertex
603 var translation
= attributes
["translation"].as(AttributeVec4) is lazy
# TODO attribute
605 # Set `mat` at the uniform rotation matrix
606 fun rotation
=(mat
: Matrix)
609 for r
in [rotation_row0
, rotation_row1
, rotation_row2
, rotation_row3
] do
611 glDisableVertexAttribArray r
.location
612 r
.uniform
(mat
[0, i
], mat
[1, i
], mat
[2, i
], mat
[3, i
])
616 var gl_error
= glGetError
617 assert gl_error
== gl_NO_ERROR
else print_error gl_error
620 # Rotation matrix, row0
621 var rotation_row0
= attributes
["rotation_row0"].as(AttributeVec4) is lazy
623 # Rotation matrix, row 1
624 var rotation_row1
= attributes
["rotation_row1"].as(AttributeVec4) is lazy
626 # Rotation matrix, row 2
627 var rotation_row2
= attributes
["rotation_row2"].as(AttributeVec4) is lazy
629 # Rotation matrix, row 3
630 var rotation_row3
= attributes
["rotation_row3"].as(AttributeVec4) is lazy
633 var scale
= attributes
["scale"].as(AttributeFloat) is lazy
636 var alpha
= attributes
["alpha"].as(AttributeFloat) is lazy
638 # Camera model view projection matrix
639 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
642 # Program to color objects from their normal vectors
644 # May be used in place of `BlinnPhongProgram` for debugging or effect.
646 super BlinnPhongProgram
648 redef var fragment_shader_source
= """
649 precision mediump float;
651 // Input from the vertex shader
652 varying vec3 v_normal;
656 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
658 """ @ glsl_fragment_shader
662 private var blinn_phong_program
= new BlinnPhongProgram is lazy
664 private var normals_program
= new NormalProgram is lazy