Description
Increasing Access
Currently, the way ambientColor is applied in phongFrag is simply adding up the calculated color values.
This is a layer application method called addition in illustration.
In illustration, however, there are many ways to apply color other than additive.
The four most commonly used are addition, multiplication, screen, and overlay. Otherwise, soft light and hard light may be used.
So I wanted to be able to apply it. This issue suggests it.
In the current situation where the only way to apply ambientColor is simply adding up, for example if the directionalLight is pure white, it will always be pure white if the ambientLight is pure white.
However, for example, if the application method is an overlay, the light and dark will be sharp when it is pure white.
Overlay is useful because the effect of approximating the applied color is easier to understand than additive.
The following image shows ambientColor applied to a gray torus using ADD and OVERLAY methods, respectively, using a feature to be implemented.
condition:
directionalLight(255, 255, 255, 0, 0, -1);
ambientLight(255);
ambientMaterial("white");
specularMaterial(64);
shininess(40);
fill(128);
The more options you have, the more expressions you can express, so I thought it would be meaningful to implement it.
Most appropriate sub-area of p5.js?
- Accessibility
- Color
- Core/Environment/Rendering
- Data
- DOM
- Events
- Image
- IO
- Math
- Typography
- Utilities
- WebGL
- Build Process
- Unit Testing
- Internalization
- Friendly Errors
- Other (specify if possible)
Feature request details
I will explain how to implement it.
First, let Renderer.GL have an integer property called ambientMode. This is used as a flag in phong.frag.
this.ambientMode = 0; // default is 0 : ADD
The correspondence with blendMode is 0: ADD, 1: SCREEN, 2: MULTIPLY, 3: OVERLAY, 4: SOFT_LIGHT, 5: HARD_LIGHT.
Then have a function called ambientMode() (kind of like textureMode()). Change the value of ambientMode by substituting the above blending constant into the argument of this. For example, if you want the application method to be OVERLAY, use ambientMode(OVERLAY).
In this function, we convert the blending constant to an integer case by case. Here is an overview of the functions:
p5.prototype.ambientMode = function (mode) {
this._assert3d('ambientMode');
p5._validateParameters('ambientMode', arguments);
if (mode === ADD){ this._renderer.ambientMode = 0; }
else if (mode === SCREEN){ this._renderer.ambientMode = 1; }
else if (mode === MULTIPLY){ this._renderer.ambientMode = 2; }
else if (mode === OVERLAY){ this._renderer.ambientMode = 3; }
else if (mode === SOFT_LIGHT){ this._renderer.ambientMode = 4; }
else if (mode === HARD_LIGHT){ this._renderer.ambientMode = 5; }
else {
console.warn('There are only 6 blendModes that can be used to apply ambientColor: ADD, SCREEN, MULTIPLY, OVERLAY, SOFT_LIGHT, HARD_LIGHT.');
}
return this;
};
Then pass this value to uAmbientColor in _setFillUniforms().
fillShader.setUniform('uAmbientMode', this.ambientMode);
Receive this in phong.frag and use it in the ambientColor application part. Rewrite it like this:
before:
gl_FragColor = vec4(diffuse * baseColor.rgb +
vAmbientColor * uAmbientMatColor.rgb +
specular * uSpecularMatColor.rgb +
uEmissiveMatColor.rgb, baseColor.a);
after:
diffuse *= baseColor.rgb;
diffuse += specular * uSpecularMatColor.rgb;
vec3 ambient = vAmbientColor * uAmbientMatColor.rgb;
if (uAmbientMode == 0) { diffuse += ambient; } // ADD.
if (uAmbientMode == 1) { diffuse = diffuse + ambient - diffuse * ambient; } // SCREEN.
if (uAmbientMode == 2) { diffuse *= ambient; } // MULTIPLY.
if (uAmbientMode == 3) { diffuse = overlay(ambient, diffuse); } // OVERLAY.
if (uAmbientMode == 4) { diffuse = softLight(ambient, diffuse); } // SOFT_LIGHT.
if (uAmbientMode == 5) { diffuse = overlay(diffuse, ambient); } // HARD_LIGHT.
diffuse += uEmissiveMatColor.rgb;
gl_FragColor = vec4(diffuse, baseColor.a);
overlay and softLight are calculated by creating separate functions.
// overlay.
vec3 overlay(in vec3 src, in vec3 dst){
vec3 result;
if(dst.r < 0.5){ result.r = 2.0*src.r*dst.r; }else{ result.r = 2.0*(src.r+dst.r-src.r*dst.r)-1.0; }
if(dst.g < 0.5){ result.g = 2.0*src.g*dst.g; }else{ result.g = 2.0*(src.g+dst.g-src.g*dst.g)-1.0; }
if(dst.b < 0.5){ result.b = 2.0*src.b*dst.b; }else{ result.b = 2.0*(src.b+dst.b-src.b*dst.b)-1.0; }
return result;
}
// softLight.
vec3 softLight(in vec3 src, in vec3 dst){
vec3 result;
if(src.r < 0.5){ result.r = 2.0*src.r*dst.r + dst.r*dst.r*(1.0-2.0*src.r); }
else{ result.r = 2.0*dst.r*(1.0-src.r) + sqrt(dst.r)*(2.0*src.r-1.0); }
if(src.g < 0.5){ result.g = 2.0*src.g*dst.g + dst.g*dst.g*(1.0-2.0*src.g); }
else{ result.g = 2.0*dst.g*(1.0-src.g) + sqrt(dst.g)*(2.0*src.g-1.0); }
if(src.b < 0.5){ result.b = 2.0*src.b*dst.b + dst.b*dst.b*(1.0-2.0*src.b); }
else{ result.b = 2.0*dst.b*(1.0-src.b) + sqrt(dst.b)*(2.0*src.b-1.0); }
return result;
}
That's all there is to the implementation.
Let me explain another motivation. Watch the video below.
noise&rotate
2023-05-01.04-06-02.mp4
As you can see, the result of applying lights() is much lighter in 1.6.0 than in 1.4.2. This is due to a change in how lighting is applied in 1.5.0.
This is not 128, 128 like lights(), but 255, 255 are set to MAX, and the result of applying with OVERLAY is shown here.
2023-05-01.04-12-56.mp4
In this way, the light and dark became clear and different appearance.
Appearance is a matter of individual subjectivity, so there is no correct answer. So this specification change does not change the default at all. AmbientMode defaults to ADD, so if you don't do anything, it will remain the same and backward compatibility will be maintained. What I want to do is increase the options in drawing.
I would like to add a simple benchmark test etc. later.