lighting.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. define("dojox/gfx3d/lighting", [
  2. "dojo/_base/lang",
  3. "dojo/_base/Color", // dojo.Color
  4. "dojo/_base/declare", // dojo.declare
  5. "dojox/gfx/_base",
  6. "./_base"
  7. ],function(lang,Color,declare,gfx,gfx3d) {
  8. var lite = gfx3d.lighting = {
  9. // color utilities
  10. black: function(){
  11. return {r: 0, g: 0, b: 0, a: 1};
  12. },
  13. white: function(){
  14. return {r: 1, g: 1, b: 1, a: 1};
  15. },
  16. toStdColor: function(c){
  17. c = gfx.normalizeColor(c);
  18. return {r: c.r / 255, g: c.g / 255, b: c.b / 255, a: c.a};
  19. },
  20. fromStdColor: function(c){
  21. return new Color([Math.round(255 * c.r), Math.round(255 * c.g), Math.round(255 * c.b), c.a]);
  22. },
  23. scaleColor: function(s, c){
  24. return {r: s * c.r, g: s * c.g, b: s * c.b, a: s * c.a};
  25. },
  26. addColor: function(a, b){
  27. return {r: a.r + b.r, g: a.g + b.g, b: a.b + b.b, a: a.a + b.a};
  28. },
  29. multiplyColor: function(a, b){
  30. return {r: a.r * b.r, g: a.g * b.g, b: a.b * b.b, a: a.a * b.a};
  31. },
  32. saturateColor: function(c){
  33. return {
  34. r: c.r < 0 ? 0 : c.r > 1 ? 1 : c.r,
  35. g: c.g < 0 ? 0 : c.g > 1 ? 1 : c.g,
  36. b: c.b < 0 ? 0 : c.b > 1 ? 1 : c.b,
  37. a: c.a < 0 ? 0 : c.a > 1 ? 1 : c.a
  38. };
  39. },
  40. mixColor: function(c1, c2, s){
  41. return lite.addColor(lite.scaleColor(s, c1), lite.scaleColor(1 - s, c2));
  42. },
  43. diff2Color: function(c1, c2){
  44. var r = c1.r - c2.r;
  45. var g = c1.g - c2.g;
  46. var b = c1.b - c2.b;
  47. var a = c1.a - c2.a;
  48. return r * r + g * g + b * b + a * a;
  49. },
  50. length2Color: function(c){
  51. return c.r * c.r + c.g * c.g + c.b * c.b + c.a * c.a;
  52. },
  53. // vector utilities
  54. //TODO: move vector utilities from this file to vector.js
  55. dot: function(a, b){
  56. return a.x * b.x + a.y * b.y + a.z * b.z;
  57. },
  58. scale: function(s, v){
  59. return {x: s * v.x, y: s * v.y, z: s * v.z};
  60. },
  61. add: function(a, b){
  62. return {x: a.x + b.x, y: a.y + b.y, z: a.z + b.z};
  63. },
  64. saturate: function(v){
  65. return Math.min(Math.max(v, 0), 1);
  66. },
  67. length: function(v){
  68. return Math.sqrt(gfx3d.lighting.dot(v, v));
  69. },
  70. normalize: function(v){
  71. return lite.scale(1 / lite.length(v), v);
  72. },
  73. faceforward: function(n, i){
  74. var p = gfx3d.lighting;
  75. var s = p.dot(i, n) < 0 ? 1 : -1;
  76. return p.scale(s, n);
  77. },
  78. reflect: function(i, n){
  79. var p = gfx3d.lighting;
  80. return p.add(i, p.scale(-2 * p.dot(i, n), n));
  81. },
  82. // lighting utilities
  83. diffuse: function(normal, lights){
  84. var c = lite.black();
  85. for(var i = 0; i < lights.length; ++i){
  86. var l = lights[i],
  87. d = lite.dot(lite.normalize(l.direction), normal);
  88. c = lite.addColor(c, lite.scaleColor(d, l.color));
  89. }
  90. return lite.saturateColor(c);
  91. },
  92. specular: function(normal, v, roughness, lights){
  93. var c = lite.black();
  94. for(var i = 0; i < lights.length; ++i){
  95. var l = lights[i],
  96. h = lite.normalize(lite.add(lite.normalize(l.direction), v)),
  97. s = Math.pow(Math.max(0, lite.dot(normal, h)), 1 / roughness);
  98. c = lite.addColor(c, lite.scaleColor(s, l.color));
  99. }
  100. return lite.saturateColor(c);
  101. },
  102. phong: function(normal, v, size, lights){
  103. normal = lite.normalize(normal);
  104. var c = lite.black();
  105. for(var i = 0; i < lights.length; ++i){
  106. var l = lights[i],
  107. r = lite.reflect(lite.scale(-1, lite.normalize(v)), normal),
  108. s = Math.pow(Math.max(0, lite.dot(r, lite.normalize(l.direction))), size);
  109. c = lite.addColor(c, lite.scaleColor(s, l.color));
  110. }
  111. return lite.saturateColor(c);
  112. }
  113. };
  114. // this lighting model is derived from RenderMan Interface Specification Version 3.2
  115. declare("dojox.gfx3d.lighting.Model", null, {
  116. constructor: function(incident, lights, ambient, specular){
  117. this.incident = lite.normalize(incident);
  118. this.lights = [];
  119. for(var i = 0; i < lights.length; ++i){
  120. var l = lights[i];
  121. this.lights.push({direction: lite.normalize(l.direction), color: lite.toStdColor(l.color)});
  122. }
  123. this.ambient = lite.toStdColor(ambient.color ? ambient.color : "white");
  124. this.ambient = lite.scaleColor(ambient.intensity, this.ambient);
  125. this.ambient = lite.scaleColor(this.ambient.a, this.ambient);
  126. this.ambient.a = 1;
  127. this.specular = lite.toStdColor(specular ? specular : "white");
  128. this.specular = lite.scaleColor(this.specular.a, this.specular);
  129. this.specular.a = 1;
  130. this.npr_cool = {r: 0, g: 0, b: 0.4, a: 1};
  131. this.npr_warm = {r: 0.4, g: 0.4, b: 0.2, a: 1};
  132. this.npr_alpha = 0.2;
  133. this.npr_beta = 0.6;
  134. this.npr_scale = 0.6;
  135. },
  136. constant: function(normal, finish, pigment){
  137. pigment = lite.toStdColor(pigment);
  138. var alpha = pigment.a, color = lite.scaleColor(alpha, pigment);
  139. color.a = alpha;
  140. return lite.fromStdColor(lite.saturateColor(color));
  141. },
  142. matte: function(normal, finish, pigment){
  143. if(typeof finish == "string"){ finish = lite.finish[finish]; }
  144. pigment = lite.toStdColor(pigment);
  145. normal = lite.faceforward(lite.normalize(normal), this.incident);
  146. var ambient = lite.scaleColor(finish.Ka, this.ambient),
  147. shadow = lite.saturate(-4 * lite.dot(normal, this.incident)),
  148. diffuse = lite.scaleColor(shadow * finish.Kd, lite.diffuse(normal, this.lights)),
  149. color = lite.scaleColor(pigment.a, lite.multiplyColor(pigment, lite.addColor(ambient, diffuse)));
  150. color.a = pigment.a;
  151. return lite.fromStdColor(lite.saturateColor(color));
  152. },
  153. metal: function(normal, finish, pigment){
  154. if(typeof finish == "string"){ finish = lite.finish[finish]; }
  155. pigment = lite.toStdColor(pigment);
  156. normal = lite.faceforward(lite.normalize(normal), this.incident);
  157. var v = lite.scale(-1, this.incident), specular, color,
  158. ambient = lite.scaleColor(finish.Ka, this.ambient),
  159. shadow = lite.saturate(-4 * lite.dot(normal, this.incident));
  160. if("phong" in finish){
  161. specular = lite.scaleColor(shadow * finish.Ks * finish.phong, lite.phong(normal, v, finish.phong_size, this.lights));
  162. }else{
  163. specular = lite.scaleColor(shadow * finish.Ks, lite.specular(normal, v, finish.roughness, this.lights));
  164. }
  165. color = lite.scaleColor(pigment.a, lite.addColor(lite.multiplyColor(pigment, ambient), lite.multiplyColor(this.specular, specular)));
  166. color.a = pigment.a;
  167. return lite.fromStdColor(lite.saturateColor(color));
  168. },
  169. plastic: function(normal, finish, pigment){
  170. if(typeof finish == "string"){ finish = lite.finish[finish]; }
  171. pigment = lite.toStdColor(pigment);
  172. normal = lite.faceforward(lite.normalize(normal), this.incident);
  173. var v = lite.scale(-1, this.incident), specular, color,
  174. ambient = lite.scaleColor(finish.Ka, this.ambient),
  175. shadow = lite.saturate(-4 * lite.dot(normal, this.incident)),
  176. diffuse = lite.scaleColor(shadow * finish.Kd, lite.diffuse(normal, this.lights));
  177. if("phong" in finish){
  178. specular = lite.scaleColor(shadow * finish.Ks * finish.phong, lite.phong(normal, v, finish.phong_size, this.lights));
  179. }else{
  180. specular = lite.scaleColor(shadow * finish.Ks, lite.specular(normal, v, finish.roughness, this.lights));
  181. }
  182. color = lite.scaleColor(pigment.a, lite.addColor(lite.multiplyColor(pigment, lite.addColor(ambient, diffuse)), lite.multiplyColor(this.specular, specular)));
  183. color.a = pigment.a;
  184. return lite.fromStdColor(lite.saturateColor(color));
  185. },
  186. npr: function(normal, finish, pigment){
  187. if(typeof finish == "string"){ finish = lite.finish[finish]; }
  188. pigment = lite.toStdColor(pigment);
  189. normal = lite.faceforward(lite.normalize(normal), this.incident);
  190. var ambient = lite.scaleColor(finish.Ka, this.ambient),
  191. shadow = lite.saturate(-4 * lite.dot(normal, this.incident)),
  192. diffuse = lite.scaleColor(shadow * finish.Kd, lite.diffuse(normal, this.lights)),
  193. color = lite.scaleColor(pigment.a, lite.multiplyColor(pigment, lite.addColor(ambient, diffuse))),
  194. cool = lite.addColor(this.npr_cool, lite.scaleColor(this.npr_alpha, color)),
  195. warm = lite.addColor(this.npr_warm, lite.scaleColor(this.npr_beta, color)),
  196. d = (1 + lite.dot(this.incident, normal)) / 2,
  197. color = lite.scaleColor(this.npr_scale, lite.addColor(color, lite.mixColor(cool, warm, d)));
  198. color.a = pigment.a;
  199. return lite.fromStdColor(lite.saturateColor(color));
  200. }
  201. });
  202. // POV-Ray basic finishes
  203. gfx3d.lighting.finish = {
  204. // Default
  205. defaults: {Ka: 0.1, Kd: 0.6, Ks: 0.0, roughness: 0.05},
  206. dull: {Ka: 0.1, Kd: 0.6, Ks: 0.5, roughness: 0.15},
  207. shiny: {Ka: 0.1, Kd: 0.6, Ks: 1.0, roughness: 0.001},
  208. glossy: {Ka: 0.1, Kd: 0.6, Ks: 1.0, roughness: 0.0001},
  209. phong_dull: {Ka: 0.1, Kd: 0.6, Ks: 0.5, phong: 0.5, phong_size: 1},
  210. phong_shiny: {Ka: 0.1, Kd: 0.6, Ks: 1.0, phong: 1.0, phong_size: 200},
  211. phong_glossy: {Ka: 0.1, Kd: 0.6, Ks: 1.0, phong: 1.0, phong_size: 300},
  212. luminous: {Ka: 1.0, Kd: 0.0, Ks: 0.0, roughness: 0.05},
  213. // Metals
  214. // very soft and dull
  215. metalA: {Ka: 0.35, Kd: 0.3, Ks: 0.8, roughness: 1/20},
  216. // fairly soft and dull
  217. metalB: {Ka: 0.30, Kd: 0.4, Ks: 0.7, roughness: 1/60},
  218. // medium reflectivity, holds color well
  219. metalC: {Ka: 0.25, Kd: 0.5, Ks: 0.8, roughness: 1/80},
  220. // highly hard and polished, high reflectivity
  221. metalD: {Ka: 0.15, Kd: 0.6, Ks: 0.8, roughness: 1/100},
  222. // very highly polished and reflective
  223. metalE: {Ka: 0.10, Kd: 0.7, Ks: 0.8, roughness: 1/120}
  224. };
  225. return lite;
  226. });