WebGLRenderer.js 56 KB

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