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
)
98 private fun setup_lights
(actor
: Actor, model
: LeafModel, camera
: Camera, program
: BlinnPhongProgram)
100 # TODO use a list of lights
102 # Light, for Lambert and Blinn-Phong
103 var light
= app
.light
104 if light
isa ParallelLight then
105 program
.light_kind
.uniform
1
107 # Vector parallel to the light source
108 program
.light_center
.uniform
(
109 -light
.pitch
.sin
* light
.yaw
.sin
,
112 else if light
isa PointLight then
113 program
.light_kind
.uniform
2
115 # Position of the light source
116 program
.light_center
.uniform
(app
.light
.position
.x
, app
.light
.position
.y
, app
.light
.position
.z
)
118 program
.light_kind
.uniform
0
121 # Draw projected shadows?
122 if not light
isa LightCastingShadows or not app
.shadow_depth_texture_available
then
123 program
.use_shadows
.uniform
false
125 else program
.use_shadows
.uniform
true
127 # Light point of view
128 program
.light_mvp
.uniform light
.camera
.mvp_matrix
131 glActiveTexture gl_TEXTURE4
132 glBindTexture
(gl_TEXTURE_2D
, app
.shadow_context
.depth_texture
)
133 program
.depth_texture
.uniform
4
134 program
.depth_texture_size
.uniform app
.shadow_resolution
.to_f
135 program
.depth_texture_taps
.uniform
2 # TODO make configurable
139 # Material with potential `diffuse_texture` and `specular_texture`
140 class TexturedMaterial
143 # Texture applied to the ambient_color
144 var ambient_texture
: nullable Texture = null is writable
146 # Texture applied to the diffuse color
147 var diffuse_texture
: nullable Texture = null is writable
149 # Texture applied to the specular color
150 var specular_texture
: nullable Texture = null is writable
153 private var normals_texture
: nullable Texture = null is writable
155 redef fun draw
(actor
, model
, camera
)
157 var mesh
= model
.mesh
159 var program
= app
.versatile_program
162 # One of the textures used, if any
163 var sample_used_texture
= null
165 var texture
= ambient_texture
166 if texture
!= null then
167 glActiveTexture gl_TEXTURE0
168 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
169 program
.use_map_ambient
.uniform
true
170 program
.map_ambient
.uniform
0
171 sample_used_texture
= texture
173 program
.use_map_ambient
.uniform
false
176 texture
= diffuse_texture
177 if texture
!= null then
178 glActiveTexture gl_TEXTURE1
179 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
180 program
.use_map_diffuse
.uniform
true
181 program
.map_diffuse
.uniform
1
182 sample_used_texture
= texture
184 program
.use_map_diffuse
.uniform
false
187 texture
= specular_texture
188 if texture
!= null then
189 glActiveTexture gl_TEXTURE2
190 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
191 program
.use_map_specular
.uniform
true
192 program
.map_specular
.uniform
2
193 sample_used_texture
= texture
195 program
.use_map_specular
.uniform
false
198 texture
= normals_texture
199 if texture
!= null then
200 glActiveTexture gl_TEXTURE3
201 glBindTexture
(gl_TEXTURE_2D
, texture
.gl_texture
)
202 program
.use_map_bump
.uniform
true
203 program
.map_bump
.uniform
3
204 sample_used_texture
= texture
206 program
.use_map_bump
.uniform
false
209 program
.mvp
.uniform camera
.mvp_matrix
210 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
211 program
.scale
.uniform actor
.scale
213 # If using a texture, set `texture_coords`
214 program
.tex_coord
.array_enabled
= sample_used_texture
!= null
215 if sample_used_texture
!= null then
216 if sample_used_texture
isa RootTexture then
217 # Coordinates are directly valid
218 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
220 # Correlate texture coordinates from the substexture and the mesh.
221 # This is slow, but should be cached on the GPU.
222 var xa
= sample_used_texture
.offset_left
223 var xd
= sample_used_texture
.offset_right
- xa
224 var ya
= sample_used_texture
.offset_top
225 var yd
= sample_used_texture
.offset_bottom
- ya
227 var tex_coords
= new Array[Float].with_capacity
(mesh
.texture_coords
.length
)
228 for i
in [0..mesh
.texture_coords
.length
/2[ do
229 tex_coords
[i
*2] = xa
+ xd
* mesh
.texture_coords
[i
*2]
230 tex_coords
[i
*2+1] = 1.0 - (ya
+ yd
* mesh
.texture_coords
[i
*2+1])
233 program
.tex_coord
.array
(tex_coords
, 2)
237 program
.coord
.array_enabled
= true
238 program
.coord
.array
(mesh
.vertices
, 3)
240 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
243 program
.ambient_color
.uniform
(ambient_color
[0]*a
, ambient_color
[1]*a
,
244 ambient_color
[2]*a
, ambient_color
[3]*a
)
245 program
.diffuse_color
.uniform
(diffuse_color
[0]*a
, diffuse_color
[1]*a
,
246 diffuse_color
[2]*a
, diffuse_color
[3]*a
)
247 program
.specular_color
.uniform
(specular_color
[0]*a
, specular_color
[1]*a
,
248 specular_color
[2]*a
, specular_color
[3]*a
)
250 program
.normal
.array_enabled
= true
251 program
.normal
.array
(mesh
.normals
, 3)
254 setup_lights
(actor
, model
, camera
, program
)
257 program
.camera
.uniform
(camera
.position
.x
, camera
.position
.y
, camera
.position
.z
)
259 if mesh
.indices
.is_empty
then
260 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
262 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
267 # Simple material using the normals of the surface as color
269 # Each axis composing the normals are translated to color values.
270 # This material is useful for debugging normals or display models in a colorful way.
271 class NormalsMaterial
274 redef fun draw
(actor
, model
, camera
)
276 var program
= app
.normals_program
278 program
.mvp
.uniform camera
.mvp_matrix
280 var mesh
= model
.mesh
282 # TODO apply normal map
284 program
.translation
.uniform
(actor
.center
.x
, actor
.center
.y
, actor
.center
.z
, 0.0)
285 program
.scale
.uniform actor
.scale
287 program
.tex_coord
.array_enabled
= true
288 program
.tex_coord
.array
(mesh
.texture_coords
, 2)
290 program
.coord
.array_enabled
= true
291 program
.coord
.array
(mesh
.vertices
, 3)
293 program
.rotation
.uniform
new Matrix.gamnit_euler_rotation
(actor
.pitch
, actor
.yaw
, actor
.roll
)
295 program
.normal
.array_enabled
= true
296 program
.normal
.array
(mesh
.normals
, 3)
298 if mesh
.indices
.is_empty
then
299 glDrawArrays
(mesh
.draw_mode
, 0, mesh
.vertices
.length
/3)
301 glDrawElements
(mesh
.draw_mode
, mesh
.indices
.length
, gl_UNSIGNED_SHORT
, mesh
.indices_c
.native_array
)
306 # Graphic program to display 3D models with Blinn-Phong specular lighting
307 class BlinnPhongProgram
308 super GamnitProgramFromSource
310 redef var vertex_shader_source
= """
311 // Vertex coordinates
312 attribute vec4 coord;
314 // Vertex translation
315 uniform vec4 translation;
320 // Vertex coordinates on textures
321 attribute vec2 tex_coord;
324 attribute vec3 normal;
326 // Camera model view projection matrix
329 uniform mat4 rotation;
332 uniform int light_kind;
333 uniform vec3 light_center;
334 uniform mat4 light_mvp;
336 // Coordinates of the camera
339 // Output for the fragment shader
340 varying vec2 v_tex_coord;
341 varying vec3 v_normal;
342 varying vec4 v_to_light;
343 varying vec4 v_to_camera;
344 varying vec4 v_depth_pos;
348 vec4 pos = (vec4(coord.xyz * scale, 1.0) * rotation + translation);
349 gl_Position = pos * mvp;
350 v_depth_pos = (pos * light_mvp) * 0.5 + 0.5;
352 // Pass varyings to the fragment shader
353 v_tex_coord = vec2(tex_coord.x, 1.0 - tex_coord.y);
354 v_normal = normalize(vec4(normal, 0.0) * rotation).xyz;
355 v_to_camera = normalize(vec4(camera, 1.0) - pos);
357 if (light_kind == 0) {
359 } else if (light_kind == 1) {
361 v_to_light = normalize(vec4(light_center, 1.0));
363 // Point light (and others?)
364 v_to_light = normalize(vec4(light_center, 1.0) - pos);
367 """ @ glsl_vertex_shader
369 redef var fragment_shader_source
= """
370 precision mediump float;
372 // Input from the vertex shader
373 varying vec2 v_tex_coord;
374 varying vec3 v_normal;
375 varying vec4 v_to_light;
376 varying vec4 v_to_camera;
377 varying vec4 v_depth_pos;
380 uniform vec4 ambient_color;
381 uniform vec4 diffuse_color;
382 uniform vec4 specular_color;
385 uniform bool use_map_ambient;
386 uniform sampler2D map_ambient;
389 uniform bool use_map_diffuse;
390 uniform sampler2D map_diffuse;
393 uniform bool use_map_specular;
394 uniform sampler2D map_specular;
397 uniform bool use_map_bump;
398 uniform sampler2D map_bump;
401 uniform bool use_map_normal;
402 uniform sampler2D map_normal;
405 uniform int light_kind;
406 uniform bool use_shadows;
407 uniform sampler2D depth_texture;
408 uniform float depth_texture_size;
409 uniform int depth_texture_taps;
411 // Shadow effect on the diffuse colors of the fragment at offset `x, y`
412 float shadow_lookup(vec2 depth_coord, float x, float y) {
413 float tap_width = 1.0;
414 float pixel_size = tap_width/depth_texture_size;
416 vec2 offset = vec2(x * pixel_size * v_depth_pos.w,
417 y * pixel_size * v_depth_pos.w);
418 depth_coord += offset;
420 float depth = v_depth_pos.z/v_depth_pos.w;
421 //vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
422 if (depth_coord.x < 0.0 || depth_coord.x > 1.0 || depth_coord.y < 0.0 || depth_coord.y > 1.0) {
423 // Out of the shadow map texture
424 //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // debug, red out of the light view
428 float shadow_depth = texture2D(depth_texture, depth_coord).r;
430 if (shadow_depth == 1.0) {
431 // Too far to be in depth texture
433 } else if (shadow_depth <= depth - bias) {
435 //gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0); // debug, blue shadows
436 return 0.2; // TODO replace with a configurable ambient light
439 //gl_FragColor = vec4(0.0, 1.0-(shadow_depth-depth), 0.0, 1.0); // debug, green lit surfaces
443 // Shadow effect on the diffuse colors of the fragment
445 if (!use_shadows) return 1.0;
447 vec2 depth_coord = v_depth_pos.xy/v_depth_pos.w;
449 float taps = float(depth_texture_taps);
450 float tap_step = 2.00/taps;
452 for (float x = -1.0; x <= 0.99; x += tap_step)
453 for (float y = -1.0; y <= 0.99; y += tap_step)
454 sum += shadow_lookup(depth_coord, x, y);
455 return sum / taps / taps;
461 vec3 normal = v_normal;
464 vec3 bump = 2.0 * texture2D(map_bump, v_tex_coord).rgb - 1.0;
468 vec4 ambient = ambient_color;
469 if (use_map_ambient) ambient *= texture2D(map_ambient, v_tex_coord);
471 if (light_kind == 0) {
472 // No light, show diffuse and ambient
474 vec4 diffuse = diffuse_color;
475 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
477 gl_FragColor = ambient + diffuse;
479 // Parallel light or point light (1 or 2)
481 // Diffuse Lambert light
482 vec3 to_light = v_to_light.xyz;
483 float lambert = clamp(dot(normal, to_light), 0.0, 1.0);
485 vec4 diffuse = lambert * diffuse_color;
486 if (use_map_diffuse) diffuse *= texture2D(map_diffuse, v_tex_coord);
488 // Specular Phong light
492 vec3 l = reflect(-to_light, normal);
493 s = clamp(dot(l, v_to_camera.xyz), 0.0, 1.0);
494 s = pow(s, 8.0); // TODO make this `shininess` a material attribute
500 vec4 specular = s * specular_color;
501 if (use_map_specular) specular *= texture2D(map_specular, v_tex_coord).x;
503 gl_FragColor = ambient + diffuse + specular;
506 if (gl_FragColor.a < 0.01) discard;
508 //gl_FragColor = vec4(normalize(normal).rgb, 1.0); // Debug normals
510 """ @ glsl_fragment_shader
512 # Vertices coordinates
513 var coord
= attributes
["coord"].as(AttributeVec4) is lazy
515 # Should this program use the texture `map_ambient`?
516 var use_map_ambient
= uniforms
["use_map_ambient"].as(UniformBool) is lazy
518 # Ambient texture unit
519 var map_ambient
= uniforms
["map_ambient"].as(UniformSampler2D) is lazy
521 # Should this program use the texture `map_diffuse`?
522 var use_map_diffuse
= uniforms
["use_map_diffuse"].as(UniformBool) is lazy
524 # Diffuse texture unit
525 var map_diffuse
= uniforms
["map_diffuse"].as(UniformSampler2D) is lazy
527 # Should this program use the texture `map_specular`?
528 var use_map_specular
= uniforms
["use_map_specular"].as(UniformBool) is lazy
530 # Specularity texture unit
531 var map_specular
= uniforms
["map_specular"].as(UniformSampler2D) is lazy
533 # Should this program use the texture `map_bump`?
534 var use_map_bump
= uniforms
["use_map_bump"].as(UniformBool) is lazy
537 var map_bump
= uniforms
["map_bump"].as(UniformSampler2D) is lazy
540 var normal
= attributes
["normal"].as(AttributeVec3) is lazy
542 # Coordinates on the textures, per vertex
543 var tex_coord
= attributes
["tex_coord"].as(AttributeVec2) is lazy
546 var ambient_color
= uniforms
["ambient_color"].as(UniformVec4) is lazy
549 var diffuse_color
= uniforms
["diffuse_color"].as(UniformVec4) is lazy
552 var specular_color
= uniforms
["specular_color"].as(UniformVec4) is lazy
554 # Kind of lights: 0 -> no light, 1 -> parallel, 2 -> point
555 var light_kind
= uniforms
["light_kind"].as(UniformInt) is lazy
557 # Center position of the light *or* vector to parallel light source
558 var light_center
= uniforms
["light_center"].as(UniformVec3) is lazy
560 # Light model view projection matrix
561 var light_mvp
= uniforms
["light_mvp"].as(UniformMat4) is lazy
563 # Should shadow be drawn? Would use `depth_texture` and `light_mvp`.
564 var use_shadows
= uniforms
["use_shadows"].as(UniformBool) is lazy
566 # Diffuse texture unit
567 var depth_texture
= uniforms
["depth_texture"].as(UniformSampler2D) is lazy
569 # Size, in pixels, of `depth_texture`
570 var depth_texture_size
= uniforms
["depth_texture_size"].as(UniformFloat) is lazy
572 # Times to tap the `depth_texture`, square root (set to 3 for a total of 9 taps)
573 var depth_texture_taps
= uniforms
["depth_texture_taps"].as(UniformInt) is lazy
576 var camera
= uniforms
["camera"].as(UniformVec3) is lazy
578 # Translation applied to each vertex
579 var translation
= uniforms
["translation"].as(UniformVec4) is lazy
582 var rotation
= uniforms
["rotation"].as(UniformMat4) is lazy
585 var scale
= uniforms
["scale"].as(UniformFloat) is lazy
587 # Camera model view projection matrix
588 var mvp
= uniforms
["mvp"].as(UniformMat4) is lazy
591 # Program to color objects from their normal vectors
593 # May be used in place of `BlinnPhongProgram` for debugging or effect.
595 super BlinnPhongProgram
597 redef var fragment_shader_source
= """
598 precision mediump float;
600 // Input from the vertex shader
601 varying vec3 v_normal;
605 gl_FragColor = vec4(v_normal*0.5 + 0.5, 1.0);
607 """ @ glsl_fragment_shader
611 private var versatile_program
= new BlinnPhongProgram is lazy
613 private var normals_program
= new NormalProgram is lazy