Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shader hooks for dev-2.0 #7267

Merged
merged 26 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e18794f
Add shader hooks
davepagurek Apr 13, 2024
286fc2f
Remove per-pixel lighting
davepagurek Apr 20, 2024
7ab36a1
Start adding more hooks
davepagurek Apr 22, 2024
9904125
Add line shader hooks
davepagurek May 3, 2024
fb65c8f
Revert "Remove per-pixel lighting"
davepagurek May 3, 2024
7815679
Start documenting hooks
davepagurek May 21, 2024
f8cfce5
Add examples, fix bugs
davepagurek Jun 23, 2024
cad9d54
Add examples, update hooks format
davepagurek Jul 26, 2024
69fdeb9
Merge branch 'main' into shader-hooks
davepagurek Jul 26, 2024
4a77779
Merge branch 'main' into shader-hooks
davepagurek Jul 26, 2024
8ee8867
Remove accidentally committed file
davepagurek Jul 26, 2024
b9e89d6
Fix missing semicolon
davepagurek Jul 26, 2024
4744428
Write out struct members as bullets
davepagurek Jul 29, 2024
22addb5
Add more examples, document shader version
davepagurek Jul 29, 2024
420bac4
Make metalness be editable too
davepagurek Jul 30, 2024
d50d0cb
Add more line examples
davepagurek Jul 30, 2024
82a92f8
Add some unit tests
davepagurek Aug 26, 2024
9fe21e3
Test different reference layout
davepagurek Sep 7, 2024
db29371
Try it as a table
davepagurek Sep 7, 2024
5af4635
Use tables for all hooks
davepagurek Sep 7, 2024
003f3cc
Update src/webgl/p5.Shader.js
davepagurek Sep 13, 2024
51f1b36
Add ability to add other functions and uniforms
davepagurek Sep 14, 2024
6f24ac5
Add beta tag
davepagurek Sep 14, 2024
e3776bd
Merge branch 'dev-2.0' into shader-hooks-2.0
davepagurek Sep 14, 2024
d6b5db5
Update test functions
davepagurek Sep 14, 2024
ff9eebc
Remove data.json
davepagurek Sep 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
859 changes: 857 additions & 2 deletions src/webgl/material.js

Large diffs are not rendered by default.

167 changes: 152 additions & 15 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1714,7 +1714,7 @@ p5.RendererGL = class RendererGL extends Renderer {
sphereMapping
);
}
this.uNMatrix.inverseTranspose(this.uMVMatrix);
this.uNMatrix.inverseTranspose(this.uViewMatrix);
this.uNMatrix.invert3x3(this.uNMatrix);
this.sphereMapping.setUniform('uFovY', this._curCamera.cameraFOV);
this.sphereMapping.setUniform('uAspect', this._curCamera.aspectRatio);
Expand Down Expand Up @@ -1785,6 +1785,15 @@ p5.RendererGL = class RendererGL extends 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 @@ -1793,7 +1802,34 @@ p5.RendererGL = class RendererGL extends 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 @@ -1823,55 +1859,163 @@ p5.RendererGL = class RendererGL extends 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 @@ -2088,7 +2232,7 @@ p5.RendererGL = class RendererGL extends 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 @@ -2161,14 +2305,7 @@ p5.RendererGL = class RendererGL extends 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
Loading