renderer.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /**
  2. * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Viewport } from '../mol-canvas3d/camera/util';
  7. import { Camera } from '../mol-canvas3d/camera';
  8. import Scene from './scene';
  9. import { WebGLContext } from './webgl/context';
  10. import { Mat4, Vec3, Vec4, Vec2 } from '../mol-math/linear-algebra';
  11. import { Renderable } from './renderable';
  12. import { Color } from '../mol-util/color';
  13. import { ValueCell } from '../mol-util';
  14. import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
  15. import { GraphicsRenderVariant } from './webgl/render-item';
  16. import { ParamDefinition as PD } from '../mol-util/param-definition';
  17. import { deepClone } from '../mol-util/object';
  18. export interface RendererStats {
  19. programCount: number
  20. shaderCount: number
  21. bufferCount: number
  22. framebufferCount: number
  23. renderbufferCount: number
  24. textureCount: number
  25. vaoCount: number
  26. drawCount: number
  27. instanceCount: number
  28. instancedDrawCount: number
  29. }
  30. interface Renderer {
  31. readonly stats: RendererStats
  32. readonly props: Readonly<RendererProps>
  33. clear: () => void
  34. render: (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => void
  35. setProps: (props: Partial<RendererProps>) => void
  36. setViewport: (x: number, y: number, width: number, height: number) => void
  37. dispose: () => void
  38. }
  39. export const RendererParams = {
  40. backgroundColor: PD.Color(Color(0x000000), { description: 'Background color of the 3D canvas' }),
  41. transparentBackground: PD.Boolean(false, { description: 'Background opacity of the 3D canvas' }),
  42. pickingAlphaThreshold: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }, { description: 'The minimum opacity value needed for an object to be pickable.' }),
  43. interiorDarkening: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
  44. lightIntensity: PD.Numeric(0.6, { min: 0.0, max: 1.0, step: 0.01 }),
  45. ambientIntensity: PD.Numeric(0.4, { min: 0.0, max: 1.0, step: 0.01 }),
  46. metalness: PD.Numeric(0.0, { min: 0.0, max: 1.0, step: 0.01 }),
  47. roughness: PD.Numeric(1.0, { min: 0.0, max: 1.0, step: 0.01 }),
  48. reflectivity: PD.Numeric(0.5, { min: 0.0, max: 1.0, step: 0.01 }),
  49. }
  50. export type RendererProps = PD.Values<typeof RendererParams>
  51. namespace Renderer {
  52. export function create(ctx: WebGLContext, props: Partial<RendererProps> = {}): Renderer {
  53. const { gl, state, stats } = ctx
  54. const p = deepClone({ ...PD.getDefaultValues(RendererParams), ...props })
  55. const viewport = Viewport()
  56. const bgColor = Color.toVec3Normalized(Vec3(), p.backgroundColor)
  57. const view = Mat4()
  58. const invView = Mat4()
  59. const modelView = Mat4()
  60. const invModelView = Mat4()
  61. const invProjection = Mat4()
  62. const modelViewProjection = Mat4()
  63. const invModelViewProjection = Mat4()
  64. const viewOffset = Vec2()
  65. const globalUniforms: GlobalUniformValues = {
  66. uModel: ValueCell.create(Mat4.identity()),
  67. uView: ValueCell.create(view),
  68. uInvView: ValueCell.create(invView),
  69. uModelView: ValueCell.create(modelView),
  70. uInvModelView: ValueCell.create(invModelView),
  71. uInvProjection: ValueCell.create(invProjection),
  72. uProjection: ValueCell.create(Mat4()),
  73. uModelViewProjection: ValueCell.create(modelViewProjection),
  74. uInvModelViewProjection: ValueCell.create(invModelViewProjection),
  75. uIsOrtho: ValueCell.create(1),
  76. uViewOffset: ValueCell.create(viewOffset),
  77. uPixelRatio: ValueCell.create(ctx.pixelRatio),
  78. uViewportHeight: ValueCell.create(viewport.height),
  79. uViewport: ValueCell.create(Viewport.toVec4(Vec4(), viewport)),
  80. uLightIntensity: ValueCell.create(p.lightIntensity),
  81. uAmbientIntensity: ValueCell.create(p.ambientIntensity),
  82. uMetalness: ValueCell.create(p.metalness),
  83. uRoughness: ValueCell.create(p.roughness),
  84. uReflectivity: ValueCell.create(p.reflectivity),
  85. uCameraPosition: ValueCell.create(Vec3()),
  86. uNear: ValueCell.create(1),
  87. uFar: ValueCell.create(10000),
  88. uFogNear: ValueCell.create(1),
  89. uFogFar: ValueCell.create(10000),
  90. uFogColor: ValueCell.create(bgColor),
  91. uTransparentBackground: ValueCell.create(p.transparentBackground ? 1 : 0),
  92. uPickingAlphaThreshold: ValueCell.create(p.pickingAlphaThreshold),
  93. uInteriorDarkening: ValueCell.create(p.interiorDarkening),
  94. }
  95. const globalUniformList = Object.entries(globalUniforms)
  96. let globalUniformsNeedUpdate = true
  97. const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: GraphicsRenderVariant) => {
  98. const program = r.getProgram(variant)
  99. if (r.state.visible) {
  100. if (state.currentProgramId !== program.id) {
  101. // console.log('new program')
  102. globalUniformsNeedUpdate = true
  103. program.use()
  104. }
  105. if (globalUniformsNeedUpdate) {
  106. // console.log('globalUniformsNeedUpdate')
  107. program.setUniforms(globalUniformList)
  108. globalUniformsNeedUpdate = false
  109. }
  110. if (r.values.dDoubleSided) {
  111. if (r.values.dDoubleSided.ref.value) {
  112. state.disable(gl.CULL_FACE)
  113. } else {
  114. state.enable(gl.CULL_FACE)
  115. }
  116. } else {
  117. // webgl default
  118. state.disable(gl.CULL_FACE)
  119. }
  120. if (r.values.dFlipSided) {
  121. if (r.values.dFlipSided.ref.value) {
  122. state.frontFace(gl.CW)
  123. state.cullFace(gl.FRONT)
  124. } else {
  125. state.frontFace(gl.CCW)
  126. state.cullFace(gl.BACK)
  127. }
  128. } else {
  129. // webgl default
  130. state.frontFace(gl.CCW)
  131. state.cullFace(gl.BACK)
  132. }
  133. r.render(variant)
  134. }
  135. }
  136. const render = (scene: Scene, camera: Camera, variant: GraphicsRenderVariant, clear: boolean) => {
  137. ValueCell.update(globalUniforms.uModel, scene.view)
  138. ValueCell.update(globalUniforms.uView, camera.view)
  139. ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
  140. ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, scene.view, camera.view))
  141. ValueCell.update(globalUniforms.uInvModelView, Mat4.invert(invModelView, modelView))
  142. ValueCell.update(globalUniforms.uProjection, camera.projection)
  143. ValueCell.update(globalUniforms.uInvProjection, Mat4.invert(invProjection, camera.projection))
  144. ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection))
  145. ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection))
  146. ValueCell.update(globalUniforms.uIsOrtho, camera.state.mode === 'orthographic' ? 1 : 0)
  147. ValueCell.update(globalUniforms.uViewOffset, camera.viewOffset.enabled ? Vec2.set(viewOffset, camera.viewOffset.offsetX * 16, camera.viewOffset.offsetY * 16) : Vec2.set(viewOffset, 0, 0))
  148. ValueCell.update(globalUniforms.uCameraPosition, camera.state.position)
  149. ValueCell.update(globalUniforms.uFar, camera.far)
  150. ValueCell.update(globalUniforms.uNear, camera.near)
  151. ValueCell.update(globalUniforms.uFogFar, camera.fogFar)
  152. ValueCell.update(globalUniforms.uFogNear, camera.fogNear)
  153. globalUniformsNeedUpdate = true
  154. state.currentRenderItemId = -1
  155. const { renderables } = scene
  156. state.disable(gl.SCISSOR_TEST)
  157. state.disable(gl.BLEND)
  158. state.depthMask(true)
  159. state.colorMask(true, true, true, true)
  160. state.enable(gl.DEPTH_TEST)
  161. if (clear) {
  162. if (variant === 'color') {
  163. state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
  164. } else {
  165. state.clearColor(1, 1, 1, 1)
  166. }
  167. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  168. }
  169. if (variant === 'color') {
  170. for (let i = 0, il = renderables.length; i < il; ++i) {
  171. const r = renderables[i]
  172. if (r.state.opaque) renderObject(r, variant)
  173. }
  174. state.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE)
  175. state.enable(gl.BLEND)
  176. for (let i = 0, il = renderables.length; i < il; ++i) {
  177. const r = renderables[i]
  178. if (!r.state.opaque) {
  179. state.depthMask(false)
  180. renderObject(r, variant)
  181. }
  182. }
  183. } else { // picking & depth
  184. for (let i = 0, il = renderables.length; i < il; ++i) {
  185. renderObject(renderables[i], variant)
  186. }
  187. }
  188. gl.finish()
  189. }
  190. return {
  191. clear: () => {
  192. state.depthMask(true)
  193. state.colorMask(true, true, true, true)
  194. state.clearColor(bgColor[0], bgColor[1], bgColor[2], p.transparentBackground ? 0 : 1)
  195. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  196. },
  197. render,
  198. setProps: (props: Partial<RendererProps>) => {
  199. if (props.pickingAlphaThreshold !== undefined && props.pickingAlphaThreshold !== p.pickingAlphaThreshold) {
  200. p.pickingAlphaThreshold = props.pickingAlphaThreshold
  201. ValueCell.update(globalUniforms.uPickingAlphaThreshold, p.pickingAlphaThreshold)
  202. }
  203. if (props.interiorDarkening !== undefined && props.interiorDarkening !== p.interiorDarkening) {
  204. p.interiorDarkening = props.interiorDarkening
  205. ValueCell.update(globalUniforms.uInteriorDarkening, p.interiorDarkening)
  206. }
  207. if (props.backgroundColor !== undefined && props.backgroundColor !== p.backgroundColor) {
  208. p.backgroundColor = props.backgroundColor
  209. Color.toVec3Normalized(bgColor, p.backgroundColor)
  210. ValueCell.update(globalUniforms.uFogColor, Vec3.copy(globalUniforms.uFogColor.ref.value, bgColor))
  211. }
  212. if (props.transparentBackground !== undefined && props.transparentBackground !== p.transparentBackground) {
  213. p.transparentBackground = props.transparentBackground
  214. ValueCell.update(globalUniforms.uTransparentBackground, p.transparentBackground ? 1 : 0)
  215. }
  216. if (props.lightIntensity !== undefined && props.lightIntensity !== p.lightIntensity) {
  217. p.lightIntensity = props.lightIntensity
  218. ValueCell.update(globalUniforms.uLightIntensity, p.lightIntensity)
  219. }
  220. if (props.ambientIntensity !== undefined && props.ambientIntensity !== p.ambientIntensity) {
  221. p.ambientIntensity = props.ambientIntensity
  222. ValueCell.update(globalUniforms.uAmbientIntensity, p.ambientIntensity)
  223. }
  224. if (props.metalness !== undefined && props.metalness !== p.metalness) {
  225. p.metalness = props.metalness
  226. ValueCell.update(globalUniforms.uMetalness, p.metalness)
  227. }
  228. if (props.roughness !== undefined && props.roughness !== p.roughness) {
  229. p.roughness = props.roughness
  230. ValueCell.update(globalUniforms.uRoughness, p.roughness)
  231. }
  232. if (props.reflectivity !== undefined && props.reflectivity !== p.reflectivity) {
  233. p.reflectivity = props.reflectivity
  234. ValueCell.update(globalUniforms.uReflectivity, p.reflectivity)
  235. }
  236. },
  237. setViewport: (x: number, y: number, width: number, height: number) => {
  238. gl.viewport(x, y, width, height)
  239. if (x !== viewport.x || y !== viewport.y || width !== viewport.width || height !== viewport.height) {
  240. Viewport.set(viewport, x, y, width, height)
  241. ValueCell.update(globalUniforms.uViewportHeight, height)
  242. ValueCell.update(globalUniforms.uViewport, Vec4.set(globalUniforms.uViewport.ref.value, x, y, width, height))
  243. }
  244. },
  245. get props() {
  246. return p
  247. },
  248. get stats(): RendererStats {
  249. return {
  250. programCount: ctx.programCache.count,
  251. shaderCount: ctx.shaderCache.count,
  252. bufferCount: stats.bufferCount,
  253. framebufferCount: stats.framebufferCount,
  254. renderbufferCount: stats.renderbufferCount,
  255. textureCount: stats.textureCount,
  256. vaoCount: stats.vaoCount,
  257. drawCount: stats.drawCount,
  258. instanceCount: stats.instanceCount,
  259. instancedDrawCount: stats.instancedDrawCount,
  260. }
  261. },
  262. dispose: () => {
  263. // TODO
  264. }
  265. }
  266. }
  267. }
  268. export default Renderer