WebGLState.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. import {
  2. CullFaceNone, CullFaceBack, CullFaceFront, DoubleSide, BackSide,
  3. NormalBlending, NoBlending, CustomBlending, AddEquation,
  4. AdditiveBlending, SubtractiveBlending, MultiplyBlending, SubtractEquation, ReverseSubtractEquation,
  5. ZeroFactor, OneFactor, SrcColorFactor, SrcAlphaFactor, SrcAlphaSaturateFactor, DstColorFactor, DstAlphaFactor,
  6. OneMinusSrcColorFactor, OneMinusSrcAlphaFactor, OneMinusDstColorFactor, OneMinusDstAlphaFactor,
  7. NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth
  8. } from 'three';
  9. let initialized = false, equationToGL, factorToGL;
  10. class WebGLState {
  11. constructor( backend ) {
  12. this.backend = backend;
  13. this.gl = this.backend.gl;
  14. this.enabled = {};
  15. this.currentFlipSided = null;
  16. this.currentCullFace = null;
  17. this.currentProgram = null;
  18. this.currentBlendingEnabled = false;
  19. this.currentBlending = null;
  20. this.currentBlendSrc = null;
  21. this.currentBlendDst = null;
  22. this.currentBlendSrcAlpha = null;
  23. this.currentBlendDstAlpha = null;
  24. this.currentPremultipledAlpha = null;
  25. this.currentPolygonOffsetFactor = null;
  26. this.currentPolygonOffsetUnits = null;
  27. this.currentColorMask = null;
  28. this.currentDepthFunc = null;
  29. this.currentDepthMask = null;
  30. this.currentStencilFunc = null;
  31. this.currentStencilRef = null;
  32. this.currentStencilFuncMask = null;
  33. this.currentStencilFail = null;
  34. this.currentStencilZFail = null;
  35. this.currentStencilZPass = null;
  36. this.currentStencilMask = null;
  37. this.currentLineWidth = null;
  38. this.currentBoundFramebuffers = {};
  39. this.currentDrawbuffers = new WeakMap();
  40. this.maxTextures = this.gl.getParameter( this.gl.MAX_TEXTURE_IMAGE_UNITS );
  41. this.currentTextureSlot = null;
  42. this.currentBoundTextures = {};
  43. if ( initialized === false ) {
  44. this._init( this.gl );
  45. initialized = true;
  46. }
  47. }
  48. _init( gl ) {
  49. // Store only WebGL constants here.
  50. equationToGL = {
  51. [ AddEquation ]: gl.FUNC_ADD,
  52. [ SubtractEquation ]: gl.FUNC_SUBTRACT,
  53. [ ReverseSubtractEquation ]: gl.FUNC_REVERSE_SUBTRACT
  54. };
  55. factorToGL = {
  56. [ ZeroFactor ]: gl.ZERO,
  57. [ OneFactor ]: gl.ONE,
  58. [ SrcColorFactor ]: gl.SRC_COLOR,
  59. [ SrcAlphaFactor ]: gl.SRC_ALPHA,
  60. [ SrcAlphaSaturateFactor ]: gl.SRC_ALPHA_SATURATE,
  61. [ DstColorFactor ]: gl.DST_COLOR,
  62. [ DstAlphaFactor ]: gl.DST_ALPHA,
  63. [ OneMinusSrcColorFactor ]: gl.ONE_MINUS_SRC_COLOR,
  64. [ OneMinusSrcAlphaFactor ]: gl.ONE_MINUS_SRC_ALPHA,
  65. [ OneMinusDstColorFactor ]: gl.ONE_MINUS_DST_COLOR,
  66. [ OneMinusDstAlphaFactor ]: gl.ONE_MINUS_DST_ALPHA
  67. };
  68. }
  69. enable( id ) {
  70. const { enabled } = this;
  71. if ( enabled[ id ] !== true ) {
  72. this.gl.enable( id );
  73. enabled[ id ] = true;
  74. }
  75. }
  76. disable( id ) {
  77. const { enabled } = this;
  78. if ( enabled[ id ] !== false ) {
  79. this.gl.disable( id );
  80. enabled[ id ] = false;
  81. }
  82. }
  83. setFlipSided( flipSided ) {
  84. if ( this.currentFlipSided !== flipSided ) {
  85. const { gl } = this;
  86. if ( flipSided ) {
  87. gl.frontFace( gl.CW );
  88. } else {
  89. gl.frontFace( gl.CCW );
  90. }
  91. this.currentFlipSided = flipSided;
  92. }
  93. }
  94. setCullFace( cullFace ) {
  95. const { gl } = this;
  96. if ( cullFace !== CullFaceNone ) {
  97. this.enable( gl.CULL_FACE );
  98. if ( cullFace !== this.currentCullFace ) {
  99. if ( cullFace === CullFaceBack ) {
  100. gl.cullFace( gl.BACK );
  101. } else if ( cullFace === CullFaceFront ) {
  102. gl.cullFace( gl.FRONT );
  103. } else {
  104. gl.cullFace( gl.FRONT_AND_BACK );
  105. }
  106. }
  107. } else {
  108. this.disable( gl.CULL_FACE );
  109. }
  110. this.currentCullFace = cullFace;
  111. }
  112. setLineWidth( width ) {
  113. const { currentLineWidth, gl } = this;
  114. if ( width !== currentLineWidth ) {
  115. gl.lineWidth( width );
  116. this.currentLineWidth = width;
  117. }
  118. }
  119. setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {
  120. const { gl } = this;
  121. if ( blending === NoBlending ) {
  122. if ( this.currentBlendingEnabled === true ) {
  123. this.disable( gl.BLEND );
  124. this.currentBlendingEnabled = false;
  125. }
  126. return;
  127. }
  128. if ( this.currentBlendingEnabled === false ) {
  129. this.enable( gl.BLEND );
  130. this.currentBlendingEnabled = true;
  131. }
  132. if ( blending !== CustomBlending ) {
  133. if ( blending !== this.currentBlending || premultipliedAlpha !== this.currentPremultipledAlpha ) {
  134. if ( this.currentBlendEquation !== AddEquation || this.currentBlendEquationAlpha !== AddEquation ) {
  135. gl.blendEquation( gl.FUNC_ADD );
  136. this.currentBlendEquation = AddEquation;
  137. this.currentBlendEquationAlpha = AddEquation;
  138. }
  139. if ( premultipliedAlpha ) {
  140. switch ( blending ) {
  141. case NormalBlending:
  142. gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
  143. break;
  144. case AdditiveBlending:
  145. gl.blendFunc( gl.ONE, gl.ONE );
  146. break;
  147. case SubtractiveBlending:
  148. gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
  149. break;
  150. case MultiplyBlending:
  151. gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );
  152. break;
  153. default:
  154. console.error( 'THREE.WebGLState: Invalid blending: ', blending );
  155. break;
  156. }
  157. } else {
  158. switch ( blending ) {
  159. case NormalBlending:
  160. gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );
  161. break;
  162. case AdditiveBlending:
  163. gl.blendFunc( gl.SRC_ALPHA, gl.ONE );
  164. break;
  165. case SubtractiveBlending:
  166. gl.blendFuncSeparate( gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ZERO, gl.ONE );
  167. break;
  168. case MultiplyBlending:
  169. gl.blendFunc( gl.ZERO, gl.SRC_COLOR );
  170. break;
  171. default:
  172. console.error( 'THREE.WebGLState: Invalid blending: ', blending );
  173. break;
  174. }
  175. }
  176. this.currentBlendSrc = null;
  177. this.currentBlendDst = null;
  178. this.currentBlendSrcAlpha = null;
  179. this.currentBlendDstAlpha = null;
  180. this.currentBlending = blending;
  181. this.currentPremultipledAlpha = premultipliedAlpha;
  182. }
  183. return;
  184. }
  185. // custom blending
  186. blendEquationAlpha = blendEquationAlpha || blendEquation;
  187. blendSrcAlpha = blendSrcAlpha || blendSrc;
  188. blendDstAlpha = blendDstAlpha || blendDst;
  189. if ( blendEquation !== this.currentBlendEquation || blendEquationAlpha !== this.currentBlendEquationAlpha ) {
  190. gl.blendEquationSeparate( equationToGL[ blendEquation ], equationToGL[ blendEquationAlpha ] );
  191. this.currentBlendEquation = blendEquation;
  192. this.currentBlendEquationAlpha = blendEquationAlpha;
  193. }
  194. if ( blendSrc !== this.currentBlendSrc || blendDst !== this.currentBlendDst || blendSrcAlpha !== this.currentBlendSrcAlpha || blendDstAlpha !== this.currentBlendDstAlpha ) {
  195. gl.blendFuncSeparate( factorToGL[ blendSrc ], factorToGL[ blendDst ], factorToGL[ blendSrcAlpha ], factorToGL[ blendDstAlpha ] );
  196. this.currentBlendSrc = blendSrc;
  197. this.currentBlendDst = blendDst;
  198. this.currentBlendSrcAlpha = blendSrcAlpha;
  199. this.currentBlendDstAlpha = blendDstAlpha;
  200. }
  201. this.currentBlending = blending;
  202. this.currentPremultipledAlpha = false;
  203. }
  204. setColorMask( colorMask ) {
  205. if ( this.currentColorMask !== colorMask ) {
  206. this.gl.colorMask( colorMask, colorMask, colorMask, colorMask );
  207. this.currentColorMask = colorMask;
  208. }
  209. }
  210. setDepthTest( depthTest ) {
  211. const { gl } = this;
  212. if ( depthTest ) {
  213. this.enable( gl.DEPTH_TEST );
  214. } else {
  215. this.disable( gl.DEPTH_TEST );
  216. }
  217. }
  218. setDepthMask( depthMask ) {
  219. if ( this.currentDepthMask !== depthMask ) {
  220. this.gl.depthMask( depthMask );
  221. this.currentDepthMask = depthMask;
  222. }
  223. }
  224. setDepthFunc( depthFunc ) {
  225. if ( this.currentDepthFunc !== depthFunc ) {
  226. const { gl } = this;
  227. switch ( depthFunc ) {
  228. case NeverDepth:
  229. gl.depthFunc( gl.NEVER );
  230. break;
  231. case AlwaysDepth:
  232. gl.depthFunc( gl.ALWAYS );
  233. break;
  234. case LessDepth:
  235. gl.depthFunc( gl.LESS );
  236. break;
  237. case LessEqualDepth:
  238. gl.depthFunc( gl.LEQUAL );
  239. break;
  240. case EqualDepth:
  241. gl.depthFunc( gl.EQUAL );
  242. break;
  243. case GreaterEqualDepth:
  244. gl.depthFunc( gl.GEQUAL );
  245. break;
  246. case GreaterDepth:
  247. gl.depthFunc( gl.GREATER );
  248. break;
  249. case NotEqualDepth:
  250. gl.depthFunc( gl.NOTEQUAL );
  251. break;
  252. default:
  253. gl.depthFunc( gl.LEQUAL );
  254. }
  255. this.currentDepthFunc = depthFunc;
  256. }
  257. }
  258. setStencilTest( stencilTest ) {
  259. const { gl } = this;
  260. if ( stencilTest ) {
  261. this.enable( gl.STENCIL_TEST );
  262. } else {
  263. this.disable( gl.STENCIL_TEST );
  264. }
  265. }
  266. setStencilMask( stencilMask ) {
  267. if ( this.currentStencilMask !== stencilMask ) {
  268. this.gl.stencilMask( stencilMask );
  269. this.currentStencilMask = stencilMask;
  270. }
  271. }
  272. setStencilFunc( stencilFunc, stencilRef, stencilMask ) {
  273. if ( this.currentStencilFunc !== stencilFunc ||
  274. this.currentStencilRef !== stencilRef ||
  275. this.currentStencilFuncMask !== stencilMask ) {
  276. this.gl.stencilFunc( stencilFunc, stencilRef, stencilMask );
  277. this.currentStencilFunc = stencilFunc;
  278. this.currentStencilRef = stencilRef;
  279. this.currentStencilFuncMask = stencilMask;
  280. }
  281. }
  282. setStencilOp( stencilFail, stencilZFail, stencilZPass ) {
  283. if ( this.currentStencilFail !== stencilFail ||
  284. this.currentStencilZFail !== stencilZFail ||
  285. this.currentStencilZPass !== stencilZPass ) {
  286. this.gl.stencilOp( stencilFail, stencilZFail, stencilZPass );
  287. this.currentStencilFail = stencilFail;
  288. this.currentStencilZFail = stencilZFail;
  289. this.currentStencilZPass = stencilZPass;
  290. }
  291. }
  292. setMaterial( material, frontFaceCW ) {
  293. const { gl } = this;
  294. material.side === DoubleSide
  295. ? this.disable( gl.CULL_FACE )
  296. : this.enable( gl.CULL_FACE );
  297. let flipSided = ( material.side === BackSide );
  298. if ( frontFaceCW ) flipSided = ! flipSided;
  299. this.setFlipSided( flipSided );
  300. ( material.blending === NormalBlending && material.transparent === false )
  301. ? this.setBlending( NoBlending )
  302. : this.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );
  303. this.setDepthFunc( material.depthFunc );
  304. this.setDepthTest( material.depthTest );
  305. this.setDepthMask( material.depthWrite );
  306. this.setColorMask( material.colorWrite );
  307. const stencilWrite = material.stencilWrite;
  308. this.setStencilTest( stencilWrite );
  309. if ( stencilWrite ) {
  310. this.setStencilMask( material.stencilWriteMask );
  311. this.setStencilFunc( material.stencilFunc, material.stencilRef, material.stencilFuncMask );
  312. this.setStencilOp( material.stencilFail, material.stencilZFail, material.stencilZPass );
  313. }
  314. this.setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );
  315. material.alphaToCoverage === true
  316. ? this.enable( gl.SAMPLE_ALPHA_TO_COVERAGE )
  317. : this.disable( gl.SAMPLE_ALPHA_TO_COVERAGE );
  318. }
  319. setPolygonOffset( polygonOffset, factor, units ) {
  320. const { gl } = this;
  321. if ( polygonOffset ) {
  322. this.enable( gl.POLYGON_OFFSET_FILL );
  323. if ( this.currentPolygonOffsetFactor !== factor || this.currentPolygonOffsetUnits !== units ) {
  324. gl.polygonOffset( factor, units );
  325. this.currentPolygonOffsetFactor = factor;
  326. this.currentPolygonOffsetUnits = units;
  327. }
  328. } else {
  329. this.disable( gl.POLYGON_OFFSET_FILL );
  330. }
  331. }
  332. useProgram( program ) {
  333. if ( this.currentProgram !== program ) {
  334. this.gl.useProgram( program );
  335. this.currentProgram = program;
  336. return true;
  337. }
  338. return false;
  339. }
  340. // framebuffer
  341. bindFramebuffer( target, framebuffer ) {
  342. const { gl, currentBoundFramebuffers } = this;
  343. if ( currentBoundFramebuffers[ target ] !== framebuffer ) {
  344. gl.bindFramebuffer( target, framebuffer );
  345. currentBoundFramebuffers[ target ] = framebuffer;
  346. // gl.DRAW_FRAMEBUFFER is equivalent to gl.FRAMEBUFFER
  347. if ( target === gl.DRAW_FRAMEBUFFER ) {
  348. currentBoundFramebuffers[ gl.FRAMEBUFFER ] = framebuffer;
  349. }
  350. if ( target === gl.FRAMEBUFFER ) {
  351. currentBoundFramebuffers[ gl.DRAW_FRAMEBUFFER ] = framebuffer;
  352. }
  353. return true;
  354. }
  355. return false;
  356. }
  357. drawBuffers( renderContext, framebuffer ) {
  358. const { gl } = this;
  359. let drawBuffers = [];
  360. let needsUpdate = false;
  361. if ( renderContext.textures !== null ) {
  362. drawBuffers = this.currentDrawbuffers.get( framebuffer );
  363. if ( drawBuffers === undefined ) {
  364. drawBuffers = [];
  365. this.currentDrawbuffers.set( framebuffer, drawBuffers );
  366. }
  367. const textures = renderContext.textures;
  368. if ( drawBuffers.length !== textures.length || drawBuffers[ 0 ] !== gl.COLOR_ATTACHMENT0 ) {
  369. for ( let i = 0, il = textures.length; i < il; i ++ ) {
  370. drawBuffers[ i ] = gl.COLOR_ATTACHMENT0 + i;
  371. }
  372. drawBuffers.length = textures.length;
  373. needsUpdate = true;
  374. }
  375. } else {
  376. if ( drawBuffers[ 0 ] !== gl.BACK ) {
  377. drawBuffers[ 0 ] = gl.BACK;
  378. needsUpdate = true;
  379. }
  380. }
  381. if ( needsUpdate ) {
  382. gl.drawBuffers( drawBuffers );
  383. }
  384. }
  385. // texture
  386. activeTexture( webglSlot ) {
  387. const { gl, currentTextureSlot, maxTextures } = this;
  388. if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;
  389. if ( currentTextureSlot !== webglSlot ) {
  390. gl.activeTexture( webglSlot );
  391. this.currentTextureSlot = webglSlot;
  392. }
  393. }
  394. bindTexture( webglType, webglTexture, webglSlot ) {
  395. const { gl, currentTextureSlot, currentBoundTextures, maxTextures } = this;
  396. if ( webglSlot === undefined ) {
  397. if ( currentTextureSlot === null ) {
  398. webglSlot = gl.TEXTURE0 + maxTextures - 1;
  399. } else {
  400. webglSlot = currentTextureSlot;
  401. }
  402. }
  403. let boundTexture = currentBoundTextures[ webglSlot ];
  404. if ( boundTexture === undefined ) {
  405. boundTexture = { type: undefined, texture: undefined };
  406. currentBoundTextures[ webglSlot ] = boundTexture;
  407. }
  408. if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {
  409. if ( currentTextureSlot !== webglSlot ) {
  410. gl.activeTexture( webglSlot );
  411. this.currentTextureSlot = webglSlot;
  412. }
  413. gl.bindTexture( webglType, webglTexture );
  414. boundTexture.type = webglType;
  415. boundTexture.texture = webglTexture;
  416. }
  417. }
  418. unbindTexture() {
  419. const { gl, currentTextureSlot, currentBoundTextures } = this;
  420. const boundTexture = currentBoundTextures[ currentTextureSlot ];
  421. if ( boundTexture !== undefined && boundTexture.type !== undefined ) {
  422. gl.bindTexture( boundTexture.type, null );
  423. boundTexture.type = undefined;
  424. boundTexture.texture = undefined;
  425. }
  426. }
  427. }
  428. export default WebGLState;