-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
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
Adding metallic feature in p5.js for both IBL and non-IBL codes. #6618
Changes from 5 commits
249ba2b
98d6248
0c6b97b
75dfefa
6c50096
383f7a4
deaf539
665783e
5c8b04a
1a65ffa
4d1982f
b2c3c71
bbf5868
324f18e
ddb69cd
5bd10ce
c09b677
c8d3011
b06b016
fba2818
dc5c4f2
0ad6575
1e1f91d
a2f1342
846d542
0d4b700
cc8dce4
c001e10
dbe3363
c27b9de
a9f5a3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1175,6 +1175,76 @@ p5.prototype.shininess = function (shine) { | |
return this; | ||
}; | ||
|
||
/** | ||
* Sets the metalness property of a material used in 3D rendering. | ||
* | ||
* The metalness property controls the degree to which the material | ||
* appears metallic. A higher metalness value makes the material more | ||
* metallic, while a lower value makes it appear less metallic. | ||
* | ||
* The default and minimum value is 0, indicating a non-metallic appearance, | ||
* while a maximum value of 100 represents a fully metallic appearance. | ||
* | ||
* @method metalness | ||
* @param {Number} metallic - The degree of metalness (ranging from 0 to 100). | ||
* @example | ||
* <div class="notest"> | ||
* <code> | ||
* let img; | ||
* let slider; | ||
* function preload() { | ||
* img = loadImage('assets/outdoor_spheremap.jpg'); | ||
* } | ||
* function setup() { | ||
* createCanvas(100, 100, WEBGL); | ||
* slider = createSlider(0, 400, 100, 1); | ||
* slider.position(0, height); | ||
* } | ||
* function draw() { | ||
* background(220); | ||
* imageMode(CENTER); | ||
* push(); | ||
* translate(0, 0, -200); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now have a |
||
* scale(2); | ||
* image(img, 0, 0, width, height); | ||
* pop(); | ||
* imageLight(img); | ||
* specularMaterial('gray'); | ||
* shininess(slider.value()); | ||
* metalness(100); | ||
* noStroke(); | ||
* scale(2); | ||
* sphere(15); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of scaling before this, could we just do |
||
* } | ||
* </code> | ||
* </div> | ||
* @example | ||
* <div> | ||
* <code> | ||
* function setup() { | ||
* createCanvas(100, 100, WEBGL); | ||
* } | ||
* function draw() { | ||
* noStroke(); | ||
* background('black'); | ||
* fill(255, 215, 0); | ||
* pointLight(255, 255, 255, 200, 150, 8000); | ||
* pointLight(255, 255, 255, 5000, 5000, 75); | ||
* specularMaterial('gray'); | ||
* shininess(2); | ||
* metalness(100); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great for one of the examples to have a slider that controls metalness so people can see the effect it has. Maybe this one would be good for that? A quick update to this that I was trying out: let slider
function setup() {
createCanvas(100, 100, WEBGL);
slider = createSlider(0, 200, 100);
}
function draw() {
noStroke();
background(100);
fill(255, 215, 0);
pointLight(255, 255, 255, 5000, 5000, 75);
specularMaterial('gray');
ambientLight(100);
shininess(2);
metalness(slider.value());
sphere(45);
} The things I was hoping to improve upon were:
|
||
* sphere(45); | ||
* } | ||
* </code> | ||
* </div> | ||
*/ | ||
|
||
p5.prototype.metalness = function (metallic) { | ||
this._assert3d('metalness'); | ||
this._renderer._useMetalness = metallic; | ||
return this; | ||
}; | ||
|
||
/** | ||
* @private blends colors according to color components. | ||
* If alpha value is less than 1, or non-standard blendMode | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -509,6 +509,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { | |
this._useEmissiveMaterial = false; | ||
this._useNormalMaterial = false; | ||
this._useShininess = 1; | ||
this._useMetalness = 0; | ||
|
||
this._useLineColor = false; | ||
this._useVertexColor = false; | ||
|
@@ -1613,6 +1614,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { | |
properties._useSpecularMaterial = this._useSpecularMaterial; | ||
properties._useEmissiveMaterial = this._useEmissiveMaterial; | ||
properties._useShininess = this._useShininess; | ||
properties._useMetalness = this._useMetalness; | ||
|
||
properties.constantAttenuation = this.constantAttenuation; | ||
properties.linearAttenuation = this.linearAttenuation; | ||
|
@@ -2039,6 +2041,17 @@ p5.RendererGL = class RendererGL extends p5.Renderer { | |
_setFillUniforms(fillShader) { | ||
fillShader.bindShader(); | ||
|
||
if (this._useMetalness > 0 && !this.curFillColor.every((value, index) => | ||
value === 1)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mind explaining a bit what the scenario is that we're checking for here with the |
||
const metalnessFactor = Math.min(this._useMetalness / 100, 0.6); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you have some visual examples that lead you to arrive at 0.6 as the max metalness and 0.4 as the min non-metalness instead of letting them both go from 0 to 1? |
||
const nonMetalnessFactor = Math.max(1 - metalnessFactor, 0.4); | ||
|
||
this.curSpecularColor = this.curSpecularColor.map( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By modifying its current value, I think that means the color will be slightly different for every successive shape we render. Instead maybe we can do something like |
||
(specularColor, index) => | ||
this.curFillColor[index] * metalnessFactor + | ||
specularColor * nonMetalnessFactor | ||
); | ||
} | ||
// TODO: optimize | ||
fillShader.setUniform('uUseVertexColor', this._useVertexColor); | ||
fillShader.setUniform('uMaterialColor', this.curFillColor); | ||
|
@@ -2055,6 +2068,7 @@ p5.RendererGL = class RendererGL extends p5.Renderer { | |
fillShader.setUniform('uSpecular', this._useSpecularMaterial); | ||
fillShader.setUniform('uEmissive', this._useEmissiveMaterial); | ||
fillShader.setUniform('uShininess', this._useShininess); | ||
fillShader.setUniform('metallic', this._useMetalness); | ||
|
||
this._setImageLightUniforms(fillShader); | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -30,6 +30,7 @@ uniform vec3 uSpotLightDirection[5]; | |||||
|
||||||
uniform bool uSpecular; | ||||||
uniform float uShininess; | ||||||
uniform float metallic; | ||||||
|
||||||
uniform float uConstantAttenuation; | ||||||
uniform float uLinearAttenuation; | ||||||
|
@@ -73,9 +74,14 @@ LightResult _light(vec3 viewDirection, vec3 normal, vec3 lightVector) { | |||||
|
||||||
//compute our diffuse & specular terms | ||||||
LightResult lr; | ||||||
if (uSpecular) | ||||||
lr.specular = _phongSpecular(lightDir, viewDirection, normal, uShininess); | ||||||
lr.diffuse = _lambertDiffuse(lightDir, normal); | ||||||
|
||||||
float invertValue = 1.0 - (metallic / 100.0); | ||||||
float specularIntensity = mix(0.4, 1.0, invertValue); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is equivalent to this:
Suggested change
Might be a bit easier to reason about without adding the extra in-between variable with the flipped value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, it looks like these changes have a different indentation level compared to the surroinding code, we should probably make it match |
||||||
float diffuseIntensity = mix(0.1, 1.0, invertValue); | ||||||
|
||||||
if (uSpecular) | ||||||
lr.specular = (_phongSpecular(lightDir, viewDirection, normal, uShininess)) * specularIntensity; | ||||||
lr.diffuse = _lambertDiffuse(lightDir, normal) * diffuseIntensity; | ||||||
return lr; | ||||||
} | ||||||
|
||||||
|
@@ -114,7 +120,8 @@ vec3 calculateImageDiffuse( vec3 vNormal, vec3 vViewPosition ){ | |||||
vec4 texture = TEXTURE( environmentMapDiffused, newTexCoor ); | ||||||
// this is to make the darker sections more dark | ||||||
// png and jpg usually flatten the brightness so it is to reverse that | ||||||
return smoothstep(vec3(0.0), vec3(0.8), texture.xyz); | ||||||
float invertedMetallic = 1.0 - metallic / 100.0; | ||||||
return mix(vec3(0.0), smoothstep(vec3(0.0), vec3(1.0), texture.xyz), invertedMetallic); | ||||||
} | ||||||
|
||||||
vec3 calculateImageSpecular( vec3 vNormal, vec3 vViewPosition ){ | ||||||
|
@@ -130,7 +137,9 @@ vec3 calculateImageSpecular( vec3 vNormal, vec3 vViewPosition ){ | |||||
#endif | ||||||
// this is to make the darker sections more dark | ||||||
// png and jpg usually flatten the brightness so it is to reverse that | ||||||
return pow(outColor.xyz, vec3(10.0)); | ||||||
float invertedMetallic = 1.0 - (metallic / 100.0); | ||||||
float mappedValue = mix(1.2, 10.0, invertedMetallic); | ||||||
return pow(outColor.xyz, vec3(mappedValue)); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I notice that as I bright the metalness slider up, in the middle, the shadows dip darker before they finally brighten: I wonder if this is because we change the specular/diffuse balance linearly, but the power exponentially (since we linearly interpolate the exponent.) Maybe we'll avoid this dip if, instead of linearly interpolating the exponent, we linearly mix between to fixed exponents, like this? return mix(
pow(outColor.xyz, vec3(1.2)),
pow(outColor.xyz, vec3(10)),
invertedMetallic
); |
||||||
} | ||||||
|
||||||
void totalLight( | ||||||
|
@@ -141,7 +150,6 @@ void totalLight( | |||||
) { | ||||||
|
||||||
totalSpecular = vec3(0.0); | ||||||
|
||||||
if (!uUseLighting) { | ||||||
totalDiffuse = vec3(1.0); | ||||||
return; | ||||||
|
@@ -164,7 +172,7 @@ void totalLight( | |||||
if (j < uPointLightCount) { | ||||||
vec3 lightPosition = (uViewMatrix * vec4(uPointLightLocation[j], 1.0)).xyz; | ||||||
vec3 lightVector = modelPosition - lightPosition; | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like some extra spaces got added here |
||||||
//calculate attenuation | ||||||
float lightDistance = length(lightVector); | ||||||
float lightFalloff = 1.0 / (uConstantAttenuation + lightDistance * uLinearAttenuation + (lightDistance * lightDistance) * uQuadraticAttenuation); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe for this slider (and in the next example if we add a slider) we can add a label next to it to say what parameter it's controlling?