shader-code.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /**
  2. * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ValueCell } from '../mol-util';
  7. import { idFactory } from '../mol-util/id-factory';
  8. import { WebGLExtensions } from './webgl/extensions';
  9. import { isWebGL2, GLRenderingContext } from './webgl/compat';
  10. export type DefineKind = 'boolean' | 'string' | 'number'
  11. export type DefineType = boolean | string
  12. export type DefineValues = { [k: string]: ValueCell<DefineType> }
  13. const shaderCodeId = idFactory();
  14. export interface ShaderExtensions {
  15. readonly standardDerivatives?: boolean
  16. readonly fragDepth?: boolean
  17. readonly drawBuffers?: boolean
  18. readonly shaderTextureLod?: boolean
  19. }
  20. export interface ShaderCode {
  21. readonly id: number
  22. readonly name: string
  23. readonly vert: string
  24. readonly frag: string
  25. readonly extensions: ShaderExtensions
  26. }
  27. import apply_fog from './shader/chunks/apply-fog.glsl';
  28. import apply_interior_color from './shader/chunks/apply-interior-color.glsl';
  29. import apply_light_color from './shader/chunks/apply-light-color.glsl';
  30. import apply_marker_color from './shader/chunks/apply-marker-color.glsl';
  31. import assign_clipping_varying from './shader/chunks/assign-clipping-varying.glsl';
  32. import assign_color_varying from './shader/chunks/assign-color-varying.glsl';
  33. import assign_group from './shader/chunks/assign-group.glsl';
  34. import assign_marker_varying from './shader/chunks/assign-marker-varying.glsl';
  35. import assign_material_color from './shader/chunks/assign-material-color.glsl';
  36. import assign_position from './shader/chunks/assign-position.glsl';
  37. import assign_size from './shader/chunks/assign-size.glsl';
  38. import check_picking_alpha from './shader/chunks/check-picking-alpha.glsl';
  39. import clip_instance from './shader/chunks/clip-instance.glsl';
  40. import clip_pixel from './shader/chunks/clip-pixel.glsl';
  41. import color_frag_params from './shader/chunks/color-frag-params.glsl';
  42. import color_vert_params from './shader/chunks/color-vert-params.glsl';
  43. import common_clip from './shader/chunks/common-clip.glsl';
  44. import common_frag_params from './shader/chunks/common-frag-params.glsl';
  45. import common_vert_params from './shader/chunks/common-vert-params.glsl';
  46. import common from './shader/chunks/common.glsl';
  47. import light_frag_params from './shader/chunks/light-frag-params.glsl';
  48. import matrix_scale from './shader/chunks/matrix-scale.glsl';
  49. import normal_frag_params from './shader/chunks/normal-frag-params.glsl';
  50. import read_from_texture from './shader/chunks/read-from-texture.glsl';
  51. import size_vert_params from './shader/chunks/size-vert-params.glsl';
  52. import texture3d_from_1d_trilinear from './shader/chunks/texture3d-from-1d-trilinear.glsl';
  53. import texture3d_from_2d_linear from './shader/chunks/texture3d-from-2d-linear.glsl';
  54. import texture3d_from_2d_nearest from './shader/chunks/texture3d-from-2d-nearest.glsl';
  55. const ShaderChunks: { [k: string]: string } = {
  56. apply_fog,
  57. apply_interior_color,
  58. apply_light_color,
  59. apply_marker_color,
  60. assign_clipping_varying,
  61. assign_color_varying,
  62. assign_group,
  63. assign_marker_varying,
  64. assign_material_color,
  65. assign_position,
  66. assign_size,
  67. check_picking_alpha,
  68. clip_instance,
  69. clip_pixel,
  70. color_frag_params,
  71. color_vert_params,
  72. common_clip,
  73. common_frag_params,
  74. common_vert_params,
  75. common,
  76. light_frag_params,
  77. matrix_scale,
  78. normal_frag_params,
  79. read_from_texture,
  80. size_vert_params,
  81. texture3d_from_1d_trilinear,
  82. texture3d_from_2d_linear,
  83. texture3d_from_2d_nearest
  84. };
  85. const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gmi;
  86. const reSingleLineComment = /[ \t]*\/\/.*\n/g;
  87. const reMultiLineComment = /[ \t]*\/\*[\s\S]*?\*\//g;
  88. const reMultipleLinebreaks = /\n{2,}/g;
  89. function addIncludes(text: string) {
  90. return text
  91. .replace(reInclude, (_, p1) => {
  92. const chunk = ShaderChunks[p1];
  93. if (!chunk) throw new Error(`empty chunk, '${p1}'`);
  94. return chunk;
  95. })
  96. .trim()
  97. .replace(reSingleLineComment, '\n')
  98. .replace(reMultiLineComment, '\n')
  99. .replace(reMultipleLinebreaks, '\n');
  100. }
  101. export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}): ShaderCode {
  102. return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions };
  103. }
  104. import points_vert from './shader/points.vert';
  105. import points_frag from './shader/points.frag';
  106. export const PointsShaderCode = ShaderCode('points', points_vert, points_frag);
  107. import spheres_vert from './shader/spheres.vert';
  108. import spheres_frag from './shader/spheres.frag';
  109. export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: true });
  110. import text_vert from './shader/text.vert';
  111. import text_frag from './shader/text.frag';
  112. export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { standardDerivatives: true });
  113. import lines_vert from './shader/lines.vert';
  114. import lines_frag from './shader/lines.frag';
  115. export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag);
  116. import mesh_vert from './shader/mesh.vert';
  117. import mesh_frag from './shader/mesh.frag';
  118. export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { standardDerivatives: true });
  119. import direct_volume_vert from './shader/direct-volume.vert';
  120. import direct_volume_frag from './shader/direct-volume.frag';
  121. export const DirectVolumeShaderCode = ShaderCode('direct-volume', direct_volume_vert, direct_volume_frag, { fragDepth: true });
  122. import image_vert from './shader/image.vert';
  123. import image_frag from './shader/image.frag';
  124. export const ImageShaderCode = ShaderCode('image', image_vert, image_frag);
  125. //
  126. export type ShaderDefines = {
  127. [k: string]: ValueCell<DefineType>
  128. }
  129. function getDefinesCode (defines: ShaderDefines) {
  130. if (defines === undefined) return '';
  131. const lines = [];
  132. for (const name in defines) {
  133. const define = defines[name];
  134. const v = define.ref.value;
  135. if (v !== undefined) {
  136. if (typeof v === 'string') {
  137. lines.push(`#define ${name}_${v}`);
  138. } else if (typeof v === 'number') {
  139. lines.push(`#define ${name} ${v}`);
  140. } else if (typeof v === 'boolean') {
  141. if (v) lines.push(`#define ${name}`);
  142. } else {
  143. throw new Error('unknown define type');
  144. }
  145. }
  146. }
  147. return lines.join('\n') + '\n';
  148. }
  149. function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
  150. const prefix: string[] = [];
  151. if (shaderExtensions.standardDerivatives) {
  152. prefix.push('#extension GL_OES_standard_derivatives : enable');
  153. prefix.push('#define enabledStandardDerivatives');
  154. }
  155. if (shaderExtensions.fragDepth) {
  156. if (extensions.fragDepth) {
  157. prefix.push('#extension GL_EXT_frag_depth : enable');
  158. prefix.push('#define enabledFragDepth');
  159. } else {
  160. throw new Error(`requested 'GL_EXT_frag_depth' extension is unavailable`);
  161. }
  162. }
  163. if (shaderExtensions.drawBuffers) {
  164. if (extensions.drawBuffers) {
  165. prefix.push('#extension GL_EXT_draw_buffers : require');
  166. prefix.push('#define requiredDrawBuffers');
  167. } else {
  168. throw new Error(`requested 'GL_EXT_draw_buffers' extension is unavailable`);
  169. }
  170. }
  171. if (shaderExtensions.shaderTextureLod) {
  172. if (extensions.shaderTextureLod) {
  173. prefix.push('#extension GL_EXT_shader_texture_lod : enable');
  174. prefix.push('#define enabledShaderTextureLod');
  175. } else {
  176. throw new Error(`requested 'GL_EXT_shader_texture_lod' extension is unavailable`);
  177. }
  178. }
  179. return prefix.join('\n') + '\n';
  180. }
  181. const glsl300VertPrefix = `#version 300 es
  182. #define attribute in
  183. #define varying out
  184. #define texture2D texture
  185. `;
  186. const glsl300FragPrefixCommon = `
  187. #define varying in
  188. #define texture2D texture
  189. #define texture2DLodEXT textureLod
  190. #define gl_FragColor out_FragData0
  191. #define gl_FragDepthEXT gl_FragDepth
  192. #define requiredDrawBuffers
  193. `;
  194. function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
  195. const prefix = [ '#version 300 es' ];
  196. if (shaderExtensions.standardDerivatives) {
  197. prefix.push('#define enabledStandardDerivatives');
  198. }
  199. if (shaderExtensions.fragDepth) {
  200. prefix.push('#define enabledFragDepth');
  201. }
  202. if (extensions.drawBuffers) {
  203. const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number;
  204. for (let i = 0, il = maxDrawBuffers; i < il; ++i) {
  205. prefix.push(`layout(location = ${i}) out highp vec4 out_FragData${i};`);
  206. }
  207. }
  208. prefix.push(glsl300FragPrefixCommon);
  209. return prefix.join('\n') + '\n';
  210. }
  211. function transformGlsl300Frag(frag: string) {
  212. return frag.replace(/gl_FragData\[([0-9]+)\]/g, 'out_FragData$1');
  213. }
  214. export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtensions, defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
  215. const header = getDefinesCode(defines);
  216. const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
  217. const fragPrefix = isWebGL2(gl)
  218. ? getGlsl300FragPrefix(gl, extensions, shaders.extensions)
  219. : getGlsl100FragPrefix(extensions, shaders.extensions);
  220. const frag = isWebGL2(gl) ? transformGlsl300Frag(shaders.frag) : shaders.frag;
  221. return {
  222. id: shaderCodeId(),
  223. name: shaders.name,
  224. vert: `${vertPrefix}${header}${shaders.vert}`,
  225. frag: `${fragPrefix}${header}${frag}`,
  226. extensions: shaders.extensions
  227. };
  228. }