Open JetStarBlues opened 3 years ago
I didn't design this and I find it a bit too subtle but there is a difference. ambientMaterial
sets the material to a specific shader. fill()
does not do this. Both set the curFillColor
so this is what makes their relationship a bit confusing. For example:
specularMaterial(0, 0, 255);
fill(255, 0, 0);
sphere(100);
results in a sphere with a red specular material applied. whereas
specularMaterial(0, 0, 255);
ambientMaterial(255, 0, 0);
sphere(100);
results in a sphere with a red ambient material applied.
Perhaps a slight redesign or a better articulation of the distinction in the docs is in order but this is my understanding of the difference currently.
Huh. Thank you for the insight.
So the current fill()
behaviour in WEBGL mode can be described as:
Sets the color of the current material.
Also used to set
text()
colorAlso used in
beginShape()
andendShape()
to set vertex color
---
Sidenote: is it correct to state that the default material of a sketch (when not explicitly created with a call to specularMaterial
etc.) is ambientMaterial
?
---
I'm not sure whether this behaviour of fill()
is consistent with the current approach of using materials - materialName(materialColor)
. The first snippet you shared looks rather confusing/akward. (I feel like using specularMaterial(255, 0, 0)
to change the color is more clear. And fill(color)
would only make sense if specularMaterial()
does not take a color as an argument).
The second one makes more sense, when you work with the assumption that only one material can be active at a time. The call to ambient consistently overrides the call to specular.
What do you think about adding this example (demonstrating WebGL behaviour) to the fill()
reference page?
Here is another example demonstrating its use for setting vertex colors when using beginShape/endShape.
The reference page for text()
already has a WebGL example. Maybe adding a fill()
call to it is good enough.
tagging @ShenpaiSharma as relevant to your GSoC project!
Here’s an update from @ShenpaiSharma’s 2022 GSoC project:
In the Processing implementation, the ambient, specular, and emissive each store the passed colors separately, which allows shaders to combine multiple material properties and colors. Example: https://openprocessing.org/sketch/1625624
Currently p5 stores one color only in the curFillColor property on the renderer. This same property is set by fill, ambientMaterial, specularMaterial, and emissiveMaterial. So currently p5 will use only the material properties and color most recently called. Any previously called material methods will be overridden. Example: https://editor.p5js.org/ShenpaiSharma/sketches/TLuatF8xu
https://github.com/ShenpaiSharma/p5.js/commit/680113fb76ffed9db98b4f4c06e30fb3656a4591 Here, we used a new renderer property called _useAmbientMaterial to check whether ambientMaterial has been called or not. If AmbientMaterial is being used we are using Ambient Color to fill the Material else we are using Specular Color to fill the Material. The existing _useSpecularMaterial property was being set to false every time ambientMaterial was called, but here that is commented out so that a material can show an ambientMaterial color with specular property.
Example:
function setup() {
createCanvas(600, 600, WEBGL);
noStroke();
}
function draw() {
background(0);
// translate(width/2, height/2);
// SpecularColor(255, 255, 255);
directionalLight(127, 127, 127, cos(frameCount * 0.1), 1, -1);
ambientLight(255, 255, 255);
// fill(255, 0, 0);
// emissive(255, 0, 0);
ambientMaterial(0, 0, 255);
specularMaterial(255, 0, 0);
shininess(100);
sphere(100);
}
In order to achieve similar results to Processing, it looks like we would need to add new properties to the renderer to store ambient, specular, and emissive colors and then modify every shader, passing in these colors as uniforms and combining them.
We are wondering:
I think it's ok to go ahead with adding more fields. I think we were trying to be sparse and not over-complicate things, but it sounds like we need more fields to implement this properly!
adding: reading over the comments, I think the thought was that ambient material may behave more like what people expect when they call fill()
in 2D mode.
I guess I'm also wondering if it's appropriate for specularMaterial()
to take any arguments, when specularColor()
and fill()
also exist? Would it be clearer that specularMaterial()
and ambientMaterial()
just change which shader is used if they took no arguments, but the ambient color is pulled from whatever the current value is for fill? And would this be more consistent for people who are new to programming and making the leap from p5's 2D to 3D mode?
(small edit just for clarity)
I vaguely remember coming to similar conclusions 2 years ago when we were working on webGL for GSOC. As I recall it was just going to be a larger project than we had the time to solve, so we punted on this work.
@kjhollen and @aferriss thanks for these insights!
@ShenpaiSharma and I had discussed a less ambitious approach, which sounds like what you're wondering about @kjhollen: to remove the parameters for the material methods to clarify:
This would be simpler to implement, as no new fields would need to be added, nor would the shaders need to be modified. This approach, however, still would not allow for material property and color combinations that are possible in Processing. Without storing colors separately, it looks like we'd still need use only one material at a time.
EDIT: I had initially written that perhaps a color could be passed in specularMaterial in place of using specularColor(), but I realized that specularColor is tied to lights, not materials.
Here's a Processing example in which emissive, ambient, and specular are combined with three different colors:
void setup() {
size(400, 400, P3D);
noStroke();
}
void draw() {
background(0);
translate(width/2, height/2);
lightSpecular(255, 255, 255);
directionalLight(64, 64, 64, cos(frameCount * 0.1), 1, -1);
ambientLight(64, 64, 64);
fill(0, 255, 0);
emissive(64, 64, 0);
ambient(128, 0, 0);
specular(0, 0, 128);
sphere(100);
}
Tagging @kjhollen
So finally, I and @calebfoss have decided to move away from the current overwriting of fill colors to the geometry, towards the approach that Processing uses where the material has different color properties (ambient, emissive, specular) that all contribute separately to the lighting of the surface.
So we have implemented a few new uniforms (uSpecularMatColor, uAmbientMatColor, uEmissiveMatColor) which store the RGB colors and fill the geometry with different color properties and thus contributing separately to the lighting of the surface.
Here are the p5.js examples showing the multiple material properties for the geometry:
function setup() {
createCanvas(600, 600, WEBGL);
noStroke();
}
function draw() {
background(0);
specularColor(255, 255, 255);
directionalLight(255, 255, 255, cos(frameCount * 0.1), 1, -1);
ambientLight(255, 255, 255);
emissiveMaterial(0, 0, 255);
ambientMaterial(0, 255, 0);
specularMaterial(255, 0, 0);
shininess(100);
sphere(100);
}
In comparison to processing:
void setup() {
size(400, 400, P3D);
noStroke();
}
void draw() {
background(0);
translate(width/2, height/2);
lightSpecular(255, 255, 255);
directionalLight(255, 255, 255, cos(frameCount * 0.1), 1, -1);
ambientLight(255, 255, 255);
emissive(0, 0, 255);
ambient(0, 255, 0);
specular(255, 0, 0);
shininess(100);
sphere(100);
}
How would this new feature help increase access
Most appropriate sub-area of p5.js?
Details:
I'm currently working on improving the material documentation.
The current documentation for
ambientMaterial()
does not mentionfill()
. I'm wondering if the two are equivalent since they have identical behaviour. See here for live demo.I.e. is
fill(255, 0, 0)
the same asambientMaterial(255, 0, 0)
?If they are equivalent, I would like to mention this in the documenation.
Link to
fill
(WEBGL) source code. Link toambientMaterial
source code.