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
.versatile_program
54 program
.mvp
.uniform camera
.mvp_matrix
59 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
60 program
.scale
.uniform actor
.scale
61 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
64 program
.coord
.array_enabled
= true
65 program
.coord
.array
(mesh
.vertices
, 3)
67 program
.normal
.array_enabled
= true
68 program
.normal
.array
(mesh
.normals
, 3)
71 program
.use_map_ambient
.uniform
false
72 program
.use_map_diffuse
.uniform
false
73 program
.use_map_specular
.uniform
false
74 program
.tex_coord
.array_enabled
= false
77 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
79 # Colors from the material
81 program
.ambient_color
.uniform
(ambient_color
[0]*a
, ambient_color
[1]*a
,
82 ambient_color
[2]*a
, ambient_color
[3]*a
)
83 program
.diffuse_color
.uniform
(diffuse_color
[0]*a
, diffuse_color
[1]*a
,
84 diffuse_color
[2]*a
, diffuse_color
[3]*a
)
85 program
.specular_color
.uniform
(specular_color
[0]*a
, specular_color
[1]*a
,
86 specular_color
[2]*a
, specular_color
[3]*a
)
88 setup_lights
(actor
, model
, camera
, program
)
91 if mesh
.indices
.is_empty
then
92 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
94 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
97 assert glGetError
== gl_NO_ERROR
100 private fun setup_lights
(actor
: Actor, model
: LeafModel, camera
: Camera, program
: BlinnPhongProgram)
102 # TODO use a list of lights
104 # Light, for Lambert and Blinn-Phong
105 var light
= app
.light
106 if light
isa ParallelLight then
107 program
.light_kind
.uniform
1
109 # Vector parallel to the light source
110 program
.light_center
.uniform
(
111 -light
.pitch
.sin
* light
.yaw
.sin
,
114 else if light
isa PointLight then
115 program
.light_kind
.uniform
2
117 # Position of the light source
118 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
120 program
.light_kind
.uniform
0
123 # Draw projected shadows?
124 if not light
isa LightCastingShadows or not app
.shadow_depth_texture_available
then
125 program
.use_shadows
.uniform
false
127 else program
.use_shadows
.uniform
true
129 # Light point of view
130 program
.light_mvp
.uniform light
.camera
.mvp_matrix
133 glActiveTexture gl_TEXTURE4
134 glBindTexture
(gl_TEXTURE_2D
, app
.shadow_context
.depth_texture
)
135 program
.depth_texture
.uniform
4
136 program
.depth_texture_size
.uniform app
.shadow_resolution
.to_f
137 program
.depth_texture_taps
.uniform
2 # TODO make configurable
141 # Material with potential `diffuse_texture` and `specular_texture`
142 class TexturedMaterial
145 # Texture applied to the ambient_color
146 var ambient_texture
: nullable Texture = null is writable
148 # Texture applied to the diffuse color
149 var diffuse_texture
: nullable Texture = null is writable
151 # Texture applied to the specular color
152 var specular_texture
: nullable Texture = null is writable
155 private var normals_texture
: nullable Texture = null is writable
157 redef fun draw
(actor
, model
, camera
)
159 var mesh
= model
.mesh
161 var program
= app
.versatile_program
164 # One of the textures used, if any
165 var sample_used_texture
= null
167 var texture
= ambient_texture
168 if texture
!= null then
169 glActiveTexture gl_TEXTURE0
170 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
171 program
.use_map_ambient
.uniform
true
172 program
.map_ambient
.uniform
0
173 sample_used_texture
= texture
175 program
.use_map_ambient
.uniform
false
178 texture
= diffuse_texture
179 if texture
!= null then
180 glActiveTexture gl_TEXTURE1
181 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
182 program
.use_map_diffuse
.uniform
true
183 program
.map_diffuse
.uniform
1
184 sample_used_texture
= texture
186 program
.use_map_diffuse
.uniform
false
189 texture
= specular_texture
190 if texture
!= null then
191 glActiveTexture gl_TEXTURE2
192 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
193 program
.use_map_specular
.uniform
true
194 program
.map_specular
.uniform
2
195 sample_used_texture
= texture
197 program
.use_map_specular
.uniform
false
200 texture
= normals_texture
201 if texture
!= null then
202 glActiveTexture gl_TEXTURE3
203 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
204 program
.use_map_bump
.uniform
true
205 program
.map_bump
.uniform
3
206 sample_used_texture
= texture
208 program
.use_map_bump
.uniform
false
211 program
.mvp
.uniform camera
.mvp_matrix
212 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
213 program
.scale
.uniform actor
.scale
215 # If using a texture, set `texture_coords`
216 program
.tex_coord
.array_enabled
= sample_used_texture
!= null
217 if sample_used_texture
!= null then
218 if sample_used_texture
isa RootTexture then
219 # Coordinates are directly valid
220 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
222 # Correlate texture coordinates from the substexture and the mesh.
223 # This is slow, but should be cached on the GPU.
224 var xa
= sample_used_texture
.offset_left
225 var xd
= sample_used_texture
.offset_right
- xa
226 var ya
= sample_used_texture
.offset_top
227 var yd
= sample_used_texture
.offset_bottom
- ya
229 var tex_coords
= new Array[Float].with_capacity
(mesh
.texture_coords
.length
)
230 for i
in [0..mesh
.texture_coords
.length
/2[ do
231 tex_coords
[i
*2] = xa
+ xd
* mesh
.texture_coords
[i
*2]
232 tex_coords
[i
*2+1] = 1.0 - (ya
+ yd
* mesh
.texture_coords
[i
*2+1])
235 program
.tex_coord
.array
(tex_coords
, 2)
239 program
.coord
.array_enabled
= true
240 program
.coord
.array
(mesh
.vertices
, 3)
242 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
245 program
.ambient_color
.uniform
(ambient_color
[0]*a
, ambient_color
[1]*a
,
246 ambient_color
[2]*a
, ambient_color
[3]*a
)
247 program
.diffuse_color
.uniform
(diffuse_color
[0]*a
, diffuse_color
[1]*a
,
248 diffuse_color
[2]*a
, diffuse_color
[3]*a
)
249 program
.specular_color
.uniform
(specular_color
[0]*a
, specular_color
[1]*a
,
250 specular_color
[2]*a
, specular_color
[3]*a
)
252 program
.normal
.array_enabled
= true
253 program
.normal
.array
(mesh
.normals
, 3)
256 setup_lights
(actor
, model
, camera
, program
)
259 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
261 if mesh
.indices
.is_empty
then
262 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
264 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
269 # Simple material using the normals of the surface as color
271 # Each axis composing the normals are translated to color values.
272 # This material is useful for debugging normals or display models in a colorful way.
273 class NormalsMaterial
276 redef fun draw
(actor
, model
, camera
)
278 var program
= app
.normals_program
280 program
.mvp
.uniform camera
.mvp_matrix
282 var mesh
= model
.mesh
284 # TODO apply normal map
286 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
287 program
.scale
.uniform actor
.scale
289 program
.tex_coord
.array_enabled
= true
290 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
292 program
.coord
.array_enabled
= true
293 program
.coord
.array
(mesh
.vertices
, 3)
295 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
297 program
.normal
.array_enabled
= true
298 program
.normal
.array
(mesh
.normals
, 3)
300 if mesh
.indices
.is_empty
then
301 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
303 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
308 # Graphic program to display 3D models with Blinn-Phong specular lighting
309 class BlinnPhongProgram
310 super GamnitProgramFromSource
312 redef var vertex_shader_source
= """
313 // Vertex coordinates
314 attribute vec4 coord;
316 // Vertex translation
317 uniform vec4 translation;
322 // Vertex coordinates on textures
323 attribute vec2 tex_coord;
326 attribute vec3 normal;
328 // Camera model view projection matrix
331 uniform mat4 rotation;
334 uniform lowp int light_kind;
335 uniform vec3 light_center;
336 uniform mat4 light_mvp;
338 // Coordinates of the camera
341 // Output for the fragment shader
342 varying vec2 v_tex_coord;
343 varying vec3 v_normal;
344 varying vec4 v_to_light;
345 varying vec4 v_to_camera;
346 varying vec4 v_depth_pos;
350 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
351 gl_Position = pos * mvp;
352 v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
354 // Pass varyings to the fragment shader
355 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
356 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
357 v_to_camera = normalize(vec4(camera, 1.0) - pos);
359 if (light_kind == 0) {
361 } else if (light_kind == 1) {
363 v_to_light = normalize(vec4(light_center, 1.0));
365 // Point light (and others?)
366 v_to_light = normalize(vec4(light_center, 1.0) - pos);
369 """ @ glsl_vertex_shader
371 redef var fragment_shader_source
= """
372 precision mediump float;
374 // Input from the vertex shader
375 varying vec2 v_tex_coord;
376 varying vec3 v_normal;
377 varying vec4 v_to_light;
378 varying vec4 v_to_camera;
379 varying vec4 v_depth_pos;
382 uniform vec4 ambient_color;
383 uniform vec4 diffuse_color;
384 uniform vec4 specular_color;
387 uniform bool use_map_ambient;
388 uniform sampler2D map_ambient;
391 uniform bool use_map_diffuse;
392 uniform sampler2D map_diffuse;
395 uniform bool use_map_specular;
396 uniform sampler2D map_specular;
399 uniform bool use_map_bump;
400 uniform sampler2D map_bump;
403 uniform bool use_map_normal;
404 uniform sampler2D map_normal;
407 uniform lowp int light_kind;
408 uniform bool use_shadows;
409 uniform sampler2D depth_texture;
410 uniform float depth_texture_size;
411 uniform int depth_texture_taps;
413 // Shadow effect on the diffuse colors of the fragment at offset `x, y`
414 float shadow_lookup(vec2 depth_coord, float x, float y) {
415 float tap_width = 1.0;
416 float pixel_size = tap_width/depth_texture_size;
418 vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
419 y * pixel_size * v_depth_pos.w);
420 depth_coord += offset;
422 float depth = v_depth_pos.z/v_depth_pos.w;
423 //vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
424 if (depth_coord.x < 0.0 || depth_coord.x > 1.0 || depth_coord.y < 0.0 || depth_coord.y > 1.0) {
425 // Out of the shadow map texture
426 //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // debug, red out of the light view
430 float shadow_depth = texture2D(depth_texture, depth_coord).r;
432 if (shadow_depth == 1.0) {
433 // Too far to be in depth texture
435 } else if (shadow_depth <= depth - bias) {
437 //gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // debug, blue shadows
438 return 0.2; // TODO replace with a configurable ambient light
441 //gl_FragColor = vec4(0.0, 1.0-(shadow_depth-depth), 0.0, 1.0); // debug, green lit surfaces
445 // Shadow effect on the diffuse colors of the fragment
447 if (!use_shadows) return 1.0;
449 vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
451 float taps = float(depth_texture_taps);
452 float tap_step = 2.00/taps;
454 for (float x = -1.0; x <= 0.99; x += tap_step)
455 for (float y = -1.0; y <= 0.99; y += tap_step)
456 sum += shadow_lookup(depth_coord, x, y);
457 return sum / taps / taps;
463 vec3 normal = v_normal;
466 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
470 vec4 ambient = ambient_color;
471 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
473 if (light_kind == 0) {
474 // No light, show diffuse and ambient
476 vec4 diffuse = diffuse_color;
477 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
479 gl_FragColor = ambient + diffuse;
481 // Parallel light or point light (1 or 2)
483 // Diffuse Lambert light
484 vec3 to_light = v_to_light.xyz;
485 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
487 vec4 diffuse = lambert * diffuse_color;
488 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
490 // Specular Phong light
494 vec3 l = reflect(-to_light, normal);
495 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
496 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
502 vec4 specular = s * specular_color;
503 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
505 gl_FragColor = ambient + diffuse + specular;
508 if (gl_FragColor.a < 0.01) discard;
510 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug normals
512 """ @ glsl_fragment_shader
514 # Vertices coordinates
515 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
517 # Should this program use the texture `map_ambient`?
518 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
520 # Ambient texture unit
521 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
523 # Should this program use the texture `map_diffuse`?
524 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
526 # Diffuse texture unit
527 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
529 # Should this program use the texture `map_specular`?
530 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
532 # Specularity texture unit
533 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
535 # Should this program use the texture `map_bump`?
536 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
539 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
542 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
544 # Coordinates on the textures, per vertex
545 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
548 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
551 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
554 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
556 # Kind of lights: 0 -> no light, 1 -> parallel, 2 -> point
557 var light_kind
= uniforms
["light_kind"].as(UniformInt) is lazy
559 # Center position of the light *or* vector to parallel light source
560 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
562 # Light model view projection matrix
563 var light_mvp
= uniforms
["light_mvp"].as(UniformMat4) is lazy
565 # Should shadow be drawn? Would use `depth_texture` and `light_mvp`.
566 var use_shadows
= uniforms
["use_shadows"].as(UniformBool) is lazy
568 # Diffuse texture unit
569 var depth_texture
= uniforms
["depth_texture"].as(UniformSampler2D) is lazy
571 # Size, in pixels, of `depth_texture`
572 var depth_texture_size
= uniforms
["depth_texture_size"].as(UniformFloat) is lazy
574 # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
575 var depth_texture_taps
= uniforms
["depth_texture_taps"].as(UniformInt) is lazy
578 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
580 # Translation applied to each vertex
581 var translation
= uniforms
["translation"].as(UniformVec4) is lazy
584 var rotation
= uniforms
["rotation"].as(UniformMat4) is lazy
587 var scale
= uniforms
["scale"].as(UniformFloat) is lazy
589 # Camera model view projection matrix
590 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
593 # Program to color objects from their normal vectors
595 # May be used in place of `BlinnPhongProgram` for debugging or effect.
597 super BlinnPhongProgram
599 redef var fragment_shader_source
= """
600 precision mediump float;
602 // Input from the vertex shader
603 varying vec3 v_normal;
607 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
609 """ @ glsl_fragment_shader
613 private var versatile_program
= new BlinnPhongProgram is lazy
615 private var normals_program
= new NormalProgram is lazy