Skip to content

Commit

Permalink
Debug shader output functionality (#5261)
Browse files Browse the repository at this point in the history
* Debug shader output functionality

* updated docs

* added comma

* var -> const

* renamed constants

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
  • Loading branch information
mvaligursky and Martin Valigursky authored Apr 24, 2023
1 parent 1af7fae commit a7aebb0
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 22 deletions.
36 changes: 35 additions & 1 deletion examples/src/examples/graphics/multi-view.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,36 @@
import React from 'react';
import * as pc from '../../../../';
import { BindingTwoWay, LabelGroup, Panel, SelectInput } from '@playcanvas/pcui/react';
import { Observer } from '@playcanvas/observer';

class MultiViewExample {
static CATEGORY = 'Graphics';
static NAME = 'Multi View';
static WEBGPU_ENABLED = true;

example(canvas: HTMLCanvasElement, deviceType: string): void {
controls(data: Observer) {
return <>
<Panel headerText='Debug Shader Rendering'>
{<LabelGroup text='Mode'>
<SelectInput binding={new BindingTwoWay()} link={{ observer: data, path: 'settings.shaderPassName' }} type="string" options={[
{ v: pc.SHADERPASS_FORWARD, t: 'Normal' },
{ v: pc.SHADERPASS_ALBEDO, t: 'Albedo' },
{ v: pc.SHADERPASS_OPACITY, t: 'Opacity' },
{ v: pc.SHADERPASS_WORLDNORMAL, t: 'World Normal' },
{ v: pc.SHADERPASS_SPECULARITY, t: 'Specularity' },
{ v: pc.SHADERPASS_GLOSS, t: 'Gloss' },
{ v: pc.SHADERPASS_METALNESS, t: 'Metalness' },
{ v: pc.SHADERPASS_AO, t: 'AO' },
{ v: pc.SHADERPASS_EMISSION, t: 'Emission' },
{ v: pc.SHADERPASS_LIGHTING, t: 'Lighting' },
{ v: pc.SHADERPASS_UV0, t: 'UV0' }
]} />
</LabelGroup>}
</Panel>
</>;
}

example(canvas: HTMLCanvasElement, deviceType: string, data: any): void {

// set up and load draco module, as the glb we load is draco compressed
pc.WasmModule.setConfig('DracoDecoderModule', {
Expand Down Expand Up @@ -67,6 +92,10 @@ class MultiViewExample {

app.start();

data.set('settings', {
shaderPassName: pc.SHADERPASS_FORWARD
});

// get the instance of the chess board and set up with render component
const boardEntity = assets.board.resource.instantiateRenderEntity({
castShadows: true,
Expand Down Expand Up @@ -137,6 +166,11 @@ class MultiViewExample {
app.scene.toneMapping = pc.TONEMAP_ACES;
app.scene.skyboxMip = 1;

// handle HUD changes - update the debug mode on the top camera
data.on('*:set', (path: string, value: any) => {
cameraTop.camera.setShaderPass(value);
});

// update function called once per frame
let time = 0;
app.on("update", function (dt) {
Expand Down
70 changes: 68 additions & 2 deletions src/framework/components/camera/component.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Debug } from '../../../core/debug.js';

import { ASPECT_AUTO, LAYERID_UI, LAYERID_DEPTH } from '../../../scene/constants.js';
import { Camera } from '../../../scene/camera.js';
import { ShaderPass } from '../../../scene/shader-pass.js';

import { Component } from '../component.js';

import { PostEffectQueue } from './post-effect-queue.js';
import { Debug } from '../../../core/debug.js';

/**
* Callback used by {@link CameraComponent#calculateTransform} and {@link CameraComponent#calculateProjection}.
Expand Down Expand Up @@ -93,6 +95,9 @@ class CameraComponent extends Component {
*/
_disablePostEffectsLayer = LAYERID_UI;

/** @private */
_camera = new Camera();

/**
* Create a new CameraComponent instance.
*
Expand All @@ -104,13 +109,74 @@ class CameraComponent extends Component {
constructor(system, entity) {
super(system, entity);

this._camera = new Camera();
this._camera.node = entity;

// postprocessing management
this._postEffects = new PostEffectQueue(system.app, this);
}

/**
* Sets the name of the shader pass the camera will use when rendering.
*
* @param {string} name - The name of the shader pass. Defaults to undefined, which is
* equivalent to {@link SHADERPASS_FORWARD}. Can be:
*
* - {@link SHADERPASS_FORWARD}
* - {@link SHADERPASS_ALBEDO}
* - {@link SHADERPASS_OPACITY}
* - {@link SHADERPASS_WORLDNORMAL}
* - {@link SHADERPASS_SPECULARITY}
* - {@link SHADERPASS_GLOSS}
* - {@link SHADERPASS_METALNESS}
* - {@link SHADERPASS_AO}
* - {@link SHADERPASS_EMISSION}
* - {@link SHADERPASS_LIGHTING}
* - {@link SHADERPASS_UV0}
*
* Additionally, a new name can be specified, which creates a new shader pass with the given
* name. The name provided can only use alphanumeric characters and underscores. When a shader
* is compiled for the new pass, a define is added to the shader. For example, if the name is
* 'custom_rendering', the define 'CUSTOM_RENDERING_PASS' is added to the shader, allowing the
* shader code to conditionally execute code only when that shader pass is active.
*
* Another instance where this approach may prove useful is when a camera needs to render a more
* cost-effective version of shaders, such as when creating a reflection texture. To accomplish
* this, a callback on the material that triggers during shader compilation can be used. This
* callback can modify the shader generation options specifically for this shader pass.
*
* ```javascript
* const shaderPassId = camera.setShaderPass('custom_rendering');
*
* material.onUpdateShader = function (options) {
* if (options.pass === shaderPassId) {
* options.litOptions.normalMapEnabled = false;
* options.litOptions.useSpecular = false;
* }
* return options;
* };
* ```
*
* @returns {number} The id of the shader pass.
*/
setShaderPass(name) {
const shaderPass = ShaderPass.get(this.system.app.graphicsDevice);
const shaderPassInfo = name ? shaderPass.allocate(name, {
isForward: true
}) : null;
this._camera.shaderPassInfo = shaderPassInfo;

return shaderPassInfo.index;
}

/**
* Shader pass name.
*
* @returns {string} The name of the shader pass, or undefined if no shader pass is set.
*/
getShaderPass() {
return this._camera.shaderPassInfo?.name;
}

/**
* Set camera aperture in f-stops, the default value is 16.0. Higher value means less exposure.
*
Expand Down
7 changes: 7 additions & 0 deletions src/scene/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const _frustumPoints = [new Vec3(), new Vec3(), new Vec3(), new Vec3(), new Vec3
* @ignore
*/
class Camera {
/**
* @type {import('./shader-pass.js').ShaderPassInfo|null}
*/
shaderPassInfo;

constructor() {
this._aspectRatio = 16 / 9;
this._aspectRatioMode = ASPECT_AUTO;
Expand Down Expand Up @@ -419,6 +424,8 @@ class Camera {
this.shutter = other.shutter;
this.sensitivity = other.sensitivity;

this.shaderPassInfo = other.shaderPassInfo;

this._projMatDirty = true;

return this;
Expand Down
63 changes: 56 additions & 7 deletions src/scene/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -658,28 +658,77 @@ export const SHADER_SHADOW = 4;
*
* @type {string}
*/
export const SHADERTYPE_FORWARD = 'forward';
export const SHADERPASS_FORWARD = 'forward';

/**
* Shader that performs depth rendering.
* Shader used for debug rendering of albedo.
*
* @type {string}
*/
export const SHADERTYPE_DEPTH = 'depth';
export const SHADERPASS_ALBEDO = 'debug_albedo';

/**
* Shader used for picking.
* Shader used for debug rendering of world normal.
*
* @type {string}
*/
export const SHADERTYPE_PICK = 'pick';
export const SHADERPASS_WORLDNORMAL = 'debug_world_normal';

/**
* Shader used for rendering shadow textures.
* Shader used for debug rendering of opacity.
*
* @type {string}
*/
export const SHADERTYPE_SHADOW = 'shadow';
export const SHADERPASS_OPACITY = 'debug_opacity';

/**
* Shader used for debug rendering of specularity.
*
* @type {string}
*/
export const SHADERPASS_SPECULARITY = 'debug_specularity';

/**
* Shader used for debug rendering of gloss.
*
* @type {string}
*/
export const SHADERPASS_GLOSS = 'debug_gloss';

/**
* Shader used for debug rendering of metalness.
*
* @type {string}
*/
export const SHADERPASS_METALNESS = 'debug_metalness';

/**
* Shader used for debug rendering of ao.
*
* @type {string}
*/
export const SHADERPASS_AO = 'debug_ao';

/**
* Shader used for debug rendering of emission.
*
* @type {string}
*/
export const SHADERPASS_EMISSION = 'debug_emission';

/**
* Shader used for debug rendering of lighting.
*
* @type {string}
*/
export const SHADERPASS_LIGHTING = 'debug_lighting';

/**
* Shader used for debug rendering of UV0 texture coordinates.
*
* @type {string}
*/
export const SHADERPASS_UV0 = 'debug_uv0';

/**
* This mode renders a sprite as a simple quad.
Expand Down
5 changes: 4 additions & 1 deletion src/scene/renderer/forward-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1100,12 +1100,15 @@ class ForwardRenderer extends Renderer {
// has flipY enabled
const flipFaces = !!(camera.camera._flipFaces ^ renderAction?.renderTarget?.flipY);

// shader pass - use setting from camera if available, otherwise use layer setting
const shaderPass = camera.camera.shaderPassInfo?.index ?? layer.shaderPass;

const draws = this._forwardDrawCalls;
this.renderForward(camera.camera,
visible.list,
visible.length,
layer._splitLights,
layer.shaderPass,
shaderPass,
layer.cullingMask,
layer.onDrawCall,
layer,
Expand Down
4 changes: 4 additions & 0 deletions src/scene/shader-lib/chunks/chunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import cookiePS from './lit/frag/cookie.js';
import cubeMapProjectBoxPS from './lit/frag/cubeMapProjectBox.js';
import cubeMapProjectNonePS from './lit/frag/cubeMapProjectNone.js';
import cubeMapRotatePS from './lit/frag/cubeMapRotate.js';
import debugOutputPS from './lit/frag/debug-output.js';
import debugProcessFrontendPS from './lit/frag/debug-process-frontend.js';
import decodePS from './common/frag/decode.js';
import detailModesPS from './standard/frag/detailModes.js';
import diffusePS from './standard/frag/diffuse.js';
Expand Down Expand Up @@ -233,6 +235,8 @@ const shaderChunks = {
cubeMapProjectBoxPS,
cubeMapProjectNonePS,
cubeMapRotatePS,
debugOutputPS,
debugProcessFrontendPS,
detailModesPS,
diffusePS,
diffuseDetailMapPS,
Expand Down
37 changes: 37 additions & 0 deletions src/scene/shader-lib/chunks/lit/frag/debug-output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
export default /* glsl */`
#ifdef DEBUG_ALBEDO_PASS
gl_FragColor = vec4(litShaderArgs.albedo , 1.0);
#endif
#ifdef DEBUG_UV0_PASS
gl_FragColor = vec4(litShaderArgs.albedo , 1.0);
#endif
#ifdef DEBUG_WORLD_NORMAL_PASS
gl_FragColor = vec4(litShaderArgs.worldNormal * 0.5 + 0.5, 1.0);
#endif
#ifdef DEBUG_OPACITY_PASS
gl_FragColor = vec4(vec3(litShaderArgs.opacity) , 1.0);
#endif
#ifdef DEBUG_SPECULARITY_PASS
gl_FragColor = vec4(litShaderArgs.specularity, 1.0);
#endif
#ifdef DEBUG_GLOSS_PASS
gl_FragColor = vec4(vec3(litShaderArgs.gloss) , 1.0);
#endif
#ifdef DEBUG_METALNESS_PASS
gl_FragColor = vec4(vec3(litShaderArgs.metalness) , 1.0);
#endif
#ifdef DEBUG_AO_PASS
gl_FragColor = vec4(vec3(litShaderArgs.ao) , 1.0);
#endif
#ifdef DEBUG_EMISSION_PASS
gl_FragColor = vec4(litShaderArgs.emission, 1.0);
#endif
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default /* glsl */`
#ifdef DEBUG_LIGHTING_PASS
litShaderArgs.albedo = vec3(0.5);
#endif
#ifdef DEBUG_UV0_PASS
litShaderArgs.albedo = vec3(vUv0, 0);
#endif
`;
6 changes: 3 additions & 3 deletions src/scene/shader-lib/programs/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ const basic = {
}

const shaderPassInfo = ShaderPass.get(device).getByIndex(options.pass);
const shaderPassDefine = shaderPassInfo.shaderDefine;
const shaderPassDefines = shaderPassInfo.shaderDefines;

// GENERATE VERTEX SHADER
let vshader = shaderPassDefine;
let vshader = shaderPassDefines;

// VERTEX SHADER DECLARATIONS
vshader += shaderChunks.transformDeclVS;
Expand Down Expand Up @@ -102,7 +102,7 @@ const basic = {
vshader += end();

// GENERATE FRAGMENT SHADER
let fshader = shaderPassDefine;
let fshader = shaderPassDefines;

// FRAGMENT SHADER DECLARATIONS
if (options.vertexColors) {
Expand Down
Loading

0 comments on commit a7aebb0

Please sign in to comment.