shader-code.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /**
  2. * Copyright (c) 2018-2021 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. type ShaderExtensionsValue = 'required' | 'optional'
  15. export interface ShaderExtensions {
  16. readonly fragDepth?: ShaderExtensionsValue
  17. readonly drawBuffers?: ShaderExtensionsValue
  18. readonly shaderTextureLod?: ShaderExtensionsValue
  19. }
  20. type FragOutTypes = { [k in number]: 'vec4' | 'ivec4' }
  21. type IgnoreDefine = (name: string, variant: string, defines: ShaderDefines) => boolean
  22. export interface ShaderCode {
  23. readonly id: number
  24. readonly name: string
  25. readonly vert: string
  26. readonly frag: string
  27. readonly extensions: ShaderExtensions
  28. /** Fragment shader output type only applicable for webgl2 */
  29. readonly outTypes: FragOutTypes
  30. readonly ignoreDefine?: IgnoreDefine
  31. }
  32. import { apply_fog } from './shader/chunks/apply-fog.glsl';
  33. import { apply_interior_color } from './shader/chunks/apply-interior-color.glsl';
  34. import { apply_light_color } from './shader/chunks/apply-light-color.glsl';
  35. import { apply_marker_color } from './shader/chunks/apply-marker-color.glsl';
  36. import { assign_clipping_varying } from './shader/chunks/assign-clipping-varying.glsl';
  37. import { assign_color_varying } from './shader/chunks/assign-color-varying.glsl';
  38. import { assign_group } from './shader/chunks/assign-group.glsl';
  39. import { assign_marker_varying } from './shader/chunks/assign-marker-varying.glsl';
  40. import { assign_material_color } from './shader/chunks/assign-material-color.glsl';
  41. import { assign_position } from './shader/chunks/assign-position.glsl';
  42. import { assign_size } from './shader/chunks/assign-size.glsl';
  43. import { check_picking_alpha } from './shader/chunks/check-picking-alpha.glsl';
  44. import { clip_instance } from './shader/chunks/clip-instance.glsl';
  45. import { clip_pixel } from './shader/chunks/clip-pixel.glsl';
  46. import { color_frag_params } from './shader/chunks/color-frag-params.glsl';
  47. import { color_vert_params } from './shader/chunks/color-vert-params.glsl';
  48. import { common_clip } from './shader/chunks/common-clip.glsl';
  49. import { common_frag_params } from './shader/chunks/common-frag-params.glsl';
  50. import { common_vert_params } from './shader/chunks/common-vert-params.glsl';
  51. import { common } from './shader/chunks/common.glsl';
  52. import { float_to_rgba } from './shader/chunks/float-to-rgba.glsl';
  53. import { light_frag_params } from './shader/chunks/light-frag-params.glsl';
  54. import { matrix_scale } from './shader/chunks/matrix-scale.glsl';
  55. import { normal_frag_params } from './shader/chunks/normal-frag-params.glsl';
  56. import { read_from_texture } from './shader/chunks/read-from-texture.glsl';
  57. import { rgba_to_float } from './shader/chunks/rgba-to-float.glsl';
  58. import { size_vert_params } from './shader/chunks/size-vert-params.glsl';
  59. import { texture3d_from_1d_trilinear } from './shader/chunks/texture3d-from-1d-trilinear.glsl';
  60. import { texture3d_from_2d_linear } from './shader/chunks/texture3d-from-2d-linear.glsl';
  61. import { texture3d_from_2d_nearest } from './shader/chunks/texture3d-from-2d-nearest.glsl';
  62. import { wboit_write } from './shader/chunks/wboit-write.glsl';
  63. const ShaderChunks: { [k: string]: string } = {
  64. apply_fog,
  65. apply_interior_color,
  66. apply_light_color,
  67. apply_marker_color,
  68. assign_clipping_varying,
  69. assign_color_varying,
  70. assign_group,
  71. assign_marker_varying,
  72. assign_material_color,
  73. assign_position,
  74. assign_size,
  75. check_picking_alpha,
  76. clip_instance,
  77. clip_pixel,
  78. color_frag_params,
  79. color_vert_params,
  80. common_clip,
  81. common_frag_params,
  82. common_vert_params,
  83. common,
  84. float_to_rgba,
  85. light_frag_params,
  86. matrix_scale,
  87. normal_frag_params,
  88. read_from_texture,
  89. rgba_to_float,
  90. size_vert_params,
  91. texture3d_from_1d_trilinear,
  92. texture3d_from_2d_linear,
  93. texture3d_from_2d_nearest,
  94. wboit_write
  95. };
  96. const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gm;
  97. const reUnrollLoop = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*\+\+i\s*\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;
  98. const reSingleLineComment = /[ \t]*\/\/.*\n/g;
  99. const reMultiLineComment = /[ \t]*\/\*[\s\S]*?\*\//g;
  100. const reMultipleLinebreaks = /\n{2,}/g;
  101. function addIncludes(text: string) {
  102. return text
  103. .replace(reInclude, (_, p1) => {
  104. const chunk = ShaderChunks[p1];
  105. if (!chunk) throw new Error(`empty chunk, '${p1}'`);
  106. return chunk;
  107. })
  108. .trim()
  109. .replace(reSingleLineComment, '\n')
  110. .replace(reMultiLineComment, '\n')
  111. .replace(reMultipleLinebreaks, '\n');
  112. }
  113. function unrollLoops(str: string) {
  114. return str.replace(reUnrollLoop, loopReplacer);
  115. }
  116. function loopReplacer(match: string, start: string, end: string, snippet: string) {
  117. let out = '';
  118. for (let i = parseInt(start); i < parseInt(end); ++i) {
  119. out += snippet
  120. .replace(/\[\s*i\s*\]/g, `[${i}]`)
  121. .replace(/UNROLLED_LOOP_INDEX/g, `${i}`);
  122. }
  123. return out;
  124. }
  125. function replaceCounts(str: string, defines: ShaderDefines) {
  126. if (defines.dLightCount) str = str.replace(/dLightCount/g, `${defines.dLightCount.ref.value}`);
  127. if (defines.dClipObjectCount) str = str.replace(/dClipObjectCount/g, `${defines.dClipObjectCount.ref.value}`);
  128. return str;
  129. }
  130. function preprocess(str: string, defines: ShaderDefines) {
  131. return unrollLoops(replaceCounts(str, defines));
  132. }
  133. export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}, ignoreDefine?: IgnoreDefine): ShaderCode {
  134. return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes, ignoreDefine };
  135. }
  136. // Note: `drawBuffers` need to be 'optional' for wboit
  137. function ignoreDefine(name: string, variant: string, defines: ShaderDefines): boolean {
  138. if (variant.startsWith('color')) {
  139. if (name === 'dLightCount') {
  140. return !!defines.dIgnoreLight?.ref.value;
  141. }
  142. } else {
  143. return [
  144. 'dColorType', 'dUsePalette',
  145. 'dLightCount',
  146. 'dOverpaintType', 'dOverpaint',
  147. 'dSubstanceType', 'dSubstance',
  148. ].includes(name);
  149. }
  150. return false;
  151. };
  152. function ignoreDefineUnlit(name: string, variant: string, defines: ShaderDefines): boolean {
  153. if (name === 'dLightCount') return true;
  154. return ignoreDefine(name, variant, defines);
  155. };
  156. import { points_vert } from './shader/points.vert';
  157. import { points_frag } from './shader/points.frag';
  158. export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
  159. import { spheres_vert } from './shader/spheres.vert';
  160. import { spheres_frag } from './shader/spheres.frag';
  161. export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
  162. import { cylinders_vert } from './shader/cylinders.vert';
  163. import { cylinders_frag } from './shader/cylinders.frag';
  164. export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
  165. import { text_vert } from './shader/text.vert';
  166. import { text_frag } from './shader/text.frag';
  167. export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
  168. import { lines_vert } from './shader/lines.vert';
  169. import { lines_frag } from './shader/lines.frag';
  170. export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
  171. import { mesh_vert } from './shader/mesh.vert';
  172. import { mesh_frag } from './shader/mesh.frag';
  173. export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' }, {}, ignoreDefine);
  174. import { directVolume_vert } from './shader/direct-volume.vert';
  175. import { directVolume_frag } from './shader/direct-volume.frag';
  176. export const DirectVolumeShaderCode = ShaderCode('direct-volume', directVolume_vert, directVolume_frag, { fragDepth: 'optional', drawBuffers: 'optional' }, {}, ignoreDefine);
  177. import { image_vert } from './shader/image.vert';
  178. import { image_frag } from './shader/image.frag';
  179. export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
  180. //
  181. export type ShaderDefines = {
  182. [k: string]: ValueCell<DefineType>
  183. }
  184. function getDefinesCode(defines: ShaderDefines, ignore?: IgnoreDefine) {
  185. if (defines === undefined) return '';
  186. const variant = (defines.dRenderVariant?.ref.value || '') as string;
  187. const lines = [];
  188. for (const name in defines) {
  189. if (ignore?.(name, variant, defines)) continue;
  190. const define = defines[name];
  191. const v = define.ref.value;
  192. if (v !== undefined) {
  193. if (typeof v === 'string') {
  194. lines.push(`#define ${name}_${v}`);
  195. } else if (typeof v === 'number') {
  196. lines.push(`#define ${name} ${v}`);
  197. } else if (typeof v === 'boolean') {
  198. if (v) lines.push(`#define ${name}`);
  199. } else {
  200. throw new Error('unknown define type');
  201. }
  202. }
  203. }
  204. return lines.join('\n') + '\n';
  205. }
  206. function getGlsl100FragPrefix(extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
  207. const prefix: string[] = [
  208. '#extension GL_OES_standard_derivatives : enable'
  209. ];
  210. if (shaderExtensions.fragDepth) {
  211. if (extensions.fragDepth) {
  212. prefix.push('#extension GL_EXT_frag_depth : enable');
  213. prefix.push('#define enabledFragDepth');
  214. } else if (shaderExtensions.fragDepth === 'required') {
  215. throw new Error(`required 'GL_EXT_frag_depth' extension not available`);
  216. }
  217. }
  218. if (shaderExtensions.drawBuffers) {
  219. if (extensions.drawBuffers) {
  220. prefix.push('#extension GL_EXT_draw_buffers : require');
  221. prefix.push('#define requiredDrawBuffers');
  222. prefix.push('#define gl_FragColor gl_FragData[0]');
  223. } else if (shaderExtensions.drawBuffers === 'required') {
  224. throw new Error(`required 'GL_EXT_draw_buffers' extension not available`);
  225. }
  226. }
  227. if (shaderExtensions.shaderTextureLod) {
  228. if (extensions.shaderTextureLod) {
  229. prefix.push('#extension GL_EXT_shader_texture_lod : enable');
  230. prefix.push('#define enabledShaderTextureLod');
  231. } else if (shaderExtensions.shaderTextureLod === 'required') {
  232. throw new Error(`required 'GL_EXT_shader_texture_lod' extension not available`);
  233. }
  234. }
  235. if (extensions.depthTexture) {
  236. prefix.push('#define depthTextureSupport');
  237. }
  238. return prefix.join('\n') + '\n';
  239. }
  240. const glsl300VertPrefix = `#version 300 es
  241. #define attribute in
  242. #define varying out
  243. #define texture2D texture
  244. `;
  245. const glsl300FragPrefixCommon = `
  246. #define varying in
  247. #define texture2D texture
  248. #define texture2DLodEXT textureLod
  249. #define gl_FragColor out_FragData0
  250. #define gl_FragDepthEXT gl_FragDepth
  251. #define depthTextureSupport
  252. `;
  253. function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions, outTypes: FragOutTypes) {
  254. const prefix = [
  255. '#version 300 es',
  256. `layout(location = 0) out highp ${outTypes[0] || 'vec4'} out_FragData0;`
  257. ];
  258. if (shaderExtensions.fragDepth) {
  259. prefix.push('#define enabledFragDepth');
  260. }
  261. if (shaderExtensions.drawBuffers) {
  262. prefix.push('#define requiredDrawBuffers');
  263. const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number;
  264. for (let i = 1, il = maxDrawBuffers; i < il; ++i) {
  265. prefix.push(`layout(location = ${i}) out highp ${outTypes[i] || 'vec4'} out_FragData${i};`);
  266. }
  267. }
  268. if (shaderExtensions.shaderTextureLod) {
  269. prefix.push('#define enabledShaderTextureLod');
  270. }
  271. prefix.push(glsl300FragPrefixCommon);
  272. return prefix.join('\n') + '\n';
  273. }
  274. function transformGlsl300Frag(frag: string) {
  275. return frag.replace(/gl_FragData\[([0-9]+)\]/g, 'out_FragData$1');
  276. }
  277. export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtensions, defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
  278. const vertHeader = getDefinesCode(defines, shaders.ignoreDefine);
  279. const fragHeader = getDefinesCode(defines, shaders.ignoreDefine);
  280. const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
  281. const fragPrefix = isWebGL2(gl)
  282. ? getGlsl300FragPrefix(gl, extensions, shaders.extensions, shaders.outTypes)
  283. : getGlsl100FragPrefix(extensions, shaders.extensions);
  284. const frag = isWebGL2(gl) ? transformGlsl300Frag(shaders.frag) : shaders.frag;
  285. return {
  286. id: shaderCodeId(),
  287. name: shaders.name,
  288. vert: `${vertPrefix}${vertHeader}${preprocess(shaders.vert, defines)}`,
  289. frag: `${fragPrefix}${fragHeader}${preprocess(frag, defines)}`,
  290. extensions: shaders.extensions,
  291. outTypes: shaders.outTypes
  292. };
  293. }