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

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

Open
1 task done
JetStarBlues opened this issue Jun 13, 2021 · 12 comments
Open
1 task done

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

JetStarBlues opened this issue Jun 13, 2021 · 12 comments

Comments

@JetStarBlues
Copy link
Contributor

How would this new feature help increase access

  • Document behaviour

Most appropriate sub-area of p5.js?

  • Other (documentation)

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
Copy link
Contributor

stalgiag commented Jun 14, 2021

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
Copy link
Contributor Author

JetStarBlues commented Jun 14, 2021

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
Copy link
Contributor Author

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

@JetStarBlues
Copy link
Contributor Author

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
Copy link
Member

kjhollen commented Jul 5, 2022

tagging @ShenpaiSharma as relevant to your GSoC project!

@calebfoss
Copy link
Contributor

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

ShenpaiSharma@680113f
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:

  • Why do the material methods currently switch off the other material boolean properties?
  • Are there any reasons we might not be aware of for storing only one color for webGL materials?
  • Are there any conflicts that may come up when making the described changes?

@kjhollen
Copy link
Member

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
Copy link
Member

kjhollen commented Aug 11, 2022

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
Copy link
Contributor

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
Copy link
Contributor

calebfoss commented Aug 11, 2022

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

  • fill() changes the color of materials
  • ambientMaterial(), specularMaterial(), emissiveMaterial() changes how materials reacts to light (no arguments)
  • specularColor() changes the color of the highlight of lights when they hit a specular material

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
Copy link
Contributor

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
Copy link
Contributor

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: System Level Changes
Development

No branches or pull requests

6 participants