/src/renderers/WebGLRenderer.js

http://github.com/mrdoob/three.js · JavaScript · 2160 lines · 1171 code · 896 blank · 93 comment · 292 complexity · d1aeffa85613273e5048dad273374a4a MD5 · raw file

  1. import {
  2. REVISION,
  3. BackSide,
  4. DoubleSide,
  5. FrontSide,
  6. RGBAFormat,
  7. HalfFloatType,
  8. FloatType,
  9. UnsignedByteType,
  10. LinearEncoding,
  11. NoToneMapping,
  12. LinearMipmapLinearFilter,
  13. NearestFilter,
  14. ClampToEdgeWrapping
  15. } from '../constants.js';
  16. import { Frustum } from '../math/Frustum.js';
  17. import { Matrix4 } from '../math/Matrix4.js';
  18. import { Vector3 } from '../math/Vector3.js';
  19. import { Vector4 } from '../math/Vector4.js';
  20. import { WebGLAnimation } from './webgl/WebGLAnimation.js';
  21. import { WebGLAttributes } from './webgl/WebGLAttributes.js';
  22. import { WebGLBackground } from './webgl/WebGLBackground.js';
  23. import { WebGLBindingStates } from './webgl/WebGLBindingStates.js';
  24. import { WebGLBufferRenderer } from './webgl/WebGLBufferRenderer.js';
  25. import { WebGLCapabilities } from './webgl/WebGLCapabilities.js';
  26. import { WebGLClipping } from './webgl/WebGLClipping.js';
  27. import { WebGLCubeMaps } from './webgl/WebGLCubeMaps.js';
  28. import { WebGLCubeUVMaps } from './webgl/WebGLCubeUVMaps.js';
  29. import { WebGLExtensions } from './webgl/WebGLExtensions.js';
  30. import { WebGLGeometries } from './webgl/WebGLGeometries.js';
  31. import { WebGLIndexedBufferRenderer } from './webgl/WebGLIndexedBufferRenderer.js';
  32. import { WebGLInfo } from './webgl/WebGLInfo.js';
  33. import { WebGLMorphtargets } from './webgl/WebGLMorphtargets.js';
  34. import { WebGLMultisampleRenderTarget } from './WebGLMultisampleRenderTarget.js';
  35. import { WebGLObjects } from './webgl/WebGLObjects.js';
  36. import { WebGLPrograms } from './webgl/WebGLPrograms.js';
  37. import { WebGLProperties } from './webgl/WebGLProperties.js';
  38. import { WebGLRenderLists } from './webgl/WebGLRenderLists.js';
  39. import { WebGLRenderStates } from './webgl/WebGLRenderStates.js';
  40. import { WebGLRenderTarget } from './WebGLRenderTarget.js';
  41. import { WebGLShadowMap } from './webgl/WebGLShadowMap.js';
  42. import { WebGLState } from './webgl/WebGLState.js';
  43. import { WebGLTextures } from './webgl/WebGLTextures.js';
  44. import { WebGLUniforms } from './webgl/WebGLUniforms.js';
  45. import { WebGLUtils } from './webgl/WebGLUtils.js';
  46. import { WebXRManager } from './webxr/WebXRManager.js';
  47. import { WebGLMaterials } from './webgl/WebGLMaterials.js';
  48. import { createElementNS } from '../utils.js';
  49. function createCanvasElement() {
  50. const canvas = createElementNS( 'canvas' );
  51. canvas.style.display = 'block';
  52. return canvas;
  53. }
  54. function WebGLRenderer( parameters = {} ) {
  55. const _canvas = parameters.canvas !== undefined ? parameters.canvas : createCanvasElement(),
  56. _context = parameters.context !== undefined ? parameters.context : null,
  57. _alpha = parameters.alpha !== undefined ? parameters.alpha : false,
  58. _depth = parameters.depth !== undefined ? parameters.depth : true,
  59. _stencil = parameters.stencil !== undefined ? parameters.stencil : true,
  60. _antialias = parameters.antialias !== undefined ? parameters.antialias : false,
  61. _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,
  62. _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,
  63. _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default',
  64. _failIfMajorPerformanceCaveat = parameters.failIfMajorPerformanceCaveat !== undefined ? parameters.failIfMajorPerformanceCaveat : false;
  65. let currentRenderList = null;
  66. let currentRenderState = null;
  67. // render() can be called from within a callback triggered by another render.
  68. // We track this so that the nested render call gets its list and state isolated from the parent render call.
  69. const renderListStack = [];
  70. const renderStateStack = [];
  71. // public properties
  72. this.domElement = _canvas;
  73. // Debug configuration container
  74. this.debug = {
  75. /**
  76. * Enables error checking and reporting when shader programs are being compiled
  77. * @type {boolean}
  78. */
  79. checkShaderErrors: true
  80. };
  81. // clearing
  82. this.autoClear = true;
  83. this.autoClearColor = true;
  84. this.autoClearDepth = true;
  85. this.autoClearStencil = true;
  86. // scene graph
  87. this.sortObjects = true;
  88. // user-defined clipping
  89. this.clippingPlanes = [];
  90. this.localClippingEnabled = false;
  91. // physically based shading
  92. this.gammaFactor = 2.0; // for backwards compatibility
  93. this.outputEncoding = LinearEncoding;
  94. // physical lights
  95. this.physicallyCorrectLights = false;
  96. // tone mapping
  97. this.toneMapping = NoToneMapping;
  98. this.toneMappingExposure = 1.0;
  99. // internal properties
  100. const _this = this;
  101. let _isContextLost = false;
  102. // internal state cache
  103. let _currentActiveCubeFace = 0;
  104. let _currentActiveMipmapLevel = 0;
  105. let _currentRenderTarget = null;
  106. let _currentMaterialId = - 1;
  107. let _currentCamera = null;
  108. const _currentViewport = new Vector4();
  109. const _currentScissor = new Vector4();
  110. let _currentScissorTest = null;
  111. //
  112. let _width = _canvas.width;
  113. let _height = _canvas.height;
  114. let _pixelRatio = 1;
  115. let _opaqueSort = null;
  116. let _transparentSort = null;
  117. const _viewport = new Vector4( 0, 0, _width, _height );
  118. const _scissor = new Vector4( 0, 0, _width, _height );
  119. let _scissorTest = false;
  120. //
  121. const _currentDrawBuffers = [];
  122. // frustum
  123. const _frustum = new Frustum();
  124. // clipping
  125. let _clippingEnabled = false;
  126. let _localClippingEnabled = false;
  127. // transmission
  128. let _transmissionRenderTarget = null;
  129. // camera matrices cache
  130. const _projScreenMatrix = new Matrix4();
  131. const _vector3 = new Vector3();
  132. const _emptyScene = { background: null, fog: null, environment: null, overrideMaterial: null, isScene: true };
  133. function getTargetPixelRatio() {
  134. return _currentRenderTarget === null ? _pixelRatio : 1;
  135. }
  136. // initialize
  137. let _gl = _context;
  138. function getContext( contextNames, contextAttributes ) {
  139. for ( let i = 0; i < contextNames.length; i ++ ) {
  140. const contextName = contextNames[ i ];
  141. const context = _canvas.getContext( contextName, contextAttributes );
  142. if ( context !== null ) return context;
  143. }
  144. return null;
  145. }
  146. try {
  147. const contextAttributes = {
  148. alpha: _alpha,
  149. depth: _depth,
  150. stencil: _stencil,
  151. antialias: _antialias,
  152. premultipliedAlpha: _premultipliedAlpha,
  153. preserveDrawingBuffer: _preserveDrawingBuffer,
  154. powerPreference: _powerPreference,
  155. failIfMajorPerformanceCaveat: _failIfMajorPerformanceCaveat
  156. };
  157. _canvas.setAttribute( 'data-engine', `three.js r${REVISION}` );
  158. // event listeners must be registered before WebGL context is created, see #12753
  159. _canvas.addEventListener( 'webglcontextlost', onContextLost, false );
  160. _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );
  161. if ( _gl === null ) {
  162. const contextNames = [ 'webgl2', 'webgl', 'experimental-webgl' ];
  163. if ( _this.isWebGL1Renderer === true ) {
  164. contextNames.shift();
  165. }
  166. _gl = getContext( contextNames, contextAttributes );
  167. if ( _gl === null ) {
  168. if ( getContext( contextNames ) ) {
  169. throw new Error( 'Error creating WebGL context with your selected attributes.' );
  170. } else {
  171. throw new Error( 'Error creating WebGL context.' );
  172. }
  173. }
  174. }
  175. // Some experimental-webgl implementations do not have getShaderPrecisionFormat
  176. if ( _gl.getShaderPrecisionFormat === undefined ) {
  177. _gl.getShaderPrecisionFormat = function () {
  178. return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };
  179. };
  180. }
  181. } catch ( error ) {
  182. console.error( 'THREE.WebGLRenderer: ' + error.message );
  183. throw error;
  184. }
  185. let extensions, capabilities, state, info;
  186. let properties, textures, cubemaps, cubeuvmaps, attributes, geometries, objects;
  187. let programCache, materials, renderLists, renderStates, clipping, shadowMap;
  188. let background, morphtargets, bufferRenderer, indexedBufferRenderer;
  189. let utils, bindingStates;
  190. function initGLContext() {
  191. extensions = new WebGLExtensions( _gl );
  192. capabilities = new WebGLCapabilities( _gl, extensions, parameters );
  193. extensions.init( capabilities );
  194. utils = new WebGLUtils( _gl, extensions, capabilities );
  195. state = new WebGLState( _gl, extensions, capabilities );
  196. _currentDrawBuffers[ 0 ] = _gl.BACK;
  197. info = new WebGLInfo( _gl );
  198. properties = new WebGLProperties();
  199. textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
  200. cubemaps = new WebGLCubeMaps( _this );
  201. cubeuvmaps = new WebGLCubeUVMaps( _this );
  202. attributes = new WebGLAttributes( _gl, capabilities );
  203. bindingStates = new WebGLBindingStates( _gl, extensions, attributes, capabilities );
  204. geometries = new WebGLGeometries( _gl, attributes, info, bindingStates );
  205. objects = new WebGLObjects( _gl, geometries, attributes, info );
  206. morphtargets = new WebGLMorphtargets( _gl, capabilities, textures );
  207. clipping = new WebGLClipping( properties );
  208. programCache = new WebGLPrograms( _this, cubemaps, cubeuvmaps, extensions, capabilities, bindingStates, clipping );
  209. materials = new WebGLMaterials( properties );
  210. renderLists = new WebGLRenderLists( properties );
  211. renderStates = new WebGLRenderStates( extensions, capabilities );
  212. background = new WebGLBackground( _this, cubemaps, state, objects, _premultipliedAlpha );
  213. shadowMap = new WebGLShadowMap( _this, objects, capabilities );
  214. bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info, capabilities );
  215. indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info, capabilities );
  216. info.programs = programCache.programs;
  217. _this.capabilities = capabilities;
  218. _this.extensions = extensions;
  219. _this.properties = properties;
  220. _this.renderLists = renderLists;
  221. _this.shadowMap = shadowMap;
  222. _this.state = state;
  223. _this.info = info;
  224. }
  225. initGLContext();
  226. // xr
  227. const xr = new WebXRManager( _this, _gl );
  228. this.xr = xr;
  229. // API
  230. this.getContext = function () {
  231. return _gl;
  232. };
  233. this.getContextAttributes = function () {
  234. return _gl.getContextAttributes();
  235. };
  236. this.forceContextLoss = function () {
  237. const extension = extensions.get( 'WEBGL_lose_context' );
  238. if ( extension ) extension.loseContext();
  239. };
  240. this.forceContextRestore = function () {
  241. const extension = extensions.get( 'WEBGL_lose_context' );
  242. if ( extension ) extension.restoreContext();
  243. };
  244. this.getPixelRatio = function () {
  245. return _pixelRatio;
  246. };
  247. this.setPixelRatio = function ( value ) {
  248. if ( value === undefined ) return;
  249. _pixelRatio = value;
  250. this.setSize( _width, _height, false );
  251. };
  252. this.getSize = function ( target ) {
  253. return target.set( _width, _height );
  254. };
  255. this.setSize = function ( width, height, updateStyle ) {
  256. if ( xr.isPresenting ) {
  257. console.warn( 'THREE.WebGLRenderer: Can\'t change size while VR device is presenting.' );
  258. return;
  259. }
  260. _width = width;
  261. _height = height;
  262. _canvas.width = Math.floor( width * _pixelRatio );
  263. _canvas.height = Math.floor( height * _pixelRatio );
  264. if ( updateStyle !== false ) {
  265. _canvas.style.width = width + 'px';
  266. _canvas.style.height = height + 'px';
  267. }
  268. this.setViewport( 0, 0, width, height );
  269. };
  270. this.getDrawingBufferSize = function ( target ) {
  271. return target.set( _width * _pixelRatio, _height * _pixelRatio ).floor();
  272. };
  273. this.setDrawingBufferSize = function ( width, height, pixelRatio ) {
  274. _width = width;
  275. _height = height;
  276. _pixelRatio = pixelRatio;
  277. _canvas.width = Math.floor( width * pixelRatio );
  278. _canvas.height = Math.floor( height * pixelRatio );
  279. this.setViewport( 0, 0, width, height );
  280. };
  281. this.getCurrentViewport = function ( target ) {
  282. return target.copy( _currentViewport );
  283. };
  284. this.getViewport = function ( target ) {
  285. return target.copy( _viewport );
  286. };
  287. this.setViewport = function ( x, y, width, height ) {
  288. if ( x.isVector4 ) {
  289. _viewport.set( x.x, x.y, x.z, x.w );
  290. } else {
  291. _viewport.set( x, y, width, height );
  292. }
  293. state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor() );
  294. };
  295. this.getScissor = function ( target ) {
  296. return target.copy( _scissor );
  297. };
  298. this.setScissor = function ( x, y, width, height ) {
  299. if ( x.isVector4 ) {
  300. _scissor.set( x.x, x.y, x.z, x.w );
  301. } else {
  302. _scissor.set( x, y, width, height );
  303. }
  304. state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor() );
  305. };
  306. this.getScissorTest = function () {
  307. return _scissorTest;
  308. };
  309. this.setScissorTest = function ( boolean ) {
  310. state.setScissorTest( _scissorTest = boolean );
  311. };
  312. this.setOpaqueSort = function ( method ) {
  313. _opaqueSort = method;
  314. };
  315. this.setTransparentSort = function ( method ) {
  316. _transparentSort = method;
  317. };
  318. // Clearing
  319. this.getClearColor = function ( target ) {
  320. return target.copy( background.getClearColor() );
  321. };
  322. this.setClearColor = function () {
  323. background.setClearColor.apply( background, arguments );
  324. };
  325. this.getClearAlpha = function () {
  326. return background.getClearAlpha();
  327. };
  328. this.setClearAlpha = function () {
  329. background.setClearAlpha.apply( background, arguments );
  330. };
  331. this.clear = function ( color, depth, stencil ) {
  332. let bits = 0;
  333. if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;
  334. if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;
  335. if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;
  336. _gl.clear( bits );
  337. };
  338. this.clearColor = function () {
  339. this.clear( true, false, false );
  340. };
  341. this.clearDepth = function () {
  342. this.clear( false, true, false );
  343. };
  344. this.clearStencil = function () {
  345. this.clear( false, false, true );
  346. };
  347. //
  348. this.dispose = function () {
  349. _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );
  350. _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );
  351. renderLists.dispose();
  352. renderStates.dispose();
  353. properties.dispose();
  354. cubemaps.dispose();
  355. cubeuvmaps.dispose();
  356. objects.dispose();
  357. bindingStates.dispose();
  358. xr.dispose();
  359. xr.removeEventListener( 'sessionstart', onXRSessionStart );
  360. xr.removeEventListener( 'sessionend', onXRSessionEnd );
  361. if ( _transmissionRenderTarget ) {
  362. _transmissionRenderTarget.dispose();
  363. _transmissionRenderTarget = null;
  364. }
  365. animation.stop();
  366. };
  367. // Events
  368. function onContextLost( event ) {
  369. event.preventDefault();
  370. console.log( 'THREE.WebGLRenderer: Context Lost.' );
  371. _isContextLost = true;
  372. }
  373. function onContextRestore( /* event */ ) {
  374. console.log( 'THREE.WebGLRenderer: Context Restored.' );
  375. _isContextLost = false;
  376. const infoAutoReset = info.autoReset;
  377. const shadowMapEnabled = shadowMap.enabled;
  378. const shadowMapAutoUpdate = shadowMap.autoUpdate;
  379. const shadowMapNeedsUpdate = shadowMap.needsUpdate;
  380. const shadowMapType = shadowMap.type;
  381. initGLContext();
  382. info.autoReset = infoAutoReset;
  383. shadowMap.enabled = shadowMapEnabled;
  384. shadowMap.autoUpdate = shadowMapAutoUpdate;
  385. shadowMap.needsUpdate = shadowMapNeedsUpdate;
  386. shadowMap.type = shadowMapType;
  387. }
  388. function onMaterialDispose( event ) {
  389. const material = event.target;
  390. material.removeEventListener( 'dispose', onMaterialDispose );
  391. deallocateMaterial( material );
  392. }
  393. // Buffer deallocation
  394. function deallocateMaterial( material ) {
  395. releaseMaterialProgramReferences( material );
  396. properties.remove( material );
  397. }
  398. function releaseMaterialProgramReferences( material ) {
  399. const programs = properties.get( material ).programs;
  400. if ( programs !== undefined ) {
  401. programs.forEach( function ( program ) {
  402. programCache.releaseProgram( program );
  403. } );
  404. }
  405. }
  406. // Buffer rendering
  407. this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {
  408. if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)
  409. const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );
  410. const program = setProgram( camera, scene, geometry, material, object );
  411. state.setMaterial( material, frontFaceCW );
  412. //
  413. let index = geometry.index;
  414. const position = geometry.attributes.position;
  415. //
  416. if ( index === null ) {
  417. if ( position === undefined || position.count === 0 ) return;
  418. } else if ( index.count === 0 ) {
  419. return;
  420. }
  421. //
  422. let rangeFactor = 1;
  423. if ( material.wireframe === true ) {
  424. index = geometries.getWireframeAttribute( geometry );
  425. rangeFactor = 2;
  426. }
  427. bindingStates.setup( object, material, program, geometry, index );
  428. let attribute;
  429. let renderer = bufferRenderer;
  430. if ( index !== null ) {
  431. attribute = attributes.get( index );
  432. renderer = indexedBufferRenderer;
  433. renderer.setIndex( attribute );
  434. }
  435. //
  436. const dataCount = ( index !== null ) ? index.count : position.count;
  437. const rangeStart = geometry.drawRange.start * rangeFactor;
  438. const rangeCount = geometry.drawRange.count * rangeFactor;
  439. const groupStart = group !== null ? group.start * rangeFactor : 0;
  440. const groupCount = group !== null ? group.count * rangeFactor : Infinity;
  441. const drawStart = Math.max( rangeStart, groupStart );
  442. const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;
  443. const drawCount = Math.max( 0, drawEnd - drawStart + 1 );
  444. if ( drawCount === 0 ) return;
  445. //
  446. if ( object.isMesh ) {
  447. if ( material.wireframe === true ) {
  448. state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );
  449. renderer.setMode( _gl.LINES );
  450. } else {
  451. renderer.setMode( _gl.TRIANGLES );
  452. }
  453. } else if ( object.isLine ) {
  454. let lineWidth = material.linewidth;
  455. if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material
  456. state.setLineWidth( lineWidth * getTargetPixelRatio() );
  457. if ( object.isLineSegments ) {
  458. renderer.setMode( _gl.LINES );
  459. } else if ( object.isLineLoop ) {
  460. renderer.setMode( _gl.LINE_LOOP );
  461. } else {
  462. renderer.setMode( _gl.LINE_STRIP );
  463. }
  464. } else if ( object.isPoints ) {
  465. renderer.setMode( _gl.POINTS );
  466. } else if ( object.isSprite ) {
  467. renderer.setMode( _gl.TRIANGLES );
  468. }
  469. if ( object.isInstancedMesh ) {
  470. renderer.renderInstances( drawStart, drawCount, object.count );
  471. } else if ( geometry.isInstancedBufferGeometry ) {
  472. const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );
  473. renderer.renderInstances( drawStart, drawCount, instanceCount );
  474. } else {
  475. renderer.render( drawStart, drawCount );
  476. }
  477. };
  478. // Compile
  479. this.compile = function ( scene, camera ) {
  480. currentRenderState = renderStates.get( scene );
  481. currentRenderState.init();
  482. renderStateStack.push( currentRenderState );
  483. scene.traverseVisible( function ( object ) {
  484. if ( object.isLight && object.layers.test( camera.layers ) ) {
  485. currentRenderState.pushLight( object );
  486. if ( object.castShadow ) {
  487. currentRenderState.pushShadow( object );
  488. }
  489. }
  490. } );
  491. currentRenderState.setupLights( _this.physicallyCorrectLights );
  492. scene.traverse( function ( object ) {
  493. const material = object.material;
  494. if ( material ) {
  495. if ( Array.isArray( material ) ) {
  496. for ( let i = 0; i < material.length; i ++ ) {
  497. const material2 = material[ i ];
  498. getProgram( material2, scene, object );
  499. }
  500. } else {
  501. getProgram( material, scene, object );
  502. }
  503. }
  504. } );
  505. renderStateStack.pop();
  506. currentRenderState = null;
  507. };
  508. // Animation Loop
  509. let onAnimationFrameCallback = null;
  510. function onAnimationFrame( time ) {
  511. if ( onAnimationFrameCallback ) onAnimationFrameCallback( time );
  512. }
  513. function onXRSessionStart() {
  514. animation.stop();
  515. }
  516. function onXRSessionEnd() {
  517. animation.start();
  518. }
  519. const animation = new WebGLAnimation();
  520. animation.setAnimationLoop( onAnimationFrame );
  521. if ( typeof window !== 'undefined' ) animation.setContext( window );
  522. this.setAnimationLoop = function ( callback ) {
  523. onAnimationFrameCallback = callback;
  524. xr.setAnimationLoop( callback );
  525. ( callback === null ) ? animation.stop() : animation.start();
  526. };
  527. xr.addEventListener( 'sessionstart', onXRSessionStart );
  528. xr.addEventListener( 'sessionend', onXRSessionEnd );
  529. // Rendering
  530. this.render = function ( scene, camera ) {
  531. if ( camera !== undefined && camera.isCamera !== true ) {
  532. console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );
  533. return;
  534. }
  535. if ( _isContextLost === true ) return;
  536. // update scene graph
  537. if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
  538. // update camera matrices and frustum
  539. if ( camera.parent === null ) camera.updateMatrixWorld();
  540. if ( xr.enabled === true && xr.isPresenting === true ) {
  541. if ( xr.cameraAutoUpdate === true ) xr.updateCamera( camera );
  542. camera = xr.getCamera(); // use XR camera for rendering
  543. }
  544. //
  545. if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, _currentRenderTarget );
  546. currentRenderState = renderStates.get( scene, renderStateStack.length );
  547. currentRenderState.init();
  548. renderStateStack.push( currentRenderState );
  549. _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
  550. _frustum.setFromProjectionMatrix( _projScreenMatrix );
  551. _localClippingEnabled = this.localClippingEnabled;
  552. _clippingEnabled = clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
  553. currentRenderList = renderLists.get( scene, renderListStack.length );
  554. currentRenderList.init();
  555. renderListStack.push( currentRenderList );
  556. projectObject( scene, camera, 0, _this.sortObjects );
  557. currentRenderList.finish();
  558. if ( _this.sortObjects === true ) {
  559. currentRenderList.sort( _opaqueSort, _transparentSort );
  560. }
  561. //
  562. if ( _clippingEnabled === true ) clipping.beginShadows();
  563. const shadowsArray = currentRenderState.state.shadowsArray;
  564. shadowMap.render( shadowsArray, scene, camera );
  565. if ( _clippingEnabled === true ) clipping.endShadows();
  566. //
  567. if ( this.info.autoReset === true ) this.info.reset();
  568. //
  569. background.render( currentRenderList, scene );
  570. // render scene
  571. currentRenderState.setupLights( _this.physicallyCorrectLights );
  572. if ( camera.isArrayCamera ) {
  573. const cameras = camera.cameras;
  574. for ( let i = 0, l = cameras.length; i < l; i ++ ) {
  575. const camera2 = cameras[ i ];
  576. renderScene( currentRenderList, scene, camera2, camera2.viewport );
  577. }
  578. } else {
  579. renderScene( currentRenderList, scene, camera );
  580. }
  581. //
  582. if ( _currentRenderTarget !== null ) {
  583. // resolve multisample renderbuffers to a single-sample texture if necessary
  584. textures.updateMultisampleRenderTarget( _currentRenderTarget );
  585. // Generate mipmap if we're using any kind of mipmap filtering
  586. textures.updateRenderTargetMipmap( _currentRenderTarget );
  587. }
  588. //
  589. if ( scene.isScene === true ) scene.onAfterRender( _this, scene, camera );
  590. // Ensure depth buffer writing is enabled so it can be cleared on next render
  591. state.buffers.depth.setTest( true );
  592. state.buffers.depth.setMask( true );
  593. state.buffers.color.setMask( true );
  594. state.setPolygonOffset( false );
  595. // _gl.finish();
  596. bindingStates.resetDefaultState();
  597. _currentMaterialId = - 1;
  598. _currentCamera = null;
  599. renderStateStack.pop();
  600. if ( renderStateStack.length > 0 ) {
  601. currentRenderState = renderStateStack[ renderStateStack.length - 1 ];
  602. } else {
  603. currentRenderState = null;
  604. }
  605. renderListStack.pop();
  606. if ( renderListStack.length > 0 ) {
  607. currentRenderList = renderListStack[ renderListStack.length - 1 ];
  608. } else {
  609. currentRenderList = null;
  610. }
  611. };
  612. function projectObject( object, camera, groupOrder, sortObjects ) {
  613. if ( object.visible === false ) return;
  614. const visible = object.layers.test( camera.layers );
  615. if ( visible ) {
  616. if ( object.isGroup ) {
  617. groupOrder = object.renderOrder;
  618. } else if ( object.isLOD ) {
  619. if ( object.autoUpdate === true ) object.update( camera );
  620. } else if ( object.isLight ) {
  621. currentRenderState.pushLight( object );
  622. if ( object.castShadow ) {
  623. currentRenderState.pushShadow( object );
  624. }
  625. } else if ( object.isSprite ) {
  626. if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
  627. if ( sortObjects ) {
  628. _vector3.setFromMatrixPosition( object.matrixWorld )
  629. .applyMatrix4( _projScreenMatrix );
  630. }
  631. const geometry = objects.update( object );
  632. const material = object.material;
  633. if ( material.visible ) {
  634. currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
  635. }
  636. }
  637. } else if ( object.isMesh || object.isLine || object.isPoints ) {
  638. if ( object.isSkinnedMesh ) {
  639. // update skeleton only once in a frame
  640. if ( object.skeleton.frame !== info.render.frame ) {
  641. object.skeleton.update();
  642. object.skeleton.frame = info.render.frame;
  643. }
  644. }
  645. if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
  646. if ( sortObjects ) {
  647. _vector3.setFromMatrixPosition( object.matrixWorld )
  648. .applyMatrix4( _projScreenMatrix );
  649. }
  650. const geometry = objects.update( object );
  651. const material = object.material;
  652. if ( Array.isArray( material ) ) {
  653. const groups = geometry.groups;
  654. for ( let i = 0, l = groups.length; i < l; i ++ ) {
  655. const group = groups[ i ];
  656. const groupMaterial = material[ group.materialIndex ];
  657. if ( groupMaterial && groupMaterial.visible ) {
  658. currentRenderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
  659. }
  660. }
  661. } else if ( material.visible ) {
  662. currentRenderList.push( object, geometry, material, groupOrder, _vector3.z, null );
  663. }
  664. }
  665. }
  666. }
  667. const children = object.children;
  668. for ( let i = 0, l = children.length; i < l; i ++ ) {
  669. projectObject( children[ i ], camera, groupOrder, sortObjects );
  670. }
  671. }
  672. function renderScene( currentRenderList, scene, camera, viewport ) {
  673. const opaqueObjects = currentRenderList.opaque;
  674. const transmissiveObjects = currentRenderList.transmissive;
  675. const transparentObjects = currentRenderList.transparent;
  676. currentRenderState.setupLightsView( camera );
  677. if ( transmissiveObjects.length > 0 ) renderTransmissionPass( opaqueObjects, scene, camera );
  678. if ( viewport ) state.viewport( _currentViewport.copy( viewport ) );
  679. if ( opaqueObjects.length > 0 ) renderObjects( opaqueObjects, scene, camera );
  680. if ( transmissiveObjects.length > 0 ) renderObjects( transmissiveObjects, scene, camera );
  681. if ( transparentObjects.length > 0 ) renderObjects( transparentObjects, scene, camera );
  682. }
  683. function renderTransmissionPass( opaqueObjects, scene, camera ) {
  684. if ( _transmissionRenderTarget === null ) {
  685. const needsAntialias = _antialias === true && capabilities.isWebGL2 === true;
  686. const renderTargetType = needsAntialias ? WebGLMultisampleRenderTarget : WebGLRenderTarget;
  687. _transmissionRenderTarget = new renderTargetType( 1024, 1024, {
  688. generateMipmaps: true,
  689. type: utils.convert( HalfFloatType ) !== null ? HalfFloatType : UnsignedByteType,
  690. minFilter: LinearMipmapLinearFilter,
  691. magFilter: NearestFilter,
  692. wrapS: ClampToEdgeWrapping,
  693. wrapT: ClampToEdgeWrapping
  694. } );
  695. }
  696. const currentRenderTarget = _this.getRenderTarget();
  697. _this.setRenderTarget( _transmissionRenderTarget );
  698. _this.clear();
  699. // Turn off the features which can affect the frag color for opaque objects pass.
  700. // Otherwise they are applied twice in opaque objects pass and transmission objects pass.
  701. const currentToneMapping = _this.toneMapping;
  702. _this.toneMapping = NoToneMapping;
  703. renderObjects( opaqueObjects, scene, camera );
  704. _this.toneMapping = currentToneMapping;
  705. textures.updateMultisampleRenderTarget( _transmissionRenderTarget );
  706. textures.updateRenderTargetMipmap( _transmissionRenderTarget );
  707. _this.setRenderTarget( currentRenderTarget );
  708. }
  709. function renderObjects( renderList, scene, camera ) {
  710. const overrideMaterial = scene.isScene === true ? scene.overrideMaterial : null;
  711. for ( let i = 0, l = renderList.length; i < l; i ++ ) {
  712. const renderItem = renderList[ i ];
  713. const object = renderItem.object;
  714. const geometry = renderItem.geometry;
  715. const material = overrideMaterial === null ? renderItem.material : overrideMaterial;
  716. const group = renderItem.group;
  717. if ( object.layers.test( camera.layers ) ) {
  718. renderObject( object, scene, camera, geometry, material, group );
  719. }
  720. }
  721. }
  722. function renderObject( object, scene, camera, geometry, material, group ) {
  723. object.onBeforeRender( _this, scene, camera, geometry, material, group );
  724. object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
  725. object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
  726. material.onBeforeRender( _this, scene, camera, geometry, object, group );
  727. if ( material.transparent === true && material.side === DoubleSide ) {
  728. material.side = BackSide;
  729. material.needsUpdate = true;
  730. _this.renderBufferDirect( camera, scene, geometry, material, object, group );
  731. material.side = FrontSide;
  732. material.needsUpdate = true;
  733. _this.renderBufferDirect( camera, scene, geometry, material, object, group );
  734. material.side = DoubleSide;
  735. } else {
  736. _this.renderBufferDirect( camera, scene, geometry, material, object, group );
  737. }
  738. object.onAfterRender( _this, scene, camera, geometry, material, group );
  739. }
  740. function getProgram( material, scene, object ) {
  741. if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
  742. const materialProperties = properties.get( material );
  743. const lights = currentRenderState.state.lights;
  744. const shadowsArray = currentRenderState.state.shadowsArray;
  745. const lightsStateVersion = lights.state.version;
  746. const parameters = programCache.getParameters( material, lights.state, shadowsArray, scene, object );
  747. const programCacheKey = programCache.getProgramCacheKey( parameters );
  748. let programs = materialProperties.programs;
  749. // always update environment and fog - changing these trigger an getProgram call, but it's possible that the program doesn't change
  750. materialProperties.environment = material.isMeshStandardMaterial ? scene.environment : null;
  751. materialProperties.fog = scene.fog;
  752. materialProperties.envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || materialProperties.environment );
  753. if ( programs === undefined ) {
  754. // new material
  755. material.addEventListener( 'dispose', onMaterialDispose );
  756. programs = new Map();
  757. materialProperties.programs = programs;
  758. }
  759. let program = programs.get( programCacheKey );
  760. if ( program !== undefined ) {
  761. // early out if program and light state is identical
  762. if ( materialProperties.currentProgram === program && materialProperties.lightsStateVersion === lightsStateVersion ) {
  763. updateCommonMaterialProperties( material, parameters );
  764. return program;
  765. }
  766. } else {
  767. parameters.uniforms = programCache.getUniforms( material );
  768. material.onBuild( object, parameters, _this );
  769. material.onBeforeCompile( parameters, _this );
  770. program = programCache.acquireProgram( parameters, programCacheKey );
  771. programs.set( programCacheKey, program );
  772. materialProperties.uniforms = parameters.uniforms;
  773. }
  774. const uniforms = materialProperties.uniforms;
  775. if ( ( ! material.isShaderMaterial && ! material.isRawShaderMaterial ) || material.clipping === true ) {
  776. uniforms.clippingPlanes = clipping.uniform;
  777. }
  778. updateCommonMaterialProperties( material, parameters );
  779. // store the light setup it was created for
  780. materialProperties.needsLights = materialNeedsLights( material );
  781. materialProperties.lightsStateVersion = lightsStateVersion;
  782. if ( materialProperties.needsLights ) {
  783. // wire up the material to this renderer's lighting state
  784. uniforms.ambientLightColor.value = lights.state.ambient;
  785. uniforms.lightProbe.value = lights.state.probe;
  786. uniforms.directionalLights.value = lights.state.directional;
  787. uniforms.directionalLightShadows.value = lights.state.directionalShadow;
  788. uniforms.spotLights.value = lights.state.spot;
  789. uniforms.spotLightShadows.value = lights.state.spotShadow;
  790. uniforms.rectAreaLights.value = lights.state.rectArea;
  791. uniforms.ltc_1.value = lights.state.rectAreaLTC1;
  792. uniforms.ltc_2.value = lights.state.rectAreaLTC2;
  793. uniforms.pointLights.value = lights.state.point;
  794. uniforms.pointLightShadows.value = lights.state.pointShadow;
  795. uniforms.hemisphereLights.value = lights.state.hemi;
  796. uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;
  797. uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;
  798. uniforms.spotShadowMap.value = lights.state.spotShadowMap;
  799. uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;
  800. uniforms.pointShadowMap.value = lights.state.pointShadowMap;
  801. uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;
  802. // TODO (abelnation): add area lights shadow info to uniforms
  803. }
  804. const progUniforms = program.getUniforms();
  805. const uniformsList = WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );
  806. materialProperties.currentProgram = program;
  807. materialProperties.uniformsList = uniformsList;
  808. return program;
  809. }
  810. function updateCommonMaterialProperties( material, parameters ) {
  811. const materialProperties = properties.get( material );
  812. materialProperties.outputEncoding = parameters.outputEncoding;
  813. materialProperties.instancing = parameters.instancing;
  814. materialProperties.skinning = parameters.skinning;
  815. materialProperties.morphTargets = parameters.morphTargets;
  816. materialProperties.morphNormals = parameters.morphNormals;
  817. materialProperties.morphTargetsCount = parameters.morphTargetsCount;
  818. materialProperties.numClippingPlanes = parameters.numClippingPlanes;
  819. materialProperties.numIntersection = parameters.numClipIntersection;
  820. materialProperties.vertexAlphas = parameters.vertexAlphas;
  821. materialProperties.vertexTangents = parameters.vertexTangents;
  822. }
  823. function setProgram( camera, scene, geometry, material, object ) {
  824. if ( scene.isScene !== true ) scene = _emptyScene; // scene could be a Mesh, Line, Points, ...
  825. textures.resetTextureUnits();
  826. const fog = scene.fog;
  827. const environment = material.isMeshStandardMaterial ? scene.environment : null;
  828. const encoding = ( _currentRenderTarget === null ) ? _this.outputEncoding : _currentRenderTarget.texture.encoding;
  829. const envMap = ( material.isMeshStandardMaterial ? cubeuvmaps : cubemaps ).get( material.envMap || environment );
  830. const vertexAlphas = material.vertexColors === true && !! geometry.attributes.color && geometry.attributes.color.itemSize === 4;
  831. const vertexTangents = !! material.normalMap && !! geometry.attributes.tangent;
  832. const morphTargets = !! geometry.morphAttributes.position;
  833. const morphNormals = !! geometry.morphAttributes.normal;
  834. const morphTargetsCount = !! geometry.morphAttributes.position ? geometry.morphAttributes.position.length : 0;
  835. const materialProperties = properties.get( material );
  836. const lights = currentRenderState.state.lights;
  837. if ( _clippingEnabled === true ) {
  838. if ( _localClippingEnabled === true || camera !== _currentCamera ) {
  839. const useCache =
  840. camera === _currentCamera &&
  841. material.id === _currentMaterialId;
  842. // we might want to call this function with some ClippingGroup
  843. // object instead of the material, once it becomes feasible
  844. // (#8465, #8379)
  845. clipping.setState( material, camera, useCache );
  846. }
  847. }
  848. //
  849. let needsProgramChange = false;
  850. if ( material.version === materialProperties.__version ) {
  851. if ( materialProperties.needsLights && ( materialProperties.lightsStateVersion !== lights.state.version ) ) {
  852. needsProgramChange = true;
  853. } else if ( materialProperties.outputEncoding !== encoding ) {
  854. needsProgramChange = true;
  855. } else if ( object.isInstancedMesh && materialProperties.instancing === false ) {
  856. needsProgramChange = true;
  857. } else if ( ! object.isInstancedMesh && materialProperties.instancing === true ) {
  858. needsProgramChange = true;
  859. } else if ( object.isSkinnedMesh && materialProperties.skinning === false ) {
  860. needsProgramChange = true;
  861. } else if ( ! object.isSkinnedMesh && materialProperties.skinning === true ) {
  862. needsProgramChange = true;
  863. } else if ( materialProperties.envMap !== envMap ) {
  864. needsProgramChange = true;
  865. } else if ( material.fog && materialProperties.fog !== fog ) {
  866. needsProgramChange = true;
  867. } else if ( materialProperties.numClippingPlanes !== undefined &&
  868. ( materialProperties.numClippingPlanes !== clipping.numPlanes ||
  869. materialProperties.numIntersection !== clipping.numIntersection ) ) {
  870. needsProgramChange = true;
  871. } else if ( materialProperties.vertexAlphas !== vertexAlphas ) {
  872. needsProgramChange = true;
  873. } else if ( materialProperties.vertexTangents !== vertexTangents ) {
  874. needsProgramChange = true;
  875. } else if ( materialProperties.morphTargets !== morphTargets ) {
  876. needsProgramChange = true;
  877. } else if ( materialProperties.morphNormals !== morphNormals ) {
  878. needsProgramChange = true;
  879. } else if ( capabilities.isWebGL2 === true && materialProperties.morphTargetsCount !== morphTargetsCount ) {
  880. needsProgramChange = true;
  881. }
  882. } else {
  883. needsProgramChange = true;
  884. materialProperties.__version = material.version;
  885. }
  886. //
  887. let program = materialProperties.currentProgram;
  888. if ( needsProgramChange === true ) {
  889. program = getProgram( material, scene, object );
  890. }
  891. let refreshProgram = false;
  892. let refreshMaterial = false;
  893. let refreshLights = false;
  894. const p_uniforms = program.getUniforms(),
  895. m_uniforms = materialProperties.uniforms;
  896. if ( state.useProgram( program.program ) ) {
  897. refreshProgram = true;
  898. refreshMaterial = true;
  899. refreshLights = true;
  900. }
  901. if ( material.id !== _currentMaterialId ) {
  902. _currentMaterialId = material.id;
  903. refreshMaterial = true;
  904. }
  905. if ( refreshProgram || _currentCamera !== camera ) {
  906. p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );
  907. if ( capabilities.logarithmicDepthBuffer ) {
  908. p_uniforms.setValue( _gl, 'logDepthBufFC',
  909. 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
  910. }
  911. if ( _currentCamera !== camera ) {
  912. _currentCamera = camera;
  913. // lighting uniforms depend on the camera so enforce an update
  914. // now, in case this material supports lights - or later, when
  915. // the next material that does gets activated:
  916. refreshMaterial = true; // set to true on material change
  917. refreshLights = true; // remains set until update done
  918. }
  919. // load material specific uniforms
  920. // (shader material also gets them for the sake of genericity)
  921. if ( material.isShaderMaterial ||
  922. material.isMeshPhongMaterial ||
  923. material.isMeshToonMaterial ||
  924. material.isMeshStandardMaterial ||
  925. material.envMap ) {
  926. const uCamPos = p_uniforms.map.cameraPosition;
  927. if ( uCamPos !== undefined ) {
  928. uCamPos.setValue( _gl,
  929. _vector3.setFromMatrixPosition( camera.matrixWorld ) );
  930. }
  931. }
  932. if ( material.isMeshPhongMaterial ||
  933. material.isMeshToonMaterial ||
  934. material.isMeshLambertMaterial ||
  935. material.isMeshBasicMaterial ||
  936. material.isMeshStandardMaterial ||
  937. material.isShaderMaterial ) {
  938. p_uniforms.setValue( _gl, 'isOrthographic', camera.isOrthographicCamera === true );
  939. }
  940. if ( material.isMeshPhongMaterial ||
  941. material.isMeshToonMaterial ||
  942. material.isMeshLambertMaterial ||
  943. material.isMeshBasicMaterial ||
  944. material.isMeshStandardMaterial ||
  945. material.isShaderMaterial ||
  946. material.isShadowMaterial ||
  947. object.isSkinnedMesh ) {
  948. p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );
  949. }
  950. }
  951. // skinning and morph target uniforms must be set even if material didn't change
  952. // auto-setting of texture unit for bone and morph texture must go before other textures
  953. // otherwise textures used for skinning and morphing can take over texture units reserved for other material textures
  954. if ( object.isSkinnedMesh ) {
  955. p_uniforms.setOptional( _gl, object, 'bindMatrix' );
  956. p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );
  957. const skeleton = object.skeleton;
  958. if ( skeleton ) {
  959. if ( capabilities.floatVertexTextures ) {
  960. if ( skeleton.boneTexture === null ) skeleton.computeBoneTexture();
  961. p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture, textures );
  962. p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );
  963. } else {
  964. p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );
  965. }
  966. }
  967. }
  968. if ( !! geometry && ( geometry.morphAttributes.position !== undefined || geometry.morphAttributes.normal !== undefined ) ) {
  969. morphtargets.update( object, geometry, material, program );
  970. }
  971. if ( refreshMaterial || materialProperties.receiveShadow !== object.receiveShadow ) {
  972. materialProperties.receiveShadow = object.receiveShadow;
  973. p_uniforms.setValue( _gl, 'receiveShadow', object.receiveShadow );
  974. }
  975. if ( refreshMaterial ) {
  976. p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );
  977. if ( materialProperties.needsLights ) {
  978. // the current material requires lighting info
  979. // note: all lighting uniforms are always set correctly
  980. // they simply reference the renderer's state for their
  981. // values
  982. //
  983. // use the current material's .needsUpdate flags to set
  984. // the GL state when required
  985. markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );
  986. }
  987. // refresh uniforms common to several materials
  988. if ( fog && material.fog ) {
  989. materials.refreshFogUniforms( m_uniforms, fog );
  990. }
  991. materials.refreshMaterialUniforms( m_uniforms, material, _pixelRatio, _height, _transmissionRenderTarget );
  992. WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
  993. }
  994. if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {
  995. WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, textures );
  996. material.uniformsNeedUpdate = false;
  997. }
  998. if ( material.isSpriteMaterial ) {
  999. p_uniforms.setValue( _gl, 'center', object.center );
  1000. }
  1001. // common matrices
  1002. p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );
  1003. p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );
  1004. p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );
  1005. return program;
  1006. }
  1007. // If uniforms are marked as clean, they don't need to be loaded to the GPU.
  1008. function markUniformsLightsNeedsUpdate( uniforms, value ) {
  1009. uniforms.ambientLightColor.needsUpdate = value;
  1010. uniforms.lightProbe.needsUpdate = value;
  1011. uniforms.directionalLights.needsUpdate = value;
  1012. uniforms.directionalLightShadows.needsUpdate = value;
  1013. uniforms.pointLights.needsUpdate = value;
  1014. uniforms.pointLightShadows.needsUpdate = value;
  1015. uniforms.spotLights.needsUpdate = value;
  1016. uniforms.spotLightShadows.needsUpdate = value;
  1017. uniforms.rectAreaLights.needsUpdate = value;
  1018. uniforms.hemisphereLights.needsUpdate = value;
  1019. }
  1020. function materialNeedsLights( material ) {
  1021. return material.isMeshLambertMaterial || material.isMeshToonMaterial || material.isMeshPhongMaterial ||
  1022. material.isMeshStandardMaterial || material.isShadowMaterial ||
  1023. ( material.isShaderMaterial && material.lights === true );
  1024. }
  1025. this.getActiveCubeFace = function () {
  1026. return _currentActiveCubeFace;
  1027. };
  1028. this.getActiveMipmapLevel = function () {
  1029. return _currentActiveMipmapLevel;
  1030. };
  1031. this.getRenderTarget = function () {
  1032. return _currentRenderTarget;
  1033. };
  1034. this.setRenderTarget = function ( renderTarget, activeCubeFace = 0, activeMipmapLevel = 0 ) {
  1035. _currentRenderTarget = renderTarget;
  1036. _currentActiveCubeFace = activeCubeFace;
  1037. _currentActiveMipmapLevel = activeMipmapLevel;
  1038. if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {
  1039. textures.setupRenderTarget( renderTarget );
  1040. }
  1041. let framebuffer = null;
  1042. let isCube = false;
  1043. let isRenderTarget3D = false;
  1044. if ( renderTarget ) {
  1045. const texture = renderTarget.texture;
  1046. if ( texture.isDataTexture3D || texture.isDataTexture2DArray ) {
  1047. isRenderTarget3D = true;
  1048. }
  1049. const __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;
  1050. if ( renderTarget.isWebGLCubeRenderTarget ) {
  1051. framebuffer = __webglFramebuffer[ activeCubeFace ];
  1052. isCube = true;
  1053. } else if ( renderTarget.isWebGLMultisampleRenderTarget ) {
  1054. framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer;
  1055. } else {
  1056. framebuffer = __webglFramebuffer;
  1057. }
  1058. _currentViewport.copy( renderTarget.viewport );
  1059. _currentScissor.copy( renderTarget.scissor );
  1060. _currentScissorTest = renderTarget.scissorTest;
  1061. } else {
  1062. _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ).floor();
  1063. _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ).floor();
  1064. _currentScissorTest = _scissorTest;
  1065. }
  1066. const framebufferBound = state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
  1067. if ( framebufferBound && capabilities.drawBuffers ) {
  1068. let needsUpdate = false;
  1069. if ( renderTarget ) {
  1070. if ( renderTarget.isWebGLMultipleRenderTargets ) {
  1071. const textures = renderTarget.texture;
  1072. if ( _currentDrawBuffers.length !== textures.length || _currentDrawBuffers[ 0 ] !== _gl.COLOR_ATTACHMENT0 ) {
  1073. for ( let i = 0, il = textures.length; i < il; i ++ ) {
  1074. _currentDrawBuffers[ i ] = _gl.COLOR_ATTACHMENT0 + i;
  1075. }
  1076. _currentDrawBuffers.length = textures.length;
  1077. needsUpdate = true;
  1078. }
  1079. } else {
  1080. if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== _gl.COLOR_ATTACHMENT0 ) {
  1081. _currentDrawBuffers[ 0 ] = _gl.COLOR_ATTACHMENT0;
  1082. _currentDrawBuffers.length = 1;
  1083. needsUpdate = true;
  1084. }
  1085. }
  1086. } else {
  1087. if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== _gl.BACK ) {
  1088. _currentDrawBuffers[ 0 ] = _gl.BACK;
  1089. _currentDrawBuffers.length = 1;
  1090. needsUpdate = true;
  1091. }
  1092. }
  1093. if ( needsUpdate ) {
  1094. if ( capabilities.isWebGL2 ) {
  1095. _gl.drawBuffers( _currentDrawBuffers );
  1096. } else {
  1097. extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( _currentDrawBuffers );
  1098. }
  1099. }
  1100. }
  1101. state.viewport( _currentViewport );
  1102. state.scissor( _currentScissor );
  1103. state.setScissorTest( _currentScissorTest );
  1104. if ( isCube ) {
  1105. const textureProperties = properties.get( renderTarget.texture );
  1106. _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + activeCubeFace, textureProperties.__webglTexture, activeMipmapLevel );
  1107. } else if ( isRenderTarget3D ) {
  1108. const textureProperties = properties.get( renderTarget.texture );
  1109. const layer = activeCubeFace || 0;
  1110. _gl.framebufferTextureLayer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, textureProperties.__webglTexture, activeMipmapLevel || 0, layer );
  1111. }
  1112. _currentMaterialId = - 1; // reset current material to ensure correct uniform bindings
  1113. };
  1114. this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer, activeCubeFaceIndex ) {
  1115. if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {
  1116. console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );
  1117. return;
  1118. }
  1119. let framebuffer = properties.get( renderTarget ).__webglFramebuffer;
  1120. if ( renderTarget.isWebGLCubeRenderTarget && activeCubeFaceIndex !== undefined ) {
  1121. framebuffer = framebuffer[ activeCubeFaceIndex ];
  1122. }
  1123. if ( framebuffer ) {
  1124. state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
  1125. try {
  1126. const texture = renderTarget.texture;
  1127. const textureFormat = texture.format;
  1128. const textureType = texture.type;
  1129. if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {
  1130. console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );
  1131. return;
  1132. }
  1133. const halfFloatSupportedByExt = ( textureType === HalfFloatType ) && ( extensions.has( 'EXT_color_buffer_half_float' ) || ( capabilities.isWebGL2 && extensions.has( 'EXT_color_buffer_float' ) ) );
  1134. if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // Edge and Chrome Mac < 52 (#9513)
  1135. ! ( textureType === FloatType && ( capabilities.isWebGL2 || extensions.has( 'OES_texture_float' ) || extensions.has( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox
  1136. ! halfFloatSupportedByExt ) {
  1137. console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );
  1138. return;
  1139. }
  1140. if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {
  1141. // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)
  1142. if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {
  1143. _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );
  1144. }
  1145. } else {
  1146. console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );
  1147. }
  1148. } finally {
  1149. // restore framebuffer of current render target if necessary
  1150. const framebuffer = ( _currentRenderTarget !== null ) ? properties.get( _currentRenderTarget ).__webglFramebuffer : null;
  1151. state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
  1152. }
  1153. }
  1154. };
  1155. this.copyFramebufferToTexture = function ( position, texture, level = 0 ) {
  1156. const levelScale = Math.pow( 2, - level );
  1157. const width = Math.floor( texture.image.width * levelScale );
  1158. const height = Math.floor( texture.image.height * levelScale );
  1159. let glFormat = utils.convert( texture.format );
  1160. if ( capabilities.isWebGL2 ) {
  1161. // Workaround for https://bugs.chromium.org/p/chromium/issues/detail?id=1120100
  1162. // Not needed in Chrome 93+
  1163. if ( glFormat === _gl.RGB ) glFormat = _gl.RGB8;
  1164. if ( glFormat === _gl.RGBA ) glFormat = _gl.RGBA8;
  1165. }
  1166. textures.setTexture2D( texture, 0 );
  1167. _gl.copyTexImage2D( _gl.TEXTURE_2D, level, glFormat, position.x, position.y, width, height, 0 );
  1168. state.unbindTexture();
  1169. };
  1170. this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level = 0 ) {
  1171. const width = srcTexture.image.width;
  1172. const height = srcTexture.image.height;
  1173. const glFormat = utils.convert( dstTexture.format );
  1174. const glType = utils.convert( dstTexture.type );
  1175. textures.setTexture2D( dstTexture, 0 );
  1176. // As another texture upload may have changed pixelStorei
  1177. // parameters, make sure they are correct for the dstTexture
  1178. _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
  1179. _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
  1180. _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
  1181. if ( srcTexture.isDataTexture ) {
  1182. _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, width, height, glFormat, glType, srcTexture.image.data );
  1183. } else {
  1184. if ( srcTexture.isCompressedTexture ) {
  1185. _gl.compressedTexSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, srcTexture.mipmaps[ 0 ].width, srcTexture.mipmaps[ 0 ].height, glFormat, srcTexture.mipmaps[ 0 ].data );
  1186. } else {
  1187. _gl.texSubImage2D( _gl.TEXTURE_2D, level, position.x, position.y, glFormat, glType, srcTexture.image );
  1188. }
  1189. }
  1190. // Generate mipmaps only when copying level 0
  1191. if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( _gl.TEXTURE_2D );
  1192. state.unbindTexture();
  1193. };
  1194. this.copyTextureToTexture3D = function ( sourceBox, position, srcTexture, dstTexture, level = 0 ) {
  1195. if ( _this.isWebGL1Renderer ) {
  1196. console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: can only be used with WebGL2.' );
  1197. return;
  1198. }
  1199. const width = sourceBox.max.x - sourceBox.min.x + 1;
  1200. const height = sourceBox.max.y - sourceBox.min.y + 1;
  1201. const depth = sourceBox.max.z - sourceBox.min.z + 1;
  1202. const glFormat = utils.convert( dstTexture.format );
  1203. const glType = utils.convert( dstTexture.type );
  1204. let glTarget;
  1205. if ( dstTexture.isDataTexture3D ) {
  1206. textures.setTexture3D( dstTexture, 0 );
  1207. glTarget = _gl.TEXTURE_3D;
  1208. } else if ( dstTexture.isDataTexture2DArray ) {
  1209. textures.setTexture2DArray( dstTexture, 0 );
  1210. glTarget = _gl.TEXTURE_2D_ARRAY;
  1211. } else {
  1212. console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: only supports THREE.DataTexture3D and THREE.DataTexture2DArray.' );
  1213. return;
  1214. }
  1215. _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, dstTexture.flipY );
  1216. _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, dstTexture.premultiplyAlpha );
  1217. _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, dstTexture.unpackAlignment );
  1218. const unpackRowLen = _gl.getParameter( _gl.UNPACK_ROW_LENGTH );
  1219. const unpackImageHeight = _gl.getParameter( _gl.UNPACK_IMAGE_HEIGHT );
  1220. const unpackSkipPixels = _gl.getParameter( _gl.UNPACK_SKIP_PIXELS );
  1221. const unpackSkipRows = _gl.getParameter( _gl.UNPACK_SKIP_ROWS );
  1222. const unpackSkipImages = _gl.getParameter( _gl.UNPACK_SKIP_IMAGES );
  1223. const image = srcTexture.isCompressedTexture ? srcTexture.mipmaps[ 0 ] : srcTexture.image;
  1224. _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, image.width );
  1225. _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, image.height );
  1226. _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, sourceBox.min.x );
  1227. _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, sourceBox.min.y );
  1228. _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, sourceBox.min.z );
  1229. if ( srcTexture.isDataTexture || srcTexture.isDataTexture3D ) {
  1230. _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image.data );
  1231. } else {
  1232. if ( srcTexture.isCompressedTexture ) {
  1233. console.warn( 'THREE.WebGLRenderer.copyTextureToTexture3D: untested support for compressed srcTexture.' );
  1234. _gl.compressedTexSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, image.data );
  1235. } else {
  1236. _gl.texSubImage3D( glTarget, level, position.x, position.y, position.z, width, height, depth, glFormat, glType, image );
  1237. }
  1238. }
  1239. _gl.pixelStorei( _gl.UNPACK_ROW_LENGTH, unpackRowLen );
  1240. _gl.pixelStorei( _gl.UNPACK_IMAGE_HEIGHT, unpackImageHeight );
  1241. _gl.pixelStorei( _gl.UNPACK_SKIP_PIXELS, unpackSkipPixels );
  1242. _gl.pixelStorei( _gl.UNPACK_SKIP_ROWS, unpackSkipRows );
  1243. _gl.pixelStorei( _gl.UNPACK_SKIP_IMAGES, unpackSkipImages );
  1244. // Generate mipmaps only when copying level 0
  1245. if ( level === 0 && dstTexture.generateMipmaps ) _gl.generateMipmap( glTarget );
  1246. state.unbindTexture();
  1247. };
  1248. this.initTexture = function ( texture ) {
  1249. textures.setTexture2D( texture, 0 );
  1250. state.unbindTexture();
  1251. };
  1252. this.resetState = function () {
  1253. _currentActiveCubeFace = 0;
  1254. _currentActiveMipmapLevel = 0;
  1255. _currentRenderTarget = null;
  1256. state.reset();
  1257. bindingStates.reset();
  1258. };
  1259. if ( typeof __THREE_DEVTOOLS__ !== 'undefined' ) {
  1260. __THREE_DEVTOOLS__.dispatchEvent( new CustomEvent( 'observe', { detail: this } ) ); // eslint-disable-line no-undef
  1261. }
  1262. }
  1263. WebGLRenderer.prototype.isWebGLRenderer = true;
  1264. export { WebGLRenderer };