Skip to content

Commit

Permalink
Merge pull request #7149 from davepagurek/shader-hooks
Browse files Browse the repository at this point in the history
Shader hooks
  • Loading branch information
davepagurek authored Sep 17, 2024
2 parents 9a3dc91 + 6f24ac5 commit 5042087
Show file tree
Hide file tree
Showing 15 changed files with 338,799 additions and 112 deletions.
337,147 changes: 337,147 additions & 0 deletions docs/data.json

Large diffs are not rendered by default.

859 changes: 857 additions & 2 deletions src/webgl/material.js

Large diffs are not rendered by default.

165 changes: 151 additions & 14 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,15 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
return this._getImmediateLineShader();
}

materialShader() {
if (!this._pInst._glAttributes.perPixelLighting) {
throw new Error(
'The material shader does not support hooks without perPixelLighting. Try turning it back on.'
);
}
return this._getLightShader();
}

_getLightShader() {
if (!this._defaultLightShader) {
if (this._pInst._glAttributes.perPixelLighting) {
Expand All @@ -1806,7 +1815,34 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
this._webGL2CompatibilityPrefix('vert', 'highp') +
defaultShaders.phongVert,
this._webGL2CompatibilityPrefix('frag', 'highp') +
defaultShaders.phongFrag
defaultShaders.phongFrag,
{
vertex: {
'void beforeVertex': '() {}',
'vec3 getLocalPosition': '(vec3 position) { return position; }',
'vec3 getWorldPosition': '(vec3 position) { return position; }',
'vec3 getLocalNormal': '(vec3 normal) { return normal; }',
'vec3 getWorldNormal': '(vec3 normal) { return normal; }',
'vec2 getUV': '(vec2 uv) { return uv; }',
'vec4 getVertexColor': '(vec4 color) { return color; }',
'void afterVertex': '() {}'
},
fragment: {
'void beforeFragment': '() {}',
'Inputs getPixelInputs': '(Inputs inputs) { return inputs; }',
'vec4 combineColors': `(ColorComponents components) {
vec4 color = vec4(0.);
color.rgb += components.diffuse * components.baseColor;
color.rgb += components.ambient * components.ambientColor;
color.rgb += components.specular * components.specularColor;
color.rgb += components.emissive;
color.a = components.opacity;
return color;
}`,
'vec4 getFinalColor': '(vec4 color) { return color; }',
'void afterFragment': '() {}'
}
}
);
} else {
this._defaultLightShader = new p5.Shader(
Expand Down Expand Up @@ -1836,55 +1872,163 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
return this._defaultImmediateModeShader;
}

normalShader() {
return this._getNormalShader();
}

_getNormalShader() {
if (!this._defaultNormalShader) {
this._defaultNormalShader = new p5.Shader(
this,
this._webGL2CompatibilityPrefix('vert', 'mediump') +
defaultShaders.normalVert,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
defaultShaders.normalFrag
defaultShaders.normalFrag,
{
vertex: {
'void beforeVertex': '() {}',
'vec3 getLocalPosition': '(vec3 position) { return position; }',
'vec3 getWorldPosition': '(vec3 position) { return position; }',
'vec3 getLocalNormal': '(vec3 normal) { return normal; }',
'vec3 getWorldNormal': '(vec3 normal) { return normal; }',
'vec2 getUV': '(vec2 uv) { return uv; }',
'vec4 getVertexColor': '(vec4 color) { return color; }',
'void afterVertex': '() {}'
},
fragment: {
'void beforeFragment': '() {}',
'vec4 getFinalColor': '(vec4 color) { return color; }',
'void afterFragment': '() {}'
}
}
);
}

return this._defaultNormalShader;
}

colorShader() {
return this._getColorShader();
}

_getColorShader() {
if (!this._defaultColorShader) {
this._defaultColorShader = new p5.Shader(
this,
this._webGL2CompatibilityPrefix('vert', 'mediump') +
defaultShaders.normalVert,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
defaultShaders.basicFrag
defaultShaders.basicFrag,
{
vertex: {
'void beforeVertex': '() {}',
'vec3 getLocalPosition': '(vec3 position) { return position; }',
'vec3 getWorldPosition': '(vec3 position) { return position; }',
'vec3 getLocalNormal': '(vec3 normal) { return normal; }',
'vec3 getWorldNormal': '(vec3 normal) { return normal; }',
'vec2 getUV': '(vec2 uv) { return uv; }',
'vec4 getVertexColor': '(vec4 color) { return color; }',
'void afterVertex': '() {}'
},
fragment: {
'void beforeFragment': '() {}',
'vec4 getFinalColor': '(vec4 color) { return color; }',
'void afterFragment': '() {}'
}
}
);
}

return this._defaultColorShader;
}

/**
* TODO(dave): un-private this when there is a way to actually override the
* shader used for points
*
* Get the shader used when drawing points with <a href="#/p5/point">`point()`</a>.
*
* You can call <a href="#/p5.Shader/modify">`pointShader().modify()`</a>
* and change any of the following hooks:
* - `void beforeVertex`: Called at the start of the vertex shader.
* - `vec3 getLocalPosition`: Update the position of vertices before transforms are applied. It takes in `vec3 position` and must return a modified version.
* - `vec3 getWorldPosition`: Update the position of vertices after transforms are applied. It takes in `vec3 position` and pust return a modified version.
* - `float getPointSize`: Update the size of the point. It takes in `float size` and must return a modified version.
* - `void afterVertex`: Called at the end of the vertex shader.
* - `void beforeFragment`: Called at the start of the fragment shader.
* - `bool shouldDiscard`: Points are drawn inside a square, with the corners discarded in the fragment shader to create a circle. Use this to change this logic. It takes in a `bool willDiscard` and must return a modified version.
* - `vec4 getFinalColor`: Update the final color after mixing. It takes in a `vec4 color` and must return a modified version.
* - `void afterFragment`: Called at the end of the fragment shader.
*
* Call `pointShader().inspectHooks()` to see all the possible hooks and
* their default implementations.
*
* @returns {p5.Shader} The `point()` shader
* @private()
*/
pointShader() {
return this._getPointShader();
}

_getPointShader() {
if (!this._defaultPointShader) {
this._defaultPointShader = new p5.Shader(
this,
this._webGL2CompatibilityPrefix('vert', 'mediump') +
defaultShaders.pointVert,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
defaultShaders.pointFrag
defaultShaders.pointFrag,
{
vertex: {
'void beforeVertex': '() {}',
'vec3 getLocalPosition': '(vec3 position) { return position; }',
'vec3 getWorldPosition': '(vec3 position) { return position; }',
'float getPointSize': '(float size) { return size; }',
'void afterVertex': '() {}'
},
fragment: {
'void beforeFragment': '() {}',
'vec4 getFinalColor': '(vec4 color) { return color; }',
'bool shouldDiscard': '(bool outside) { return outside; }',
'void afterFragment': '() {}'
}
}
);
}
return this._defaultPointShader;
}

strokeShader() {
return this._getLineShader();
}

_getLineShader() {
if (!this._defaultLineShader) {
this._defaultLineShader = new p5.Shader(
this,
this._webGL2CompatibilityPrefix('vert', 'mediump') +
defaultShaders.lineVert,
this._webGL2CompatibilityPrefix('frag', 'mediump') +
defaultShaders.lineFrag
defaultShaders.lineFrag,
{
vertex: {
'void beforeVertex': '() {}',
'vec3 getLocalPosition': '(vec3 position) { return position; }',
'vec3 getWorldPosition': '(vec3 position) { return position; }',
'float getStrokeWeight': '(float weight) { return weight; }',
'vec2 getLineCenter': '(vec2 center) { return center; }',
'vec2 getLinePosition': '(vec2 position) { return position; }',
'vec4 getVertexColor': '(vec4 color) { return color; }',
'void afterVertex': '() {}'
},
fragment: {
'void beforeFragment': '() {}',
'Inputs getPixelInputs': '(Inputs inputs) { return inputs; }',
'vec4 getFinalColor': '(vec4 color) { return color; }',
'bool shouldDiscard': '(bool outside) { return outside; }',
'void afterFragment': '() {}'
}
}
);
}

Expand Down Expand Up @@ -2102,7 +2246,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
fillShader.setUniform('uSpecular', this._useSpecularMaterial);
fillShader.setUniform('uEmissive', this._useEmissiveMaterial);
fillShader.setUniform('uShininess', this._useShininess);
fillShader.setUniform('metallic', this._useMetalness);
fillShader.setUniform('uMetallic', this._useMetalness);

this._setImageLightUniforms(fillShader);

Expand Down Expand Up @@ -2175,14 +2319,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
let diffusedLight = this.getDiffusedTexture(this.activeImageLight);
shader.setUniform('environmentMapDiffused', diffusedLight);
let specularLight = this.getSpecularTexture(this.activeImageLight);
// In p5js the range of shininess is >= 1,
// Therefore roughness range will be ([0,1]*8)*20 or [0, 160]
// The factor of 8 is because currently the getSpecularTexture
// only calculated 8 different levels of roughness
// The factor of 20 is just to spread up this range so that,
// [1, max] of shininess is converted to [0,160] of roughness
let roughness = 20 / this._useShininess;
shader.setUniform('levelOfDetail', roughness * 8);

shader.setUniform('environmentMapSpecular', specularLight);
}
}
Expand Down
Loading

0 comments on commit 5042087

Please sign in to comment.