UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

137 lines (134 loc) 5.97 kB
import { Debug, DebugHelper } from '../../core/debug.js'; import { Vec4 } from '../../core/math/vec4.js'; import { DynamicBindGroup, BindGroup } from '../../platform/graphics/bind-group.js'; import { BINDGROUP_VIEW, BINDGROUP_MESH, BINDGROUP_MESH_UB, PRIMITIVE_TRIANGLES } from '../../platform/graphics/constants.js'; import { DebugGraphics } from '../../platform/graphics/debug-graphics.js'; import { ShaderProcessorOptions } from '../../platform/graphics/shader-processor-options.js'; import { UniformBuffer } from '../../platform/graphics/uniform-buffer.js'; import { ShaderUtils } from '../shader-lib/shader-utils.js'; /** * @import { Shader } from '../../platform/graphics/shader.js' */ const _quadPrimitive = { type: PRIMITIVE_TRIANGLES, base: 0, count: 6, indexed: true }; const _tempViewport = new Vec4(); const _tempScissor = new Vec4(); const _dynamicBindGroup = new DynamicBindGroup(); /** * An object that renders a quad using a {@link Shader}. * * Note: QuadRender does not modify render states. Before calling {@link QuadRender#render}, * you should set up the required states using {@link GraphicsDevice#setDrawStates}, or the * individual setters ({@link GraphicsDevice#setBlendState}, {@link GraphicsDevice#setCullMode}, * {@link GraphicsDevice#setFrontFace}, {@link GraphicsDevice#setDepthState}, * {@link GraphicsDevice#setStencilState}). Otherwise previously set states will be used. * * Example: * * ```javascript * const shader = pc.ShaderUtils.createShader(app.graphicsDevice, { * uniqueName: 'MyShader', * attributes: { aPosition: SEMANTIC_POSITION }, * vertexGLSL: '// vertex shader code', * fragmentGLSL: '// fragment shader code' * }); * const quad = new QuadRender(shader); * * // Set up render states before rendering (defaults are suitable for full-screen quads) * app.graphicsDevice.setDrawStates(); * * quad.render(); * quad.destroy(); * ``` * * @category Graphics */ class QuadRender { /** * Destroys the resources associated with this instance. */ destroy() { this.uniformBuffer?.destroy(); this.uniformBuffer = null; this.bindGroup?.destroy(); this.bindGroup = null; } /** * Renders the quad. If the viewport is provided, the original viewport and scissor is restored * after the rendering. * * @param {Vec4} [viewport] - The viewport rectangle of the quad, in pixels. The viewport is * not changed if not provided. * @param {Vec4} [scissor] - The scissor rectangle of the quad, in pixels. Used only if the * viewport is provided. * @param {number} [numInstances] - Number of instances to draw. When provided, renders * multiple quads using instanced drawing. Each instance can use the instance index * (`gl_InstanceID` in GLSL, `pcInstanceIndex` in WGSL) to fetch per-quad data from * a texture or buffer, allowing each quad to be parameterized independently. */ render(viewport, scissor, numInstances) { const device = this.shader.device; DebugGraphics.pushGpuMarker(device, 'QuadRender'); // only modify viewport or scissor if viewport supplied if (viewport) { // backup current settings _tempViewport.set(device.vx, device.vy, device.vw, device.vh); _tempScissor.set(device.sx, device.sy, device.sw, device.sh); // set new values scissor = scissor ?? viewport; device.setViewport(viewport.x, viewport.y, viewport.z, viewport.w); device.setScissor(scissor.x, scissor.y, scissor.z, scissor.w); } device.setVertexBuffer(device.quadVertexBuffer); const shader = this.shader; device.setShader(shader); if (device.supportsUniformBuffers) { // not using view bind group device.setBindGroup(BINDGROUP_VIEW, device.emptyBindGroup); // mesh bind group const bindGroup = this.bindGroup; bindGroup.update(); device.setBindGroup(BINDGROUP_MESH, bindGroup); // dynamic uniform buffer bind group const uniformBuffer = this.uniformBuffer; if (uniformBuffer) { uniformBuffer.update(_dynamicBindGroup); device.setBindGroup(BINDGROUP_MESH_UB, _dynamicBindGroup.bindGroup, _dynamicBindGroup.offsets); } else { device.setBindGroup(BINDGROUP_MESH_UB, device.emptyBindGroup); } } device.draw(_quadPrimitive, device.quadIndexBuffer, numInstances); // restore if changed if (viewport) { device.setViewport(_tempViewport.x, _tempViewport.y, _tempViewport.z, _tempViewport.w); device.setScissor(_tempScissor.x, _tempScissor.y, _tempScissor.z, _tempScissor.w); } DebugGraphics.popGpuMarker(device); } /** * Create a new QuadRender instance. * * @param {Shader} shader - The shader to be used to render the quad. */ constructor(shader){ const device = shader.device; this.shader = shader; Debug.assert(shader); if (device.supportsUniformBuffers) { // add uniform buffer support to shader const processingOptions = new ShaderProcessorOptions(); this.shader = ShaderUtils.processShader(shader, processingOptions); // uniform buffer const ubFormat = this.shader.meshUniformBufferFormat; if (ubFormat) { this.uniformBuffer = new UniformBuffer(device, ubFormat, false); } // bind group const bindGroupFormat = this.shader.meshBindGroupFormat; Debug.assert(bindGroupFormat); this.bindGroup = new BindGroup(device, bindGroupFormat); DebugHelper.setName(this.bindGroup, `QuadRender-MeshBindGroup_${this.bindGroup.id}`); } } } export { QuadRender };