renderer.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. // import { Vec3, Mat4 } from 'mol-math/linear-algebra'
  7. import { Viewport } from 'mol-canvas3d/camera/util';
  8. import { Camera } from 'mol-canvas3d/camera';
  9. import Scene from './scene';
  10. import { WebGLContext, createImageData } from './webgl/context';
  11. import { Mat4, Vec3, Vec4 } from 'mol-math/linear-algebra';
  12. import { Renderable } from './renderable';
  13. import { Color } from 'mol-util/color';
  14. import { ValueCell } from 'mol-util';
  15. import { RenderableValues, GlobalUniformValues, BaseValues } from './renderable/schema';
  16. import { RenderVariant } from './webgl/render-item';
  17. export interface RendererStats {
  18. programCount: number
  19. shaderCount: number
  20. bufferCount: number
  21. framebufferCount: number
  22. renderbufferCount: number
  23. textureCount: number
  24. vaoCount: number
  25. drawCount: number
  26. instanceCount: number
  27. instancedDrawCount: number
  28. }
  29. interface Renderer {
  30. readonly stats: RendererStats
  31. readonly props: RendererProps
  32. clear: () => void
  33. render: (scene: Scene, variant: RenderVariant) => void
  34. setViewport: (x: number, y: number, width: number, height: number) => void
  35. setClearColor: (color: Color) => void
  36. setPickingAlphaThreshold: (value: number) => void
  37. getImageData: () => ImageData
  38. dispose: () => void
  39. }
  40. export const DefaultRendererProps = {
  41. clearColor: Color(0x000000),
  42. viewport: Viewport.create(0, 0, 0, 0),
  43. pickingAlphaThreshold: 0.5,
  44. }
  45. export type RendererProps = typeof DefaultRendererProps
  46. namespace Renderer {
  47. export function create(ctx: WebGLContext, camera: Camera, props: Partial<RendererProps> = {}): Renderer {
  48. const { gl } = ctx
  49. let { clearColor, viewport: _viewport, pickingAlphaThreshold } = { ...DefaultRendererProps, ...props }
  50. const viewport = Viewport.clone(_viewport)
  51. const viewportVec4 = Viewport.toVec4(Vec4.zero(), viewport)
  52. // const lightPosition = Vec3.create(0, 0, -100)
  53. const lightColor = Vec3.create(1.0, 1.0, 1.0)
  54. const lightAmbient = Vec3.create(0.5, 0.5, 0.5)
  55. const fogColor = Vec3.create(0.0, 0.0, 0.0)
  56. function setClearColor(color: Color) {
  57. clearColor = color
  58. const [ r, g, b ] = Color.toRgbNormalized(color)
  59. gl.clearColor(r, g, b, 1.0)
  60. Vec3.set(fogColor, r, g, b)
  61. }
  62. setClearColor(clearColor)
  63. const view = Mat4.clone(camera.view)
  64. const invView = Mat4.invert(Mat4.identity(), view)
  65. const modelView = Mat4.clone(camera.view)
  66. const invModelView = Mat4.invert(Mat4.identity(), modelView)
  67. const invProjection = Mat4.invert(Mat4.identity(), camera.projection)
  68. const modelViewProjection = Mat4.mul(Mat4.identity(), modelView, camera.projection)
  69. const invModelViewProjection = Mat4.invert(Mat4.identity(), modelViewProjection)
  70. const globalUniforms: GlobalUniformValues = {
  71. uModel: ValueCell.create(Mat4.identity()),
  72. uView: ValueCell.create(camera.view),
  73. uInvView: ValueCell.create(invView),
  74. uModelView: ValueCell.create(modelView),
  75. uInvModelView: ValueCell.create(invModelView),
  76. uInvProjection: ValueCell.create(invProjection),
  77. uProjection: ValueCell.create(Mat4.clone(camera.projection)),
  78. uModelViewProjection: ValueCell.create(modelViewProjection),
  79. uInvModelViewProjection: ValueCell.create(invModelViewProjection),
  80. uPixelRatio: ValueCell.create(ctx.pixelRatio),
  81. uViewportHeight: ValueCell.create(viewport.height),
  82. uViewport: ValueCell.create(viewportVec4),
  83. uLightColor: ValueCell.create(lightColor),
  84. uLightAmbient: ValueCell.create(lightAmbient),
  85. uCameraPosition: ValueCell.create(Vec3.clone(camera.state.position)),
  86. uFogNear: ValueCell.create(camera.state.fogNear),
  87. uFogFar: ValueCell.create(camera.state.fogFar),
  88. uFogColor: ValueCell.create(fogColor),
  89. uPickingAlphaThreshold: ValueCell.create(pickingAlphaThreshold),
  90. }
  91. let globalUniformsNeedUpdate = true
  92. const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: RenderVariant) => {
  93. const program = r.getProgram(variant)
  94. if (r.state.visible) {
  95. if (ctx.currentProgramId !== program.id) {
  96. globalUniformsNeedUpdate = true
  97. }
  98. program.use()
  99. if (globalUniformsNeedUpdate) {
  100. program.setUniforms(globalUniforms)
  101. globalUniformsNeedUpdate = false
  102. }
  103. if (r.values.dDoubleSided) {
  104. if (r.values.dDoubleSided.ref.value) {
  105. gl.disable(gl.CULL_FACE)
  106. } else {
  107. gl.enable(gl.CULL_FACE)
  108. }
  109. } else {
  110. // webgl default
  111. gl.disable(gl.CULL_FACE)
  112. }
  113. if (r.values.dFlipSided) {
  114. if (r.values.dFlipSided.ref.value) {
  115. gl.frontFace(gl.CW)
  116. gl.cullFace(gl.FRONT)
  117. } else {
  118. gl.frontFace(gl.CCW)
  119. gl.cullFace(gl.BACK)
  120. }
  121. } else {
  122. // webgl default
  123. gl.frontFace(gl.CCW)
  124. gl.cullFace(gl.BACK)
  125. }
  126. r.render(variant)
  127. }
  128. }
  129. const render = (scene: Scene, variant: RenderVariant) => {
  130. ValueCell.update(globalUniforms.uModel, scene.view)
  131. ValueCell.update(globalUniforms.uView, camera.view)
  132. ValueCell.update(globalUniforms.uInvView, Mat4.invert(invView, camera.view))
  133. ValueCell.update(globalUniforms.uModelView, Mat4.mul(modelView, scene.view, camera.view))
  134. ValueCell.update(globalUniforms.uInvModelView, Mat4.invert(invModelView, modelView))
  135. ValueCell.update(globalUniforms.uProjection, camera.projection)
  136. ValueCell.update(globalUniforms.uInvProjection, Mat4.invert(invProjection, camera.projection))
  137. ValueCell.update(globalUniforms.uModelViewProjection, Mat4.mul(modelViewProjection, modelView, camera.projection))
  138. ValueCell.update(globalUniforms.uInvModelViewProjection, Mat4.invert(invModelViewProjection, modelViewProjection))
  139. ValueCell.update(globalUniforms.uCameraPosition, camera.state.position)
  140. ValueCell.update(globalUniforms.uFogFar, camera.state.fogFar)
  141. ValueCell.update(globalUniforms.uFogNear, camera.state.fogNear)
  142. globalUniformsNeedUpdate = true
  143. const { renderables } = scene
  144. if (variant === 'draw') {
  145. gl.disable(gl.BLEND)
  146. gl.enable(gl.DEPTH_TEST)
  147. gl.depthMask(true)
  148. for (let i = 0, il = renderables.length; i < il; ++i) {
  149. const r = renderables[i]
  150. if (r.state.opaque) renderObject(r, variant)
  151. }
  152. gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
  153. gl.enable(gl.BLEND)
  154. for (let i = 0, il = renderables.length; i < il; ++i) {
  155. const r = renderables[i]
  156. gl.depthMask(r.values.uAlpha.ref.value === 1.0)
  157. if (!r.state.opaque) renderObject(r, variant)
  158. }
  159. } else {
  160. // picking
  161. gl.disable(gl.BLEND)
  162. gl.enable(gl.DEPTH_TEST)
  163. gl.depthMask(true)
  164. for (let i = 0, il = renderables.length; i < il; ++i) {
  165. renderObject(renderables[i], variant)
  166. }
  167. }
  168. gl.finish()
  169. }
  170. return {
  171. clear: () => {
  172. gl.depthMask(true)
  173. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  174. },
  175. render,
  176. setClearColor,
  177. setPickingAlphaThreshold: (value: number) => {
  178. pickingAlphaThreshold = value
  179. ValueCell.update(globalUniforms.uPickingAlphaThreshold, pickingAlphaThreshold)
  180. },
  181. setViewport: (x: number, y: number, width: number, height: number) => {
  182. Viewport.set(viewport, x, y, width, height)
  183. gl.viewport(x, y, width, height)
  184. ValueCell.update(globalUniforms.uViewportHeight, height)
  185. ValueCell.update(globalUniforms.uViewport, Vec4.set(viewportVec4, x, y, width, height))
  186. },
  187. getImageData: () => {
  188. const { width, height } = viewport
  189. const buffer = new Uint8Array(width * height * 4)
  190. ctx.unbindFramebuffer()
  191. ctx.readPixels(0, 0, width, height, buffer)
  192. return createImageData(buffer, width, height)
  193. },
  194. get props() {
  195. return {
  196. clearColor,
  197. pickingAlphaThreshold,
  198. viewport
  199. }
  200. },
  201. get stats(): RendererStats {
  202. return {
  203. programCount: ctx.programCache.count,
  204. shaderCount: ctx.shaderCache.count,
  205. bufferCount: ctx.bufferCount,
  206. framebufferCount: ctx.framebufferCount,
  207. renderbufferCount: ctx.renderbufferCount,
  208. textureCount: ctx.textureCount,
  209. vaoCount: ctx.vaoCount,
  210. drawCount: ctx.drawCount,
  211. instanceCount: ctx.instanceCount,
  212. instancedDrawCount: ctx.instancedDrawCount,
  213. }
  214. },
  215. dispose: () => {
  216. // TODO
  217. }
  218. }
  219. }
  220. }
  221. export default Renderer