processing / p5.js

p5.js is a client-side JS platform that empowers artists, designers, students, and anyone to learn to code and express themselves creatively on the web. It is based on the core principles of Processing. http://twitter.com/p5xjs —
http://p5js.org/
GNU Lesser General Public License v2.1
21.66k stars 3.33k forks source link

WEBGL - Is ambientMaterial() the same as fill()? #5308

Open JetStarBlues opened 3 years ago

JetStarBlues commented 3 years ago

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 mention fill(). 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 as ambientMaterial(255, 0, 0)?

If they are equivalent, I would like to mention this in the documenation.

Link to fill (WEBGL) source code. Link to ambientMaterial source code.

stalgiag commented 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.

JetStarBlues commented 3 years ago

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() color

Also used in beginShape() and endShape() 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.

JetStarBlues commented 3 years ago

What do you think about adding this example (demonstrating WebGL behaviour) to the fill() reference page?

JetStarBlues commented 3 years ago

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.

kjhollen commented 2 years ago

tagging @ShenpaiSharma as relevant to your GSoC project!

calebfoss commented 2 years ago

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: image

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:

kjhollen commented 2 years ago

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!

kjhollen commented 2 years ago

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)

aferriss commented 2 years ago

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.

calebfoss commented 2 years ago

@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.

calebfoss commented 2 years ago

Here's a Processing example in which emissive, ambient, and specular are combined with three different colors: image

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);
}
ShenpaiSharma commented 2 years ago

Feature Implemented -- Support for Multiple materials for geometry

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: image

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: image

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);
}